Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/flask/ctx.py: 37%

174 statements  

« prev     ^ index     » next       coverage.py v7.2.2, created at 2023-03-26 06:03 +0000

1import sys 

2import typing as t 

3from functools import update_wrapper 

4from types import TracebackType 

5 

6from werkzeug.exceptions import HTTPException 

7 

8from . import typing as ft 

9from .globals import _app_ctx_stack 

10from .globals import _request_ctx_stack 

11from .signals import appcontext_popped 

12from .signals import appcontext_pushed 

13 

14if t.TYPE_CHECKING: 

15 from .app import Flask 

16 from .sessions import SessionMixin 

17 from .wrappers import Request 

18 

19 

20# a singleton sentinel value for parameter defaults 

21_sentinel = object() 

22 

23 

24class _AppCtxGlobals: 

25 """A plain object. Used as a namespace for storing data during an 

26 application context. 

27 

28 Creating an app context automatically creates this object, which is 

29 made available as the :data:`g` proxy. 

30 

31 .. describe:: 'key' in g 

32 

33 Check whether an attribute is present. 

34 

35 .. versionadded:: 0.10 

36 

37 .. describe:: iter(g) 

38 

39 Return an iterator over the attribute names. 

40 

41 .. versionadded:: 0.10 

42 """ 

43 

44 # Define attr methods to let mypy know this is a namespace object 

45 # that has arbitrary attributes. 

46 

47 def __getattr__(self, name: str) -> t.Any: 

48 try: 

49 return self.__dict__[name] 

50 except KeyError: 

51 raise AttributeError(name) from None 

52 

53 def __setattr__(self, name: str, value: t.Any) -> None: 

54 self.__dict__[name] = value 

55 

56 def __delattr__(self, name: str) -> None: 

57 try: 

58 del self.__dict__[name] 

59 except KeyError: 

60 raise AttributeError(name) from None 

61 

62 def get(self, name: str, default: t.Optional[t.Any] = None) -> t.Any: 

63 """Get an attribute by name, or a default value. Like 

64 :meth:`dict.get`. 

65 

66 :param name: Name of attribute to get. 

67 :param default: Value to return if the attribute is not present. 

68 

69 .. versionadded:: 0.10 

70 """ 

71 return self.__dict__.get(name, default) 

72 

73 def pop(self, name: str, default: t.Any = _sentinel) -> t.Any: 

74 """Get and remove an attribute by name. Like :meth:`dict.pop`. 

75 

76 :param name: Name of attribute to pop. 

77 :param default: Value to return if the attribute is not present, 

78 instead of raising a ``KeyError``. 

79 

80 .. versionadded:: 0.11 

81 """ 

82 if default is _sentinel: 

83 return self.__dict__.pop(name) 

84 else: 

85 return self.__dict__.pop(name, default) 

86 

87 def setdefault(self, name: str, default: t.Any = None) -> t.Any: 

88 """Get the value of an attribute if it is present, otherwise 

89 set and return a default value. Like :meth:`dict.setdefault`. 

90 

91 :param name: Name of attribute to get. 

92 :param default: Value to set and return if the attribute is not 

93 present. 

94 

95 .. versionadded:: 0.11 

96 """ 

97 return self.__dict__.setdefault(name, default) 

98 

99 def __contains__(self, item: str) -> bool: 

100 return item in self.__dict__ 

101 

102 def __iter__(self) -> t.Iterator[str]: 

103 return iter(self.__dict__) 

104 

105 def __repr__(self) -> str: 

106 top = _app_ctx_stack.top 

107 if top is not None: 

108 return f"<flask.g of {top.app.name!r}>" 

109 return object.__repr__(self) 

110 

111 

112def after_this_request(f: ft.AfterRequestCallable) -> ft.AfterRequestCallable: 

113 """Executes a function after this request. This is useful to modify 

114 response objects. The function is passed the response object and has 

115 to return the same or a new one. 

116 

117 Example:: 

118 

119 @app.route('/') 

120 def index(): 

121 @after_this_request 

122 def add_header(response): 

123 response.headers['X-Foo'] = 'Parachute' 

124 return response 

125 return 'Hello World!' 

126 

127 This is more useful if a function other than the view function wants to 

128 modify a response. For instance think of a decorator that wants to add 

129 some headers without converting the return value into a response object. 

130 

131 .. versionadded:: 0.9 

132 """ 

133 top = _request_ctx_stack.top 

134 

135 if top is None: 

136 raise RuntimeError( 

137 "This decorator can only be used when a request context is" 

138 " active, such as within a view function." 

139 ) 

140 

141 top._after_request_functions.append(f) 

142 return f 

143 

144 

145def copy_current_request_context(f: t.Callable) -> t.Callable: 

146 """A helper function that decorates a function to retain the current 

147 request context. This is useful when working with greenlets. The moment 

148 the function is decorated a copy of the request context is created and 

149 then pushed when the function is called. The current session is also 

150 included in the copied request context. 

151 

152 Example:: 

153 

154 import gevent 

155 from flask import copy_current_request_context 

156 

157 @app.route('/') 

158 def index(): 

159 @copy_current_request_context 

160 def do_some_work(): 

161 # do some work here, it can access flask.request or 

162 # flask.session like you would otherwise in the view function. 

163 ... 

164 gevent.spawn(do_some_work) 

165 return 'Regular response' 

166 

167 .. versionadded:: 0.10 

168 """ 

169 top = _request_ctx_stack.top 

170 

171 if top is None: 

172 raise RuntimeError( 

173 "This decorator can only be used when a request context is" 

174 " active, such as within a view function." 

175 ) 

176 

177 reqctx = top.copy() 

178 

179 def wrapper(*args, **kwargs): 

180 with reqctx: 

181 return reqctx.app.ensure_sync(f)(*args, **kwargs) 

182 

183 return update_wrapper(wrapper, f) 

184 

185 

186def has_request_context() -> bool: 

187 """If you have code that wants to test if a request context is there or 

188 not this function can be used. For instance, you may want to take advantage 

189 of request information if the request object is available, but fail 

190 silently if it is unavailable. 

191 

192 :: 

193 

194 class User(db.Model): 

195 

196 def __init__(self, username, remote_addr=None): 

197 self.username = username 

198 if remote_addr is None and has_request_context(): 

199 remote_addr = request.remote_addr 

200 self.remote_addr = remote_addr 

201 

202 Alternatively you can also just test any of the context bound objects 

203 (such as :class:`request` or :class:`g`) for truthness:: 

204 

205 class User(db.Model): 

206 

207 def __init__(self, username, remote_addr=None): 

208 self.username = username 

209 if remote_addr is None and request: 

210 remote_addr = request.remote_addr 

211 self.remote_addr = remote_addr 

212 

213 .. versionadded:: 0.7 

214 """ 

215 return _request_ctx_stack.top is not None 

216 

217 

218def has_app_context() -> bool: 

219 """Works like :func:`has_request_context` but for the application 

220 context. You can also just do a boolean check on the 

221 :data:`current_app` object instead. 

222 

223 .. versionadded:: 0.9 

224 """ 

225 return _app_ctx_stack.top is not None 

226 

227 

228class AppContext: 

229 """The application context binds an application object implicitly 

230 to the current thread or greenlet, similar to how the 

231 :class:`RequestContext` binds request information. The application 

232 context is also implicitly created if a request context is created 

233 but the application is not on top of the individual application 

234 context. 

235 """ 

236 

237 def __init__(self, app: "Flask") -> None: 

238 self.app = app 

239 self.url_adapter = app.create_url_adapter(None) 

240 self.g = app.app_ctx_globals_class() 

241 

242 # Like request context, app contexts can be pushed multiple times 

243 # but there a basic "refcount" is enough to track them. 

244 self._refcnt = 0 

245 

246 def push(self) -> None: 

247 """Binds the app context to the current context.""" 

248 self._refcnt += 1 

249 _app_ctx_stack.push(self) 

250 appcontext_pushed.send(self.app) 

251 

252 def pop(self, exc: t.Optional[BaseException] = _sentinel) -> None: # type: ignore 

253 """Pops the app context.""" 

254 try: 

255 self._refcnt -= 1 

256 if self._refcnt <= 0: 

257 if exc is _sentinel: 

258 exc = sys.exc_info()[1] 

259 self.app.do_teardown_appcontext(exc) 

260 finally: 

261 rv = _app_ctx_stack.pop() 

262 assert rv is self, f"Popped wrong app context. ({rv!r} instead of {self!r})" 

263 appcontext_popped.send(self.app) 

264 

265 def __enter__(self) -> "AppContext": 

266 self.push() 

267 return self 

268 

269 def __exit__( 

270 self, 

271 exc_type: t.Optional[type], 

272 exc_value: t.Optional[BaseException], 

273 tb: t.Optional[TracebackType], 

274 ) -> None: 

