]> jfr.im git - dlqueue.git/blob - venv/lib/python3.11/site-packages/flask/app.py
init: venv aand flask
[dlqueue.git] / venv / lib / python3.11 / site-packages / flask / app.py
1 from __future__ import annotations
2
3 import os
4 import sys
5 import typing as t
6 import weakref
7 from collections.abc import Iterator as _abc_Iterator
8 from datetime import timedelta
9 from inspect import iscoroutinefunction
10 from itertools import chain
11 from types import TracebackType
12 from urllib.parse import quote as _url_quote
13
14 import click
15 from werkzeug.datastructures import Headers
16 from werkzeug.datastructures import ImmutableDict
17 from werkzeug.exceptions import BadRequestKeyError
18 from werkzeug.exceptions import HTTPException
19 from werkzeug.exceptions import InternalServerError
20 from werkzeug.routing import BuildError
21 from werkzeug.routing import MapAdapter
22 from werkzeug.routing import RequestRedirect
23 from werkzeug.routing import RoutingException
24 from werkzeug.routing import Rule
25 from werkzeug.serving import is_running_from_reloader
26 from werkzeug.wrappers import Response as BaseResponse
27
28 from . import cli
29 from . import typing as ft
30 from .ctx import AppContext
31 from .ctx import RequestContext
32 from .globals import _cv_app
33 from .globals import _cv_request
34 from .globals import current_app
35 from .globals import g
36 from .globals import request
37 from .globals import request_ctx
38 from .globals import session
39 from .helpers import get_debug_flag
40 from .helpers import get_flashed_messages
41 from .helpers import get_load_dotenv
42 from .helpers import send_from_directory
43 from .sansio.app import App
44 from .sansio.scaffold import _sentinel
45 from .sessions import SecureCookieSessionInterface
46 from .sessions import SessionInterface
47 from .signals import appcontext_tearing_down
48 from .signals import got_request_exception
49 from .signals import request_finished
50 from .signals import request_started
51 from .signals import request_tearing_down
52 from .templating import Environment
53 from .wrappers import Request
54 from .wrappers import Response
55
56 if t.TYPE_CHECKING: # pragma: no cover
57 from .testing import FlaskClient
58 from .testing import FlaskCliRunner
59
60 T_shell_context_processor = t.TypeVar(
61 "T_shell_context_processor", bound=ft.ShellContextProcessorCallable
62 )
63 T_teardown = t.TypeVar("T_teardown", bound=ft.TeardownCallable)
64 T_template_filter = t.TypeVar("T_template_filter", bound=ft.TemplateFilterCallable)
65 T_template_global = t.TypeVar("T_template_global", bound=ft.TemplateGlobalCallable)
66 T_template_test = t.TypeVar("T_template_test", bound=ft.TemplateTestCallable)
67
68
69 def _make_timedelta(value: timedelta | int | None) -> timedelta | None:
70 if value is None or isinstance(value, timedelta):
71 return value
72
73 return timedelta(seconds=value)
74
75
76 class Flask(App):
77 """The flask object implements a WSGI application and acts as the central
78 object. It is passed the name of the module or package of the
79 application. Once it is created it will act as a central registry for
80 the view functions, the URL rules, template configuration and much more.
81
82 The name of the package is used to resolve resources from inside the
83 package or the folder the module is contained in depending on if the
84 package parameter resolves to an actual python package (a folder with
85 an :file:`__init__.py` file inside) or a standard module (just a ``.py`` file).
86
87 For more information about resource loading, see :func:`open_resource`.
88
89 Usually you create a :class:`Flask` instance in your main module or
90 in the :file:`__init__.py` file of your package like this::
91
92 from flask import Flask
93 app = Flask(__name__)
94
95 .. admonition:: About the First Parameter
96
97 The idea of the first parameter is to give Flask an idea of what
98 belongs to your application. This name is used to find resources
99 on the filesystem, can be used by extensions to improve debugging
100 information and a lot more.
101
102 So it's important what you provide there. If you are using a single
103 module, `__name__` is always the correct value. If you however are
104 using a package, it's usually recommended to hardcode the name of
105 your package there.
106
107 For example if your application is defined in :file:`yourapplication/app.py`
108 you should create it with one of the two versions below::
109
110 app = Flask('yourapplication')
111 app = Flask(__name__.split('.')[0])
112
113 Why is that? The application will work even with `__name__`, thanks
114 to how resources are looked up. However it will make debugging more
115 painful. Certain extensions can make assumptions based on the
116 import name of your application. For example the Flask-SQLAlchemy
117 extension will look for the code in your application that triggered
118 an SQL query in debug mode. If the import name is not properly set
119 up, that debugging information is lost. (For example it would only
120 pick up SQL queries in `yourapplication.app` and not
121 `yourapplication.views.frontend`)
122
123 .. versionadded:: 0.7
124 The `static_url_path`, `static_folder`, and `template_folder`
125 parameters were added.
126
127 .. versionadded:: 0.8
128 The `instance_path` and `instance_relative_config` parameters were
129 added.
130
131 .. versionadded:: 0.11
132 The `root_path` parameter was added.
133
134 .. versionadded:: 1.0
135 The ``host_matching`` and ``static_host`` parameters were added.
136
137 .. versionadded:: 1.0
138 The ``subdomain_matching`` parameter was added. Subdomain
139 matching needs to be enabled manually now. Setting
140 :data:`SERVER_NAME` does not implicitly enable it.
141
142 :param import_name: the name of the application package
143 :param static_url_path: can be used to specify a different path for the
144 static files on the web. Defaults to the name
145 of the `static_folder` folder.
146 :param static_folder: The folder with static files that is served at
147 ``static_url_path``. Relative to the application ``root_path``
148 or an absolute path. Defaults to ``'static'``.
149 :param static_host: the host to use when adding the static route.
150 Defaults to None. Required when using ``host_matching=True``
151 with a ``static_folder`` configured.
152 :param host_matching: set ``url_map.host_matching`` attribute.
153 Defaults to False.
154 :param subdomain_matching: consider the subdomain relative to
155 :data:`SERVER_NAME` when matching routes. Defaults to False.
156 :param template_folder: the folder that contains the templates that should
157 be used by the application. Defaults to
158 ``'templates'`` folder in the root path of the
159 application.
160 :param instance_path: An alternative instance path for the application.
161 By default the folder ``'instance'`` next to the
162 package or module is assumed to be the instance
163 path.
164 :param instance_relative_config: if set to ``True`` relative filenames
165 for loading the config are assumed to
166 be relative to the instance path instead
167 of the application root.
168 :param root_path: The path to the root of the application files.
169 This should only be set manually when it can't be detected
170 automatically, such as for namespace packages.
171 """
172
173 default_config = ImmutableDict(
174 {
175 "DEBUG": None,
176 "TESTING": False,
177 "PROPAGATE_EXCEPTIONS": None,
178 "SECRET_KEY": None,
179 "PERMANENT_SESSION_LIFETIME": timedelta(days=31),
180 "USE_X_SENDFILE": False,
181 "SERVER_NAME": None,
182 "APPLICATION_ROOT": "/",
183 "SESSION_COOKIE_NAME": "session",
184 "SESSION_COOKIE_DOMAIN": None,
185 "SESSION_COOKIE_PATH": None,
186 "SESSION_COOKIE_HTTPONLY": True,
187 "SESSION_COOKIE_SECURE": False,
188 "SESSION_COOKIE_SAMESITE": None,
189 "SESSION_REFRESH_EACH_REQUEST": True,
190 "MAX_CONTENT_LENGTH": None,
191 "SEND_FILE_MAX_AGE_DEFAULT": None,
192 "TRAP_BAD_REQUEST_ERRORS": None,
193 "TRAP_HTTP_EXCEPTIONS": False,
194 "EXPLAIN_TEMPLATE_LOADING": False,
195 "PREFERRED_URL_SCHEME": "http",
196 "TEMPLATES_AUTO_RELOAD": None,
197 "MAX_COOKIE_SIZE": 4093,
198 }
199 )
200
201 #: The class that is used for request objects. See :class:`~flask.Request`
202 #: for more information.
203 request_class = Request
204
205 #: The class that is used for response objects. See
206 #: :class:`~flask.Response` for more information.
207 response_class = Response
208
209 #: the session interface to use. By default an instance of
210 #: :class:`~flask.sessions.SecureCookieSessionInterface` is used here.
211 #:
212 #: .. versionadded:: 0.8
213 session_interface: SessionInterface = SecureCookieSessionInterface()
214
215 def __init__(
216 self,
217 import_name: str,
218 static_url_path: str | None = None,
219 static_folder: str | os.PathLike | None = "static",
220 static_host: str | None = None,
221 host_matching: bool = False,
222 subdomain_matching: bool = False,
223 template_folder: str | os.PathLike | None = "templates",
224 instance_path: str | None = None,
225 instance_relative_config: bool = False,
226 root_path: str | None = None,
227 ):
228 super().__init__(
229 import_name=import_name,
230 static_url_path=static_url_path,
231 static_folder=static_folder,
232 static_host=static_host,
233 host_matching=host_matching,
234 subdomain_matching=subdomain_matching,
235 template_folder=template_folder,
236 instance_path=instance_path,
237 instance_relative_config=instance_relative_config,
238 root_path=root_path,
239 )
240
241 # Add a static route using the provided static_url_path, static_host,
242 # and static_folder if there is a configured static_folder.
243 # Note we do this without checking if static_folder exists.
244 # For one, it might be created while the server is running (e.g. during
245 # development). Also, Google App Engine stores static files somewhere
246 if self.has_static_folder:
247 assert (
248 bool(static_host) == host_matching
249 ), "Invalid static_host/host_matching combination"
250 # Use a weakref to avoid creating a reference cycle between the app
251 # and the view function (see #3761).
252 self_ref = weakref.ref(self)
253 self.add_url_rule(
254 f"{self.static_url_path}/<path:filename>",
255 endpoint="static",
256 host=static_host,
257 view_func=lambda **kw: self_ref().send_static_file(**kw), # type: ignore # noqa: B950
258 )
259
260 def get_send_file_max_age(self, filename: str | None) -> int | None:
261 """Used by :func:`send_file` to determine the ``max_age`` cache
262 value for a given file path if it wasn't passed.
263
264 By default, this returns :data:`SEND_FILE_MAX_AGE_DEFAULT` from
265 the configuration of :data:`~flask.current_app`. This defaults
266 to ``None``, which tells the browser to use conditional requests
267 instead of a timed cache, which is usually preferable.
268
269 Note this is a duplicate of the same method in the Flask
270 class.
271
272 .. versionchanged:: 2.0
273 The default configuration is ``None`` instead of 12 hours.
274
275 .. versionadded:: 0.9
276 """
277 value = current_app.config["SEND_FILE_MAX_AGE_DEFAULT"]
278
279 if value is None:
280 return None
281
282 if isinstance(value, timedelta):
283 return int(value.total_seconds())
284
285 return value
286
287 def send_static_file(self, filename: str) -> Response:
288 """The view function used to serve files from
289 :attr:`static_folder`. A route is automatically registered for
290 this view at :attr:`static_url_path` if :attr:`static_folder` is
291 set.
292
293 Note this is a duplicate of the same method in the Flask
294 class.
295
296 .. versionadded:: 0.5
297
298 """
299 if not self.has_static_folder:
300 raise RuntimeError("'static_folder' must be set to serve static_files.")
301
302 # send_file only knows to call get_send_file_max_age on the app,
303 # call it here so it works for blueprints too.
304 max_age = self.get_send_file_max_age(filename)
305 return send_from_directory(
306 t.cast(str, self.static_folder), filename, max_age=max_age
307 )
308
309 def open_resource(self, resource: str, mode: str = "rb") -> t.IO[t.AnyStr]:
310 """Open a resource file relative to :attr:`root_path` for
311 reading.
312
313 For example, if the file ``schema.sql`` is next to the file
314 ``app.py`` where the ``Flask`` app is defined, it can be opened
315 with:
316
317 .. code-block:: python
318
319 with app.open_resource("schema.sql") as f:
320 conn.executescript(f.read())
321
322 :param resource: Path to the resource relative to
323 :attr:`root_path`.
324 :param mode: Open the file in this mode. Only reading is
325 supported, valid values are "r" (or "rt") and "rb".
326
327 Note this is a duplicate of the same method in the Flask
328 class.
329
330 """
331 if mode not in {"r", "rt", "rb"}:
332 raise ValueError("Resources can only be opened for reading.")
333
334 return open(os.path.join(self.root_path, resource), mode)
335
336 def open_instance_resource(self, resource: str, mode: str = "rb") -> t.IO[t.AnyStr]:
337 """Opens a resource from the application's instance folder
338 (:attr:`instance_path`). Otherwise works like
339 :meth:`open_resource`. Instance resources can also be opened for
340 writing.
341
342 :param resource: the name of the resource. To access resources within
343 subfolders use forward slashes as separator.
344 :param mode: resource file opening mode, default is 'rb'.
345 """
346 return open(os.path.join(self.instance_path, resource), mode)
347
348 def create_jinja_environment(self) -> Environment:
349 """Create the Jinja environment based on :attr:`jinja_options`
350 and the various Jinja-related methods of the app. Changing
351 :attr:`jinja_options` after this will have no effect. Also adds
352 Flask-related globals and filters to the environment.
353
354 .. versionchanged:: 0.11
355 ``Environment.auto_reload`` set in accordance with
356 ``TEMPLATES_AUTO_RELOAD`` configuration option.
357
358 .. versionadded:: 0.5
359 """
360 options = dict(self.jinja_options)
361
362 if "autoescape" not in options:
363 options["autoescape"] = self.select_jinja_autoescape
364
365 if "auto_reload" not in options:
366 auto_reload = self.config["TEMPLATES_AUTO_RELOAD"]
367
368 if auto_reload is None:
369 auto_reload = self.debug
370
371 options["auto_reload"] = auto_reload
372
373 rv = self.jinja_environment(self, **options)
374 rv.globals.update(
375 url_for=self.url_for,
376 get_flashed_messages=get_flashed_messages,
377 config=self.config,
378 # request, session and g are normally added with the
379 # context processor for efficiency reasons but for imported
380 # templates we also want the proxies in there.
381 request=request,
382 session=session,
383 g=g,
384 )
385 rv.policies["json.dumps_function"] = self.json.dumps
386 return rv
387
388 def create_url_adapter(self, request: Request | None) -> MapAdapter | None:
389 """Creates a URL adapter for the given request. The URL adapter
390 is created at a point where the request context is not yet set
391 up so the request is passed explicitly.
392
393 .. versionadded:: 0.6
394
395 .. versionchanged:: 0.9
396 This can now also be called without a request object when the
397 URL adapter is created for the application context.
398
399 .. versionchanged:: 1.0
400 :data:`SERVER_NAME` no longer implicitly enables subdomain
401 matching. Use :attr:`subdomain_matching` instead.
402 """
403 if request is not None:
404 # If subdomain matching is disabled (the default), use the
405 # default subdomain in all cases. This should be the default
406 # in Werkzeug but it currently does not have that feature.
407 if not self.subdomain_matching:
408 subdomain = self.url_map.default_subdomain or None
409 else:
410 subdomain = None
411
412 return self.url_map.bind_to_environ(
413 request.environ,
414 server_name=self.config["SERVER_NAME"],
415 subdomain=subdomain,
416 )
417 # We need at the very least the server name to be set for this
418 # to work.
419 if self.config["SERVER_NAME"] is not None:
420 return self.url_map.bind(
421 self.config["SERVER_NAME"],
422 script_name=self.config["APPLICATION_ROOT"],
423 url_scheme=self.config["PREFERRED_URL_SCHEME"],
424 )
425
426 return None
427
428 def raise_routing_exception(self, request: Request) -> t.NoReturn:
429 """Intercept routing exceptions and possibly do something else.
430
431 In debug mode, intercept a routing redirect and replace it with
432 an error if the body will be discarded.
433
434 With modern Werkzeug this shouldn't occur, since it now uses a
435 308 status which tells the browser to resend the method and
436 body.
437
438 .. versionchanged:: 2.1
439 Don't intercept 307 and 308 redirects.
440
441 :meta private:
442 :internal:
443 """
444 if (
445 not self.debug
446 or not isinstance(request.routing_exception, RequestRedirect)
447 or request.routing_exception.code in {307, 308}
448 or request.method in {"GET", "HEAD", "OPTIONS"}
449 ):
450 raise request.routing_exception # type: ignore
451
452 from .debughelpers import FormDataRoutingRedirect
453
454 raise FormDataRoutingRedirect(request)
455
456 def update_template_context(self, context: dict) -> None:
457 """Update the template context with some commonly used variables.
458 This injects request, session, config and g into the template
459 context as well as everything template context processors want
460 to inject. Note that the as of Flask 0.6, the original values
461 in the context will not be overridden if a context processor
462 decides to return a value with the same key.
463
464 :param context: the context as a dictionary that is updated in place
465 to add extra variables.
466 """
467 names: t.Iterable[str | None] = (None,)
468
469 # A template may be rendered outside a request context.
470 if request:
471 names = chain(names, reversed(request.blueprints))
472
473 # The values passed to render_template take precedence. Keep a
474 # copy to re-apply after all context functions.
475 orig_ctx = context.copy()
476
477 for name in names:
478 if name in self.template_context_processors:
479 for func in self.template_context_processors[name]:
480 context.update(self.ensure_sync(func)())
481
482 context.update(orig_ctx)
483
484 def make_shell_context(self) -> dict:
485 """Returns the shell context for an interactive shell for this
486 application. This runs all the registered shell context
487 processors.
488
489 .. versionadded:: 0.11
490 """
491 rv = {"app": self, "g": g}
492 for processor in self.shell_context_processors:
493 rv.update(processor())
494 return rv
495
496 def run(
497 self,
498 host: str | None = None,
499 port: int | None = None,
500 debug: bool | None = None,
501 load_dotenv: bool = True,
502 **options: t.Any,
503 ) -> None:
504 """Runs the application on a local development server.
505
506 Do not use ``run()`` in a production setting. It is not intended to
507 meet security and performance requirements for a production server.
508 Instead, see :doc:`/deploying/index` for WSGI server recommendations.
509
510 If the :attr:`debug` flag is set the server will automatically reload
511 for code changes and show a debugger in case an exception happened.
512
513 If you want to run the application in debug mode, but disable the
514 code execution on the interactive debugger, you can pass
515 ``use_evalex=False`` as parameter. This will keep the debugger's
516 traceback screen active, but disable code execution.
517
518 It is not recommended to use this function for development with
519 automatic reloading as this is badly supported. Instead you should
520 be using the :command:`flask` command line script's ``run`` support.
521
522 .. admonition:: Keep in Mind
523
524 Flask will suppress any server error with a generic error page
525 unless it is in debug mode. As such to enable just the
526 interactive debugger without the code reloading, you have to
527 invoke :meth:`run` with ``debug=True`` and ``use_reloader=False``.
528 Setting ``use_debugger`` to ``True`` without being in debug mode
529 won't catch any exceptions because there won't be any to
530 catch.
531
532 :param host: the hostname to listen on. Set this to ``'0.0.0.0'`` to
533 have the server available externally as well. Defaults to
534 ``'127.0.0.1'`` or the host in the ``SERVER_NAME`` config variable
535 if present.
536 :param port: the port of the webserver. Defaults to ``5000`` or the
537 port defined in the ``SERVER_NAME`` config variable if present.
538 :param debug: if given, enable or disable debug mode. See
539 :attr:`debug`.
540 :param load_dotenv: Load the nearest :file:`.env` and :file:`.flaskenv`
541 files to set environment variables. Will also change the working
542 directory to the directory containing the first file found.
543 :param options: the options to be forwarded to the underlying Werkzeug
544 server. See :func:`werkzeug.serving.run_simple` for more
545 information.
546
547 .. versionchanged:: 1.0
548 If installed, python-dotenv will be used to load environment
549 variables from :file:`.env` and :file:`.flaskenv` files.
550
551 The :envvar:`FLASK_DEBUG` environment variable will override :attr:`debug`.
552
553 Threaded mode is enabled by default.
554
555 .. versionchanged:: 0.10
556 The default port is now picked from the ``SERVER_NAME``
557 variable.
558 """
559 # Ignore this call so that it doesn't start another server if
560 # the 'flask run' command is used.
561 if os.environ.get("FLASK_RUN_FROM_CLI") == "true":
562 if not is_running_from_reloader():
563 click.secho(
564 " * Ignoring a call to 'app.run()' that would block"
565 " the current 'flask' CLI command.\n"
566 " Only call 'app.run()' in an 'if __name__ =="
567 ' "__main__"\' guard.',
568 fg="red",
569 )
570
571 return
572
573 if get_load_dotenv(load_dotenv):
574 cli.load_dotenv()
575
576 # if set, env var overrides existing value
577 if "FLASK_DEBUG" in os.environ:
578 self.debug = get_debug_flag()
579
580 # debug passed to method overrides all other sources
581 if debug is not None:
582 self.debug = bool(debug)
583
584 server_name = self.config.get("SERVER_NAME")
585 sn_host = sn_port = None
586
587 if server_name:
588 sn_host, _, sn_port = server_name.partition(":")
589
590 if not host:
591 if sn_host:
592 host = sn_host
593 else:
594 host = "127.0.0.1"
595
596 if port or port == 0:
597 port = int(port)
598 elif sn_port:
599 port = int(sn_port)
600 else:
601 port = 5000
602
603 options.setdefault("use_reloader", self.debug)
604 options.setdefault("use_debugger", self.debug)
605 options.setdefault("threaded", True)
606
607 cli.show_server_banner(self.debug, self.name)
608
609 from werkzeug.serving import run_simple
610
611 try:
612 run_simple(t.cast(str, host), port, self, **options)
613 finally:
614 # reset the first request information if the development server
615 # reset normally. This makes it possible to restart the server
616 # without reloader and that stuff from an interactive shell.
617 self._got_first_request = False
618
619 def test_client(self, use_cookies: bool = True, **kwargs: t.Any) -> FlaskClient:
620 """Creates a test client for this application. For information
621 about unit testing head over to :doc:`/testing`.
622
623 Note that if you are testing for assertions or exceptions in your
624 application code, you must set ``app.testing = True`` in order for the
625 exceptions to propagate to the test client. Otherwise, the exception
626 will be handled by the application (not visible to the test client) and
627 the only indication of an AssertionError or other exception will be a
628 500 status code response to the test client. See the :attr:`testing`
629 attribute. For example::
630
631 app.testing = True
632 client = app.test_client()
633
634 The test client can be used in a ``with`` block to defer the closing down
635 of the context until the end of the ``with`` block. This is useful if
636 you want to access the context locals for testing::
637
638 with app.test_client() as c:
639 rv = c.get('/?vodka=42')
640 assert request.args['vodka'] == '42'
641
642 Additionally, you may pass optional keyword arguments that will then
643 be passed to the application's :attr:`test_client_class` constructor.
644 For example::
645
646 from flask.testing import FlaskClient
647
648 class CustomClient(FlaskClient):
649 def __init__(self, *args, **kwargs):
650 self._authentication = kwargs.pop("authentication")
651 super(CustomClient,self).__init__( *args, **kwargs)
652
653 app.test_client_class = CustomClient
654 client = app.test_client(authentication='Basic ....')
655
656 See :class:`~flask.testing.FlaskClient` for more information.
657
658 .. versionchanged:: 0.4
659 added support for ``with`` block usage for the client.
660
661 .. versionadded:: 0.7
662 The `use_cookies` parameter was added as well as the ability
663 to override the client to be used by setting the
664 :attr:`test_client_class` attribute.
665
666 .. versionchanged:: 0.11
667 Added `**kwargs` to support passing additional keyword arguments to
668 the constructor of :attr:`test_client_class`.
669 """
670 cls = self.test_client_class
671 if cls is None:
672 from .testing import FlaskClient as cls
673 return cls( # type: ignore
674 self, self.response_class, use_cookies=use_cookies, **kwargs
675 )
676
677 def test_cli_runner(self, **kwargs: t.Any) -> FlaskCliRunner:
678 """Create a CLI runner for testing CLI commands.
679 See :ref:`testing-cli`.
680
681 Returns an instance of :attr:`test_cli_runner_class`, by default
682 :class:`~flask.testing.FlaskCliRunner`. The Flask app object is
683 passed as the first argument.
684
685 .. versionadded:: 1.0
686 """
687 cls = self.test_cli_runner_class
688
689 if cls is None:
690 from .testing import FlaskCliRunner as cls
691
692 return cls(self, **kwargs) # type: ignore
693
694 def handle_http_exception(
695 self, e: HTTPException
696 ) -> HTTPException | ft.ResponseReturnValue:
697 """Handles an HTTP exception. By default this will invoke the
698 registered error handlers and fall back to returning the
699 exception as response.
700
701 .. versionchanged:: 1.0.3
702 ``RoutingException``, used internally for actions such as
703 slash redirects during routing, is not passed to error
704 handlers.
705
706 .. versionchanged:: 1.0
707 Exceptions are looked up by code *and* by MRO, so
708 ``HTTPException`` subclasses can be handled with a catch-all
709 handler for the base ``HTTPException``.
710
711 .. versionadded:: 0.3
712 """
713 # Proxy exceptions don't have error codes. We want to always return
714 # those unchanged as errors
715 if e.code is None:
716 return e
717
718 # RoutingExceptions are used internally to trigger routing
719 # actions, such as slash redirects raising RequestRedirect. They
720 # are not raised or handled in user code.
721 if isinstance(e, RoutingException):
722 return e
723
724 handler = self._find_error_handler(e, request.blueprints)
725 if handler is None:
726 return e
727 return self.ensure_sync(handler)(e)
728
729 def handle_user_exception(
730 self, e: Exception
731 ) -> HTTPException | ft.ResponseReturnValue:
732 """This method is called whenever an exception occurs that
733 should be handled. A special case is :class:`~werkzeug
734 .exceptions.HTTPException` which is forwarded to the
735 :meth:`handle_http_exception` method. This function will either
736 return a response value or reraise the exception with the same
737 traceback.
738
739 .. versionchanged:: 1.0
740 Key errors raised from request data like ``form`` show the
741 bad key in debug mode rather than a generic bad request
742 message.
743
744 .. versionadded:: 0.7
745 """
746 if isinstance(e, BadRequestKeyError) and (
747 self.debug or self.config["TRAP_BAD_REQUEST_ERRORS"]
748 ):
749 e.show_exception = True
750
751 if isinstance(e, HTTPException) and not self.trap_http_exception(e):
752 return self.handle_http_exception(e)
753
754 handler = self._find_error_handler(e, request.blueprints)
755
756 if handler is None:
757 raise
758
759 return self.ensure_sync(handler)(e)
760
761 def handle_exception(self, e: Exception) -> Response:
762 """Handle an exception that did not have an error handler
763 associated with it, or that was raised from an error handler.
764 This always causes a 500 ``InternalServerError``.
765
766 Always sends the :data:`got_request_exception` signal.
767
768 If :data:`PROPAGATE_EXCEPTIONS` is ``True``, such as in debug
769 mode, the error will be re-raised so that the debugger can
770 display it. Otherwise, the original exception is logged, and
771 an :exc:`~werkzeug.exceptions.InternalServerError` is returned.
772
773 If an error handler is registered for ``InternalServerError`` or
774 ``500``, it will be used. For consistency, the handler will
775 always receive the ``InternalServerError``. The original
776 unhandled exception is available as ``e.original_exception``.
777
778 .. versionchanged:: 1.1.0
779 Always passes the ``InternalServerError`` instance to the
780 handler, setting ``original_exception`` to the unhandled
781 error.
782
783 .. versionchanged:: 1.1.0
784 ``after_request`` functions and other finalization is done
785 even for the default 500 response when there is no handler.
786
787 .. versionadded:: 0.3
788 """
789 exc_info = sys.exc_info()
790 got_request_exception.send(self, _async_wrapper=self.ensure_sync, exception=e)
791 propagate = self.config["PROPAGATE_EXCEPTIONS"]
792
793 if propagate is None:
794 propagate = self.testing or self.debug
795
796 if propagate:
797 # Re-raise if called with an active exception, otherwise
798 # raise the passed in exception.
799 if exc_info[1] is e:
800 raise
801
802 raise e
803
804 self.log_exception(exc_info)
805 server_error: InternalServerError | ft.ResponseReturnValue
806 server_error = InternalServerError(original_exception=e)
807 handler = self._find_error_handler(server_error, request.blueprints)
808
809 if handler is not None:
810 server_error = self.ensure_sync(handler)(server_error)
811
812 return self.finalize_request(server_error, from_error_handler=True)
813
814 def log_exception(
815 self,
816 exc_info: (tuple[type, BaseException, TracebackType] | tuple[None, None, None]),
817 ) -> None:
818 """Logs an exception. This is called by :meth:`handle_exception`
819 if debugging is disabled and right before the handler is called.
820 The default implementation logs the exception as error on the
821 :attr:`logger`.
822
823 .. versionadded:: 0.8
824 """
825 self.logger.error(
826 f"Exception on {request.path} [{request.method}]", exc_info=exc_info
827 )
828
829 def dispatch_request(self) -> ft.ResponseReturnValue:
830 """Does the request dispatching. Matches the URL and returns the
831 return value of the view or error handler. This does not have to
832 be a response object. In order to convert the return value to a
833 proper response object, call :func:`make_response`.
834
835 .. versionchanged:: 0.7
836 This no longer does the exception handling, this code was
837 moved to the new :meth:`full_dispatch_request`.
838 """
839 req = request_ctx.request
840 if req.routing_exception is not None:
841 self.raise_routing_exception(req)
842 rule: Rule = req.url_rule # type: ignore[assignment]
843 # if we provide automatic options for this URL and the
844 # request came with the OPTIONS method, reply automatically
845 if (
846 getattr(rule, "provide_automatic_options", False)
847 and req.method == "OPTIONS"
848 ):
849 return self.make_default_options_response()
850 # otherwise dispatch to the handler for that endpoint
851 view_args: dict[str, t.Any] = req.view_args # type: ignore[assignment]
852 return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
853
854 def full_dispatch_request(self) -> Response:
855 """Dispatches the request and on top of that performs request
856 pre and postprocessing as well as HTTP exception catching and
857 error handling.
858
859 .. versionadded:: 0.7
860 """
861 self._got_first_request = True
862
863 try:
864 request_started.send(self, _async_wrapper=self.ensure_sync)
865 rv = self.preprocess_request()
866 if rv is None:
867 rv = self.dispatch_request()
868 except Exception as e:
869 rv = self.handle_user_exception(e)
870 return self.finalize_request(rv)
871
872 def finalize_request(
873 self,
874 rv: ft.ResponseReturnValue | HTTPException,
875 from_error_handler: bool = False,
876 ) -> Response:
877 """Given the return value from a view function this finalizes
878 the request by converting it into a response and invoking the
879 postprocessing functions. This is invoked for both normal
880 request dispatching as well as error handlers.
881
882 Because this means that it might be called as a result of a
883 failure a special safe mode is available which can be enabled
884 with the `from_error_handler` flag. If enabled, failures in
885 response processing will be logged and otherwise ignored.
886
887 :internal:
888 """
889 response = self.make_response(rv)
890 try:
891 response = self.process_response(response)
892 request_finished.send(
893 self, _async_wrapper=self.ensure_sync, response=response
894 )
895 except Exception:
896 if not from_error_handler:
897 raise
898 self.logger.exception(
899 "Request finalizing failed with an error while handling an error"
900 )
901 return response
902
903 def make_default_options_response(self) -> Response:
904 """This method is called to create the default ``OPTIONS`` response.
905 This can be changed through subclassing to change the default
906 behavior of ``OPTIONS`` responses.
907
908 .. versionadded:: 0.7
909 """
910 adapter = request_ctx.url_adapter
911 methods = adapter.allowed_methods() # type: ignore[union-attr]
912 rv = self.response_class()
913 rv.allow.update(methods)
914 return rv
915
916 def ensure_sync(self, func: t.Callable) -> t.Callable:
917 """Ensure that the function is synchronous for WSGI workers.
918 Plain ``def`` functions are returned as-is. ``async def``
919 functions are wrapped to run and wait for the response.
920
921 Override this method to change how the app runs async views.
922
923 .. versionadded:: 2.0
924 """
925 if iscoroutinefunction(func):
926 return self.async_to_sync(func)
927
928 return func
929
930 def async_to_sync(
931 self, func: t.Callable[..., t.Coroutine]
932 ) -> t.Callable[..., t.Any]:
933 """Return a sync function that will run the coroutine function.
934
935 .. code-block:: python
936
937 result = app.async_to_sync(func)(*args, **kwargs)
938
939 Override this method to change how the app converts async code
940 to be synchronously callable.
941
942 .. versionadded:: 2.0
943 """
944 try:
945 from asgiref.sync import async_to_sync as asgiref_async_to_sync
946 except ImportError:
947 raise RuntimeError(
948 "Install Flask with the 'async' extra in order to use async views."
949 ) from None
950
951 return asgiref_async_to_sync(func)
952
953 def url_for(
954 self,
955 /,
956 endpoint: str,
957 *,
958 _anchor: str | None = None,
959 _method: str | None = None,
960 _scheme: str | None = None,
961 _external: bool | None = None,
962 **values: t.Any,
963 ) -> str:
964 """Generate a URL to the given endpoint with the given values.
965
966 This is called by :func:`flask.url_for`, and can be called
967 directly as well.
968
969 An *endpoint* is the name of a URL rule, usually added with
970 :meth:`@app.route() <route>`, and usually the same name as the
971 view function. A route defined in a :class:`~flask.Blueprint`
972 will prepend the blueprint's name separated by a ``.`` to the
973 endpoint.
974
975 In some cases, such as email messages, you want URLs to include
976 the scheme and domain, like ``https://example.com/hello``. When
977 not in an active request, URLs will be external by default, but
978 this requires setting :data:`SERVER_NAME` so Flask knows what
979 domain to use. :data:`APPLICATION_ROOT` and
980 :data:`PREFERRED_URL_SCHEME` should also be configured as
981 needed. This config is only used when not in an active request.
982
983 Functions can be decorated with :meth:`url_defaults` to modify
984 keyword arguments before the URL is built.
985
986 If building fails for some reason, such as an unknown endpoint
987 or incorrect values, the app's :meth:`handle_url_build_error`
988 method is called. If that returns a string, that is returned,
989 otherwise a :exc:`~werkzeug.routing.BuildError` is raised.
990
991 :param endpoint: The endpoint name associated with the URL to
992 generate. If this starts with a ``.``, the current blueprint
993 name (if any) will be used.
994 :param _anchor: If given, append this as ``#anchor`` to the URL.
995 :param _method: If given, generate the URL associated with this
996 method for the endpoint.
997 :param _scheme: If given, the URL will have this scheme if it
998 is external.
999 :param _external: If given, prefer the URL to be internal
1000 (False) or require it to be external (True). External URLs
1001 include the scheme and domain. When not in an active
1002 request, URLs are external by default.
1003 :param values: Values to use for the variable parts of the URL
1004 rule. Unknown keys are appended as query string arguments,
1005 like ``?a=b&c=d``.
1006
1007 .. versionadded:: 2.2
1008 Moved from ``flask.url_for``, which calls this method.
1009 """
1010 req_ctx = _cv_request.get(None)
1011
1012 if req_ctx is not None:
1013 url_adapter = req_ctx.url_adapter
1014 blueprint_name = req_ctx.request.blueprint
1015
1016 # If the endpoint starts with "." and the request matches a
1017 # blueprint, the endpoint is relative to the blueprint.
1018 if endpoint[:1] == ".":
1019 if blueprint_name is not None:
1020 endpoint = f"{blueprint_name}{endpoint}"
1021 else:
1022 endpoint = endpoint[1:]
1023
1024 # When in a request, generate a URL without scheme and
1025 # domain by default, unless a scheme is given.
1026 if _external is None:
1027 _external = _scheme is not None
1028 else:
1029 app_ctx = _cv_app.get(None)
1030
1031 # If called by helpers.url_for, an app context is active,
1032 # use its url_adapter. Otherwise, app.url_for was called
1033 # directly, build an adapter.
1034 if app_ctx is not None:
1035 url_adapter = app_ctx.url_adapter
1036 else:
1037 url_adapter = self.create_url_adapter(None)
1038
1039 if url_adapter is None:
1040 raise RuntimeError(
1041 "Unable to build URLs outside an active request"
1042 " without 'SERVER_NAME' configured. Also configure"
1043 " 'APPLICATION_ROOT' and 'PREFERRED_URL_SCHEME' as"
1044 " needed."
1045 )
1046
1047 # When outside a request, generate a URL with scheme and
1048 # domain by default.
1049 if _external is None:
1050 _external = True
1051
1052 # It is an error to set _scheme when _external=False, in order
1053 # to avoid accidental insecure URLs.
1054 if _scheme is not None and not _external:
1055 raise ValueError("When specifying '_scheme', '_external' must be True.")
1056
1057 self.inject_url_defaults(endpoint, values)
1058
1059 try:
1060 rv = url_adapter.build( # type: ignore[union-attr]
1061 endpoint,
1062 values,
1063 method=_method,
1064 url_scheme=_scheme,
1065 force_external=_external,
1066 )
1067 except BuildError as error:
1068 values.update(
1069 _anchor=_anchor, _method=_method, _scheme=_scheme, _external=_external
1070 )
1071 return self.handle_url_build_error(error, endpoint, values)
1072
1073 if _anchor is not None:
1074 _anchor = _url_quote(_anchor, safe="%!#$&'()*+,/:;=?@")
1075 rv = f"{rv}#{_anchor}"
1076
1077 return rv
1078
1079 def make_response(self, rv: ft.ResponseReturnValue) -> Response:
1080 """Convert the return value from a view function to an instance of
1081 :attr:`response_class`.
1082
1083 :param rv: the return value from the view function. The view function
1084 must return a response. Returning ``None``, or the view ending
1085 without returning, is not allowed. The following types are allowed
1086 for ``view_rv``:
1087
1088 ``str``
1089 A response object is created with the string encoded to UTF-8
1090 as the body.
1091
1092 ``bytes``
1093 A response object is created with the bytes as the body.
1094
1095 ``dict``
1096 A dictionary that will be jsonify'd before being returned.
1097
1098 ``list``
1099 A list that will be jsonify'd before being returned.
1100
1101 ``generator`` or ``iterator``
1102 A generator that returns ``str`` or ``bytes`` to be
1103 streamed as the response.
1104
1105 ``tuple``
1106 Either ``(body, status, headers)``, ``(body, status)``, or
1107 ``(body, headers)``, where ``body`` is any of the other types
1108 allowed here, ``status`` is a string or an integer, and
1109 ``headers`` is a dictionary or a list of ``(key, value)``
1110 tuples. If ``body`` is a :attr:`response_class` instance,
1111 ``status`` overwrites the exiting value and ``headers`` are
1112 extended.
1113
1114 :attr:`response_class`
1115 The object is returned unchanged.
1116
1117 other :class:`~werkzeug.wrappers.Response` class
1118 The object is coerced to :attr:`response_class`.
1119
1120 :func:`callable`
1121 The function is called as a WSGI application. The result is
1122 used to create a response object.
1123
1124 .. versionchanged:: 2.2
1125 A generator will be converted to a streaming response.
1126 A list will be converted to a JSON response.
1127
1128 .. versionchanged:: 1.1
1129 A dict will be converted to a JSON response.
1130
1131 .. versionchanged:: 0.9
1132 Previously a tuple was interpreted as the arguments for the
1133 response object.
1134 """
1135
1136 status = headers = None
1137
1138 # unpack tuple returns
1139 if isinstance(rv, tuple):
1140 len_rv = len(rv)
1141
1142 # a 3-tuple is unpacked directly
1143 if len_rv == 3:
1144 rv, status, headers = rv # type: ignore[misc]
1145 # decide if a 2-tuple has status or headers
1146 elif len_rv == 2:
1147 if isinstance(rv[1], (Headers, dict, tuple, list)):
1148 rv, headers = rv
1149 else:
1150 rv, status = rv # type: ignore[assignment,misc]
1151 # other sized tuples are not allowed
1152 else:
1153 raise TypeError(
1154 "The view function did not return a valid response tuple."
1155 " The tuple must have the form (body, status, headers),"
1156 " (body, status), or (body, headers)."
1157 )
1158
1159 # the body must not be None
1160 if rv is None:
1161 raise TypeError(
1162 f"The view function for {request.endpoint!r} did not"
1163 " return a valid response. The function either returned"
1164 " None or ended without a return statement."
1165 )
1166
1167 # make sure the body is an instance of the response class
1168 if not isinstance(rv, self.response_class):
1169 if isinstance(rv, (str, bytes, bytearray)) or isinstance(rv, _abc_Iterator):
1170 # let the response class set the status and headers instead of
1171 # waiting to do it manually, so that the class can handle any
1172 # special logic
1173 rv = self.response_class(
1174 rv,
1175 status=status,
1176 headers=headers, # type: ignore[arg-type]
1177 )
1178 status = headers = None
1179 elif isinstance(rv, (dict, list)):
1180 rv = self.json.response(rv)
1181 elif isinstance(rv, BaseResponse) or callable(rv):
1182 # evaluate a WSGI callable, or coerce a different response
1183 # class to the correct type
1184 try:
1185 rv = self.response_class.force_type(
1186 rv, request.environ # type: ignore[arg-type]
1187 )
1188 except TypeError as e:
1189 raise TypeError(
1190 f"{e}\nThe view function did not return a valid"
1191 " response. The return type must be a string,"
1192 " dict, list, tuple with headers or status,"
1193 " Response instance, or WSGI callable, but it"
1194 f" was a {type(rv).__name__}."
1195 ).with_traceback(sys.exc_info()[2]) from None
1196 else:
1197 raise TypeError(
1198 "The view function did not return a valid"
1199 " response. The return type must be a string,"
1200 " dict, list, tuple with headers or status,"
1201 " Response instance, or WSGI callable, but it was a"
1202 f" {type(rv).__name__}."
1203 )
1204
1205 rv = t.cast(Response, rv)
1206 # prefer the status if it was provided
1207 if status is not None:
1208 if isinstance(status, (str, bytes, bytearray)):
1209 rv.status = status
1210 else:
1211 rv.status_code = status
1212
1213 # extend existing headers with provided headers
1214 if headers:
1215 rv.headers.update(headers) # type: ignore[arg-type]
1216
1217 return rv
1218
1219 def preprocess_request(self) -> ft.ResponseReturnValue | None:
1220 """Called before the request is dispatched. Calls
1221 :attr:`url_value_preprocessors` registered with the app and the
1222 current blueprint (if any). Then calls :attr:`before_request_funcs`
1223 registered with the app and the blueprint.
1224
1225 If any :meth:`before_request` handler returns a non-None value, the
1226 value is handled as if it was the return value from the view, and
1227 further request handling is stopped.
1228 """
1229 names = (None, *reversed(request.blueprints))
1230
1231 for name in names:
1232 if name in self.url_value_preprocessors:
1233 for url_func in self.url_value_preprocessors[name]:
1234 url_func(request.endpoint, request.view_args)
1235
1236 for name in names:
1237 if name in self.before_request_funcs:
1238 for before_func in self.before_request_funcs[name]:
1239 rv = self.ensure_sync(before_func)()
1240
1241 if rv is not None:
1242 return rv
1243
1244 return None
1245
1246 def process_response(self, response: Response) -> Response:
1247 """Can be overridden in order to modify the response object
1248 before it's sent to the WSGI server. By default this will
1249 call all the :meth:`after_request` decorated functions.
1250
1251 .. versionchanged:: 0.5
1252 As of Flask 0.5 the functions registered for after request
1253 execution are called in reverse order of registration.
1254
1255 :param response: a :attr:`response_class` object.
1256 :return: a new response object or the same, has to be an
1257 instance of :attr:`response_class`.
1258 """
1259 ctx = request_ctx._get_current_object() # type: ignore[attr-defined]
1260
1261 for func in ctx._after_request_functions:
1262 response = self.ensure_sync(func)(response)
1263
1264 for name in chain(request.blueprints, (None,)):
1265 if name in self.after_request_funcs:
1266 for func in reversed(self.after_request_funcs[name]):
1267 response = self.ensure_sync(func)(response)
1268
1269 if not self.session_interface.is_null_session(ctx.session):
1270 self.session_interface.save_session(self, ctx.session, response)
1271
1272 return response
1273
1274 def do_teardown_request(
1275 self, exc: BaseException | None = _sentinel # type: ignore
1276 ) -> None:
1277 """Called after the request is dispatched and the response is
1278 returned, right before the request context is popped.
1279
1280 This calls all functions decorated with
1281 :meth:`teardown_request`, and :meth:`Blueprint.teardown_request`
1282 if a blueprint handled the request. Finally, the
1283 :data:`request_tearing_down` signal is sent.
1284
1285 This is called by
1286 :meth:`RequestContext.pop() <flask.ctx.RequestContext.pop>`,
1287 which may be delayed during testing to maintain access to
1288 resources.
1289
1290 :param exc: An unhandled exception raised while dispatching the
1291 request. Detected from the current exception information if
1292 not passed. Passed to each teardown function.
1293
1294 .. versionchanged:: 0.9
1295 Added the ``exc`` argument.
1296 """
1297 if exc is _sentinel:
1298 exc = sys.exc_info()[1]
1299
1300 for name in chain(request.blueprints, (None,)):
1301 if name in self.teardown_request_funcs:
1302 for func in reversed(self.teardown_request_funcs[name]):
1303 self.ensure_sync(func)(exc)
1304
1305 request_tearing_down.send(self, _async_wrapper=self.ensure_sync, exc=exc)
1306
1307 def do_teardown_appcontext(
1308 self, exc: BaseException | None = _sentinel # type: ignore
1309 ) -> None:
1310 """Called right before the application context is popped.
1311
1312 When handling a request, the application context is popped
1313 after the request context. See :meth:`do_teardown_request`.
1314
1315 This calls all functions decorated with
1316 :meth:`teardown_appcontext`. Then the
1317 :data:`appcontext_tearing_down` signal is sent.
1318
1319 This is called by
1320 :meth:`AppContext.pop() <flask.ctx.AppContext.pop>`.
1321
1322 .. versionadded:: 0.9
1323 """
1324 if exc is _sentinel:
1325 exc = sys.exc_info()[1]
1326
1327 for func in reversed(self.teardown_appcontext_funcs):
1328 self.ensure_sync(func)(exc)
1329
1330 appcontext_tearing_down.send(self, _async_wrapper=self.ensure_sync, exc=exc)
1331
1332 def app_context(self) -> AppContext:
1333 """Create an :class:`~flask.ctx.AppContext`. Use as a ``with``
1334 block to push the context, which will make :data:`current_app`
1335 point at this application.
1336
1337 An application context is automatically pushed by
1338 :meth:`RequestContext.push() <flask.ctx.RequestContext.push>`
1339 when handling a request, and when running a CLI command. Use
1340 this to manually create a context outside of these situations.
1341
1342 ::
1343
1344 with app.app_context():
1345 init_db()
1346
1347 See :doc:`/appcontext`.
1348
1349 .. versionadded:: 0.9
1350 """
1351 return AppContext(self)
1352
1353 def request_context(self, environ: dict) -> RequestContext:
1354 """Create a :class:`~flask.ctx.RequestContext` representing a
1355 WSGI environment. Use a ``with`` block to push the context,
1356 which will make :data:`request` point at this request.
1357
1358 See :doc:`/reqcontext`.
1359
1360 Typically you should not call this from your own code. A request
1361 context is automatically pushed by the :meth:`wsgi_app` when
1362 handling a request. Use :meth:`test_request_context` to create
1363 an environment and context instead of this method.
1364
1365 :param environ: a WSGI environment
1366 """
1367 return RequestContext(self, environ)
1368
1369 def test_request_context(self, *args: t.Any, **kwargs: t.Any) -> RequestContext:
1370 """Create a :class:`~flask.ctx.RequestContext` for a WSGI
1371 environment created from the given values. This is mostly useful
1372 during testing, where you may want to run a function that uses
1373 request data without dispatching a full request.
1374
1375 See :doc:`/reqcontext`.
1376
1377 Use a ``with`` block to push the context, which will make
1378 :data:`request` point at the request for the created
1379 environment. ::
1380
1381 with app.test_request_context(...):
1382 generate_report()
1383
1384 When using the shell, it may be easier to push and pop the
1385 context manually to avoid indentation. ::
1386
1387 ctx = app.test_request_context(...)
1388 ctx.push()
1389 ...
1390 ctx.pop()
1391
1392 Takes the same arguments as Werkzeug's
1393 :class:`~werkzeug.test.EnvironBuilder`, with some defaults from
1394 the application. See the linked Werkzeug docs for most of the
1395 available arguments. Flask-specific behavior is listed here.
1396
1397 :param path: URL path being requested.
1398 :param base_url: Base URL where the app is being served, which
1399 ``path`` is relative to. If not given, built from
1400 :data:`PREFERRED_URL_SCHEME`, ``subdomain``,
1401 :data:`SERVER_NAME`, and :data:`APPLICATION_ROOT`.
1402 :param subdomain: Subdomain name to append to
1403 :data:`SERVER_NAME`.
1404 :param url_scheme: Scheme to use instead of
1405 :data:`PREFERRED_URL_SCHEME`.
1406 :param data: The request body, either as a string or a dict of
1407 form keys and values.
1408 :param json: If given, this is serialized as JSON and passed as
1409 ``data``. Also defaults ``content_type`` to
1410 ``application/json``.
1411 :param args: other positional arguments passed to
1412 :class:`~werkzeug.test.EnvironBuilder`.
1413 :param kwargs: other keyword arguments passed to
1414 :class:`~werkzeug.test.EnvironBuilder`.
1415 """
1416 from .testing import EnvironBuilder
1417
1418 builder = EnvironBuilder(self, *args, **kwargs)
1419
1420 try:
1421 return self.request_context(builder.get_environ())
1422 finally:
1423 builder.close()
1424
1425 def wsgi_app(self, environ: dict, start_response: t.Callable) -> t.Any:
1426 """The actual WSGI application. This is not implemented in
1427 :meth:`__call__` so that middlewares can be applied without
1428 losing a reference to the app object. Instead of doing this::
1429
1430 app = MyMiddleware(app)
1431
1432 It's a better idea to do this instead::
1433
1434 app.wsgi_app = MyMiddleware(app.wsgi_app)
1435
1436 Then you still have the original application object around and
1437 can continue to call methods on it.
1438
1439 .. versionchanged:: 0.7
1440 Teardown events for the request and app contexts are called
1441 even if an unhandled error occurs. Other events may not be
1442 called depending on when an error occurs during dispatch.
1443 See :ref:`callbacks-and-errors`.
1444
1445 :param environ: A WSGI environment.
1446 :param start_response: A callable accepting a status code,
1447 a list of headers, and an optional exception context to
1448 start the response.
1449 """
1450 ctx = self.request_context(environ)
1451 error: BaseException | None = None
1452 try:
1453 try:
1454 ctx.push()
1455 response = self.full_dispatch_request()
1456 except Exception as e:
1457 error = e
1458 response = self.handle_exception(e)
1459 except: # noqa: B001
1460 error = sys.exc_info()[1]
1461 raise
1462 return response(environ, start_response)
1463 finally:
1464 if "werkzeug.debug.preserve_context" in environ:
1465 environ["werkzeug.debug.preserve_context"](_cv_app.get())
1466 environ["werkzeug.debug.preserve_context"](_cv_request.get())
1467
1468 if error is not None and self.should_ignore_error(error):
1469 error = None
1470
1471 ctx.pop(error)
1472
1473 def __call__(self, environ: dict, start_response: t.Callable) -> t.Any:
1474 """The WSGI server calls the Flask application object as the
1475 WSGI application. This calls :meth:`wsgi_app`, which can be
1476 wrapped to apply middleware.
1477 """
1478 return self.wsgi_app(environ, start_response)