codekingpro/portable-devtools
114k
1from __future__ import annotations2 3import collections.abc as cabc4import os5import sys6import typing as t7import weakref8from datetime import timedelta9from inspect import iscoroutinefunction10from itertools import chain11from types import TracebackType12from urllib.parse import quote as _url_quote13 14import click15from werkzeug.datastructures import Headers16from werkzeug.datastructures import ImmutableDict17from werkzeug.exceptions import BadRequestKeyError18from werkzeug.exceptions import HTTPException19from werkzeug.exceptions import InternalServerError20from werkzeug.routing import BuildError21from werkzeug.routing import MapAdapter22from werkzeug.routing import RequestRedirect23from werkzeug.routing import RoutingException24from werkzeug.routing import Rule25from werkzeug.serving import is_running_from_reloader26from werkzeug.wrappers import Response as BaseResponse27 28from . import cli29from . import typing as ft30from .ctx import AppContext31from .ctx import RequestContext32from .globals import _cv_app33from .globals import _cv_request34from .globals import current_app35from .globals import g36from .globals import request37from .globals import request_ctx38from .globals import session39from .helpers import get_debug_flag40from .helpers import get_flashed_messages41from .helpers import get_load_dotenv42from .helpers import send_from_directory43from .sansio.app import App44from .sansio.scaffold import _sentinel45from .sessions import SecureCookieSessionInterface46from .sessions import SessionInterface47from .signals import appcontext_tearing_down48from .signals import got_request_exception49from .signals import request_finished50from .signals import request_started51from .signals import request_tearing_down52from .templating import Environment53from .wrappers import Request54from .wrappers import Response55 56if t.TYPE_CHECKING: # pragma: no cover57 from _typeshed.wsgi import StartResponse58 from _typeshed.wsgi import WSGIEnvironment59 60 from .testing import FlaskClient61 from .testing import FlaskCliRunner62 63T_shell_context_processor = t.TypeVar(64 "T_shell_context_processor", bound=ft.ShellContextProcessorCallable65)66T_teardown = t.TypeVar("T_teardown", bound=ft.TeardownCallable)67T_template_filter = t.TypeVar("T_template_filter", bound=ft.TemplateFilterCallable)68T_template_global = t.TypeVar("T_template_global", bound=ft.TemplateGlobalCallable)69T_template_test = t.TypeVar("T_template_test", bound=ft.TemplateTestCallable)70 71 72def _make_timedelta(value: timedelta | int | None) -> timedelta | None:73 if value is None or isinstance(value, timedelta):74 return value75 76 return timedelta(seconds=value)77 78 79class Flask(App):80 """The flask object implements a WSGI application and acts as the central81 object. It is passed the name of the module or package of the82 application. Once it is created it will act as a central registry for83 the view functions, the URL rules, template configuration and much more.84 85 The name of the package is used to resolve resources from inside the86 package or the folder the module is contained in depending on if the87 package parameter resolves to an actual python package (a folder with88 an :file:`__init__.py` file inside) or a standard module (just a ``.py`` file).89 90 For more information about resource loading, see :func:`open_resource`.91 92 Usually you create a :class:`Flask` instance in your main module or93 in the :file:`__init__.py` file of your package like this::94 95 from flask import Flask96 app = Flask(__name__)97 98 .. admonition:: About the First Parameter99 100 The idea of the first parameter is to give Flask an idea of what101 belongs to your application. This name is used to find resources102 on the filesystem, can be used by extensions to improve debugging103 information and a lot more.104 105 So it's important what you provide there. If you are using a single106 module, `__name__` is always the correct value. If you however are107 using a package, it's usually recommended to hardcode the name of108 your package there.109 110 For example if your application is defined in :file:`yourapplication/app.py`111 you should create it with one of the two versions below::112 113 app = Flask('yourapplication')114 app = Flask(__name__.split('.')[0])115 116 Why is that? The application will work even with `__name__`, thanks117 to how resources are looked up. However it will make debugging more118 painful. Certain extensions can make assumptions based on the119 import name of your application. For example the Flask-SQLAlchemy120 extension will look for the code in your application that triggered121 an SQL query in debug mode. If the import name is not properly set122 up, that debugging information is lost. (For example it would only123 pick up SQL queries in `yourapplication.app` and not124 `yourapplication.views.frontend`)125 126 .. versionadded:: 0.7127 The `static_url_path`, `static_folder`, and `template_folder`128 parameters were added.129 130 .. versionadded:: 0.8131 The `instance_path` and `instance_relative_config` parameters were132 added.133 134 .. versionadded:: 0.11135 The `root_path` parameter was added.136 137 .. versionadded:: 1.0138 The ``host_matching`` and ``static_host`` parameters were added.139 140 .. versionadded:: 1.0141 The ``subdomain_matching`` parameter was added. Subdomain142 matching needs to be enabled manually now. Setting143 :data:`SERVER_NAME` does not implicitly enable it.144 145 :param import_name: the name of the application package146 :param static_url_path: can be used to specify a different path for the147 static files on the web. Defaults to the name148 of the `static_folder` folder.149 :param static_folder: The folder with static files that is served at150 ``static_url_path``. Relative to the application ``root_path``151 or an absolute path. Defaults to ``'static'``.152 :param static_host: the host to use when adding the static route.153 Defaults to None. Required when using ``host_matching=True``154 with a ``static_folder`` configured.155 :param host_matching: set ``url_map.host_matching`` attribute.156 Defaults to False.157 :param subdomain_matching: consider the subdomain relative to158 :data:`SERVER_NAME` when matching routes. Defaults to False.159 :param template_folder: the folder that contains the templates that should160 be used by the application. Defaults to161 ``'templates'`` folder in the root path of the162 application.163 :param instance_path: An alternative instance path for the application.164 By default the folder ``'instance'`` next to the165 package or module is assumed to be the instance166 path.167 :param instance_relative_config: if set to ``True`` relative filenames168 for loading the config are assumed to169 be relative to the instance path instead170 of the application root.171 :param root_path: The path to the root of the application files.172 This should only be set manually when it can't be detected173 automatically, such as for namespace packages.174 """175 176 default_config = ImmutableDict(177 {178 "DEBUG": None,179 "TESTING": False,180 "PROPAGATE_EXCEPTIONS": None,181 "SECRET_KEY": None,182 "PERMANENT_SESSION_LIFETIME": timedelta(days=31),183 "USE_X_SENDFILE": False,184 "SERVER_NAME": None,185 "APPLICATION_ROOT": "/",186 "SESSION_COOKIE_NAME": "session",187 "SESSION_COOKIE_DOMAIN": None,188 "SESSION_COOKIE_PATH": None,189 "SESSION_COOKIE_HTTPONLY": True,190 "SESSION_COOKIE_SECURE": False,191 "SESSION_COOKIE_SAMESITE": None,192 "SESSION_REFRESH_EACH_REQUEST": True,193 "MAX_CONTENT_LENGTH": None,194 "SEND_FILE_MAX_AGE_DEFAULT": None,195 "TRAP_BAD_REQUEST_ERRORS": None,196 "TRAP_HTTP_EXCEPTIONS": False,197 "EXPLAIN_TEMPLATE_LOADING": False,198 "PREFERRED_URL_SCHEME": "http",199 "TEMPLATES_AUTO_RELOAD": None,200 "MAX_COOKIE_SIZE": 4093,201 }202 )203 204 #: The class that is used for request objects. See :class:`~flask.Request`205 #: for more information.206 request_class: type[Request] = Request207 208 #: The class that is used for response objects. See209 #: :class:`~flask.Response` for more information.210 response_class: type[Response] = Response211 212 #: the session interface to use. By default an instance of213 #: :class:`~flask.sessions.SecureCookieSessionInterface` is used here.214 #:215 #: .. versionadded:: 0.8216 session_interface: SessionInterface = SecureCookieSessionInterface()217 218 def __init__(219 self,220 import_name: str,221 static_url_path: str | None = None,222 static_folder: str | os.PathLike[str] | None = "static",223 static_host: str | None = None,224 host_matching: bool = False,225 subdomain_matching: bool = False,226 template_folder: str | os.PathLike[str] | None = "templates",227 instance_path: str | None = None,228 instance_relative_config: bool = False,229 root_path: str | None = None,230 ):231 super().__init__(232 import_name=import_name,233 static_url_path=static_url_path,234 static_folder=static_folder,235 static_host=static_host,236 host_matching=host_matching,237 subdomain_matching=subdomain_matching,238 template_folder=template_folder,239 instance_path=instance_path,240 instance_relative_config=instance_relative_config,241 root_path=root_path,242 )243 244 #: The Click command group for registering CLI commands for this245 #: object. The commands are available from the ``flask`` command246 #: once the application has been discovered and blueprints have247 #: been registered.248 self.cli = cli.AppGroup()249 250 # Set the name of the Click group in case someone wants to add251 # the app's commands to another CLI tool.252 self.cli.name = self.name253 254 # Add a static route using the provided static_url_path, static_host,255 # and static_folder if there is a configured static_folder.256 # Note we do this without checking if static_folder exists.257 # For one, it might be created while the server is running (e.g. during258 # development). Also, Google App Engine stores static files somewhere259 if self.has_static_folder:260 assert (261 bool(static_host) == host_matching262 ), "Invalid static_host/host_matching combination"263 # Use a weakref to avoid creating a reference cycle between the app264 # and the view function (see #3761).265 self_ref = weakref.ref(self)266 self.add_url_rule(267 f"{self.static_url_path}/<path:filename>",268 endpoint="static",269 host=static_host,270 view_func=lambda **kw: self_ref().send_static_file(**kw), # type: ignore # noqa: B950271 )272 273 def get_send_file_max_age(self, filename: str | None) -> int | None:274 """Used by :func:`send_file` to determine the ``max_age`` cache275 value for a given file path if it wasn't passed.276 277 By default, this returns :data:`SEND_FILE_MAX_AGE_DEFAULT` from278 the configuration of :data:`~flask.current_app`. This defaults279 to ``None``, which tells the browser to use conditional requests280 instead of a timed cache, which is usually preferable.281 282 Note this is a duplicate of the same method in the Flask283 class.284 285 .. versionchanged:: 2.0286 The default configuration is ``None`` instead of 12 hours.287 288 .. versionadded:: 0.9289 """290 value = current_app.config["SEND_FILE_MAX_AGE_DEFAULT"]291 292 if value is None:293 return None294 295 if isinstance(value, timedelta):296 return int(value.total_seconds())297 298 return value # type: ignore[no-any-return]299 300 def send_static_file(self, filename: str) -> Response:301 """The view function used to serve files from302 :attr:`static_folder`. A route is automatically registered for303 this view at :attr:`static_url_path` if :attr:`static_folder` is304 set.305 306 Note this is a duplicate of the same method in the Flask307 class.308 309 .. versionadded:: 0.5310 311 """312 if not self.has_static_folder:313 raise RuntimeError("'static_folder' must be set to serve static_files.")314 315 # send_file only knows to call get_send_file_max_age on the app,316 # call it here so it works for blueprints too.317 max_age = self.get_send_file_max_age(filename)318 return send_from_directory(319 t.cast(str, self.static_folder), filename, max_age=max_age320 )321 322 def open_resource(self, resource: str, mode: str = "rb") -> t.IO[t.AnyStr]:323 """Open a resource file relative to :attr:`root_path` for324 reading.325 326 For example, if the file ``schema.sql`` is next to the file327 ``app.py`` where the ``Flask`` app is defined, it can be opened328 with:329 330 .. code-block:: python331 332 with app.open_resource("schema.sql") as f:333 conn.executescript(f.read())334 335 :param resource: Path to the resource relative to336 :attr:`root_path`.337 :param mode: Open the file in this mode. Only reading is338 supported, valid values are "r" (or "rt") and "rb".339 340 Note this is a duplicate of the same method in the Flask341 class.342 343 """344 if mode not in {"r", "rt", "rb"}:345 raise ValueError("Resources can only be opened for reading.")346 347 return open(os.path.join(self.root_path, resource), mode)348 349 def open_instance_resource(self, resource: str, mode: str = "rb") -> t.IO[t.AnyStr]:350 """Opens a resource from the application's instance folder351 (:attr:`instance_path`). Otherwise works like352 :meth:`open_resource`. Instance resources can also be opened for353 writing.354 355 :param resource: the name of the resource. To access resources within356 subfolders use forward slashes as separator.357 :param mode: resource file opening mode, default is 'rb'.358 """359 return open(os.path.join(self.instance_path, resource), mode)360 361 def create_jinja_environment(self) -> Environment:362 """Create the Jinja environment based on :attr:`jinja_options`363 and the various Jinja-related methods of the app. Changing364 :attr:`jinja_options` after this will have no effect. Also adds365 Flask-related globals and filters to the environment.366 367 .. versionchanged:: 0.11368 ``Environment.auto_reload`` set in accordance with369 ``TEMPLATES_AUTO_RELOAD`` configuration option.370 371 .. versionadded:: 0.5372 """373 options = dict(self.jinja_options)374 375 if "autoescape" not in options:376 options["autoescape"] = self.select_jinja_autoescape377 378 if "auto_reload" not in options:379 auto_reload = self.config["TEMPLATES_AUTO_RELOAD"]380 381 if auto_reload is None:382 auto_reload = self.debug383 384 options["auto_reload"] = auto_reload385 386 rv = self.jinja_environment(self, **options)387 rv.globals.update(388 url_for=self.url_for,389 get_flashed_messages=get_flashed_messages,390 config=self.config,391 # request, session and g are normally added with the392 # context processor for efficiency reasons but for imported393 # templates we also want the proxies in there.394 request=request,395 session=session,396 g=g,397 )398 rv.policies["json.dumps_function"] = self.json.dumps399 return rv400 401 def create_url_adapter(self, request: Request | None) -> MapAdapter | None:402 """Creates a URL adapter for the given request. The URL adapter403 is created at a point where the request context is not yet set404 up so the request is passed explicitly.405 406 .. versionadded:: 0.6407 408 .. versionchanged:: 0.9409 This can now also be called without a request object when the410 URL adapter is created for the application context.411 412 .. versionchanged:: 1.0413 :data:`SERVER_NAME` no longer implicitly enables subdomain414 matching. Use :attr:`subdomain_matching` instead.415 """416 if request is not None:417 # If subdomain matching is disabled (the default), use the418 # default subdomain in all cases. This should be the default419 # in Werkzeug but it currently does not have that feature.420 if not self.subdomain_matching:421 subdomain = self.url_map.default_subdomain or None422 else:423 subdomain = None424 425 return self.url_map.bind_to_environ(426 request.environ,427 server_name=self.config["SERVER_NAME"],428 subdomain=subdomain,429 )430 # We need at the very least the server name to be set for this431 # to work.432 if self.config["SERVER_NAME"] is not None:433 return self.url_map.bind(434 self.config["SERVER_NAME"],435 script_name=self.config["APPLICATION_ROOT"],436 url_scheme=self.config["PREFERRED_URL_SCHEME"],437 )438 439 return None440 441 def raise_routing_exception(self, request: Request) -> t.NoReturn:442 """Intercept routing exceptions and possibly do something else.443 444 In debug mode, intercept a routing redirect and replace it with445 an error if the body will be discarded.446 447 With modern Werkzeug this shouldn't occur, since it now uses a448 308 status which tells the browser to resend the method and449 body.450 451 .. versionchanged:: 2.1452 Don't intercept 307 and 308 redirects.453 454 :meta private:455 :internal:456 """457 if (458 not self.debug459 or not isinstance(request.routing_exception, RequestRedirect)460 or request.routing_exception.code in {307, 308}461 or request.method in {"GET", "HEAD", "OPTIONS"}462 ):463 raise request.routing_exception # type: ignore[misc]464 465 from .debughelpers import FormDataRoutingRedirect466 467 raise FormDataRoutingRedirect(request)468 469 def update_template_context(self, context: dict[str, t.Any]) -> None:470 """Update the template context with some commonly used variables.471 This injects request, session, config and g into the template472 context as well as everything template context processors want473 to inject. Note that the as of Flask 0.6, the original values474 in the context will not be overridden if a context processor475 decides to return a value with the same key.476 477 :param context: the context as a dictionary that is updated in place478 to add extra variables.479 """480 names: t.Iterable[str | None] = (None,)481 482 # A template may be rendered outside a request context.483 if request:484 names = chain(names, reversed(request.blueprints))485 486 # The values passed to render_template take precedence. Keep a487 # copy to re-apply after all context functions.488 orig_ctx = context.copy()489 490 for name in names:491 if name in self.template_context_processors:492 for func in self.template_context_processors[name]:493 context.update(self.ensure_sync(func)())494 495 context.update(orig_ctx)496 497 def make_shell_context(self) -> dict[str, t.Any]:498 """Returns the shell context for an interactive shell for this499 application. This runs all the registered shell context500 processors.501 502 .. versionadded:: 0.11503 """504 rv = {"app": self, "g": g}505 for processor in self.shell_context_processors:506 rv.update(processor())507 return rv508 509 def run(510 self,511 host: str | None = None,512 port: int | None = None,513 debug: bool | None = None,514 load_dotenv: bool = True,515 **options: t.Any,516 ) -> None:517 """Runs the application on a local development server.518 519 Do not use ``run()`` in a production setting. It is not intended to520 meet security and performance requirements for a production server.521 Instead, see :doc:`/deploying/index` for WSGI server recommendations.522 523 If the :attr:`debug` flag is set the server will automatically reload524 for code changes and show a debugger in case an exception happened.525 526 If you want to run the application in debug mode, but disable the527 code execution on the interactive debugger, you can pass528 ``use_evalex=False`` as parameter. This will keep the debugger's529 traceback screen active, but disable code execution.530 531 It is not recommended to use this function for development with532 automatic reloading as this is badly supported. Instead you should533 be using the :command:`flask` command line script's ``run`` support.534 535 .. admonition:: Keep in Mind536 537 Flask will suppress any server error with a generic error page538 unless it is in debug mode. As such to enable just the539 interactive debugger without the code reloading, you have to540 invoke :meth:`run` with ``debug=True`` and ``use_reloader=False``.541 Setting ``use_debugger`` to ``True`` without being in debug mode542 won't catch any exceptions because there won't be any to543 catch.544 545 :param host: the hostname to listen on. Set this to ``'0.0.0.0'`` to546 have the server available externally as well. Defaults to547 ``'127.0.0.1'`` or the host in the ``SERVER_NAME`` config variable548 if present.549 :param port: the port of the webserver. Defaults to ``5000`` or the550 port defined in the ``SERVER_NAME`` config variable if present.551 :param debug: if given, enable or disable debug mode. See552 :attr:`debug`.553 :param load_dotenv: Load the nearest :file:`.env` and :file:`.flaskenv`554 files to set environment variables. Will also change the working555 directory to the directory containing the first file found.556 :param options: the options to be forwarded to the underlying Werkzeug557 server. See :func:`werkzeug.serving.run_simple` for more558 information.559 560 .. versionchanged:: 1.0561 If installed, python-dotenv will be used to load environment562 variables from :file:`.env` and :file:`.flaskenv` files.563 564 The :envvar:`FLASK_DEBUG` environment variable will override :attr:`debug`.565 566 Threaded mode is enabled by default.567 568 .. versionchanged:: 0.10569 The default port is now picked from the ``SERVER_NAME``570 variable.571 """572 # Ignore this call so that it doesn't start another server if573 # the 'flask run' command is used.574 if os.environ.get("FLASK_RUN_FROM_CLI") == "true":575 if not is_running_from_reloader():576 click.secho(577 " * Ignoring a call to 'app.run()' that would block"578 " the current 'flask' CLI command.\n"579 " Only call 'app.run()' in an 'if __name__ =="580 ' "__main__"\' guard.',581 fg="red",582 )583 584 return585 586 if get_load_dotenv(load_dotenv):587 cli.load_dotenv()588 589 # if set, env var overrides existing value590 if "FLASK_DEBUG" in os.environ:591 self.debug = get_debug_flag()592 593 # debug passed to method overrides all other sources594 if debug is not None:595 self.debug = bool(debug)596 597 server_name = self.config.get("SERVER_NAME")598 sn_host = sn_port = None599 600 if server_name:601 sn_host, _, sn_port = server_name.partition(":")602 603 if not host:604 if sn_host:605 host = sn_host606 else:607 host = "127.0.0.1"608 609 if port or port == 0:610 port = int(port)611 elif sn_port:612 port = int(sn_port)613 else:614 port = 5000615 616 options.setdefault("use_reloader", self.debug)617 options.setdefault("use_debugger", self.debug)618 options.setdefault("threaded", True)619 620 cli.show_server_banner(self.debug, self.name)621 622 from werkzeug.serving import run_simple623 624 try:625 run_simple(t.cast(str, host), port, self, **options)626 finally:627 # reset the first request information if the development server628 # reset normally. This makes it possible to restart the server629 # without reloader and that stuff from an interactive shell.630 self._got_first_request = False631 632 def test_client(self, use_cookies: bool = True, **kwargs: t.Any) -> FlaskClient:633 """Creates a test client for this application. For information634 about unit testing head over to :doc:`/testing`.635 636 Note that if you are testing for assertions or exceptions in your637 application code, you must set ``app.testing = True`` in order for the638 exceptions to propagate to the test client. Otherwise, the exception639 will be handled by the application (not visible to the test client) and640 the only indication of an AssertionError or other exception will be a641 500 status code response to the test client. See the :attr:`testing`642 attribute. For example::643 644 app.testing = True645 client = app.test_client()646 647 The test client can be used in a ``with`` block to defer the closing down648 of the context until the end of the ``with`` block. This is useful if649 you want to access the context locals for testing::650 651 with app.test_client() as c:652 rv = c.get('/?vodka=42')653 assert request.args['vodka'] == '42'654 655 Additionally, you may pass optional keyword arguments that will then656 be passed to the application's :attr:`test_client_class` constructor.657 For example::658 659 from flask.testing import FlaskClient660 661 class CustomClient(FlaskClient):662 def __init__(self, *args, **kwargs):663 self._authentication = kwargs.pop("authentication")664 super(CustomClient,self).__init__( *args, **kwargs)665 666 app.test_client_class = CustomClient667 client = app.test_client(authentication='Basic ....')668 669 See :class:`~flask.testing.FlaskClient` for more information.670 671 .. versionchanged:: 0.4672 added support for ``with`` block usage for the client.673 674 .. versionadded:: 0.7675 The `use_cookies` parameter was added as well as the ability676 to override the client to be used by setting the677 :attr:`test_client_class` attribute.678 679 .. versionchanged:: 0.11680 Added `**kwargs` to support passing additional keyword arguments to681 the constructor of :attr:`test_client_class`.682 """683 cls = self.test_client_class684 if cls is None:685 from .testing import FlaskClient as cls686 return cls( # type: ignore687 self, self.response_class, use_cookies=use_cookies, **kwargs688 )689 690 def test_cli_runner(self, **kwargs: t.Any) -> FlaskCliRunner:691 """Create a CLI runner for testing CLI commands.692 See :ref:`testing-cli`.693 694 Returns an instance of :attr:`test_cli_runner_class`, by default695 :class:`~flask.testing.FlaskCliRunner`. The Flask app object is696 passed as the first argument.697 698 .. versionadded:: 1.0699 """700 cls = self.test_cli_runner_class701 702 if cls is None:703 from .testing import FlaskCliRunner as cls704 705 return cls(self, **kwargs) # type: ignore706 707 def handle_http_exception(708 self, e: HTTPException709 ) -> HTTPException | ft.ResponseReturnValue:710 """Handles an HTTP exception. By default this will invoke the711 registered error handlers and fall back to returning the712 exception as response.713 714 .. versionchanged:: 1.0.3715 ``RoutingException``, used internally for actions such as716 slash redirects during routing, is not passed to error717 handlers.718 719 .. versionchanged:: 1.0720 Exceptions are looked up by code *and* by MRO, so721 ``HTTPException`` subclasses can be handled with a catch-all722 handler for the base ``HTTPException``.723 724 .. versionadded:: 0.3725 """726 # Proxy exceptions don't have error codes. We want to always return727 # those unchanged as errors728 if e.code is None:729 return e730 731 # RoutingExceptions are used internally to trigger routing732 # actions, such as slash redirects raising RequestRedirect. They733 # are not raised or handled in user code.734 if isinstance(e, RoutingException):735 return e736 737 handler = self._find_error_handler(e, request.blueprints)738 if handler is None:739 return e740 return self.ensure_sync(handler)(e) # type: ignore[no-any-return]741 742 def handle_user_exception(743 self, e: Exception744 ) -> HTTPException | ft.ResponseReturnValue:745 """This method is called whenever an exception occurs that746 should be handled. A special case is :class:`~werkzeug747 .exceptions.HTTPException` which is forwarded to the748 :meth:`handle_http_exception` method. This function will either749 return a response value or reraise the exception with the same750 traceback.751 752 .. versionchanged:: 1.0753 Key errors raised from request data like ``form`` show the754 bad key in debug mode rather than a generic bad request755 message.756 757 .. versionadded:: 0.7758 """759 if isinstance(e, BadRequestKeyError) and (760 self.debug or self.config["TRAP_BAD_REQUEST_ERRORS"]761 ):762 e.show_exception = True763 764 if isinstance(e, HTTPException) and not self.trap_http_exception(e):765 return self.handle_http_exception(e)766 767 handler = self._find_error_handler(e, request.blueprints)768 769 if handler is None:770 raise771 772 return self.ensure_sync(handler)(e) # type: ignore[no-any-return]773 774 def handle_exception(self, e: Exception) -> Response:775 """Handle an exception that did not have an error handler776 associated with it, or that was raised from an error handler.777 This always causes a 500 ``InternalServerError``.778 779 Always sends the :data:`got_request_exception` signal.780 781 If :data:`PROPAGATE_EXCEPTIONS` is ``True``, such as in debug782 mode, the error will be re-raised so that the debugger can783 display it. Otherwise, the original exception is logged, and784 an :exc:`~werkzeug.exceptions.InternalServerError` is returned.785 786 If an error handler is registered for ``InternalServerError`` or787 ``500``, it will be used. For consistency, the handler will788 always receive the ``InternalServerError``. The original789 unhandled exception is available as ``e.original_exception``.790 791 .. versionchanged:: 1.1.0792 Always passes the ``InternalServerError`` instance to the793 handler, setting ``original_exception`` to the unhandled794 error.795 796 .. versionchanged:: 1.1.0797 ``after_request`` functions and other finalization is done798 even for the default 500 response when there is no handler.799 800 .. versionadded:: 0.3801 """802 exc_info = sys.exc_info()803 got_request_exception.send(self, _async_wrapper=self.ensure_sync, exception=e)804 propagate = self.config["PROPAGATE_EXCEPTIONS"]805 806 if propagate is None:807 propagate = self.testing or self.debug808 809 if propagate:810 # Re-raise if called with an active exception, otherwise811 # raise the passed in exception.812 if exc_info[1] is e:813 raise814 815 raise e816 817 self.log_exception(exc_info)818 server_error: InternalServerError | ft.ResponseReturnValue819 server_error = InternalServerError(original_exception=e)820 handler = self._find_error_handler(server_error, request.blueprints)821 822 if handler is not None:823 server_error = self.ensure_sync(handler)(server_error)824 825 return self.finalize_request(server_error, from_error_handler=True)826 827 def log_exception(828 self,829 exc_info: (tuple[type, BaseException, TracebackType] | tuple[None, None, None]),830 ) -> None:831 """Logs an exception. This is called by :meth:`handle_exception`832 if debugging is disabled and right before the handler is called.833 The default implementation logs the exception as error on the834 :attr:`logger`.835 836 .. versionadded:: 0.8837 """838 self.logger.error(839 f"Exception on {request.path} [{request.method}]", exc_info=exc_info840 )841 842 def dispatch_request(self) -> ft.ResponseReturnValue:843 """Does the request dispatching. Matches the URL and returns the844 return value of the view or error handler. This does not have to845 be a response object. In order to convert the return value to a846 proper response object, call :func:`make_response`.847 848 .. versionchanged:: 0.7849 This no longer does the exception handling, this code was850 moved to the new :meth:`full_dispatch_request`.851 """852 req = request_ctx.request853 if req.routing_exception is not None:854 self.raise_routing_exception(req)855 rule: Rule = req.url_rule # type: ignore[assignment]856 # if we provide automatic options for this URL and the857 # request came with the OPTIONS method, reply automatically858 if (859 getattr(rule, "provide_automatic_options", False)860 and req.method == "OPTIONS"861 ):862 return self.make_default_options_response()863 # otherwise dispatch to the handler for that endpoint864 view_args: dict[str, t.Any] = req.view_args # type: ignore[assignment]865 return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return]866 867 def full_dispatch_request(self) -> Response:868 """Dispatches the request and on top of that performs request869 pre and postprocessing as well as HTTP exception catching and870 error handling.871 872 .. versionadded:: 0.7873 """874 self._got_first_request = True875 876 try:877 request_started.send(self, _async_wrapper=self.ensure_sync)878 rv = self.preprocess_request()879 if rv is None:880 rv = self.dispatch_request()881 except Exception as e:882 rv = self.handle_user_exception(e)883 return self.finalize_request(rv)884 885 def finalize_request(886 self,887 rv: ft.ResponseReturnValue | HTTPException,888 from_error_handler: bool = False,889 ) -> Response:890 """Given the return value from a view function this finalizes891 the request by converting it into a response and invoking the892 postprocessing functions. This is invoked for both normal893 request dispatching as well as error handlers.894 895 Because this means that it might be called as a result of a896 failure a special safe mode is available which can be enabled897 with the `from_error_handler` flag. If enabled, failures in898 response processing will be logged and otherwise ignored.899 900 :internal:901 """902 response = self.make_response(rv)903 try:904 response = self.process_response(response)905 request_finished.send(906 self, _async_wrapper=self.ensure_sync, response=response907 )908 except Exception:909 if not from_error_handler:910 raise911 self.logger.exception(912 "Request finalizing failed with an error while handling an error"913 )914 return response915 916 def make_default_options_response(self) -> Response:917 """This method is called to create the default ``OPTIONS`` response.918 This can be changed through subclassing to change the default919 behavior of ``OPTIONS`` responses.920 921 .. versionadded:: 0.7922 """923 adapter = request_ctx.url_adapter924 methods = adapter.allowed_methods() # type: ignore[union-attr]925 rv = self.response_class()926 rv.allow.update(methods)927 return rv928 929 def ensure_sync(self, func: t.Callable[..., t.Any]) -> t.Callable[..., t.Any]:930 """Ensure that the function is synchronous for WSGI workers.931 Plain ``def`` functions are returned as-is. ``async def``932 functions are wrapped to run and wait for the response.933 934 Override this method to change how the app runs async views.935 936 .. versionadded:: 2.0937 """938 if iscoroutinefunction(func):939 return self.async_to_sync(func)940 941 return func942 943 def async_to_sync(944 self, func: t.Callable[..., t.Coroutine[t.Any, t.Any, t.Any]]945 ) -> t.Callable[..., t.Any]:946 """Return a sync function that will run the coroutine function.947 948 .. code-block:: python949 950 result = app.async_to_sync(func)(*args, **kwargs)951 952 Override this method to change how the app converts async code953 to be synchronously callable.954 955 .. versionadded:: 2.0956 """957 try:958 from asgiref.sync import async_to_sync as asgiref_async_to_sync959 except ImportError:960 raise RuntimeError(961 "Install Flask with the 'async' extra in order to use async views."962 ) from None963 964 return asgiref_async_to_sync(func)965 966 def url_for(967 self,968 /,969 endpoint: str,970 *,971 _anchor: str | None = None,972 _method: str | None = None,973 _scheme: str | None = None,974 _external: bool | None = None,975 **values: t.Any,976 ) -> str:977 """Generate a URL to the given endpoint with the given values.978 979 This is called by :func:`flask.url_for`, and can be called980 directly as well.981 982 An *endpoint* is the name of a URL rule, usually added with983 :meth:`@app.route() <route>`, and usually the same name as the984 view function. A route defined in a :class:`~flask.Blueprint`985 will prepend the blueprint's name separated by a ``.`` to the986 endpoint.987 988 In some cases, such as email messages, you want URLs to include989 the scheme and domain, like ``https://example.com/hello``. When990 not in an active request, URLs will be external by default, but991 this requires setting :data:`SERVER_NAME` so Flask knows what992 domain to use. :data:`APPLICATION_ROOT` and993 :data:`PREFERRED_URL_SCHEME` should also be configured as994 needed. This config is only used when not in an active request.995 996 Functions can be decorated with :meth:`url_defaults` to modify997 keyword arguments before the URL is built.998 999 If building fails for some reason, such as an unknown endpoint1000 or incorrect values, the app's :meth:`handle_url_build_error`1001 method is called. If that returns a string, that is returned,1002 otherwise a :exc:`~werkzeug.routing.BuildError` is raised.1003 1004 :param endpoint: The endpoint name associated with the URL to1005 generate. If this starts with a ``.``, the current blueprint1006 name (if any) will be used.1007 :param _anchor: If given, append this as ``#anchor`` to the URL.1008 :param _method: If given, generate the URL associated with this1009 method for the endpoint.1010 :param _scheme: If given, the URL will have this scheme if it1011 is external.1012 :param _external: If given, prefer the URL to be internal1013 (False) or require it to be external (True). External URLs1014 include the scheme and domain. When not in an active1015 request, URLs are external by default.1016 :param values: Values to use for the variable parts of the URL1017 rule. Unknown keys are appended as query string arguments,1018 like ``?a=b&c=d``.1019 1020 .. versionadded:: 2.21021 Moved from ``flask.url_for``, which calls this method.1022 """1023 req_ctx = _cv_request.get(None)1024 1025 if req_ctx is not None:1026 url_adapter = req_ctx.url_adapter1027 blueprint_name = req_ctx.request.blueprint1028 1029 # If the endpoint starts with "." and the request matches a1030 # blueprint, the endpoint is relative to the blueprint.1031 if endpoint[:1] == ".":1032 if blueprint_name is not None:1033 endpoint = f"{blueprint_name}{endpoint}"1034 else:1035 endpoint = endpoint[1:]1036 1037 # When in a request, generate a URL without scheme and1038 # domain by default, unless a scheme is given.1039 if _external is None:1040 _external = _scheme is not None1041 else:1042 app_ctx = _cv_app.get(None)1043 1044 # If called by helpers.url_for, an app context is active,1045 # use its url_adapter. Otherwise, app.url_for was called1046 # directly, build an adapter.1047 if app_ctx is not None:1048 url_adapter = app_ctx.url_adapter1049 else:1050 url_adapter = self.create_url_adapter(None)1051 1052 if url_adapter is None:1053 raise RuntimeError(1054 "Unable to build URLs outside an active request"1055 " without 'SERVER_NAME' configured. Also configure"1056 " 'APPLICATION_ROOT' and 'PREFERRED_URL_SCHEME' as"1057 " needed."1058 )1059 1060 # When outside a request, generate a URL with scheme and1061 # domain by default.1062 if _external is None:1063 _external = True1064 1065 # It is an error to set _scheme when _external=False, in order1066 # to avoid accidental insecure URLs.1067 if _scheme is not None and not _external:1068 raise ValueError("When specifying '_scheme', '_external' must be True.")1069 1070 self.inject_url_defaults(endpoint, values)1071 1072 try:1073 rv = url_adapter.build( # type: ignore[union-attr]1074 endpoint,1075 values,1076 method=_method,1077 url_scheme=_scheme,1078 force_external=_external,1079 )1080 except BuildError as error:1081 values.update(1082 _anchor=_anchor, _method=_method, _scheme=_scheme, _external=_external1083 )1084 return self.handle_url_build_error(error, endpoint, values)1085 1086 if _anchor is not None:1087 _anchor = _url_quote(_anchor, safe="%!#$&'()*+,/:;=?@")1088 rv = f"{rv}#{_anchor}"1089 1090 return rv1091 1092 def make_response(self, rv: ft.ResponseReturnValue) -> Response:1093 """Convert the return value from a view function to an instance of1094 :attr:`response_class`.1095 1096 :param rv: the return value from the view function. The view function1097 must return a response. Returning ``None``, or the view ending1098 without returning, is not allowed. The following types are allowed1099 for ``view_rv``:1100 1101 ``str``1102 A response object is created with the string encoded to UTF-81103 as the body.1104 1105 ``bytes``1106 A response object is created with the bytes as the body.1107 1108 ``dict``1109 A dictionary that will be jsonify'd before being returned.1110 1111 ``list``1112 A list that will be jsonify'd before being returned.1113 1114 ``generator`` or ``iterator``1115 A generator that returns ``str`` or ``bytes`` to be1116 streamed as the response.1117 1118 ``tuple``1119 Either ``(body, status, headers)``, ``(body, status)``, or1120 ``(body, headers)``, where ``body`` is any of the other types1121 allowed here, ``status`` is a string or an integer, and1122 ``headers`` is a dictionary or a list of ``(key, value)``1123 tuples. If ``body`` is a :attr:`response_class` instance,1124 ``status`` overwrites the exiting value and ``headers`` are1125 extended.1126 1127 :attr:`response_class`1128 The object is returned unchanged.1129 1130 other :class:`~werkzeug.wrappers.Response` class1131 The object is coerced to :attr:`response_class`.1132 1133 :func:`callable`1134 The function is called as a WSGI application. The result is1135 used to create a response object.1136 1137 .. versionchanged:: 2.21138 A generator will be converted to a streaming response.1139 A list will be converted to a JSON response.1140 1141 .. versionchanged:: 1.11142 A dict will be converted to a JSON response.1143 1144 .. versionchanged:: 0.91145 Previously a tuple was interpreted as the arguments for the1146 response object.1147 """1148 1149 status = headers = None1150 1151 # unpack tuple returns1152 if isinstance(rv, tuple):1153 len_rv = len(rv)1154 1155 # a 3-tuple is unpacked directly1156 if len_rv == 3:1157 rv, status, headers = rv # type: ignore[misc]1158 # decide if a 2-tuple has status or headers1159 elif len_rv == 2:1160 if isinstance(rv[1], (Headers, dict, tuple, list)):1161 rv, headers = rv1162 else:1163 rv, status = rv # type: ignore[assignment,misc]1164 # other sized tuples are not allowed1165 else:1166 raise TypeError(1167 "The view function did not return a valid response tuple."1168 " The tuple must have the form (body, status, headers),"1169 " (body, status), or (body, headers)."1170 )1171 1172 # the body must not be None1173 if rv is None:1174 raise TypeError(1175 f"The view function for {request.endpoint!r} did not"1176 " return a valid response. The function either returned"1177 " None or ended without a return statement."1178 )1179 1180 # make sure the body is an instance of the response class1181 if not isinstance(rv, self.response_class):1182 if isinstance(rv, (str, bytes, bytearray)) or isinstance(rv, cabc.Iterator):1183 # let the response class set the status and headers instead of1184 # waiting to do it manually, so that the class can handle any1185 # special logic1186 rv = self.response_class(1187 rv,1188 status=status,1189 headers=headers, # type: ignore[arg-type]1190 )1191 status = headers = None1192 elif isinstance(rv, (dict, list)):1193 rv = self.json.response(rv)1194 elif isinstance(rv, BaseResponse) or callable(rv):1195 # evaluate a WSGI callable, or coerce a different response1196 # class to the correct type1197 try:1198 rv = self.response_class.force_type(1199 rv, # type: ignore[arg-type]1200 request.environ,