incompatible type and expected "type[_MiddlewareClass[[]]]" [arg-type]
Hi ,
am using asgi-logger and extended AccessLoggerMiddleware to customise the logging in the application and when I upgrade fastapi to 0.109.x version getting issue on mypy, any suggestions ?
error: Argument 1 to "Middleware" has incompatible type "type[AccessLoggerMiddleware]"; expected "type[_MiddlewareClass[[]]]" [arg-type]
middleware = [
Middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["PUT", "GET", "DELETE"],
allow_headers=["Authorization"],
),
Middleware(access_logger.AccessLoggerMiddleware),
]
and this is my customise version of logging.
import structlog
from asgi_logger import middleware as asl
from asgiref.typing import ASGI3Application, HTTPScope
class AccessLoggerMiddleware(asl.AccessLoggerMiddleware):
//code
what version of mypy are you using? have you tried updating to the latest version, or using pyright which generally in my experience is more reliable?
I tried with mypy 1.15.0 as well but still same issue.
Can you offer a self-contained MRE?
I'm seeing the same issue here in the Nominatim project after updating to starlette 0.47. Example of failing CI: here.
The problem appears when creating a custom middleware with initialisation parameters.
Minimal code example to reproduce:
from starlette.applications import Starlette
from starlette.responses import Response, JSONResponse
from starlette.routing import Route
from starlette.middleware import Middleware
from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
from starlette.requests import Request
class FileLoggingMiddleware(BaseHTTPMiddleware):
def __init__(self, app: Starlette, file_name: str = '') -> None:
super().__init__(app)
async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -> Response:
return await call_next(request)
async def homepage(request):
return JSONResponse({'hello': 'world'})
app = Starlette(debug=True, routes=[Route('/', homepage)],
middleware=[Middleware(FileLoggingMiddleware, file_name='/tmp')])
Running mypy results in:
test.py:23: error: Argument 1 to "Middleware" has incompatible type "type[FileLoggingMiddleware]"; expected "_MiddlewareFactory[[]]" [arg-type]
test.py:23: note: Following member(s) of "FileLoggingMiddleware" have conflicts:
test.py:23: note: Expected:
test.py:23: note: def __call__(self, Callable[[MutableMapping[str, Any], Callable[[], Awaitable[MutableMapping[str, Any]]], Callable[[MutableMapping[str, Any]], Awaitable[None]]], Awaitable[None]], /) -> Callable[[MutableMapping[str, Any], Callable[[], Awaitable[MutableMapping[str, Any]]], Callable[[MutableMapping[str, Any]], Awaitable[None]]], Awaitable[None]]
test.py:23: note: Got:
test.py:23: note: def __init__(app: Starlette, file_name: str = ...) -> FileLoggingMiddleware
test.py:23: note: "_MiddlewareFactory[[]].__call__" has type "Callable[[Callable[[MutableMapping[str, Any], Callable[[], Awaitable[MutableMapping[str, Any]]], Callable[[MutableMapping[str, Any]], Awaitable[None]]], Awaitable[None]]], Callable[[MutableMapping[str, Any], Callable[[], Awaitable[MutableMapping[str, Any]]], Callable[[MutableMapping[str, Any]], Awaitable[None]]], Awaitable[None]]]"
Hit this as well while implementing a pure ASGI middleware using asgiref types.
MRE
# asgi_mre.py
from asgiref.typing import (
ASGI3Application,
ASGIReceiveCallable,
ASGISendCallable,
Scope,
)
from starlette.applications import Starlette
from starlette.middleware import Middleware
class MyMiddleware:
def __init__(self, app: ASGI3Application) -> None:
self.app = app
async def __call__(
self, scope: Scope, receive: ASGIReceiveCallable, send: ASGISendCallable
) -> None: ...
app = Starlette(
middleware=[Middleware(MyMiddleware)],
)
mypy
asgi_mre.py:21: error: Argument 1 to "Middleware" has incompatible type "type[MyMiddleware]"; expected "_MiddlewareFactory[[]]" [arg-type]
asgi_mre.py:21: note: Following member(s) of "MyMiddleware" have conflicts:
asgi_mre.py:21: note: Expected:
asgi_mre.py:21: note: def __call__(self, Callable[[MutableMapping[str, Any], Callable[[], Awaitable[MutableMapping[str, Any]]], Callable[[MutableMapping[str, Any]], Awaitable[None]]], Awaitable[None]], /) -> Callable[[MutableMapping[str, Any], Callable[[], Awaitable[MutableMapping[str, Any]]], Callable[[MutableMapping[str, Any]], Awaitable[None]]], Awaitable[None]]
asgi_mre.py:21: note: Got:
asgi_mre.py:21: note: def __init__(app: Callable[[HTTPScope | WebSocketScope | LifespanScope, Callable[[], Awaitable[HTTPRequestEvent | HTTPDisconnectEvent | WebSocketConnectEvent | WebSocketReceiveEvent | WebSocketDisconnectEvent | LifespanStartupEvent | LifespanShutdownEvent]], Callable[[HTTPResponseStartEvent | HTTPResponseBodyEvent | HTTPResponseTrailersEvent | HTTPServerPushEvent | HTTPDisconnectEvent | <9 more items>], Awaitable[None]]], Awaitable[None]]) -> MyMiddleware
asgi_mre.py:21: note: "_MiddlewareFactory[[]].__call__" has type "Callable[[Callable[[MutableMapping[str, Any], Callable[[], Awaitable[MutableMapping[str, Any]]], Callable[[MutableMapping[str, Any]], Awaitable[None]]], Awaitable[None]]], Callable[[MutableMapping[str, Any], Callable[[], Awaitable[MutableMapping[str, Any]]], Callable[[MutableMapping[str, Any]], Awaitable[None]]], Awaitable[None]]]"
Found 1 error in 1 file (checked 1 source file)
pyright
asgi_mre.py:21:28 - error: Argument of type "type[MyMiddleware]" cannot be assigned to parameter "cls" of type "_MiddlewareFactory[P@__init__]" in function "__init__"
Type "type[MyMiddleware]" is not assignable to type "(app: ASGIApp, /) -> ASGIApp"
Parameter 1: type "ASGIApp" is incompatible with type "ASGI3Application"
Type "ASGIApp" is not assignable to type "ASGI3Application"
Parameter 1: type "Scope" is incompatible with type "Scope"
Type "asgiref.typing.Scope" is not assignable to type "starlette.types.Scope"
Parameter 2: type "ASGIReceiveCallable" is incompatible with type "Receive"
Type "ASGIReceiveCallable" is not assignable to type "Receive"
Parameter 3: type "ASGISendCallable" is incompatible with type "Send"
... (reportArgumentType)
1 error, 0 warnings, 0 informations
Versions
- Python: 3.10
- Starlette: 0.47.0
- mypy: 1.16.0
- pyright: 1.1.401
Do we need to revert something?
Do we need to revert something?
Actually, I'm not sure it even worked at some point actually. The _MiddlewareClass protocol was introduced in #2381 (released in 0.35.0) almost two years ago. The error in this version is slightly different:
mre.py:23: error: Argument 1 to "Middleware" has incompatible type "type[MyMiddleware]"; expected "type[_MiddlewareClass[[]]]" [arg-type]
It passes in version 0.34.0, but that's expected, since there was no typing (i.e. any type).
The root problem (from what I understand) is that in Starlette, Scope and Message are just mappings:
https://github.com/encode/starlette/blob/78da9b9e218ab289117df7d62aee200ed4c59617/starlette/types.py#L12-L13
In asgiref, they are actually unions of TypedDict with strict definitions of each scope/message:
https://github.com/django/asgiref/blob/5eff04dce46b74172a1d6d1c5b02409f0afe33b6/asgiref/typing.py#L106-L107 https://github.com/django/asgiref/blob/5eff04dce46b74172a1d6d1c5b02409f0afe33b6/asgiref/typing.py#L227-L235
Surprisingly, a MutableMapping is apparently not compatible with a TypedDict: https://github.com/python/mypy/issues/4976
So, not really sure how to solve this, apart from using asgiref types in Starlette instead of the homemade types; but I guess it comes with a lot of problems too.
Another data point here: this only shows up after updating to mypy 1.16.0. So, either downgrading to mypy 1.15.0 or to starlette 0.46.2 makes the issue go away.
Getting the same error when upgrading mypy:
care/main.py:54: error: Argument 1 to "add_middleware" of "Starlette" has incompatible type "Callable[[Request, Callable[[Request], Awaitable[Response]]], Coroutine[Any, Any, Response]]"; expected "_MiddlewareFactory[[]]" [arg-type]
care/main.py:54: note: "_MiddlewareFactory[[]].__call__" has type "Callable[[Callable[[MutableMapping[str, Any], Callable[[], Awaitable[MutableMapping[str, Any]]], Callable[[MutableMapping[str, Any]], Awaitable[None]]], Awaitable[None]]], Callable[[MutableMapping[str, Any], Callable[[], Awaitable[MutableMapping[str, Any]]], Callable[[MutableMapping[str, Any]], Awaitable[None]]], Awaitable[None]]]"
Found 1 error in 1 file (checked 1 source file)
In my case I'm defining a logging function with FastAPI according to their docs:
async def MyMiddleware(
request: Request,
call_next: Callable[[Request], Awaitable[Response]],
) -> Response:
pass
Besides a valid fix to the library, is there any way to patch this from my app code?