Even though it is nice to have very specific type annotations on the serve() function in hypercorn.asyncio for users that write their own apps and use hypercorn to run them, I would assume that many users of hypercorn use it to drive ASGI applications that are implemented by some existing code such as a framework or app that already runs with a different ASGI server.
This leads to a situation where popular type checkers will create difficult-to-fix warnings a lot of valid ASGI apps.
Consider for example the attached trivial hello world app. It will run fine on my system, invoked with uv run another.py but type checkers such as mypy or ty will tell you something like:
% uv run ty check src/hypercorn_lab/another.py
error[invalid-argument-type]: Argument to function `serve` is incorrect
--> src/hypercorn_lab/another.py:35:17
|
35 | await serve(get_app(), config)
| ^^^^^^^^^ Expected `((hypercorn.typing.HTTPScope | WebsocketScope | hypercorn.typing.LifespanScope, () -> Awaitable[hypercorn.typing.HTTPRequestEvent | hypercorn.typing.HTTPDisconnectEvent | WebsocketConnectEvent | ... omitted 4 union elements], (hypercorn.typing.HTTPResponseStartEvent | hypercorn.typing.HTTPResponseBodyEvent | hypercorn.typing.HTTPResponseTrailersEvent | ... omitted 12 union elements, /) -> Awaitable[None], /) -> Awaitable[None]) | ((dict[Unknown, Unknown], (...) -> Unknown, /) -> Iterable[bytes])`, found `(asgiref.typing.HTTPScope | WebSocketScope | asgiref.typing.LifespanScope, () -> Awaitable[asgiref.typing.HTTPRequestEvent | asgiref.typing.HTTPDisconnectEvent | WebSocketConnectEvent | ... omitted 4 union elements], (asgiref.typing.HTTPResponseStartEvent | asgiref.typing.HTTPResponseBodyEvent | asgiref.typing.HTTPResponseTrailersEvent | ... omitted 11 union elements, /) -> Awaitable[None], /) -> Awaitable[None]`
|
info: type `(HTTPScope | WebSocketScope | LifespanScope, () -> Awaitable[HTTPRequestEvent | HTTPDisconnectEvent | WebSocketConnectEvent | ... omitted 4 union elements], (HTTPResponseStartEvent | HTTPResponseBodyEvent | HTTPResponseTrailersEvent | ... omitted 11 union elements, /) -> Awaitable[None], /) -> Awaitable[None]` is not assignable to any element of the union `((HTTPScope | WebsocketScope | LifespanScope, () -> Awaitable[HTTPRequestEvent | HTTPDisconnectEvent | WebsocketConnectEvent | ... omitted 4 union elements], (HTTPResponseStartEvent | HTTPResponseBodyEvent | HTTPResponseTrailersEvent | ... omitted 12 union elements, /) -> Awaitable[None], /) -> Awaitable[None]) | ((dict[Unknown, Unknown], (...) -> Unknown, /) -> Iterable[bytes])`
info: ├── the first parameter has an incompatible type: `HTTPScope | WebsocketScope | LifespanScope` is not assignable to `HTTPScope | WebSocketScope | LifespanScope`
info: │ └── element `HTTPScope` of union `HTTPScope | WebsocketScope | LifespanScope` is not assignable to `HTTPScope | WebSocketScope | LifespanScope`
info: │ └── type `HTTPScope` is not assignable to any element of the union `HTTPScope | WebSocketScope | LifespanScope`
info: │ ├── field "asgi" on TypedDict `HTTPScope` has type `ASGIVersions` which is not assignable to type `ASGIVersions` expected by TypedDict `HTTPScope`
info: │ │ └── field "spec_version" is required in TypedDict `ASGIVersions` but not required in TypedDict `ASGIVersions`
info: │ ├── field "asgi" on TypedDict `HTTPScope` has type `ASGIVersions` which is not assignable to type `ASGIVersions` expected by TypedDict `WebSocketScope`
info: │ └── field "asgi" on TypedDict `HTTPScope` has type `ASGIVersions` which is not assignable to type `ASGIVersions` expected by TypedDict `LifespanScope`
info: └── incompatible return types: `Awaitable[None]` is not assignable to `Iterable[bytes]`
info: └── protocol `Awaitable[None]` is not assignable to protocol `Iterable[bytes]`
info: └── protocol member `__iter__` is not defined on type `Awaitable[None]`
info: Function defined here
--> .venv/lib/python3.14/site-packages/hypercorn/asyncio/__init__.py:13:11
|
13 | async def serve(
| ^^^^^
14 | app: Framework,
| -------------- Parameter declared here
|
I picked the asgiref types in my example because they are commonly used and to the best of my understanding seem to reflect the ASGI specification, but it seems to me like working with regular dicts would be equally valid.
One way of addressing this would be to create an alternative variant to serve() with a more relaxed type signature for the ASGI app. I'm happy to volunteer implementing and verify it is with some commonly used frameworks and type checkers if there is interest
another.py
Even though it is nice to have very specific type annotations on the
serve()function inhypercorn.asynciofor users that write their own apps and use hypercorn to run them, I would assume that many users of hypercorn use it to drive ASGI applications that are implemented by some existing code such as a framework or app that already runs with a different ASGI server.This leads to a situation where popular type checkers will create difficult-to-fix warnings a lot of valid ASGI apps.
Consider for example the attached trivial hello world app. It will run fine on my system, invoked with
uv run another.pybut type checkers such asmypyortywill tell you something like:I picked the
asgireftypes in my example because they are commonly used and to the best of my understanding seem to reflect the ASGI specification, but it seems to me like working with regular dicts would be equally valid.One way of addressing this would be to create an alternative variant to
serve()with a more relaxed type signature for the ASGI app. I'm happy to volunteer implementing and verify it is with some commonly used frameworks and type checkers if there is interestanother.py