(test_client_factory: TestClientFactory)
| 643 | |
| 644 | |
| 645 | def test_lifespan_state_async_cm(test_client_factory: TestClientFactory) -> None: |
| 646 | startup_complete = False |
| 647 | shutdown_complete = False |
| 648 | |
| 649 | class State(TypedDict): |
| 650 | count: int |
| 651 | items: list[int] |
| 652 | |
| 653 | async def hello_world(request: Request) -> Response: |
| 654 | # modifications to the state should not leak across requests |
| 655 | assert request.state.count == 0 |
| 656 | # modify the state, this should not leak to the lifespan or other requests |
| 657 | request.state.count += 1 |
| 658 | # since state.items is a mutable object this modification _will_ leak across |
| 659 | # requests and to the lifespan |
| 660 | request.state.items.append(1) |
| 661 | return PlainTextResponse("hello, world") |
| 662 | |
| 663 | @contextlib.asynccontextmanager |
| 664 | async def lifespan(app: Starlette) -> AsyncIterator[State]: |
| 665 | nonlocal startup_complete, shutdown_complete |
| 666 | startup_complete = True |
| 667 | state = State(count=0, items=[]) |
| 668 | yield state |
| 669 | shutdown_complete = True |
| 670 | # modifications made to the state from a request do not leak to the lifespan |
| 671 | assert state["count"] == 0 |
| 672 | # unless of course the request mutates a mutable object that is referenced |
| 673 | # via state |
| 674 | assert state["items"] == [1, 1] |
| 675 | |
| 676 | app = Router( |
| 677 | lifespan=lifespan, |
| 678 | routes=[Route("/", hello_world)], |
| 679 | ) |
| 680 | |
| 681 | assert not startup_complete |
| 682 | assert not shutdown_complete |
| 683 | with test_client_factory(app) as client: |
| 684 | assert startup_complete |
| 685 | assert not shutdown_complete |
| 686 | client.get("/") |
| 687 | # Calling it a second time to ensure that the state is preserved. |
| 688 | client.get("/") |
| 689 | assert startup_complete |
| 690 | assert shutdown_complete |
| 691 | |
| 692 | |
| 693 | def test_raise_on_startup(test_client_factory: TestClientFactory) -> None: |
nothing calls this directly
no test coverage detected