starlette icon indicating copy to clipboard operation
starlette copied to clipboard

Allow to pass any object as lifespan state

Open Kludex opened this issue 7 months ago • 2 comments

This is a breaking change.

This is here first for discussion.

Kludex avatar May 27 '25 13:05 Kludex

It breaks probably one of the most popular patterns: request.state.DEPNAME For example, a database session middleware may set current session like that

class DbMiddleware:
    def __init__(self, app):
        self.app = app

    async def __call__(self, scope, receive, send):
        scope["state"]['dbsession'] = "database_connection"
        await self.app(scope, receive, send)

and request it in an endpoint like

async def view(request: Request[LifespanState]):
    return JSONResponse(
        {
            "request_state": request.state.dbsession,
        }
    )

Now it throws AttributeError: 'State' object has no attribute 'dbsession'. IMO, this breaks too many third-party integrations.

alex-oleshkevich avatar Jun 01 '25 18:06 alex-oleshkevich

What if we bind LifespanStateT to State to require all child states to inherit from State? This way we can maintain b/w compatibility and users can provide types?

_LifespanStateT = TypeVar("_LifespanStateT", default=State, bind=State)

class MyState(State):
    key: str
    dbsession: Any

class DbMiddleware:
    async def __call__(self, scope, receive, send):
        scope["state"]['dbsession'] = "database_connection"
        await self.app(scope, receive, send)

async def lifespan_state():
    yield MyState(key='value')

async def view(request: Request[MyState]):
    print(request.state.dbsession) # works
    print(request.state.key) # also works

alex-oleshkevich avatar Jun 01 '25 19:06 alex-oleshkevich

@alex-oleshkevich Would there be a way to move away from the State class? But yeah, I agree that what you are proposing is better for the ecosystem.

Kludex avatar Jun 24 '25 10:06 Kludex

@alex-oleshkevich Would there be a way to move away from the State class? But yeah, I agree that what you are proposing is better for the ecosystem.

State can be a protocol, but this is not very convenient. In short, we only need a common interface to set/get values from the state.

alex-oleshkevich avatar Jun 24 '25 10:06 alex-oleshkevich

I've applied your comment, but this doesn't provide the DX that I'm aiming. State is initialized with a dict, which is not type safe.

Kludex avatar Jul 12 '25 07:07 Kludex

Due to the limitations of Python's type system, I don't think there will be an elegant way to implement this feature. For now, using TypedDict/dataclass is a good compromise design.

abersheeran avatar Jul 13 '25 11:07 abersheeran

I agree @abersheeran , let me try implementing it focusing on TypedDict.

Kludex avatar Sep 10 '25 20:09 Kludex