switch from aiohttp to httpx

This commit is contained in:
2024-10-24 23:18:23 +08:00
parent 2ca7f8e908
commit e2e4083321
8 changed files with 126 additions and 125 deletions

View File

@@ -9,7 +9,7 @@ RUN adduser -D luser
USER luser
WORKDIR /home/luser
COPY --chown=luser:users ./requirements-dev.txt ./requirements-dev.txt
COPY --chown=luser:users ./requirements-dev.txt ./requirements-run.txt
COPY --chown=luser:users ./requirements-run.txt ./requirements-run.txt
WORKDIR /home/luser/
RUN python -m venv .venv
RUN --mount=type=cache,target=/home/luser/.cache/pip,uid=1000,gid=1000 .venv/bin/pip wheel -w /home/luser/wheel -r requirements-dev.txt pygraphviz

View File

@@ -29,7 +29,7 @@ dependencies = [
"PyYAML",
"pygraphviz",
"aiofiles",
"aiohttp[speedups]"
"httpx[http2]"
]
[project.optional-dependencies]

View File

@@ -2,38 +2,27 @@
# This file is autogenerated by pip-compile with Python 3.10
# by the following command:
#
# pip-compile --extra-index-url=https://gitea.woggioni.net/api/packages/woggioni/pypi/simple --extra=dev --output-file=requirements-dev.txt --strip-extras pyproject.toml
# pip-compile --extra-index-url=https://gitea.woggioni.net/api/packages/woggioni/pypi/simple --extra=dev --output-file=requirements-dev.txt pyproject.toml
#
--extra-index-url https://gitea.woggioni.net/api/packages/woggioni/pypi/simple
aiodns==3.2.0
# via aiohttp
aiofiles==24.1.0
# via bugis (pyproject.toml)
aiohappyeyeballs==2.4.3
# via aiohttp
aiohttp==3.10.10
# via bugis (pyproject.toml)
aiosignal==1.3.1
# via aiohttp
anyio==4.6.2.post1
# via httpx
asttokens==2.4.1
# via stack-data
async-timeout==4.0.3
# via aiohttp
attrs==24.2.0
# via aiohttp
backports-tarfile==1.2.0
# via jaraco-context
brotli==1.1.0
# via aiohttp
build==1.2.2.post1
# via bugis (pyproject.toml)
certifi==2024.8.30
# via requests
cffi==1.17.1
# via
# cryptography
# pycares
# httpcore
# httpx
# requests
cffi==1.17.1
# via cryptography
charset-normalizer==3.4.0
# via requests
click==8.1.7
@@ -47,19 +36,30 @@ decorator==5.1.1
docutils==0.21.2
# via readme-renderer
exceptiongroup==1.2.2
# via ipython
# via
# anyio
# ipython
executing==2.1.0
# via stack-data
frozenlist==1.4.1
# via
# aiohttp
# aiosignal
granian==1.6.1
# via bugis (pyproject.toml)
h11==0.14.0
# via httpcore
h2==4.1.0
# via httpx
hpack==4.0.0
# via h2
httpcore==1.0.6
# via httpx
httpx[http2]==0.27.2
# via bugis (pyproject.toml)
hyperframe==6.0.1
# via h2
idna==3.10
# via
# anyio
# httpx
# requests
# yarl
importlib-metadata==8.5.0
# via
# keyring
@@ -94,10 +94,6 @@ more-itertools==10.5.0
# via
# jaraco-classes
# jaraco-functools
multidict==6.1.0
# via
# aiohttp
# yarl
mypy==1.12.1
# via bugis (pyproject.toml)
mypy-extensions==1.0.0
@@ -114,16 +110,12 @@ pkginfo==1.10.0
# via twine
prompt-toolkit==3.0.48
# via ipython
propcache==0.2.0
# via yarl
ptyprocess==0.7.0
# via pexpect
pure-eval==0.2.3
# via stack-data
pwo==0.0.3
# via bugis (pyproject.toml)
pycares==4.4.0
# via aiodns
pycparser==2.22
# via cffi
pygments==2.18.0
@@ -154,6 +146,10 @@ secretstorage==3.3.3
# via keyring
six==1.16.0
# via asttokens
sniffio==1.3.1
# via
# anyio
# httpx
stack-data==0.6.3
# via ipython
tomli==2.0.2
@@ -169,8 +165,8 @@ twine==5.1.1
# via bugis (pyproject.toml)
typing-extensions==4.7.1
# via
# anyio
# ipython
# multidict
# mypy
# pwo
# rich
@@ -184,7 +180,5 @@ watchdog==5.0.3
# via bugis (pyproject.toml)
wcwidth==0.2.13
# via prompt-toolkit
yarl==1.16.0
# via aiohttp
zipp==3.20.2
# via importlib-metadata

