Add initial type hints

This should make it easier for users to correctly use Flask. The hints
are from Quart.
This commit is contained in:
pgjones 2021-04-24 12:22:26 +01:00
parent f405c6f19e
commit 77237093da
20 changed files with 820 additions and 461 deletions

View file

@ -1,11 +1,14 @@
import functools
import inspect
import logging
import os
import sys
import typing as t
import weakref
from datetime import timedelta
from itertools import chain
from threading import Lock
from types import TracebackType
from werkzeug.datastructures import Headers
from werkzeug.datastructures import ImmutableDict
@ -15,6 +18,7 @@ from werkzeug.exceptions import HTTPException
from werkzeug.exceptions import InternalServerError
from werkzeug.routing import BuildError
from werkzeug.routing import Map
from werkzeug.routing import MapAdapter
from werkzeug.routing import RequestRedirect
from werkzeug.routing import RoutingException
from werkzeug.routing import Rule
@ -53,15 +57,30 @@ from .signals import request_started
from .signals import request_tearing_down
from .templating import DispatchingJinjaLoader
from .templating import Environment
from .typing import AfterRequestCallable
from .typing import BeforeRequestCallable
from .typing import ErrorHandlerCallable
from .typing import ResponseReturnValue
from .typing import TeardownCallable
from .typing import TemplateContextProcessorCallable
from .typing import TemplateFilterCallable
from .typing import TemplateGlobalCallable
from .typing import TemplateTestCallable
from .typing import URLDefaultCallable
from .typing import URLValuePreprocessorCallable
from .wrappers import Request
from .wrappers import Response
if t.TYPE_CHECKING:
from .blueprints import Blueprint
from .testing import FlaskClient
from .testing import FlaskCliRunner
if sys.version_info >= (3, 8):
iscoroutinefunction = inspect.iscoroutinefunction
else:
def iscoroutinefunction(func):
def iscoroutinefunction(func: t.Any) -> bool:
while inspect.ismethod(func):
func = func.__func__
@ -71,7 +90,7 @@ else:
return inspect.iscoroutinefunction(func)
def _make_timedelta(value):
def _make_timedelta(value: t.Optional[timedelta]) -> t.Optional[timedelta]:
if value is None or isinstance(value, timedelta):
return value
@ -295,7 +314,7 @@ class Flask(Scaffold):
#: This is a ``dict`` instead of an ``ImmutableDict`` to allow
#: easier configuration.
#:
jinja_options = {}
jinja_options: dict = {}
#: Default configuration parameters.
default_config = ImmutableDict(
@ -347,7 +366,7 @@ class Flask(Scaffold):
#: the test client that is used with when `test_client` is used.
#:
#: .. versionadded:: 0.7
test_client_class = None
test_client_class: t.Optional[t.Type["FlaskClient"]] = None
#: The :class:`~click.testing.CliRunner` subclass, by default
#: :class:`~flask.testing.FlaskCliRunner` that is used by
@ -355,7 +374,7 @@ class Flask(Scaffold):
#: Flask app object as the first argument.
#:
#: .. versionadded:: 1.0
test_cli_runner_class = None
test_cli_runner_class: t.Optional[t.Type["FlaskCliRunner"]] = None
#: the session interface to use. By default an instance of
#: :class:`~flask.sessions.SecureCookieSessionInterface` is used here.
@ -365,16 +384,16 @@ class Flask(Scaffold):
def __init__(
self,
import_name,
static_url_path=None,
static_folder="static",
static_host=None,
host_matching=False,
subdomain_matching=False,
template_folder="templates",
instance_path=None,
instance_relative_config=False,
root_path=None,
import_name: str,
static_url_path: t.Optional[str] = None,
static_folder: t.Optional[str] = "static",
static_host: t.Optional[str] = None,
host_matching: bool = False,
subdomain_matching: bool = False,
template_folder: t.Optional[str] = "templates",
instance_path: t.Optional[str] = None,
instance_relative_config: bool = False,
root_path: t.Optional[str] = None,
):
super().__init__(
import_name=import_name,
@ -409,14 +428,16 @@ class Flask(Scaffold):
#: tried.
#:
#: .. versionadded:: 0.9
self.url_build_error_handlers = []
self.url_build_error_handlers: t.List[
t.Callable[[Exception, str, dict], str]
] = []
#: A list of functions that will be called at the beginning of the
#: first request to this instance. To register a function, use the
#: :meth:`before_first_request` decorator.
#:
#: .. versionadded:: 0.8
self.before_first_request_funcs = []
self.before_first_request_funcs: t.List[BeforeRequestCallable] = []
#: A list of functions that are called when the application context
#: is destroyed. Since the application context is also torn down
@ -424,13 +445,13 @@ class Flask(Scaffold):
#: from databases.
#:
#: .. versionadded:: 0.9
self.teardown_appcontext_funcs = []
self.teardown_appcontext_funcs: t.List[TeardownCallable] = []
#: A list of shell context processor functions that should be run
#: when a shell context is created.
#:
#: .. versionadded:: 0.11
self.shell_context_processors = []
self.shell_context_processors: t.List[t.Callable[[], t.Dict[str, t.Any]]] = []
#: Maps registered blueprint names to blueprint objects. The
#: dict retains the order the blueprints were registered in.
@ -438,7 +459,7 @@ class Flask(Scaffold):
#: not track how often they were attached.
#:
#: .. versionadded:: 0.7
self.blueprints = {}
self.blueprints: t.Dict[str, "Blueprint"] = {}
#: a place where extensions can store application specific state. For
#: example this is where an extension could store database engines and
@ -449,7 +470,7 @@ class Flask(Scaffold):
#: ``'foo'``.
#:
#: .. versionadded:: 0.7
self.extensions = {}
self.extensions: dict = {}
#: The :class:`~werkzeug.routing.Map` for this instance. You can use
#: this to change the routing converters after the class was created
@ -492,18 +513,18 @@ class Flask(Scaffold):
f"{self.static_url_path}/<path:filename>",
endpoint="static",
host=static_host,
view_func=lambda **kw: self_ref().send_static_file(**kw),
view_func=lambda **kw: self_ref().send_static_file(**kw), # type: ignore # noqa: B950
)
# Set the name of the Click group in case someone wants to add
# the app's commands to another CLI tool.
self.cli.name = self.name
def _is_setup_finished(self):
def _is_setup_finished(self) -> bool:
return self.debug and self._got_first_request
@locked_cached_property
def name(self):
def name(self) -> str: # type: ignore
"""The name of the application. This is usually the import name
with the difference that it's guessed from the run file if the
import name is main. This name is used as a display name when
@ -520,7 +541,7 @@ class Flask(Scaffold):
return self.import_name
@property
def propagate_exceptions(self):
def propagate_exceptions(self) -> bool:
"""Returns the value of the ``PROPAGATE_EXCEPTIONS`` configuration
value in case it's set, otherwise a sensible default is returned.
@ -532,7 +553,7 @@ class Flask(Scaffold):
return self.testing or self.debug
@property
def preserve_context_on_exception(self):
def preserve_context_on_exception(self) -> bool:
"""Returns the value of the ``PRESERVE_CONTEXT_ON_EXCEPTION``
configuration value in case it's set, otherwise a sensible default
is returned.
@ -545,7 +566,7 @@ class Flask(Scaffold):
return self.debug
@locked_cached_property
def logger(self):
def logger(self) -> logging.Logger:
"""A standard Python :class:`~logging.Logger` for the app, with
the same name as :attr:`name`.
@ -572,7 +593,7 @@ class Flask(Scaffold):
return create_logger(self)
@locked_cached_property
def jinja_env(self):
def jinja_env(self) -> Environment:
"""The Jinja environment used to load templates.
The environment is created the first time this property is
@ -582,7 +603,7 @@ class Flask(Scaffold):
return self.create_jinja_environment()
@property
def got_first_request(self):
def got_first_request(self) -> bool:
"""This attribute is set to ``True`` if the application started
handling the first request.
@ -590,7 +611,7 @@ class Flask(Scaffold):
"""
return self._got_first_request
def make_config(self, instance_relative=False):
def make_config(self, instance_relative: bool = False) -> Config:
"""Used to create the config attribute by the Flask constructor.
The `instance_relative` parameter is passed in from the constructor
of Flask (there named `instance_relative_config`) and indicates if
@ -607,7 +628,7 @@ class Flask(Scaffold):
defaults["DEBUG"] = get_debug_flag()
return self.config_class(root_path, defaults)
def auto_find_instance_path(self):
def auto_find_instance_path(self) -> str:
"""Tries to locate the instance path if it was not provided to the
constructor of the application class. It will basically calculate
the path to a folder named ``instance`` next to your main file or
@ -620,7 +641,7 @@ class Flask(Scaffold):
return os.path.join(package_path, "instance")
return os.path.join(prefix, "var", f"{self.name}-instance")
def open_instance_resource(self, resource, mode="rb"):
def open_instance_resource(self, resource: str, mode: str = "rb") -> t.IO[t.AnyStr]:
"""Opens a resource from the application's instance folder
(:attr:`instance_path`). Otherwise works like
:meth:`open_resource`. Instance resources can also be opened for
@ -633,7 +654,7 @@ class Flask(Scaffold):
return open(os.path.join(self.instance_path, resource), mode)
@property
def templates_auto_reload(self):
def templates_auto_reload(self) -> bool:
"""Reload templates when they are changed. Used by
:meth:`create_jinja_environment`.
@ -648,10 +669,10 @@ class Flask(Scaffold):
return rv if rv is not None else self.debug
@templates_auto_reload.setter
def templates_auto_reload(self, value):
def templates_auto_reload(self, value: bool) -> None:
self.config["TEMPLATES_AUTO_RELOAD"] = value
def create_jinja_environment(self):
def create_jinja_environment(self) -> Environment:
"""Create the Jinja environment based on :attr:`jinja_options`
and the various Jinja-related methods of the app. Changing
:attr:`jinja_options` after this will have no effect. Also adds
@ -683,10 +704,10 @@ class Flask(Scaffold):
session=session,
g=g,
)
rv.policies["json.dumps_function"] = json.dumps
rv.policies["json.dumps_function"] = json.dumps # type: ignore
return rv
def create_global_jinja_loader(self):
def create_global_jinja_loader(self) -> DispatchingJinjaLoader:
"""Creates the loader for the Jinja2 environment. Can be used to
override just the loader and keeping the rest unchanged. It's
discouraged to override this function. Instead one should override
@ -699,7 +720,7 @@ class Flask(Scaffold):
"""
return DispatchingJinjaLoader(self)
def select_jinja_autoescape(self, filename):
def select_jinja_autoescape(self, filename: str) -> bool:
"""Returns ``True`` if autoescaping should be active for the given
template name. If no template name is given, returns `True`.
@ -709,7 +730,7 @@ class Flask(Scaffold):
return True
return filename.endswith((".html", ".htm", ".xml", ".xhtml"))
def update_template_context(self, context):
def update_template_context(self, context: dict) -> None:
"""Update the template context with some commonly used variables.
This injects request, session, config and g into the template
context as well as everything template context processors want
@ -720,7 +741,9 @@ class Flask(Scaffold):
:param context: the context as a dictionary that is updated in place
to add extra variables.
"""
funcs = self.template_context_processors[None]
funcs: t.Iterable[
TemplateContextProcessorCallable
] = self.template_context_processors[None]
reqctx = _request_ctx_stack.top
if reqctx is not None:
for bp in self._request_blueprints():
@ -734,7 +757,7 @@ class Flask(Scaffold):
# existing views.
context.update(orig_ctx)
def make_shell_context(self):
def make_shell_context(self) -> dict:
"""Returns the shell context for an interactive shell for this
application. This runs all the registered shell context
processors.
@ -758,7 +781,7 @@ class Flask(Scaffold):
env = ConfigAttribute("ENV")
@property
def debug(self):
def debug(self) -> bool:
"""Whether debug mode is enabled. When using ``flask run`` to start
the development server, an interactive debugger will be shown for
unhandled exceptions, and the server will be reloaded when code
@ -775,11 +798,18 @@ class Flask(Scaffold):
return self.config["DEBUG"]
@debug.setter
def debug(self, value):
def debug(self, value: bool) -> None:
self.config["DEBUG"] = value
self.jinja_env.auto_reload = self.templates_auto_reload
def run(self, host=None, port=None, debug=None, load_dotenv=True, **options):
def run(
self,
host: t.Optional[str] = None,
port: t.Optional[int] = None,
debug: t.Optional[bool] = None,
load_dotenv: bool = True,
**options: t.Any,
) -> None:
"""Runs the application on a local development server.
Do not use ``run()`` in a production setting. It is not intended to
@ -887,14 +917,14 @@ class Flask(Scaffold):
from werkzeug.serving import run_simple
try:
run_simple(host, port, self, **options)
run_simple(t.cast(str, host), port, self, **options)
finally:
# reset the first request information if the development server
# reset normally. This makes it possible to restart the server
# without reloader and that stuff from an interactive shell.
self._got_first_request = False
def test_client(self, use_cookies=True, **kwargs):
def test_client(self, use_cookies: bool = True, **kwargs: t.Any) -> "FlaskClient":
"""Creates a test client for this application. For information
about unit testing head over to :doc:`/testing`.
@ -947,10 +977,12 @@ class Flask(Scaffold):
"""
cls = self.test_client_class
if cls is None:
from .testing import FlaskClient as cls
return cls(self, self.response_class, use_cookies=use_cookies, **kwargs)
from .testing import FlaskClient as cls # type: ignore
return cls( # type: ignore
self, self.response_class, use_cookies=use_cookies, **kwargs
)
def test_cli_runner(self, **kwargs):
def test_cli_runner(self, **kwargs: t.Any) -> "FlaskCliRunner":
"""Create a CLI runner for testing CLI commands.
See :ref:`testing-cli`.
@ -963,12 +995,12 @@ class Flask(Scaffold):
cls = self.test_cli_runner_class
if cls is None:
from .testing import FlaskCliRunner as cls
from .testing import FlaskCliRunner as cls # type: ignore
return cls(self, **kwargs)
return cls(self, **kwargs) # type: ignore
@setupmethod
def register_blueprint(self, blueprint, **options):
def register_blueprint(self, blueprint: "Blueprint", **options: t.Any) -> None:
"""Register a :class:`~flask.Blueprint` on the application. Keyword
arguments passed to this method will override the defaults set on the
blueprint.
@ -989,7 +1021,7 @@ class Flask(Scaffold):
"""
blueprint.register(self, options)
def iter_blueprints(self):
def iter_blueprints(self) -> t.ValuesView["Blueprint"]:
"""Iterates over all blueprints by the order they were registered.
.. versionadded:: 0.11
@ -999,14 +1031,14 @@ class Flask(Scaffold):
@setupmethod
def add_url_rule(
self,
rule,
endpoint=None,
view_func=None,
provide_automatic_options=None,
**options,
):
rule: str,
endpoint: t.Optional[str] = None,
view_func: t.Optional[t.Callable] = None,
provide_automatic_options: t.Optional[bool] = None,
**options: t.Any,
) -> None:
if endpoint is None:
endpoint = _endpoint_from_view_func(view_func)
endpoint = _endpoint_from_view_func(view_func) # type: ignore
options["endpoint"] = endpoint
methods = options.pop("methods", None)
@ -1043,13 +1075,13 @@ class Flask(Scaffold):
methods |= required_methods
rule = self.url_rule_class(rule, methods=methods, **options)
rule.provide_automatic_options = provide_automatic_options
rule.provide_automatic_options = provide_automatic_options # type: ignore
self.url_map.add(rule)
if view_func is not None:
old_func = self.view_functions.get(endpoint)
if getattr(old_func, "_flask_sync_wrapper", False):
old_func = old_func.__wrapped__
old_func = old_func.__wrapped__ # type: ignore
if old_func is not None and old_func != view_func:
raise AssertionError(
"View function mapping is overwriting an existing"
@ -1058,7 +1090,7 @@ class Flask(Scaffold):
self.view_functions[endpoint] = self.ensure_sync(view_func)
@setupmethod
def template_filter(self, name=None):
def template_filter(self, name: t.Optional[str] = None) -> t.Callable:
"""A decorator that is used to register custom template filter.
You can specify a name for the filter, otherwise the function
name will be used. Example::
@ -1071,14 +1103,16 @@ class Flask(Scaffold):
function name will be used.
"""
def decorator(f):
def decorator(f: TemplateFilterCallable) -> TemplateFilterCallable:
self.add_template_filter(f, name=name)
return f
return decorator
@setupmethod
def add_template_filter(self, f, name=None):
def add_template_filter(
self, f: TemplateFilterCallable, name: t.Optional[str] = None
) -> None:
"""Register a custom template filter. Works exactly like the
:meth:`template_filter` decorator.
@ -1088,7 +1122,7 @@ class Flask(Scaffold):
self.jinja_env.filters[name or f.__name__] = f
@setupmethod
def template_test(self, name=None):
def template_test(self, name: t.Optional[str] = None) -> t.Callable:
"""A decorator that is used to register custom template test.
You can specify a name for the test, otherwise the function
name will be used. Example::
@ -1108,14 +1142,16 @@ class Flask(Scaffold):
function name will be used.
"""
def decorator(f):
def decorator(f: TemplateTestCallable) -> TemplateTestCallable:
self.add_template_test(f, name=name)
return f
return decorator
@setupmethod
def add_template_test(self, f, name=None):
def add_template_test(
self, f: TemplateTestCallable, name: t.Optional[str] = None
) -> None:
"""Register a custom template test. Works exactly like the
:meth:`template_test` decorator.
@ -1127,7 +1163,7 @@ class Flask(Scaffold):
self.jinja_env.tests[name or f.__name__] = f
@setupmethod
def template_global(self, name=None):
def template_global(self, name: t.Optional[str] = None) -> t.Callable:
"""A decorator that is used to register a custom template global function.
You can specify a name for the global function, otherwise the function
name will be used. Example::
@ -1142,14 +1178,16 @@ class Flask(Scaffold):
function name will be used.
"""
def decorator(f):
def decorator(f: TemplateGlobalCallable) -> TemplateGlobalCallable:
self.add_template_global(f, name=name)
return f
return decorator
@setupmethod
def add_template_global(self, f, name=None):
def add_template_global(
self, f: TemplateGlobalCallable, name: t.Optional[str] = None
) -> None:
"""Register a custom template global function. Works exactly like the
:meth:`template_global` decorator.
@ -1161,7 +1199,7 @@ class Flask(Scaffold):
self.jinja_env.globals[name or f.__name__] = f
@setupmethod
def before_first_request(self, f):
def before_first_request(self, f: BeforeRequestCallable) -> BeforeRequestCallable:
"""Registers a function to be run before the first request to this
instance of the application.
@ -1174,7 +1212,7 @@ class Flask(Scaffold):
return f
@setupmethod
def teardown_appcontext(self, f):
def teardown_appcontext(self, f: TeardownCallable) -> TeardownCallable:
"""Registers a function to be called when the application context
ends. These functions are typically also called when the request
context is popped.
@ -1207,7 +1245,7 @@ class Flask(Scaffold):
return f
@setupmethod
def shell_context_processor(self, f):
def shell_context_processor(self, f: t.Callable) -> t.Callable:
"""Registers a shell context processor function.
.. versionadded:: 0.11
@ -1215,7 +1253,7 @@ class Flask(Scaffold):
self.shell_context_processors.append(f)
return f
def _find_error_handler(self, e):
def _find_error_handler(self, e: Exception) -> t.Optional[ErrorHandlerCallable]:
"""Return a registered error handler for an exception in this order:
blueprint handler for a specific code, app handler for a specific code,
blueprint handler for an exception class, app handler for an exception
@ -1235,8 +1273,11 @@ class Flask(Scaffold):
if handler is not None:
return handler
return None
def handle_http_exception(self, e):
def handle_http_exception(
self, e: HTTPException
) -> t.Union[HTTPException, ResponseReturnValue]:
"""Handles an HTTP exception. By default this will invoke the
registered error handlers and fall back to returning the
exception as response.
@ -1269,7 +1310,7 @@ class Flask(Scaffold):
return e
return handler(e)
def trap_http_exception(self, e):
def trap_http_exception(self, e: Exception) -> bool:
"""Checks if an HTTP exception should be trapped or not. By default
this will return ``False`` for all exceptions except for a bad request
key error if ``TRAP_BAD_REQUEST_ERRORS`` is set to ``True``. It
@ -1304,7 +1345,9 @@ class Flask(Scaffold):
return False
def handle_user_exception(self, e):
def handle_user_exception(
self, e: Exception
) -> t.Union[HTTPException, ResponseReturnValue]:
"""This method is called whenever an exception occurs that
should be handled. A special case is :class:`~werkzeug
.exceptions.HTTPException` which is forwarded to the
@ -1334,7 +1377,7 @@ class Flask(Scaffold):
return handler(e)
def handle_exception(self, e):
def handle_exception(self, e: Exception) -> Response:
"""Handle an exception that did not have an error handler
associated with it, or that was raised from an error handler.
This always causes a 500 ``InternalServerError``.
@ -1374,6 +1417,7 @@ class Flask(Scaffold):
raise e
self.log_exception(exc_info)
server_error: t.Union[InternalServerError, ResponseReturnValue]
server_error = InternalServerError(original_exception=e)
handler = self._find_error_handler(server_error)
@ -1382,7 +1426,12 @@ class Flask(Scaffold):
return self.finalize_request(server_error, from_error_handler=True)
def log_exception(self, exc_info):
def log_exception(
self,
exc_info: t.Union[
t.Tuple[type, BaseException, TracebackType], t.Tuple[None, None, None]
],
) -> None:
"""Logs an exception. This is called by :meth:`handle_exception`
if debugging is disabled and right before the handler is called.
The default implementation logs the exception as error on the
@ -1394,7 +1443,7 @@ class Flask(Scaffold):
f"Exception on {request.path} [{request.method}]", exc_info=exc_info
)
def raise_routing_exception(self, request):
def raise_routing_exception(self, request: Request) -> t.NoReturn:
"""Exceptions that are recording during routing are reraised with
this method. During debug we are not reraising redirect requests
for non ``GET``, ``HEAD``, or ``OPTIONS`` requests and we're raising
@ -1407,13 +1456,13 @@ class Flask(Scaffold):
or not isinstance(request.routing_exception, RequestRedirect)
or request.method in ("GET", "HEAD", "OPTIONS")
):
raise request.routing_exception
raise request.routing_exception # type: ignore
from .debughelpers import FormDataRoutingRedirect
raise FormDataRoutingRedirect(request)
def dispatch_request(self):
def dispatch_request(self) -> ResponseReturnValue:
"""Does the request dispatching. Matches the URL and returns the
return value of the view or error handler. This does not have to
be a response object. In order to convert the return value to a
@ -1437,7 +1486,7 @@ class Flask(Scaffold):
# otherwise dispatch to the handler for that endpoint
return self.view_functions[rule.endpoint](**req.view_args)
def full_dispatch_request(self):
def full_dispatch_request(self) -> Response:
"""Dispatches the request and on top of that performs request
pre and postprocessing as well as HTTP exception catching and
error handling.
@ -1454,7 +1503,11 @@ class Flask(Scaffold):
rv = self.handle_user_exception(e)
return self.finalize_request(rv)
def finalize_request(self, rv, from_error_handler=False):
def finalize_request(
self,
rv: t.Union[ResponseReturnValue, HTTPException],
from_error_handler: bool = False,
) -> Response:
"""Given the return value from a view function this finalizes
the request by converting it into a response and invoking the
postprocessing functions. This is invoked for both normal
@ -1479,7 +1532,7 @@ class Flask(Scaffold):
)
return response
def try_trigger_before_first_request_functions(self):
def try_trigger_before_first_request_functions(self) -> None:
"""Called before each request and will ensure that it triggers
the :attr:`before_first_request_funcs` and only exactly once per
application instance (which means process usually).
@ -1495,7 +1548,7 @@ class Flask(Scaffold):
func()
self._got_first_request = True
def make_default_options_response(self):
def make_default_options_response(self) -> Response:
"""This method is called to create the default ``OPTIONS`` response.
This can be changed through subclassing to change the default
behavior of ``OPTIONS`` responses.
@ -1508,7 +1561,7 @@ class Flask(Scaffold):
rv.allow.update(methods)
return rv
def should_ignore_error(self, error):
def should_ignore_error(self, error: t.Optional[BaseException]) -> bool:
"""This is called to figure out if an error should be ignored
or not as far as the teardown system is concerned. If this
function returns ``True`` then the teardown handlers will not be
@ -1518,7 +1571,7 @@ class Flask(Scaffold):
"""
return False
def ensure_sync(self, func):
def ensure_sync(self, func: t.Callable) -> t.Callable:
"""Ensure that the function is synchronous for WSGI workers.
Plain ``def`` functions are returned as-is. ``async def``
functions are wrapped to run and wait for the response.
@ -1532,7 +1585,7 @@ class Flask(Scaffold):
return func
def make_response(self, rv):
def make_response(self, rv: ResponseReturnValue) -> Response:
"""Convert the return value from a view function to an instance of
:attr:`response_class`.
@ -1620,7 +1673,7 @@ class Flask(Scaffold):
# evaluate a WSGI callable, or coerce a different response
# class to the correct type
try:
rv = self.response_class.force_type(rv, request.environ)
rv = self.response_class.force_type(rv, request.environ) # type: ignore # noqa: B950
except TypeError as e:
raise TypeError(
f"{e}\nThe view function did not return a valid"
@ -1636,10 +1689,11 @@ class Flask(Scaffold):
f" callable, but it was a {type(rv).__name__}."
)
rv = t.cast(Response, rv)
# prefer the status if it was provided
if status is not None:
if isinstance(status, (str, bytes, bytearray)):
rv.status = status
rv.status = status # type: ignore
else:
rv.status_code = status
@ -1649,7 +1703,9 @@ class Flask(Scaffold):
return rv
def create_url_adapter(self, request):
def create_url_adapter(
self, request: t.Optional[Request]
) -> t.Optional[MapAdapter]:
"""Creates a URL adapter for the given request. The URL adapter
is created at a point where the request context is not yet set
up so the request is passed explicitly.
@ -1687,21 +1743,25 @@ class Flask(Scaffold):
url_scheme=self.config["PREFERRED_URL_SCHEME"],
)
def inject_url_defaults(self, endpoint, values):
return None
def inject_url_defaults(self, endpoint: str, values: dict) -> None:
"""Injects the URL defaults for the given endpoint directly into
the values dictionary passed. This is used internally and
automatically called on URL building.
.. versionadded:: 0.7
"""
funcs = self.url_default_functions[None]
funcs: t.Iterable[URLDefaultCallable] = self.url_default_functions[None]
if "." in endpoint:
bp = endpoint.rsplit(".", 1)[0]
funcs = chain(funcs, self.url_default_functions[bp])
for func in funcs:
func(endpoint, values)
def handle_url_build_error(self, error, endpoint, values):
def handle_url_build_error(
self, error: Exception, endpoint: str, values: dict
) -> str:
"""Handle :class:`~werkzeug.routing.BuildError` on
:meth:`url_for`.
"""
@ -1722,7 +1782,7 @@ class Flask(Scaffold):
raise error
def preprocess_request(self):
def preprocess_request(self) -> t.Optional[ResponseReturnValue]:
"""Called before the request is dispatched. Calls
:attr:`url_value_preprocessors` registered with the app and the
current blueprint (if any). Then calls :attr:`before_request_funcs`
@ -1733,14 +1793,16 @@ class Flask(Scaffold):
further request handling is stopped.
"""
funcs = self.url_value_preprocessors[None]
funcs: t.Iterable[URLValuePreprocessorCallable] = self.url_value_preprocessors[
None
]
for bp in self._request_blueprints():
if bp in self.url_value_preprocessors:
funcs = chain(funcs, self.url_value_preprocessors[bp])
for func in funcs:
func(request.endpoint, request.view_args)
funcs = self.before_request_funcs[None]
funcs: t.Iterable[BeforeRequestCallable] = self.before_request_funcs[None]
for bp in self._request_blueprints():
if bp in self.before_request_funcs:
funcs = chain(funcs, self.before_request_funcs[bp])
@ -1749,7 +1811,9 @@ class Flask(Scaffold):
if rv is not None:
return rv
def process_response(self, response):
return None
def process_response(self, response: Response) -> Response:
"""Can be overridden in order to modify the response object
before it's sent to the WSGI server. By default this will
call all the :meth:`after_request` decorated functions.
@ -1763,7 +1827,7 @@ class Flask(Scaffold):
instance of :attr:`response_class`.
"""
ctx = _request_ctx_stack.top
funcs = ctx._after_request_functions
funcs: t.Iterable[AfterRequestCallable] = ctx._after_request_functions
for bp in self._request_blueprints():
if bp in self.after_request_funcs:
funcs = chain(funcs, reversed(self.after_request_funcs[bp]))
@ -1775,7 +1839,9 @@ class Flask(Scaffold):
self.session_interface.save_session(self, ctx.session, response)
return response
def do_teardown_request(self, exc=_sentinel):
def do_teardown_request(
self, exc: t.Optional[BaseException] = _sentinel # type: ignore
) -> None:
"""Called after the request is dispatched and the response is
returned, right before the request context is popped.
@ -1798,7 +1864,9 @@ class Flask(Scaffold):
"""
if exc is _sentinel:
exc = sys.exc_info()[1]
funcs = reversed(self.teardown_request_funcs[None])
funcs: t.Iterable[TeardownCallable] = reversed(
self.teardown_request_funcs[None]
)
for bp in self._request_blueprints():
if bp in self.teardown_request_funcs:
funcs = chain(funcs, reversed(self.teardown_request_funcs[bp]))
@ -1806,7 +1874,9 @@ class Flask(Scaffold):
func(exc)
request_tearing_down.send(self, exc=exc)
def do_teardown_appcontext(self, exc=_sentinel):
def do_teardown_appcontext(
self, exc: t.Optional[BaseException] = _sentinel # type: ignore
) -> None:
"""Called right before the application context is popped.
When handling a request, the application context is popped
@ -1827,7 +1897,7 @@ class Flask(Scaffold):
func(exc)
appcontext_tearing_down.send(self, exc=exc)
def app_context(self):
def app_context(self) -> AppContext:
"""Create an :class:`~flask.ctx.AppContext`. Use as a ``with``
block to push the context, which will make :data:`current_app`
point at this application.
@ -1848,7 +1918,7 @@ class Flask(Scaffold):
"""
return AppContext(self)
def request_context(self, environ):
def request_context(self, environ: dict) -> RequestContext:
"""Create a :class:`~flask.ctx.RequestContext` representing a
WSGI environment. Use a ``with`` block to push the context,
which will make :data:`request` point at this request.
@ -1864,7 +1934,7 @@ class Flask(Scaffold):
"""
return RequestContext(self, environ)
def test_request_context(self, *args, **kwargs):
def test_request_context(self, *args: t.Any, **kwargs: t.Any) -> RequestContext:
"""Create a :class:`~flask.ctx.RequestContext` for a WSGI
environment created from the given values. This is mostly useful
during testing, where you may want to run a function that uses
@ -1920,7 +1990,7 @@ class Flask(Scaffold):
finally:
builder.close()
def wsgi_app(self, environ, start_response):
def wsgi_app(self, environ: dict, start_response: t.Callable) -> t.Any:
"""The actual WSGI application. This is not implemented in
:meth:`__call__` so that middlewares can be applied without
losing a reference to the app object. Instead of doing this::
@ -1946,7 +2016,7 @@ class Flask(Scaffold):
start the response.
"""
ctx = self.request_context(environ)
error = None
error: t.Optional[BaseException] = None
try:
try:
ctx.push()
@ -1963,14 +2033,14 @@ class Flask(Scaffold):
error = None
ctx.auto_pop(error)
def __call__(self, environ, start_response):
def __call__(self, environ: dict, start_response: t.Callable) -> t.Any:
"""The WSGI server calls the Flask application object as the
WSGI application. This calls :meth:`wsgi_app`, which can be
wrapped to apply middleware.
"""
return self.wsgi_app(environ, start_response)
def _request_blueprints(self):
def _request_blueprints(self) -> t.Iterable[str]:
if _request_ctx_stack.top.request.blueprint is None:
return []
else: