import json
import uuid
from collections.abc import AsyncGenerator, AsyncIterator, Generator
-from typing import Callable, TypedDict
+from typing import Callable
import pytest
from starlette.applications import Starlette
+from starlette.datastructures import State
from starlette.exceptions import HTTPException
from starlette.middleware import Middleware
from starlette.requests import Request
startup_complete = False
shutdown_complete = False
- class State(TypedDict):
+ class LifespanState(State):
count: int
items: list[int]
- async def hello_world(request: Request[State]) -> Response:
+ async def hello_world(request: Request[LifespanState]) -> Response:
# from version 0.46.3, the state object is only immutable if defined by the user
- assert request.state["count"] in (0, 1)
+ assert request.state.count in (0, 1)
# modify the state, this should not leak to the lifespan or other requests
- request.state["count"] += 1
+ request.state.count += 1
# since state.items is a mutable object this modification _will_ leak across
# requests and to the lifespan
- request.state["items"].append(1)
+ request.state.items.append(1)
return PlainTextResponse("hello, world")
@contextlib.asynccontextmanager
- async def lifespan(app: Starlette) -> AsyncIterator[State]:
+ async def lifespan(app: Starlette) -> AsyncIterator[LifespanState]:
nonlocal startup_complete, shutdown_complete
startup_complete = True
- state = State(count=0, items=[])
+ state = LifespanState({"count": 0, "items": []})
yield state
shutdown_complete = True
# from version 0.46.3, objects from the state are mutable if the state itself is mutable
- assert state["count"] == 2
+ assert state.count == 2
# unless of course the request mutates a mutable object that is referenced
# via state
- assert state["items"] == [1, 1]
+ assert state.items == [1, 1]
app = Router(
lifespan=lifespan,