Coverage for tests / test_dependency_yield_scope.py: 100%

141 statements  

« prev     ^ index     » next       coverage.py v7.13.3, created at 2026-02-12 18:15 +0000

1import json 1abcd

2from typing import Annotated, Any 1abcd

3 

4import pytest 1abcd

5from fastapi import APIRouter, Depends, FastAPI, HTTPException 1abcd

6from fastapi.exceptions import FastAPIError 1abcd

7from fastapi.responses import StreamingResponse 1abcd

8from fastapi.testclient import TestClient 1abcd

9 

10 

11class Session: 1abcd

12 def __init__(self) -> None: 1abcd

13 self.open = True 1qhkrensiltfoujmvgp

14 

15 

16def dep_session() -> Any: 1abcd

17 s = Session() 1qhkrensiltfoujmvgp

18 yield s 1qhkrensiltfoujmvgp

19 s.open = False 1qhkrensiltfoujmvgp

20 

21 

22def raise_after_yield() -> Any: 1abcd

23 yield 1wxFCyzGDABHE

24 raise HTTPException(status_code=503, detail="Exception after yield") 1wxFCyzGDABHE

25 

26 

27SessionFuncDep = Annotated[Session, Depends(dep_session, scope="function")] 1abcd

28SessionRequestDep = Annotated[Session, Depends(dep_session, scope="request")] 1abcd

29SessionDefaultDep = Annotated[Session, Depends(dep_session)] 1abcd

30 

31 

32class NamedSession: 1abcd

33 def __init__(self, name: str = "default") -> None: 1abcd

34 self.name = name 1hkeilfjmg

35 self.open = True 1hkeilfjmg

36 

37 

38def get_named_session(session: SessionRequestDep, session_b: SessionDefaultDep) -> Any: 1abcd

39 assert session is session_b 1efg

40 named_session = NamedSession(name="named") 1efg

41 yield named_session, session_b 1efg

42 named_session.open = False 1efg

43 

44 

45NamedSessionsDep = Annotated[tuple[NamedSession, Session], Depends(get_named_session)] 1abcd

46 

47 

48def get_named_func_session(session: SessionFuncDep) -> Any: 1abcd

49 named_session = NamedSession(name="named") 1hij

50 yield named_session, session 1hij

51 named_session.open = False 1hij

52 

53 

54def get_named_regular_func_session(session: SessionFuncDep) -> Any: 1abcd

55 named_session = NamedSession(name="named") 1klm

56 return named_session, session 1klm

57 

58 

59BrokenSessionsDep = Annotated[ 1abcd

60 tuple[NamedSession, Session], Depends(get_named_func_session) 

61] 

62NamedSessionsFuncDep = Annotated[ 1abcd

63 tuple[NamedSession, Session], Depends(get_named_func_session, scope="function") 

64] 

65 

66RegularSessionsDep = Annotated[ 1abcd

67 tuple[NamedSession, Session], Depends(get_named_regular_func_session) 

68] 

69 

70app = FastAPI() 1abcd

71router = APIRouter() 1abcd

72 

73 

74@router.get("/") 1abcd

75def get_index(): 1abcd

76 return {"status": "ok"} 1FCGDHE

77 

78 

79@app.get("/function-scope") 1abcd

80def function_scope(session: SessionFuncDep) -> Any: 1abcd

81 def iter_data(): 1qsu

82 yield json.dumps({"is_open": session.open}) 1qsu

83 

84 return StreamingResponse(iter_data()) 1qsu

85 

86 

87@app.get("/request-scope") 1abcd

88def request_scope(session: SessionRequestDep) -> Any: 1abcd

89 def iter_data(): 1rtv

90 yield json.dumps({"is_open": session.open}) 1rtv

91 

92 return StreamingResponse(iter_data()) 1rtv

93 

94 

95@app.get("/two-scopes") 1abcd

96def get_stream_session( 1abcd

97 function_session: SessionFuncDep, request_session: SessionRequestDep 

98) -> Any: 

99 def iter_data(): 1nop

100 yield json.dumps( 1nop

101 {"func_is_open": function_session.open, "req_is_open": request_session.open} 

102 ) 

103 

104 return StreamingResponse(iter_data()) 1nop

105 

106 

107@app.get("/sub") 1abcd

108def get_sub(sessions: NamedSessionsDep) -> Any: 1abcd

109 def iter_data(): 1efg

110 yield json.dumps( 1efg

111 {"named_session_open": sessions[0].open, "session_open": sessions[1].open} 

112 ) 

113 

114 return StreamingResponse(iter_data()) 1efg

115 

116 

117@app.get("/named-function-scope") 1abcd

118def get_named_function_scope(sessions: NamedSessionsFuncDep) -> Any: 1abcd

119 def iter_data(): 1hij