275 self.pop(exc_value) 

276 

277 

278class RequestContext: 

279 """The request context contains all request relevant information. It is 

280 created at the beginning of the request and pushed to the 

281 `_request_ctx_stack` and removed at the end of it. It will create the 

282 URL adapter and request object for the WSGI environment provided. 

283 

284 Do not attempt to use this class directly, instead use 

285 :meth:`~flask.Flask.test_request_context` and 

286 :meth:`~flask.Flask.request_context` to create this object. 

287 

288 When the request context is popped, it will evaluate all the 

289 functions registered on the application for teardown execution 

290 (:meth:`~flask.Flask.teardown_request`). 

291 

292 The request context is automatically popped at the end of the request 

293 for you. In debug mode the request context is kept around if 

294 exceptions happen so that interactive debuggers have a chance to 

295 introspect the data. With 0.4 this can also be forced for requests 

296 that did not fail and outside of ``DEBUG`` mode. By setting 

297 ``'flask._preserve_context'`` to ``True`` on the WSGI environment the 

298 context will not pop itself at the end of the request. This is used by 

299 the :meth:`~flask.Flask.test_client` for example to implement the 

300 deferred cleanup functionality. 

301 

302 You might find this helpful for unittests where you need the 

303 information from the context local around for a little longer. Make 

304 sure to properly :meth:`~werkzeug.LocalStack.pop` the stack yourself in 

305 that situation, otherwise your unittests will leak memory. 

306 """ 

307 

308 def __init__( 

309 self, 

310 app: "Flask", 

311 environ: dict, 

312 request: t.Optional["Request"] = None, 

313 session: t.Optional["SessionMixin"] = None, 

314 ) -> None: 

315 self.app = app 

316 if request is None: 

317 request = app.request_class(environ) 

318 self.request = request 

319 self.url_adapter = None 

320 try: 

321 self.url_adapter = app.create_url_adapter(self.request) 

322 except HTTPException as e: 

323 self.request.routing_exception = e 

324 self.flashes = None 

325 self.session = session 

326 

327 # Request contexts can be pushed multiple times and interleaved with 

328 # other request contexts. Now only if the last level is popped we 

329 # get rid of them. Additionally if an application context is missing 

330 # one is created implicitly so for each level we add this information 

331 self._implicit_app_ctx_stack: t.List[t.Optional["AppContext"]] = [] 

332 

333 # indicator if the context was preserved. Next time another context 

334 # is pushed the preserved context is popped. 

335 self.preserved = False 

336 

337 # remembers the exception for pop if there is one in case the context 

338 # preservation kicks in. 

339 self._preserved_exc = None 

340 

341 # Functions that should be executed after the request on the response 

342 # object. These will be called before the regular "after_request" 

343 # functions. 

344 self._after_request_functions: t.List[ft.AfterRequestCallable] = [] 

345 

346 @property 

347 def g(self) -> _AppCtxGlobals: 

348 import warnings 

349 

350 warnings.warn( 

351 "Accessing 'g' on the request context is deprecated and" 

352 " will be removed in Flask 2.2. Access `g` directly or from" 

353 "the application context instead.", 

354 DeprecationWarning, 

355 stacklevel=2, 

356 ) 

357 return _app_ctx_stack.top.g 

358 

359 @g.setter 

360 def g(self, value: _AppCtxGlobals) -> None: 

361 import warnings 

362 

363 warnings.warn( 

364 "Setting 'g' on the request context is deprecated and" 

365 " will be removed in Flask 2.2. Set it on the application" 

366 " context instead.", 

367 DeprecationWarning, 

368 stacklevel=2, 

369 ) 

370 _app_ctx_stack.top.g = value 

371 

372 def copy(self) -> "RequestContext": 

373 """Creates a copy of this request context with the same request object. 

374 This can be used to move a request context to a different greenlet. 

375 Because the actual request object is the same this cannot be used to 

376 move a request context to a different thread unless access to the 

377 request object is locked. 

378 

379 .. versionadded:: 0.10 

380 

381 .. versionchanged:: 1.1 

382 The current session object is used instead of reloading the original 

383 data. This prevents `flask.session` pointing to an out-of-date object. 

384 """ 

385 return self.__class__( 

386 self.app, 

387 environ=self.request.environ, 

388 request=self.request, 

389 session=self.session, 

390 ) 

391 

392 def match_request(self) -> None: 

393 """Can be overridden by a subclass to hook into the matching 

394 of the request. 

395 """ 

396 try: 

