Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions starlette/datastructures.py
Original file line number Diff line number Diff line change
Expand Up @@ -690,3 +690,18 @@ def __getattr__(self, key: Any) -> Any:

def __delattr__(self, key: Any) -> None:
del self._state[key]

def __getitem__(self, key: str) -> Any:
return self._state[key]

def __setitem__(self, key: str, value: Any) -> None:
self._state[key] = value

def __delitem__(self, key: str) -> None:
del self._state[key]

def __iter__(self) -> Iterator[str]:
return iter(self._state)

def __len__(self) -> int:
return len(self._state)
14 changes: 9 additions & 5 deletions starlette/requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
import json
from collections.abc import AsyncGenerator, Iterator, Mapping
from http import cookies as http_cookies
from typing import TYPE_CHECKING, Any, NoReturn, cast
from typing import TYPE_CHECKING, Any, Generic, NoReturn, cast

import anyio
from typing_extensions import TypeVar

from starlette._utils import AwaitableOrContextManager, AwaitableOrContextManagerWrapper
from starlette.datastructures import URL, Address, FormData, Headers, QueryParams, State
Expand Down Expand Up @@ -68,7 +69,10 @@ class ClientDisconnect(Exception):
pass


class HTTPConnection(Mapping[str, Any]):
StateT = TypeVar("StateT", bound=Mapping[str, Any] | State, default=State)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if this will pass the TypedDict check in some static type checkers.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which one you don't think it will not? We test with mypy here, but pyright doesn't seem to be failing (I use it on my environment).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's great to be able to pass the inspection.



class HTTPConnection(Mapping[str, Any], Generic[StateT]):
"""
A base class for incoming HTTP connections, that is used to provide
any functionality that is common to both `Request` and `WebSocket`.
Expand Down Expand Up @@ -172,14 +176,14 @@ def user(self) -> Any:
return self.scope["user"]

@property
def state(self) -> State:
def state(self) -> StateT:
if not hasattr(self, "_state"):
# Ensure 'state' has an empty dict if it's not already populated.
self.scope.setdefault("state", {})
# Create a state instance with a reference to the dict in which it should
# store info
self._state = State(self.scope["state"])
return self._state
return cast(StateT, self._state)

def url_for(self, name: str, /, **path_params: Any) -> URL:
url_path_provider: Router | Starlette | None = self.scope.get("router") or self.scope.get("app")
Expand All @@ -197,7 +201,7 @@ async def empty_send(message: Message) -> NoReturn:
raise RuntimeError("Send channel has not been made available")


class Request(HTTPConnection):
class Request(HTTPConnection[StateT]):
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are missing the same in WebSockets.

_form: FormData | None

def __init__(self, scope: Scope, receive: Receive = empty_receive, send: Send = empty_send):
Expand Down
Loading