encode / starlette

The little ASGI framework that shines. 🌟

Home Page:https://www.starlette.io/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Incorrect typing for Scope and Message in ASGIApp

ods opened this issue · comments

  • Initially raised as discussion #2040 (but ignored there)

According to ASGI spec both scope and event message must be dictionaries, not an arbitrary mutable mapping:

  • scope: The connection scope information, a dictionary that contains at least a type key specifying the protocol that is incoming
  • receive: an awaitable callable that will yield a new event dictionary when one is available
  • send: an awaitable callable taking a single event dictionary as a positional argument that will return once the send has been completed or the connection has been closed

and in other place:

scope must be a dict.

But in starlette they are declared as MutableMapping:

Scope = typing.MutableMapping[str, typing.Any]
Message = typing.MutableMapping[str, typing.Any]

Receive = typing.Callable[[], typing.Awaitable[Message]]
Send = typing.Callable[[Message], typing.Awaitable[None]]

ASGIApp = typing.Callable[[Scope, Receive, Send], typing.Awaitable[None]]

This actually breaks interoperability with other libraries that follows the spec. For example, the following code

from starlette.applications import Starlette
from httpx import ASGITransport

ASGITransport(Starlette())

gives the following error when type-checked with mypy:

error: Argument 1 to "ASGITransport" has incompatible type "Starlette"; expected "Callable[[Dict[str, Any], Callable[[], Awaitable[Dict[str, Any]]], Callable[[Dict[str, Any]], Coroutine[None, None, None]]], Coroutine[None, None, None]]"  [arg-type]
note: "Starlette.__call__" has type "Callable[[Arg(MutableMapping[str, Any], 'scope'), Arg(Callable[[], Awaitable[MutableMapping[str, Any]]], 'receive'), Arg(Callable[[MutableMapping[str, Any]], Awaitable[None]], 'send')], Coroutine[Any, Any, None]]"

The typing of ASGI app in httpx follows the spec:

_Message = typing.Dict[str, typing.Any]
_Receive = typing.Callable[[], typing.Awaitable[_Message]]
_Send = typing.Callable[
    [typing.Dict[str, typing.Any]], typing.Coroutine[None, None, None]
]
_ASGIApp = typing.Callable[
    [typing.Dict[str, typing.Any], _Receive, _Send], typing.Coroutine[None, None, None]
]