View File

@@ -2,65 +2,59 @@
# This file is autogenerated by pip-compile with Python 3.10
# by the following command:
#
# pip-compile --extra-index-url=https://gitea.woggioni.net/api/packages/woggioni/pypi/simple --extra=run --output-file=requirements-run.txt --strip-extras pyproject.toml
# pip-compile --extra-index-url=https://gitea.woggioni.net/api/packages/woggioni/pypi/simple --extra=run --output-file=requirements-run.txt pyproject.toml
#
--extra-index-url https://gitea.woggioni.net/api/packages/woggioni/pypi/simple
aiodns==3.2.0
# via aiohttp
aiofiles==24.1.0
# via bugis (pyproject.toml)
aiohappyeyeballs==2.4.3
# via aiohttp
aiohttp==3.10.10
# via bugis (pyproject.toml)
aiosignal==1.3.1
# via aiohttp
async-timeout==4.0.3
# via aiohttp
attrs==24.2.0
# via aiohttp
brotli==1.1.0
# via aiohttp
cffi==1.17.1
# via pycares
anyio==4.6.2.post1
# via httpx
certifi==2024.8.30
# via
# httpcore
# httpx
click==8.1.7
# via granian
frozenlist==1.4.1
# via
# aiohttp
# aiosignal
exceptiongroup==1.2.2
# via anyio
granian==1.6.1
# via bugis (pyproject.toml)
h11==0.14.0
# via httpcore
h2==4.1.0
# via httpx
hpack==4.0.0
# via h2
httpcore==1.0.6
# via httpx
httpx[http2]==0.27.2
# via bugis (pyproject.toml)
hyperframe==6.0.1
# via h2
idna==3.10
# via yarl
# via
# anyio
# httpx
markdown==3.7
# via bugis (pyproject.toml)
multidict==6.1.0
# via
# aiohttp
# yarl
propcache==0.2.0
# via yarl
pwo==0.0.3
# via bugis (pyproject.toml)
pycares==4.4.0
# via aiodns
pycparser==2.22
# via cffi
pygments==2.18.0
# via bugis (pyproject.toml)
pygraphviz==1.14
# via bugis (pyproject.toml)
pyyaml==6.0.2
# via bugis (pyproject.toml)
sniffio==1.3.1
# via
# anyio
# httpx
typing-extensions==4.7.1
# via
# multidict
# anyio
# pwo
uvloop==0.21.0
# via granian
watchdog==5.0.3
# via bugis (pyproject.toml)
yarl==1.16.0
# via aiohttp

View File

@@ -6,55 +6,49 @@
#
--extra-index-url https://gitea.woggioni.net/api/packages/woggioni/pypi/simple
aiodns==3.2.0
# via aiohttp
aiofiles==24.1.0
# via bugis (pyproject.toml)
aiohappyeyeballs==2.4.3
# via aiohttp
aiohttp==3.10.10
# via bugis (pyproject.toml)
aiosignal==1.3.1
# via aiohttp
async-timeout==4.0.3
# via aiohttp
attrs==24.2.0
# via aiohttp
brotli==1.1.0
# via aiohttp
cffi==1.17.1
# via pycares
frozenlist==1.4.1
anyio==4.6.2.post1
# via httpx
certifi==2024.8.30
# via
# aiohttp
# aiosignal
# httpcore
# httpx
exceptiongroup==1.2.2
# via anyio
h11==0.14.0
# via httpcore
h2==4.1.0
# via httpx
hpack==4.0.0
# via h2
httpcore==1.0.6
# via httpx
httpx==0.27.2
# via bugis (pyproject.toml)
hyperframe==6.0.1
# via h2
idna==3.10
# via yarl
# via
# anyio
# httpx
markdown==3.7
# via bugis (pyproject.toml)
multidict==6.1.0
# via
# aiohttp
# yarl
propcache==0.2.0
# via yarl
pwo==0.0.3
# via bugis (pyproject.toml)
pycares==4.4.0
# via aiodns
pycparser==2.22
# via cffi
pygments==2.18.0
# via bugis (pyproject.toml)
pygraphviz==1.14
# via bugis (pyproject.toml)
pyyaml==6.0.2
# via bugis (pyproject.toml)
sniffio==1.3.1
# via
# anyio
# httpx
typing-extensions==4.7.1
# via
# multidict
# anyio
# pwo
watchdog==5.0.3
# via bugis (pyproject.toml)
yarl==1.16.0
# via aiohttp