397 result = self.url_adapter.match(return_rule=True) # type: ignore 

398 self.request.url_rule, self.request.view_args = result # type: ignore 

399 except HTTPException as e: 

400 self.request.routing_exception = e 

401 

402 def push(self) -> None: 

403 """Binds the request context to the current context.""" 

404 # If an exception occurs in debug mode or if context preservation is 

405 # activated under exception situations exactly one context stays 

406 # on the stack. The rationale is that you want to access that 

407 # information under debug situations. However if someone forgets to 

408 # pop that context again we want to make sure that on the next push 

409 # it's invalidated, otherwise we run at risk that something leaks 

410 # memory. This is usually only a problem in test suite since this 

411 # functionality is not active in production environments. 

412 top = _request_ctx_stack.top 

413 if top is not None and top.preserved: 

414 top.pop(top._preserved_exc) 

415 

416 # Before we push the request context we have to ensure that there 

417 # is an application context. 

418 app_ctx = _app_ctx_stack.top 

419 if app_ctx is None or app_ctx.app != self.app: 

420 app_ctx = self.app.app_context() 

421 app_ctx.push() 

422 self._implicit_app_ctx_stack.append(app_ctx) 

423 else: 

424 self._implicit_app_ctx_stack.append(None) 

425 

426 _request_ctx_stack.push(self) 

427 

428 # Open the session at the moment that the request context is available. 

429 # This allows a custom open_session method to use the request context. 

430 # Only open a new session if this is the first time the request was 

431 # pushed, otherwise stream_with_context loses the session. 

432 if self.session is None: 

433 session_interface = self.app.session_interface 

434 self.session = session_interface.open_session(self.app, self.request) 

435 

436 if self.session is None: 

437 self.session = session_interface.make_null_session(self.app) 

438 

439 # Match the request URL after loading the session, so that the 

440 # session is available in custom URL converters. 

441 if self.url_adapter is not None: 

442 self.match_request() 

443 

444 def pop(self, exc: t.Optional[BaseException] = _sentinel) -> None: # type: ignore 

445 """Pops the request context and unbinds it by doing that. This will 

446 also trigger the execution of functions registered by the 

447 :meth:`~flask.Flask.teardown_request` decorator. 

448 

449 .. versionchanged:: 0.9 

450 Added the `exc` argument. 

451 """ 

452 app_ctx = self._implicit_app_ctx_stack.pop() 

453 clear_request = False 

454 

455 try: 

456 if not self._implicit_app_ctx_stack: 

457 self.preserved = False 

458 self._preserved_exc = None 

459 if exc is _sentinel: 

460 exc = sys.exc_info()[1] 

461 self.app.do_teardown_request(exc) 

462 

463 request_close = getattr(self.request, "close", None) 

464 if request_close is not None: 

465 request_close() 

466 clear_request = True 

467 finally: 

468 rv = _request_ctx_stack.pop() 

469 

470 # get rid of circular dependencies at the end of the request 

471 # so that we don't require the GC to be active. 

472 if clear_request: 

473 rv.request.environ["werkzeug.request"] = None 

474 

475 # Get rid of the app as well if necessary. 

476 if app_ctx is not None: 

477 app_ctx.pop(exc) 

478 

479 assert ( 

480 rv is self 

481 ), f"Popped wrong request context. ({rv!r} instead of {self!r})" 

482 

483 def auto_pop(self, exc: t.Optional[BaseException]) -> None: 

484 if self.request.environ.get("flask._preserve_context") or ( 

485 exc is not None and self.app.preserve_context_on_exception 

486 ): 

487 self.preserved = True 

488 self._preserved_exc = exc # type: ignore 

489 else: 

490 self.pop(exc) 

491 

492 def __enter__(self) -> "RequestContext": 

493 self.push() 

494 return self 

495 

496 def __exit__( 

497 self, 

498 exc_type: t.Optional[type], 

499 exc_value: t.Optional[BaseException], 

500 tb: t.Optional[TracebackType], 

501 ) -> None: 

502 # do not pop the request stack if we are in debug mode and an 

503 # exception happened. This will allow the debugger to still 

504 # access the request object in the interactive shell. Furthermore 

505 # the context can be force kept alive for the test client. 

506 # See flask.testing for how this works. 

507 self.auto_pop(exc_value) 

508 

509 def __repr__(self) -> str: 

510 return ( 

511 f"<{type(self).__name__} {self.request.url!r}" 

512 f" [{self.request.method}] of {self.app.name}>" 

513 )