Coverage for tests/test_dependency_after_yield_streaming.py: 100%
80 statements
« prev ^ index » next coverage.py v7.6.1, created at 2025-09-29 03:37 +0000
« prev ^ index » next coverage.py v7.6.1, created at 2025-09-29 03:37 +0000
1from contextlib import contextmanager 1abcdef
2from typing import Any, Generator 1abcdef
4import pytest 1abcdef
5from fastapi import Depends, FastAPI 1abcdef
6from fastapi.responses import StreamingResponse 1abcdef
7from fastapi.testclient import TestClient 1abcdef
8from typing_extensions import Annotated 1abcdef
11class Session: 1abcdef
12 def __init__(self) -> None: 1abcdef
13 self.data = ["foo", "bar", "baz"] 1EmgnFyQGohpHzRIqirJASKsjtLBTMukvNCUOwlxPDV
14 self.open = True 1EmgnFyQGohpHzRIqirJASKsjtLBTMukvNCUOwlxPDV
16 def __iter__(self) -> Generator[str, None, None]: 1abcdef
17 for item in self.data: 1EmgnFyGohpHzIqirJAKsjtLBMukvNCOwlxPD
18 if self.open: 1EmgnFyGohpHzIqirJAKsjtLBMukvNCOwlxPD
19 yield item 1FyHzJALBNCPD
20 else:
21 raise ValueError("Session closed") 1EmgnGohpIqirKsjtMukvOwlx
24@contextmanager 1abcdef
25def acquire_session() -> Generator[Session, None, None]: 1abcdef
26 session = Session() 1EmgnFyQGohpHzRIqirJASKsjtLBTMukvNCUOwlxPDV
27 try: 1EmgnFyQGohpHzRIqirJASKsjtLBTMukvNCUOwlxPDV
28 yield session 1EmgnFyQGohpHzRIqirJASKsjtLBTMukvNCUOwlxPDV
29 finally:
30 session.open = False 1EmgnFyQGohpHzRIqirJASKsjtLBTMukvNCUOwlxPDV
33def dep_session() -> Any: 1abcdef
34 with acquire_session() as s: 1FyQHzRJASLBTNCUPDV
35 yield s 1FyQHzRJASLBTNCUPDV
38def broken_dep_session() -> Any: 1abcdef
39 with acquire_session() as s: 1EmgnGohpIqirKsjtMukvOwlx
40 s.open = False 1EmgnGohpIqirKsjtMukvOwlx
41 yield s 1EmgnGohpIqirKsjtMukvOwlx
44SessionDep = Annotated[Session, Depends(dep_session)] 1abcdef
45BrokenSessionDep = Annotated[Session, Depends(broken_dep_session)] 1abcdef
47app = FastAPI() 1abcdef
50@app.get("/data") 1abcdef
51def get_data(session: SessionDep) -> Any: 1abcdef
52 data = list(session) 1FHJLNP
53 return data 1FHJLNP
56@app.get("/stream-simple") 1abcdef
57def get_stream_simple(session: SessionDep) -> Any: 1abcdef
58 def iter_data(): 1QRSTUV
59 yield from ["x", "y", "z"] 1QRSTUV
61 return StreamingResponse(iter_data()) 1QRSTUV
64@app.get("/stream-session") 1abcdef
65def get_stream_session(session: SessionDep) -> Any: 1abcdef
66 def iter_data(): 1yzABCD
67 yield from session 1yzABCD
69 return StreamingResponse(iter_data()) 1yzABCD
72@app.get("/broken-session-data") 1abcdef
73def get_broken_session_data(session: BrokenSessionDep) -> Any: 1abcdef
74 return list(session) 1EmGoIqKsMuOw
77@app.get("/broken-session-stream") 1abcdef
78def get_broken_session_stream(session: BrokenSessionDep) -> Any: 1abcdef
79 def iter_data(): 1gnhpirjtkvlx
80 yield from session 1gnhpirjtkvlx
82 return StreamingResponse(iter_data()) 1gnhpirjtkvlx
85client = TestClient(app) 1abcdef
88def test_regular_no_stream(): 1abcdef
89 response = client.get("/data") 1FHJLNP
90 assert response.json() == ["foo", "bar", "baz"] 1FHJLNP
93def test_stream_simple(): 1abcdef
94 response = client.get("/stream-simple") 1QRSTUV
95 assert response.text == "xyz" 1QRSTUV
98def test_stream_session(): 1abcdef
99 response = client.get("/stream-session") 1yzABCD
100 assert response.text == "foobarbaz" 1yzABCD
103def test_broken_session_data(): 1abcdef
104 with pytest.raises(ValueError, match="Session closed"): 1EGIKMO
105 client.get("/broken-session-data") 1EGIKMO
108def test_broken_session_data_no_raise(): 1abcdef
109 client = TestClient(app, raise_server_exceptions=False) 1moqsuw
110 response = client.get("/broken-session-data") 1moqsuw
111 assert response.status_code == 500 1moqsuw
112 assert response.text == "Internal Server Error" 1moqsuw
115def test_broken_session_stream_raise(): 1abcdef
116 # Can raise ValueError on Pydantic v2 and ExceptionGroup on Pydantic v1
117 with pytest.raises((ValueError, Exception)): 1nprtvx
118 client.get("/broken-session-stream") 1nprtvx
121def test_broken_session_stream_no_raise(): 1abcdef
122 """
123 When a dependency with yield raises after the streaming response already started
124 the 200 status code is already sent, but there's still an error in the server
125 afterwards, an exception is raised and captured or shown in the server logs.
126 """
127 with TestClient(app, raise_server_exceptions=False) as client: 1ghijkl
128 response = client.get("/broken-session-stream") 1ghijkl
129 assert response.status_code == 200 1ghijkl
130 assert response.text == "" 1ghijkl