-
-
Notifications
You must be signed in to change notification settings - Fork 999
Improve typing with structured ASGI scopes and WSGI environment TypedDicts #2642
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -45,8 +45,10 @@ | |
| from falcon._typing import AsgiSend | ||
| from falcon._typing import AsgiSinkCallable | ||
| from falcon._typing import AsyncMiddleware | ||
| from falcon._typing import LifespanScope | ||
| from falcon._typing import Resource | ||
| from falcon._typing import SinkPrefix | ||
| from falcon._typing import WebSocketScope | ||
| import falcon.app | ||
| from falcon.app_helpers import AsyncPreparedMiddlewareResult | ||
| from falcon.app_helpers import AsyncPreparedMiddlewareWsResult | ||
|
|
@@ -466,16 +468,19 @@ async def __call__( # type: ignore[override] # noqa: C901 | |
| # PERF(kgriffs): This should usually be present, so use a | ||
| # try..except | ||
| try: | ||
| asgi_info: dict[str, str] = scope['asgi'] | ||
| raw = scope['asgi'] | ||
| except KeyError: | ||
| # NOTE(kgriffs): According to the ASGI spec, "2.0" is | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please don't remove the notes or make unrelated changes to code. |
||
| # the default version. | ||
| asgi_info = scope['asgi'] = {'version': '2.0'} | ||
| # Default per ASGI spec | ||
| raw = {'version': '2.0'} | ||
| scope['asgi'] = raw | ||
|
|
||
| try: | ||
| spec_version: str | None = asgi_info['spec_version'] | ||
| except KeyError: | ||
| spec_version = None | ||
| # Normalize into proper _ASGIVersions shape | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please don't make changes that can affect performance in this performance-critical path. |
||
| asgi_info = { | ||
| 'spec_version': str(raw.get('spec_version', '2.0')), | ||
| 'version': str(raw.get('version', '2.0')), | ||
| } | ||
|
|
||
| spec_version: str | None = asgi_info['spec_version'] | ||
|
|
||
| try: | ||
| http_version: str = scope['http_version'] | ||
|
|
@@ -488,12 +493,12 @@ async def __call__( # type: ignore[override] # noqa: C901 | |
| # PERF(vytas): Evaluate the potentially recurring WebSocket path | ||
| # first (in contrast to one-shot lifespan events). | ||
| if scope_type == 'websocket': | ||
| await self._handle_websocket(spec_version, scope, receive, send) | ||
| await self._handle_websocket(spec_version, scope, receive, send) # type: ignore[arg-type] | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The whole point was to get rid of type-ignores, not to add new ones! |
||
| return | ||
|
|
||
| # NOTE(vytas): Else 'lifespan' -- other scope_type values have been | ||
| # eliminated by _validate_asgi_scope at this point. | ||
| await self._call_lifespan_handlers(spec_version, scope, receive, send) | ||
| await self._call_lifespan_handlers(spec_version, scope, receive, send) # type: ignore[arg-type] | ||
| return | ||
|
|
||
| # NOTE(kgriffs): Per the ASGI spec, we should not proceed with request | ||
|
|
@@ -514,7 +519,10 @@ async def __call__( # type: ignore[override] # noqa: C901 | |
| assert first_event_type == 'http.request' | ||
|
|
||
| req = self._request_type( | ||
| scope, receive, first_event=first_event, options=self.req_options | ||
| scope, # type: ignore[arg-type] | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here, no new ignores! |
||
| receive, | ||
| first_event=first_event, | ||
| options=self.req_options, | ||
| ) | ||
| resp = self._response_type(options=self.resp_options) | ||
|
|
||
|
|
@@ -1118,7 +1126,7 @@ def _schedule_callbacks(self, resp: Response) -> None: | |
| loop.run_in_executor(None, cb) | ||
|
|
||
| async def _call_lifespan_handlers( | ||
| self, ver: str, scope: dict[str, Any], receive: AsgiReceive, send: AsgiSend | ||
| self, ver: str, scope: LifespanScope, receive: AsgiReceive, send: AsgiSend | ||
| ) -> None: | ||
| while True: | ||
| event = await receive() | ||
|
|
@@ -1127,7 +1135,7 @@ async def _call_lifespan_handlers( | |
| # startup, as opposed to repeating them every request. | ||
|
|
||
| # NOTE(vytas): If missing, 'asgi' is populated in __call__. | ||
| asgi_info: dict[str, str] = scope['asgi'] | ||
| asgi_info: dict[str, str] = scope['asgi'] # type: ignore[assignment] | ||
| version = asgi_info.get('version', '2.0 (implicit)') | ||
| if not version.startswith('3.'): | ||
| await send( | ||
|
|
@@ -1188,7 +1196,7 @@ async def _call_lifespan_handlers( | |
| return | ||
|
|
||
| async def _handle_websocket( | ||
| self, ver: str, scope: dict[str, Any], receive: AsgiReceive, send: AsgiSend | ||
| self, ver: str, scope: WebSocketScope, receive: AsgiReceive, send: AsgiSend | ||
| ) -> None: | ||
| first_event = await receive() | ||
| if first_event['type'] != EventType.WS_CONNECT: | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did you remove
WSGIEnvironmentfrom here?