120 yield json.dumps( 1hij

121 {"named_session_open": sessions[0].open, "session_open": sessions[1].open} 

122 ) 

123 

124 return StreamingResponse(iter_data()) 1hij

125 

126 

127@app.get("/regular-function-scope") 1abcd

128def get_regular_function_scope(sessions: RegularSessionsDep) -> Any: 1abcd

129 def iter_data(): 1klm

130 yield json.dumps( 1klm

131 {"named_session_open": sessions[0].open, "session_open": sessions[1].open} 

132 ) 

133 

134 return StreamingResponse(iter_data()) 1klm

135 

136 

137app.include_router( 1abcd

138 prefix="/router-scope-function", 

139 router=router, 

140 dependencies=[Depends(raise_after_yield, scope="function")], 

141) 

142 

143app.include_router( 1abcd

144 prefix="/router-scope-request", 

145 router=router, 

146 dependencies=[Depends(raise_after_yield, scope="request")], 

147) 

148 

149client = TestClient(app) 1abcd

150 

151 

152def test_function_scope() -> None: 1abcd

153 response = client.get("/function-scope") 1qsu

154 assert response.status_code == 200 1qsu

155 data = response.json() 1qsu

156 assert data["is_open"] is False 1qsu

157 

158 

159def test_request_scope() -> None: 1abcd

160 response = client.get("/request-scope") 1rtv

161 assert response.status_code == 200 1rtv

162 data = response.json() 1rtv

163 assert data["is_open"] is True 1rtv

164 

165 

166def test_two_scopes() -> None: 1abcd

167 response = client.get("/two-scopes") 1nop

168 assert response.status_code == 200 1nop

169 data = response.json() 1nop

170 assert data["func_is_open"] is False 1nop

171 assert data["req_is_open"] is True 1nop

172 

173 

174def test_sub() -> None: 1abcd

175 response = client.get("/sub") 1efg

176 assert response.status_code == 200 1efg

177 data = response.json() 1efg

178 assert data["named_session_open"] is True 1efg

179 assert data["session_open"] is True 1efg

180 

181 

182def test_broken_scope() -> None: 1abcd

183 with pytest.raises( 1IJK

184 FastAPIError, 

185 match='The dependency "get_named_func_session" has a scope of "request", it cannot depend on dependencies with scope "function"', 

186 ): 

187 

188 @app.get("/broken-scope") 1IJK

189 def get_broken(sessions: BrokenSessionsDep) -> Any: # pragma: no cover 1IJK

190 pass 

191 

192 

193def test_named_function_scope() -> None: 1abcd

194 response = client.get("/named-function-scope") 1hij

195 assert response.status_code == 200 1hij

196 data = response.json() 1hij

197 assert data["named_session_open"] is False 1hij

198 assert data["session_open"] is False 1hij

199 

200 

201def test_regular_function_scope() -> None: 1abcd

202 response = client.get("/regular-function-scope") 1klm

203 assert response.status_code == 200 1klm

204 data = response.json() 1klm

205 assert data["named_session_open"] is True 1klm

206 assert data["session_open"] is False 1klm

207 

208 

209def test_router_level_dep_scope_function() -> None: 1abcd

210 response = client.get("/router-scope-function/") 1FGH

211 assert response.status_code == 503 1FGH

212 assert response.json() == {"detail": "Exception after yield"} 1FGH

213 

214 

215def test_router_level_dep_scope_request() -> None: 1abcd

216 with TestClient(app, raise_server_exceptions=False) as client: 1CDE

217 response = client.get("/router-scope-request/") 1CDE

218 assert response.status_code == 200 1CDE

219 assert response.json() == {"status": "ok"} 1CDE

220 

221 

222def test_app_level_dep_scope_function() -> None: 1abcd

223 app = FastAPI(dependencies=[Depends(raise_after_yield, scope="function")]) 1wyA

224 

225 @app.get("/app-scope-function") 1wyA

226 def get_app_scope_function(): 1wyA

227 return {"status": "ok"} 1wyA

228 

229 with TestClient(app) as client: 1wyA

230 response = client.get("/app-scope-function") 1wyA

231 assert response.status_code == 503 1wyA

232 assert response.json() == {"detail": "Exception after yield"} 1wyA

233 

234 

235def test_app_level_dep_scope_request() -> None: 1abcd

236 app = FastAPI(dependencies=[Depends(raise_after_yield, scope="request")]) 1xzB

237 

238 @app.get("/app-scope-request") 1xzB

239 def get_app_scope_request(): 1xzB

240 return {"status": "ok"} 1xzB

241 

242 with TestClient(app, raise_server_exceptions=False) as client: 1xzB

243 response = client.get("/app-scope-request") 1xzB

244 assert response.status_code == 200 1xzB

245 assert response.json() == {"status": "ok"} 1xzB