View File

@@ -14,14 +14,14 @@ from pwo import Maybe
from .server import Server
from asyncio import get_running_loop
from .asgi_utils import decode_headers
from typing import Optional
from typing import Optional, Awaitable, Callable, Any, Mapping
log = logging.getLogger('access')
log.propagate = False
_server : Optional[Server] = None
async def application(scope, receive, send):
async def application(scope, receive, send : Callable[[Mapping[str, Any]], Awaitable[None]]):
global _server
if scope['type'] == 'lifespan':
while True:

View File

@@ -1,17 +1,19 @@
from typing import TYPE_CHECKING
from aiofiles import open as async_open
from aiohttp import ClientSession
from .configuration import Configuration
from yarl import URL
if TYPE_CHECKING:
from _typeshed import StrOrBytesPath
from httpx import AsyncClient, URL
from typing import Callable, Awaitable
from urllib.parse import urljoin
async def render_plant_uml(path: 'StrOrBytesPath') -> bytes:
async with ClientSession() as session:
url = URL(Configuration.instance.plant_uml_server_address) / 'svg'
chunk_size = 0x10000
async def render_plant_uml(client: AsyncClient, path: 'StrOrBytesPath', send : Callable[[bytes], Awaitable[None]]):
url = URL(urljoin(Configuration.instance.plant_uml_server_address, 'svg'))
async with async_open(path, 'rb') as file:
source = await file.read()
async with session.post(url, data=source) as response:
response = await client.post(url, content=source)
response.raise_for_status()
return await response.read()
async for chunk in response.aiter_bytes(chunk_size=chunk_size):
await send(chunk)

View File

@@ -6,7 +6,7 @@ from io import BytesIO
from mimetypes import init as mimeinit, guess_type
from os import getcwd
from os.path import join, normpath, splitext, relpath, basename
from typing import Callable, TYPE_CHECKING, Optional, Awaitable, AsyncGenerator, Any
from typing import Callable, TYPE_CHECKING, Optional, Awaitable, AsyncGenerator, Any, Mapping
import pygraphviz as pgv
from aiofiles import open as async_open
@@ -14,6 +14,7 @@ from aiofiles.base import AiofilesContextManager
from aiofiles.os import listdir
from aiofiles.ospath import exists, isdir, isfile, getmtime
from aiofiles.threadpool.binary import AsyncBufferedReader
from httpx import AsyncClient
from pwo import Maybe
from .asgi_utils import encode_headers
@@ -52,7 +53,10 @@ def is_plant_uml(filepath):
logger = logging.getLogger(__name__)
class Server:
_loop : AbstractEventLoop
root_dir: 'StrOrBytesPath'
prefix: Optional['StrOrBytesPath']
_loop: AbstractEventLoop
_client: AsyncClient
def __init__(self,
root_dir: 'StrOrBytesPath' = getcwd(),
@@ -63,8 +67,15 @@ class Server:
self.file_watcher = FileWatcher(cwd)
self.prefix = prefix and normpath(f'{prefix.decode()}')
self._loop = loop
self._client = AsyncClient()
async def handle_request(self, method: str, url_path: str, etag: Optional[str], query_string: Optional[str], send):
async def handle_request(self,
method: str,
url_path: str,
etag: Optional[str],
query_string: Optional[str],
send: Callable[[Mapping[str, Any]], Awaitable[None]]
):
if method != 'GET':
await send({
'type': 'http.response.start',
@@ -88,7 +99,7 @@ class Server:
lambda: completed_future(mtime)
)
if etag and etag == digest:
await self.not_modified(send, digest, ('Cache-Control', 'must-revalidate, max-age=86400'))
await self.not_modified(send, digest, 'must-revalidate, max-age=86400')
return
elif content:
mime_type = guess_type(basename(url_path))[0] or 'application/octet-stream'
@@ -163,7 +174,6 @@ class Server:
})
elif is_plant_uml(path):
logger.debug("Starting PlantUML rendering for file '%s'", path)
body = await render_plant_uml(path)
logger.debug("Completed PlantUML rendering for file '%s'", path)
await send({
'type': 'http.response.start',
@@ -174,9 +184,15 @@ class Server:
'Cache-Control': 'no-cache'
})
})
await render_plant_uml(self._client, path, lambda chunk: send({
'type': 'http.response.body',
'body': chunk,
'more_body': True
}))
await send({
'type': 'http.response.body',
'body': body
'body': '',
'more_body': False
})
else:
async def read_file(file_path):
@@ -374,3 +390,4 @@ class Server:
async def stop(self):
await self.file_watcher.stop()
await self._client.aclose()