mirror of https://github.com/pallets/flask.git
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:
parent
f405c6f19e
commit
77237093da
|
@ -74,6 +74,7 @@ Unreleased
|
||||||
``python`` shell if ``readline`` is installed. :issue:`3941`
|
``python`` shell if ``readline`` is installed. :issue:`3941`
|
||||||
- ``helpers.total_seconds()`` is deprecated. Use
|
- ``helpers.total_seconds()`` is deprecated. Use
|
||||||
``timedelta.total_seconds()`` instead. :pr:`3962`
|
``timedelta.total_seconds()`` instead. :pr:`3962`
|
||||||
|
- Add type hinting. :pr:`3973`.
|
||||||
|
|
||||||
|
|
||||||
Version 1.1.2
|
Version 1.1.2
|
||||||
|
|
292
src/flask/app.py
292
src/flask/app.py
|
@ -1,11 +1,14 @@
|
||||||
import functools
|
import functools
|
||||||
import inspect
|
import inspect
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import typing as t
|
||||||
import weakref
|
import weakref
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
|
from types import TracebackType
|
||||||
|
|
||||||
from werkzeug.datastructures import Headers
|
from werkzeug.datastructures import Headers
|
||||||
from werkzeug.datastructures import ImmutableDict
|
from werkzeug.datastructures import ImmutableDict
|
||||||
|
@ -15,6 +18,7 @@ from werkzeug.exceptions import HTTPException
|
||||||
from werkzeug.exceptions import InternalServerError
|
from werkzeug.exceptions import InternalServerError
|
||||||
from werkzeug.routing import BuildError
|
from werkzeug.routing import BuildError
|
||||||
from werkzeug.routing import Map
|
from werkzeug.routing import Map
|
||||||
|
from werkzeug.routing import MapAdapter
|
||||||
from werkzeug.routing import RequestRedirect
|
from werkzeug.routing import RequestRedirect
|
||||||
from werkzeug.routing import RoutingException
|
from werkzeug.routing import RoutingException
|
||||||
from werkzeug.routing import Rule
|
from werkzeug.routing import Rule
|
||||||
|
@ -53,15 +57,30 @@ from .signals import request_started
|
||||||
from .signals import request_tearing_down
|
from .signals import request_tearing_down
|
||||||
from .templating import DispatchingJinjaLoader
|
from .templating import DispatchingJinjaLoader
|
||||||
from .templating import Environment
|
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 Request
|
||||||
from .wrappers import Response
|
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):
|
if sys.version_info >= (3, 8):
|
||||||
iscoroutinefunction = inspect.iscoroutinefunction
|
iscoroutinefunction = inspect.iscoroutinefunction
|
||||||
else:
|
else:
|
||||||
|
|
||||||
def iscoroutinefunction(func):
|
def iscoroutinefunction(func: t.Any) -> bool:
|
||||||
while inspect.ismethod(func):
|
while inspect.ismethod(func):
|
||||||
func = func.__func__
|
func = func.__func__
|
||||||
|
|
||||||
|
@ -71,7 +90,7 @@ else:
|
||||||
return inspect.iscoroutinefunction(func)
|
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):
|
if value is None or isinstance(value, timedelta):
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
@ -295,7 +314,7 @@ class Flask(Scaffold):
|
||||||
#: This is a ``dict`` instead of an ``ImmutableDict`` to allow
|
#: This is a ``dict`` instead of an ``ImmutableDict`` to allow
|
||||||
#: easier configuration.
|
#: easier configuration.
|
||||||
#:
|
#:
|
||||||
jinja_options = {}
|
jinja_options: dict = {}
|
||||||
|
|
||||||
#: Default configuration parameters.
|
#: Default configuration parameters.
|
||||||
default_config = ImmutableDict(
|
default_config = ImmutableDict(
|
||||||
|
@ -347,7 +366,7 @@ class Flask(Scaffold):
|
||||||
#: the test client that is used with when `test_client` is used.
|
#: the test client that is used with when `test_client` is used.
|
||||||
#:
|
#:
|
||||||
#: .. versionadded:: 0.7
|
#: .. versionadded:: 0.7
|
||||||
test_client_class = None
|
test_client_class: t.Optional[t.Type["FlaskClient"]] = None
|
||||||
|
|
||||||
#: The :class:`~click.testing.CliRunner` subclass, by default
|
#: The :class:`~click.testing.CliRunner` subclass, by default
|
||||||
#: :class:`~flask.testing.FlaskCliRunner` that is used by
|
#: :class:`~flask.testing.FlaskCliRunner` that is used by
|
||||||
|
@ -355,7 +374,7 @@ class Flask(Scaffold):
|
||||||
#: Flask app object as the first argument.
|
#: Flask app object as the first argument.
|
||||||
#:
|
#:
|
||||||
#: .. versionadded:: 1.0
|
#: .. 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
|
#: the session interface to use. By default an instance of
|
||||||
#: :class:`~flask.sessions.SecureCookieSessionInterface` is used here.
|
#: :class:`~flask.sessions.SecureCookieSessionInterface` is used here.
|
||||||
|
@ -365,16 +384,16 @@ class Flask(Scaffold):
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
import_name,
|
import_name: str,
|
||||||
static_url_path=None,
|
static_url_path: t.Optional[str] = None,
|
||||||
static_folder="static",
|
static_folder: t.Optional[str] = "static",
|
||||||
static_host=None,
|
static_host: t.Optional[str] = None,
|
||||||
host_matching=False,
|
host_matching: bool = False,
|
||||||
subdomain_matching=False,
|
subdomain_matching: bool = False,
|
||||||
template_folder="templates",
|
template_folder: t.Optional[str] = "templates",
|
||||||
instance_path=None,
|
instance_path: t.Optional[str] = None,
|
||||||
instance_relative_config=False,
|
instance_relative_config: bool = False,
|
||||||
root_path=None,
|
root_path: t.Optional[str] = None,
|
||||||
):
|
):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
import_name=import_name,
|
import_name=import_name,
|
||||||
|
@ -409,14 +428,16 @@ class Flask(Scaffold):
|
||||||
#: tried.
|
#: tried.
|
||||||
#:
|
#:
|
||||||
#: .. versionadded:: 0.9
|
#: .. 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
|
#: A list of functions that will be called at the beginning of the
|
||||||
#: first request to this instance. To register a function, use the
|
#: first request to this instance. To register a function, use the
|
||||||
#: :meth:`before_first_request` decorator.
|
#: :meth:`before_first_request` decorator.
|
||||||
#:
|
#:
|
||||||
#: .. versionadded:: 0.8
|
#: .. 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
|
#: A list of functions that are called when the application context
|
||||||
#: is destroyed. Since the application context is also torn down
|
#: is destroyed. Since the application context is also torn down
|
||||||
|
@ -424,13 +445,13 @@ class Flask(Scaffold):
|
||||||
#: from databases.
|
#: from databases.
|
||||||
#:
|
#:
|
||||||
#: .. versionadded:: 0.9
|
#: .. 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
|
#: A list of shell context processor functions that should be run
|
||||||
#: when a shell context is created.
|
#: when a shell context is created.
|
||||||
#:
|
#:
|
||||||
#: .. versionadded:: 0.11
|
#: .. 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
|
#: Maps registered blueprint names to blueprint objects. The
|
||||||
#: dict retains the order the blueprints were registered in.
|
#: dict retains the order the blueprints were registered in.
|
||||||
|
@ -438,7 +459,7 @@ class Flask(Scaffold):
|
||||||
#: not track how often they were attached.
|
#: not track how often they were attached.
|
||||||
#:
|
#:
|
||||||
#: .. versionadded:: 0.7
|
#: .. versionadded:: 0.7
|
||||||
self.blueprints = {}
|
self.blueprints: t.Dict[str, "Blueprint"] = {}
|
||||||
|
|
||||||
#: a place where extensions can store application specific state. For
|
#: a place where extensions can store application specific state. For
|
||||||
#: example this is where an extension could store database engines and
|
#: example this is where an extension could store database engines and
|
||||||
|
@ -449,7 +470,7 @@ class Flask(Scaffold):
|
||||||
#: ``'foo'``.
|
#: ``'foo'``.
|
||||||
#:
|
#:
|
||||||
#: .. versionadded:: 0.7
|
#: .. versionadded:: 0.7
|
||||||
self.extensions = {}
|
self.extensions: dict = {}
|
||||||
|
|
||||||
#: The :class:`~werkzeug.routing.Map` for this instance. You can use
|
#: The :class:`~werkzeug.routing.Map` for this instance. You can use
|
||||||
#: this to change the routing converters after the class was created
|
#: 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>",
|
f"{self.static_url_path}/<path:filename>",
|
||||||
endpoint="static",
|
endpoint="static",
|
||||||
host=static_host,
|
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
|
# Set the name of the Click group in case someone wants to add
|
||||||
# the app's commands to another CLI tool.
|
# the app's commands to another CLI tool.
|
||||||
self.cli.name = self.name
|
self.cli.name = self.name
|
||||||
|
|
||||||
def _is_setup_finished(self):
|
def _is_setup_finished(self) -> bool:
|
||||||
return self.debug and self._got_first_request
|
return self.debug and self._got_first_request
|
||||||
|
|
||||||
@locked_cached_property
|
@locked_cached_property
|
||||||
def name(self):
|
def name(self) -> str: # type: ignore
|
||||||
"""The name of the application. This is usually the import name
|
"""The name of the application. This is usually the import name
|
||||||
with the difference that it's guessed from the run file if the
|
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
|
import name is main. This name is used as a display name when
|
||||||
|
@ -520,7 +541,7 @@ class Flask(Scaffold):
|
||||||
return self.import_name
|
return self.import_name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def propagate_exceptions(self):
|
def propagate_exceptions(self) -> bool:
|
||||||
"""Returns the value of the ``PROPAGATE_EXCEPTIONS`` configuration
|
"""Returns the value of the ``PROPAGATE_EXCEPTIONS`` configuration
|
||||||
value in case it's set, otherwise a sensible default is returned.
|
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
|
return self.testing or self.debug
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def preserve_context_on_exception(self):
|
def preserve_context_on_exception(self) -> bool:
|
||||||
"""Returns the value of the ``PRESERVE_CONTEXT_ON_EXCEPTION``
|
"""Returns the value of the ``PRESERVE_CONTEXT_ON_EXCEPTION``
|
||||||
configuration value in case it's set, otherwise a sensible default
|
configuration value in case it's set, otherwise a sensible default
|
||||||
is returned.
|
is returned.
|
||||||
|
@ -545,7 +566,7 @@ class Flask(Scaffold):
|
||||||
return self.debug
|
return self.debug
|
||||||
|
|
||||||
@locked_cached_property
|
@locked_cached_property
|
||||||
def logger(self):
|
def logger(self) -> logging.Logger:
|
||||||
"""A standard Python :class:`~logging.Logger` for the app, with
|
"""A standard Python :class:`~logging.Logger` for the app, with
|
||||||
the same name as :attr:`name`.
|
the same name as :attr:`name`.
|
||||||
|
|
||||||
|
@ -572,7 +593,7 @@ class Flask(Scaffold):
|
||||||
return create_logger(self)
|
return create_logger(self)
|
||||||
|
|
||||||
@locked_cached_property
|
@locked_cached_property
|
||||||
def jinja_env(self):
|
def jinja_env(self) -> Environment:
|
||||||
"""The Jinja environment used to load templates.
|
"""The Jinja environment used to load templates.
|
||||||
|
|
||||||
The environment is created the first time this property is
|
The environment is created the first time this property is
|
||||||
|
@ -582,7 +603,7 @@ class Flask(Scaffold):
|
||||||
return self.create_jinja_environment()
|
return self.create_jinja_environment()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def got_first_request(self):
|
def got_first_request(self) -> bool:
|
||||||
"""This attribute is set to ``True`` if the application started
|
"""This attribute is set to ``True`` if the application started
|
||||||
handling the first request.
|
handling the first request.
|
||||||
|
|
||||||
|
@ -590,7 +611,7 @@ class Flask(Scaffold):
|
||||||
"""
|
"""
|
||||||
return self._got_first_request
|
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.
|
"""Used to create the config attribute by the Flask constructor.
|
||||||
The `instance_relative` parameter is passed in from the constructor
|
The `instance_relative` parameter is passed in from the constructor
|
||||||
of Flask (there named `instance_relative_config`) and indicates if
|
of Flask (there named `instance_relative_config`) and indicates if
|
||||||
|
@ -607,7 +628,7 @@ class Flask(Scaffold):
|
||||||
defaults["DEBUG"] = get_debug_flag()
|
defaults["DEBUG"] = get_debug_flag()
|
||||||
return self.config_class(root_path, defaults)
|
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
|
"""Tries to locate the instance path if it was not provided to the
|
||||||
constructor of the application class. It will basically calculate
|
constructor of the application class. It will basically calculate
|
||||||
the path to a folder named ``instance`` next to your main file or
|
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(package_path, "instance")
|
||||||
return os.path.join(prefix, "var", f"{self.name}-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
|
"""Opens a resource from the application's instance folder
|
||||||
(:attr:`instance_path`). Otherwise works like
|
(:attr:`instance_path`). Otherwise works like
|
||||||
:meth:`open_resource`. Instance resources can also be opened for
|
: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)
|
return open(os.path.join(self.instance_path, resource), mode)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def templates_auto_reload(self):
|
def templates_auto_reload(self) -> bool:
|
||||||
"""Reload templates when they are changed. Used by
|
"""Reload templates when they are changed. Used by
|
||||||
:meth:`create_jinja_environment`.
|
:meth:`create_jinja_environment`.
|
||||||
|
|
||||||
|
@ -648,10 +669,10 @@ class Flask(Scaffold):
|
||||||
return rv if rv is not None else self.debug
|
return rv if rv is not None else self.debug
|
||||||
|
|
||||||
@templates_auto_reload.setter
|
@templates_auto_reload.setter
|
||||||
def templates_auto_reload(self, value):
|
def templates_auto_reload(self, value: bool) -> None:
|
||||||
self.config["TEMPLATES_AUTO_RELOAD"] = value
|
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`
|
"""Create the Jinja environment based on :attr:`jinja_options`
|
||||||
and the various Jinja-related methods of the app. Changing
|
and the various Jinja-related methods of the app. Changing
|
||||||
:attr:`jinja_options` after this will have no effect. Also adds
|
:attr:`jinja_options` after this will have no effect. Also adds
|
||||||
|
@ -683,10 +704,10 @@ class Flask(Scaffold):
|
||||||
session=session,
|
session=session,
|
||||||
g=g,
|
g=g,
|
||||||
)
|
)
|
||||||
rv.policies["json.dumps_function"] = json.dumps
|
rv.policies["json.dumps_function"] = json.dumps # type: ignore
|
||||||
return rv
|
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
|
"""Creates the loader for the Jinja2 environment. Can be used to
|
||||||
override just the loader and keeping the rest unchanged. It's
|
override just the loader and keeping the rest unchanged. It's
|
||||||
discouraged to override this function. Instead one should override
|
discouraged to override this function. Instead one should override
|
||||||
|
@ -699,7 +720,7 @@ class Flask(Scaffold):
|
||||||
"""
|
"""
|
||||||
return DispatchingJinjaLoader(self)
|
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
|
"""Returns ``True`` if autoescaping should be active for the given
|
||||||
template name. If no template name is given, returns `True`.
|
template name. If no template name is given, returns `True`.
|
||||||
|
|
||||||
|
@ -709,7 +730,7 @@ class Flask(Scaffold):
|
||||||
return True
|
return True
|
||||||
return filename.endswith((".html", ".htm", ".xml", ".xhtml"))
|
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.
|
"""Update the template context with some commonly used variables.
|
||||||
This injects request, session, config and g into the template
|
This injects request, session, config and g into the template
|
||||||
context as well as everything template context processors want
|
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
|
:param context: the context as a dictionary that is updated in place
|
||||||
to add extra variables.
|
to add extra variables.
|
||||||
"""
|
"""
|
||||||
funcs = self.template_context_processors[None]
|
funcs: t.Iterable[
|
||||||
|
TemplateContextProcessorCallable
|
||||||
|
] = self.template_context_processors[None]
|
||||||
reqctx = _request_ctx_stack.top
|
reqctx = _request_ctx_stack.top
|
||||||
if reqctx is not None:
|
if reqctx is not None:
|
||||||
for bp in self._request_blueprints():
|
for bp in self._request_blueprints():
|
||||||
|
@ -734,7 +757,7 @@ class Flask(Scaffold):
|
||||||
# existing views.
|
# existing views.
|
||||||
context.update(orig_ctx)
|
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
|
"""Returns the shell context for an interactive shell for this
|
||||||
application. This runs all the registered shell context
|
application. This runs all the registered shell context
|
||||||
processors.
|
processors.
|
||||||
|
@ -758,7 +781,7 @@ class Flask(Scaffold):
|
||||||
env = ConfigAttribute("ENV")
|
env = ConfigAttribute("ENV")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def debug(self):
|
def debug(self) -> bool:
|
||||||
"""Whether debug mode is enabled. When using ``flask run`` to start
|
"""Whether debug mode is enabled. When using ``flask run`` to start
|
||||||
the development server, an interactive debugger will be shown for
|
the development server, an interactive debugger will be shown for
|
||||||
unhandled exceptions, and the server will be reloaded when code
|
unhandled exceptions, and the server will be reloaded when code
|
||||||
|
@ -775,11 +798,18 @@ class Flask(Scaffold):
|
||||||
return self.config["DEBUG"]
|
return self.config["DEBUG"]
|
||||||
|
|
||||||
@debug.setter
|
@debug.setter
|
||||||
def debug(self, value):
|
def debug(self, value: bool) -> None:
|
||||||
self.config["DEBUG"] = value
|
self.config["DEBUG"] = value
|
||||||
self.jinja_env.auto_reload = self.templates_auto_reload
|
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.
|
"""Runs the application on a local development server.
|
||||||
|
|
||||||
Do not use ``run()`` in a production setting. It is not intended to
|
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
|
from werkzeug.serving import run_simple
|
||||||
|
|
||||||
try:
|
try:
|
||||||
run_simple(host, port, self, **options)
|
run_simple(t.cast(str, host), port, self, **options)
|
||||||
finally:
|
finally:
|
||||||
# reset the first request information if the development server
|
# reset the first request information if the development server
|
||||||
# reset normally. This makes it possible to restart the server
|
# reset normally. This makes it possible to restart the server
|
||||||
# without reloader and that stuff from an interactive shell.
|
# without reloader and that stuff from an interactive shell.
|
||||||
self._got_first_request = False
|
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
|
"""Creates a test client for this application. For information
|
||||||
about unit testing head over to :doc:`/testing`.
|
about unit testing head over to :doc:`/testing`.
|
||||||
|
|
||||||
|
@ -947,10 +977,12 @@ class Flask(Scaffold):
|
||||||
"""
|
"""
|
||||||
cls = self.test_client_class
|
cls = self.test_client_class
|
||||||
if cls is None:
|
if cls is None:
|
||||||
from .testing import FlaskClient as cls
|
from .testing import FlaskClient as cls # type: ignore
|
||||||
return cls(self, self.response_class, use_cookies=use_cookies, **kwargs)
|
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.
|
"""Create a CLI runner for testing CLI commands.
|
||||||
See :ref:`testing-cli`.
|
See :ref:`testing-cli`.
|
||||||
|
|
||||||
|
@ -963,12 +995,12 @@ class Flask(Scaffold):
|
||||||
cls = self.test_cli_runner_class
|
cls = self.test_cli_runner_class
|
||||||
|
|
||||||
if cls is None:
|
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
|
@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
|
"""Register a :class:`~flask.Blueprint` on the application. Keyword
|
||||||
arguments passed to this method will override the defaults set on the
|
arguments passed to this method will override the defaults set on the
|
||||||
blueprint.
|
blueprint.
|
||||||
|
@ -989,7 +1021,7 @@ class Flask(Scaffold):
|
||||||
"""
|
"""
|
||||||
blueprint.register(self, options)
|
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.
|
"""Iterates over all blueprints by the order they were registered.
|
||||||
|
|
||||||
.. versionadded:: 0.11
|
.. versionadded:: 0.11
|
||||||
|
@ -999,14 +1031,14 @@ class Flask(Scaffold):
|
||||||
@setupmethod
|
@setupmethod
|
||||||
def add_url_rule(
|
def add_url_rule(
|
||||||
self,
|
self,
|
||||||
rule,
|
rule: str,
|
||||||
endpoint=None,
|
endpoint: t.Optional[str] = None,
|
||||||
view_func=None,
|
view_func: t.Optional[t.Callable] = None,
|
||||||
provide_automatic_options=None,
|
provide_automatic_options: t.Optional[bool] = None,
|
||||||
**options,
|
**options: t.Any,
|
||||||
):
|
) -> None:
|
||||||
if endpoint is None:
|
if endpoint is None:
|
||||||
endpoint = _endpoint_from_view_func(view_func)
|
endpoint = _endpoint_from_view_func(view_func) # type: ignore
|
||||||
options["endpoint"] = endpoint
|
options["endpoint"] = endpoint
|
||||||
methods = options.pop("methods", None)
|
methods = options.pop("methods", None)
|
||||||
|
|
||||||
|
@ -1043,13 +1075,13 @@ class Flask(Scaffold):
|
||||||
methods |= required_methods
|
methods |= required_methods
|
||||||
|
|
||||||
rule = self.url_rule_class(rule, methods=methods, **options)
|
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)
|
self.url_map.add(rule)
|
||||||
if view_func is not None:
|
if view_func is not None:
|
||||||
old_func = self.view_functions.get(endpoint)
|
old_func = self.view_functions.get(endpoint)
|
||||||
if getattr(old_func, "_flask_sync_wrapper", False):
|
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:
|
if old_func is not None and old_func != view_func:
|
||||||
raise AssertionError(
|
raise AssertionError(
|
||||||
"View function mapping is overwriting an existing"
|
"View function mapping is overwriting an existing"
|
||||||
|
@ -1058,7 +1090,7 @@ class Flask(Scaffold):
|
||||||
self.view_functions[endpoint] = self.ensure_sync(view_func)
|
self.view_functions[endpoint] = self.ensure_sync(view_func)
|
||||||
|
|
||||||
@setupmethod
|
@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.
|
"""A decorator that is used to register custom template filter.
|
||||||
You can specify a name for the filter, otherwise the function
|
You can specify a name for the filter, otherwise the function
|
||||||
name will be used. Example::
|
name will be used. Example::
|
||||||
|
@ -1071,14 +1103,16 @@ class Flask(Scaffold):
|
||||||
function name will be used.
|
function name will be used.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def decorator(f):
|
def decorator(f: TemplateFilterCallable) -> TemplateFilterCallable:
|
||||||
self.add_template_filter(f, name=name)
|
self.add_template_filter(f, name=name)
|
||||||
return f
|
return f
|
||||||
|
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
@setupmethod
|
@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
|
"""Register a custom template filter. Works exactly like the
|
||||||
:meth:`template_filter` decorator.
|
:meth:`template_filter` decorator.
|
||||||
|
|
||||||
|
@ -1088,7 +1122,7 @@ class Flask(Scaffold):
|
||||||
self.jinja_env.filters[name or f.__name__] = f
|
self.jinja_env.filters[name or f.__name__] = f
|
||||||
|
|
||||||
@setupmethod
|
@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.
|
"""A decorator that is used to register custom template test.
|
||||||
You can specify a name for the test, otherwise the function
|
You can specify a name for the test, otherwise the function
|
||||||
name will be used. Example::
|
name will be used. Example::
|
||||||
|
@ -1108,14 +1142,16 @@ class Flask(Scaffold):
|
||||||
function name will be used.
|
function name will be used.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def decorator(f):
|
def decorator(f: TemplateTestCallable) -> TemplateTestCallable:
|
||||||
self.add_template_test(f, name=name)
|
self.add_template_test(f, name=name)
|
||||||
return f
|
return f
|
||||||
|
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
@setupmethod
|
@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
|
"""Register a custom template test. Works exactly like the
|
||||||
:meth:`template_test` decorator.
|
:meth:`template_test` decorator.
|
||||||
|
|
||||||
|
@ -1127,7 +1163,7 @@ class Flask(Scaffold):
|
||||||
self.jinja_env.tests[name or f.__name__] = f
|
self.jinja_env.tests[name or f.__name__] = f
|
||||||
|
|
||||||
@setupmethod
|
@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.
|
"""A decorator that is used to register a custom template global function.
|
||||||
You can specify a name for the global function, otherwise the function
|
You can specify a name for the global function, otherwise the function
|
||||||
name will be used. Example::
|
name will be used. Example::
|
||||||
|
@ -1142,14 +1178,16 @@ class Flask(Scaffold):
|
||||||
function name will be used.
|
function name will be used.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def decorator(f):
|
def decorator(f: TemplateGlobalCallable) -> TemplateGlobalCallable:
|
||||||
self.add_template_global(f, name=name)
|
self.add_template_global(f, name=name)
|
||||||
return f
|
return f
|
||||||
|
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
@setupmethod
|
@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
|
"""Register a custom template global function. Works exactly like the
|
||||||
:meth:`template_global` decorator.
|
:meth:`template_global` decorator.
|
||||||
|
|
||||||
|
@ -1161,7 +1199,7 @@ class Flask(Scaffold):
|
||||||
self.jinja_env.globals[name or f.__name__] = f
|
self.jinja_env.globals[name or f.__name__] = f
|
||||||
|
|
||||||
@setupmethod
|
@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
|
"""Registers a function to be run before the first request to this
|
||||||
instance of the application.
|
instance of the application.
|
||||||
|
|
||||||
|
@ -1174,7 +1212,7 @@ class Flask(Scaffold):
|
||||||
return f
|
return f
|
||||||
|
|
||||||
@setupmethod
|
@setupmethod
|
||||||
def teardown_appcontext(self, f):
|
def teardown_appcontext(self, f: TeardownCallable) -> TeardownCallable:
|
||||||
"""Registers a function to be called when the application context
|
"""Registers a function to be called when the application context
|
||||||
ends. These functions are typically also called when the request
|
ends. These functions are typically also called when the request
|
||||||
context is popped.
|
context is popped.
|
||||||
|
@ -1207,7 +1245,7 @@ class Flask(Scaffold):
|
||||||
return f
|
return f
|
||||||
|
|
||||||
@setupmethod
|
@setupmethod
|
||||||
def shell_context_processor(self, f):
|
def shell_context_processor(self, f: t.Callable) -> t.Callable:
|
||||||
"""Registers a shell context processor function.
|
"""Registers a shell context processor function.
|
||||||
|
|
||||||
.. versionadded:: 0.11
|
.. versionadded:: 0.11
|
||||||
|
@ -1215,7 +1253,7 @@ class Flask(Scaffold):
|
||||||
self.shell_context_processors.append(f)
|
self.shell_context_processors.append(f)
|
||||||
return 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:
|
"""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 a specific code, app handler for a specific code,
|
||||||
blueprint handler for an exception class, app handler for an exception
|
blueprint handler for an exception class, app handler for an exception
|
||||||
|
@ -1235,8 +1273,11 @@ class Flask(Scaffold):
|
||||||
|
|
||||||
if handler is not None:
|
if handler is not None:
|
||||||
return handler
|
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
|
"""Handles an HTTP exception. By default this will invoke the
|
||||||
registered error handlers and fall back to returning the
|
registered error handlers and fall back to returning the
|
||||||
exception as response.
|
exception as response.
|
||||||
|
@ -1269,7 +1310,7 @@ class Flask(Scaffold):
|
||||||
return e
|
return e
|
||||||
return handler(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
|
"""Checks if an HTTP exception should be trapped or not. By default
|
||||||
this will return ``False`` for all exceptions except for a bad request
|
this will return ``False`` for all exceptions except for a bad request
|
||||||
key error if ``TRAP_BAD_REQUEST_ERRORS`` is set to ``True``. It
|
key error if ``TRAP_BAD_REQUEST_ERRORS`` is set to ``True``. It
|
||||||
|
@ -1304,7 +1345,9 @@ class Flask(Scaffold):
|
||||||
|
|
||||||
return False
|
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
|
"""This method is called whenever an exception occurs that
|
||||||
should be handled. A special case is :class:`~werkzeug
|
should be handled. A special case is :class:`~werkzeug
|
||||||
.exceptions.HTTPException` which is forwarded to the
|
.exceptions.HTTPException` which is forwarded to the
|
||||||
|
@ -1334,7 +1377,7 @@ class Flask(Scaffold):
|
||||||
|
|
||||||
return handler(e)
|
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
|
"""Handle an exception that did not have an error handler
|
||||||
associated with it, or that was raised from an error handler.
|
associated with it, or that was raised from an error handler.
|
||||||
This always causes a 500 ``InternalServerError``.
|
This always causes a 500 ``InternalServerError``.
|
||||||
|
@ -1374,6 +1417,7 @@ class Flask(Scaffold):
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
self.log_exception(exc_info)
|
self.log_exception(exc_info)
|
||||||
|
server_error: t.Union[InternalServerError, ResponseReturnValue]
|
||||||
server_error = InternalServerError(original_exception=e)
|
server_error = InternalServerError(original_exception=e)
|
||||||
handler = self._find_error_handler(server_error)
|
handler = self._find_error_handler(server_error)
|
||||||
|
|
||||||
|
@ -1382,7 +1426,12 @@ class Flask(Scaffold):
|
||||||
|
|
||||||
return self.finalize_request(server_error, from_error_handler=True)
|
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`
|
"""Logs an exception. This is called by :meth:`handle_exception`
|
||||||
if debugging is disabled and right before the handler is called.
|
if debugging is disabled and right before the handler is called.
|
||||||
The default implementation logs the exception as error on the
|
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
|
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
|
"""Exceptions that are recording during routing are reraised with
|
||||||
this method. During debug we are not reraising redirect requests
|
this method. During debug we are not reraising redirect requests
|
||||||
for non ``GET``, ``HEAD``, or ``OPTIONS`` requests and we're raising
|
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 not isinstance(request.routing_exception, RequestRedirect)
|
||||||
or request.method in ("GET", "HEAD", "OPTIONS")
|
or request.method in ("GET", "HEAD", "OPTIONS")
|
||||||
):
|
):
|
||||||
raise request.routing_exception
|
raise request.routing_exception # type: ignore
|
||||||
|
|
||||||
from .debughelpers import FormDataRoutingRedirect
|
from .debughelpers import FormDataRoutingRedirect
|
||||||
|
|
||||||
raise FormDataRoutingRedirect(request)
|
raise FormDataRoutingRedirect(request)
|
||||||
|
|
||||||
def dispatch_request(self):
|
def dispatch_request(self) -> ResponseReturnValue:
|
||||||
"""Does the request dispatching. Matches the URL and returns the
|
"""Does the request dispatching. Matches the URL and returns the
|
||||||
return value of the view or error handler. This does not have to
|
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
|
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
|
# otherwise dispatch to the handler for that endpoint
|
||||||
return self.view_functions[rule.endpoint](**req.view_args)
|
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
|
"""Dispatches the request and on top of that performs request
|
||||||
pre and postprocessing as well as HTTP exception catching and
|
pre and postprocessing as well as HTTP exception catching and
|
||||||
error handling.
|
error handling.
|
||||||
|
@ -1454,7 +1503,11 @@ class Flask(Scaffold):
|
||||||
rv = self.handle_user_exception(e)
|
rv = self.handle_user_exception(e)
|
||||||
return self.finalize_request(rv)
|
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
|
"""Given the return value from a view function this finalizes
|
||||||
the request by converting it into a response and invoking the
|
the request by converting it into a response and invoking the
|
||||||
postprocessing functions. This is invoked for both normal
|
postprocessing functions. This is invoked for both normal
|
||||||
|
@ -1479,7 +1532,7 @@ class Flask(Scaffold):
|
||||||
)
|
)
|
||||||
return response
|
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
|
"""Called before each request and will ensure that it triggers
|
||||||
the :attr:`before_first_request_funcs` and only exactly once per
|
the :attr:`before_first_request_funcs` and only exactly once per
|
||||||
application instance (which means process usually).
|
application instance (which means process usually).
|
||||||
|
@ -1495,7 +1548,7 @@ class Flask(Scaffold):
|
||||||
func()
|
func()
|
||||||
self._got_first_request = True
|
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 method is called to create the default ``OPTIONS`` response.
|
||||||
This can be changed through subclassing to change the default
|
This can be changed through subclassing to change the default
|
||||||
behavior of ``OPTIONS`` responses.
|
behavior of ``OPTIONS`` responses.
|
||||||
|
@ -1508,7 +1561,7 @@ class Flask(Scaffold):
|
||||||
rv.allow.update(methods)
|
rv.allow.update(methods)
|
||||||
return rv
|
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
|
"""This is called to figure out if an error should be ignored
|
||||||
or not as far as the teardown system is concerned. If this
|
or not as far as the teardown system is concerned. If this
|
||||||
function returns ``True`` then the teardown handlers will not be
|
function returns ``True`` then the teardown handlers will not be
|
||||||
|
@ -1518,7 +1571,7 @@ class Flask(Scaffold):
|
||||||
"""
|
"""
|
||||||
return False
|
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.
|
"""Ensure that the function is synchronous for WSGI workers.
|
||||||
Plain ``def`` functions are returned as-is. ``async def``
|
Plain ``def`` functions are returned as-is. ``async def``
|
||||||
functions are wrapped to run and wait for the response.
|
functions are wrapped to run and wait for the response.
|
||||||
|
@ -1532,7 +1585,7 @@ class Flask(Scaffold):
|
||||||
|
|
||||||
return func
|
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
|
"""Convert the return value from a view function to an instance of
|
||||||
:attr:`response_class`.
|
:attr:`response_class`.
|
||||||
|
|
||||||
|
@ -1620,7 +1673,7 @@ class Flask(Scaffold):
|
||||||
# evaluate a WSGI callable, or coerce a different response
|
# evaluate a WSGI callable, or coerce a different response
|
||||||
# class to the correct type
|
# class to the correct type
|
||||||
try:
|
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:
|
except TypeError as e:
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
f"{e}\nThe view function did not return a valid"
|
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__}."
|
f" callable, but it was a {type(rv).__name__}."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
rv = t.cast(Response, rv)
|
||||||
# prefer the status if it was provided
|
# prefer the status if it was provided
|
||||||
if status is not None:
|
if status is not None:
|
||||||
if isinstance(status, (str, bytes, bytearray)):
|
if isinstance(status, (str, bytes, bytearray)):
|
||||||
rv.status = status
|
rv.status = status # type: ignore
|
||||||
else:
|
else:
|
||||||
rv.status_code = status
|
rv.status_code = status
|
||||||
|
|
||||||
|
@ -1649,7 +1703,9 @@ class Flask(Scaffold):
|
||||||
|
|
||||||
return rv
|
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
|
"""Creates a URL adapter for the given request. The URL adapter
|
||||||
is created at a point where the request context is not yet set
|
is created at a point where the request context is not yet set
|
||||||
up so the request is passed explicitly.
|
up so the request is passed explicitly.
|
||||||
|
@ -1687,21 +1743,25 @@ class Flask(Scaffold):
|
||||||
url_scheme=self.config["PREFERRED_URL_SCHEME"],
|
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
|
"""Injects the URL defaults for the given endpoint directly into
|
||||||
the values dictionary passed. This is used internally and
|
the values dictionary passed. This is used internally and
|
||||||
automatically called on URL building.
|
automatically called on URL building.
|
||||||
|
|
||||||
.. versionadded:: 0.7
|
.. versionadded:: 0.7
|
||||||
"""
|
"""
|
||||||
funcs = self.url_default_functions[None]
|
funcs: t.Iterable[URLDefaultCallable] = self.url_default_functions[None]
|
||||||
if "." in endpoint:
|
if "." in endpoint:
|
||||||
bp = endpoint.rsplit(".", 1)[0]
|
bp = endpoint.rsplit(".", 1)[0]
|
||||||
funcs = chain(funcs, self.url_default_functions[bp])
|
funcs = chain(funcs, self.url_default_functions[bp])
|
||||||
for func in funcs:
|
for func in funcs:
|
||||||
func(endpoint, values)
|
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
|
"""Handle :class:`~werkzeug.routing.BuildError` on
|
||||||
:meth:`url_for`.
|
:meth:`url_for`.
|
||||||
"""
|
"""
|
||||||
|
@ -1722,7 +1782,7 @@ class Flask(Scaffold):
|
||||||
|
|
||||||
raise error
|
raise error
|
||||||
|
|
||||||
def preprocess_request(self):
|
def preprocess_request(self) -> t.Optional[ResponseReturnValue]:
|
||||||
"""Called before the request is dispatched. Calls
|
"""Called before the request is dispatched. Calls
|
||||||
:attr:`url_value_preprocessors` registered with the app and the
|
:attr:`url_value_preprocessors` registered with the app and the
|
||||||
current blueprint (if any). Then calls :attr:`before_request_funcs`
|
current blueprint (if any). Then calls :attr:`before_request_funcs`
|
||||||
|
@ -1733,14 +1793,16 @@ class Flask(Scaffold):
|
||||||
further request handling is stopped.
|
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():
|
for bp in self._request_blueprints():
|
||||||
if bp in self.url_value_preprocessors:
|
if bp in self.url_value_preprocessors:
|
||||||
funcs = chain(funcs, self.url_value_preprocessors[bp])
|
funcs = chain(funcs, self.url_value_preprocessors[bp])
|
||||||
for func in funcs:
|
for func in funcs:
|
||||||
func(request.endpoint, request.view_args)
|
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():
|
for bp in self._request_blueprints():
|
||||||
if bp in self.before_request_funcs:
|
if bp in self.before_request_funcs:
|
||||||
funcs = chain(funcs, self.before_request_funcs[bp])
|
funcs = chain(funcs, self.before_request_funcs[bp])
|
||||||
|
@ -1749,7 +1811,9 @@ class Flask(Scaffold):
|
||||||
if rv is not None:
|
if rv is not None:
|
||||||
return rv
|
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
|
"""Can be overridden in order to modify the response object
|
||||||
before it's sent to the WSGI server. By default this will
|
before it's sent to the WSGI server. By default this will
|
||||||
call all the :meth:`after_request` decorated functions.
|
call all the :meth:`after_request` decorated functions.
|
||||||
|
@ -1763,7 +1827,7 @@ class Flask(Scaffold):
|
||||||
instance of :attr:`response_class`.
|
instance of :attr:`response_class`.
|
||||||
"""
|
"""
|
||||||
ctx = _request_ctx_stack.top
|
ctx = _request_ctx_stack.top
|
||||||
funcs = ctx._after_request_functions
|
funcs: t.Iterable[AfterRequestCallable] = ctx._after_request_functions
|
||||||
for bp in self._request_blueprints():
|
for bp in self._request_blueprints():
|
||||||
if bp in self.after_request_funcs:
|
if bp in self.after_request_funcs:
|
||||||
funcs = chain(funcs, reversed(self.after_request_funcs[bp]))
|
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)
|
self.session_interface.save_session(self, ctx.session, response)
|
||||||
return 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
|
"""Called after the request is dispatched and the response is
|
||||||
returned, right before the request context is popped.
|
returned, right before the request context is popped.
|
||||||
|
|
||||||
|
@ -1798,7 +1864,9 @@ class Flask(Scaffold):
|
||||||
"""
|
"""
|
||||||
if exc is _sentinel:
|
if exc is _sentinel:
|
||||||
exc = sys.exc_info()[1]
|
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():
|
for bp in self._request_blueprints():
|
||||||
if bp in self.teardown_request_funcs:
|
if bp in self.teardown_request_funcs:
|
||||||
funcs = chain(funcs, reversed(self.teardown_request_funcs[bp]))
|
funcs = chain(funcs, reversed(self.teardown_request_funcs[bp]))
|
||||||
|
@ -1806,7 +1874,9 @@ class Flask(Scaffold):
|
||||||
func(exc)
|
func(exc)
|
||||||
request_tearing_down.send(self, exc=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.
|
"""Called right before the application context is popped.
|
||||||
|
|
||||||
When handling a request, the application context is popped
|
When handling a request, the application context is popped
|
||||||
|
@ -1827,7 +1897,7 @@ class Flask(Scaffold):
|
||||||
func(exc)
|
func(exc)
|
||||||
appcontext_tearing_down.send(self, exc=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``
|
"""Create an :class:`~flask.ctx.AppContext`. Use as a ``with``
|
||||||
block to push the context, which will make :data:`current_app`
|
block to push the context, which will make :data:`current_app`
|
||||||
point at this application.
|
point at this application.
|
||||||
|
@ -1848,7 +1918,7 @@ class Flask(Scaffold):
|
||||||
"""
|
"""
|
||||||
return AppContext(self)
|
return AppContext(self)
|
||||||
|
|
||||||
def request_context(self, environ):
|
def request_context(self, environ: dict) -> RequestContext:
|
||||||
"""Create a :class:`~flask.ctx.RequestContext` representing a
|
"""Create a :class:`~flask.ctx.RequestContext` representing a
|
||||||
WSGI environment. Use a ``with`` block to push the context,
|
WSGI environment. Use a ``with`` block to push the context,
|
||||||
which will make :data:`request` point at this request.
|
which will make :data:`request` point at this request.
|
||||||
|
@ -1864,7 +1934,7 @@ class Flask(Scaffold):
|
||||||
"""
|
"""
|
||||||
return RequestContext(self, environ)
|
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
|
"""Create a :class:`~flask.ctx.RequestContext` for a WSGI
|
||||||
environment created from the given values. This is mostly useful
|
environment created from the given values. This is mostly useful
|
||||||
during testing, where you may want to run a function that uses
|
during testing, where you may want to run a function that uses
|
||||||
|
@ -1920,7 +1990,7 @@ class Flask(Scaffold):
|
||||||
finally:
|
finally:
|
||||||
builder.close()
|
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
|
"""The actual WSGI application. This is not implemented in
|
||||||
:meth:`__call__` so that middlewares can be applied without
|
:meth:`__call__` so that middlewares can be applied without
|
||||||
losing a reference to the app object. Instead of doing this::
|
losing a reference to the app object. Instead of doing this::
|
||||||
|
@ -1946,7 +2016,7 @@ class Flask(Scaffold):
|
||||||
start the response.
|
start the response.
|
||||||
"""
|
"""
|
||||||
ctx = self.request_context(environ)
|
ctx = self.request_context(environ)
|
||||||
error = None
|
error: t.Optional[BaseException] = None
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
ctx.push()
|
ctx.push()
|
||||||
|
@ -1963,14 +2033,14 @@ class Flask(Scaffold):
|
||||||
error = None
|
error = None
|
||||||
ctx.auto_pop(error)
|
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
|
"""The WSGI server calls the Flask application object as the
|
||||||
WSGI application. This calls :meth:`wsgi_app`, which can be
|
WSGI application. This calls :meth:`wsgi_app`, which can be
|
||||||
wrapped to apply middleware.
|
wrapped to apply middleware.
|
||||||
"""
|
"""
|
||||||
return self.wsgi_app(environ, start_response)
|
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:
|
if _request_ctx_stack.top.request.blueprint is None:
|
||||||
return []
|
return []
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -1,9 +1,25 @@
|
||||||
|
import typing as t
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from functools import update_wrapper
|
from functools import update_wrapper
|
||||||
|
|
||||||
from .scaffold import _endpoint_from_view_func
|
from .scaffold import _endpoint_from_view_func
|
||||||
from .scaffold import _sentinel
|
from .scaffold import _sentinel
|
||||||
from .scaffold import Scaffold
|
from .scaffold import Scaffold
|
||||||
|
from .typing import AfterRequestCallable
|
||||||
|
from .typing import BeforeRequestCallable
|
||||||
|
from .typing import ErrorHandlerCallable
|
||||||
|
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
|
||||||
|
|
||||||
|
if t.TYPE_CHECKING:
|
||||||
|
from .app import Flask
|
||||||
|
|
||||||
|
DeferredSetupFunction = t.Callable[["BlueprintSetupState"], t.Callable]
|
||||||
|
|
||||||
|
|
||||||
class BlueprintSetupState:
|
class BlueprintSetupState:
|
||||||
|
@ -13,7 +29,13 @@ class BlueprintSetupState:
|
||||||
to all register callback functions.
|
to all register callback functions.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, blueprint, app, options, first_registration):
|
def __init__(
|
||||||
|
self,
|
||||||
|
blueprint: "Blueprint",
|
||||||
|
app: "Flask",
|
||||||
|
options: t.Any,
|
||||||
|
first_registration: bool,
|
||||||
|
) -> None:
|
||||||
#: a reference to the current application
|
#: a reference to the current application
|
||||||
self.app = app
|
self.app = app
|
||||||
|
|
||||||
|
@ -52,7 +74,13 @@ class BlueprintSetupState:
|
||||||
self.url_defaults = dict(self.blueprint.url_values_defaults)
|
self.url_defaults = dict(self.blueprint.url_values_defaults)
|
||||||
self.url_defaults.update(self.options.get("url_defaults", ()))
|
self.url_defaults.update(self.options.get("url_defaults", ()))
|
||||||
|
|
||||||
def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
|
def add_url_rule(
|
||||||
|
self,
|
||||||
|
rule: str,
|
||||||
|
endpoint: t.Optional[str] = None,
|
||||||
|
view_func: t.Optional[t.Callable] = None,
|
||||||
|
**options: t.Any,
|
||||||
|
) -> None:
|
||||||
"""A helper method to register a rule (and optionally a view function)
|
"""A helper method to register a rule (and optionally a view function)
|
||||||
to the application. The endpoint is automatically prefixed with the
|
to the application. The endpoint is automatically prefixed with the
|
||||||
blueprint's name.
|
blueprint's name.
|
||||||
|
@ -64,7 +92,7 @@ class BlueprintSetupState:
|
||||||
rule = self.url_prefix
|
rule = self.url_prefix
|
||||||
options.setdefault("subdomain", self.subdomain)
|
options.setdefault("subdomain", self.subdomain)
|
||||||
if endpoint is None:
|
if endpoint is None:
|
||||||
endpoint = _endpoint_from_view_func(view_func)
|
endpoint = _endpoint_from_view_func(view_func) # type: ignore
|
||||||
defaults = self.url_defaults
|
defaults = self.url_defaults
|
||||||
if "defaults" in options:
|
if "defaults" in options:
|
||||||
defaults = dict(defaults, **options.pop("defaults"))
|
defaults = dict(defaults, **options.pop("defaults"))
|
||||||
|
@ -142,16 +170,16 @@ class Blueprint(Scaffold):
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
name,
|
name: str,
|
||||||
import_name,
|
import_name: str,
|
||||||
static_folder=None,
|
static_folder: t.Optional[str] = None,
|
||||||
static_url_path=None,
|
static_url_path: t.Optional[str] = None,
|
||||||
template_folder=None,
|
template_folder: t.Optional[str] = None,
|
||||||
url_prefix=None,
|
url_prefix: t.Optional[str] = None,
|
||||||
subdomain=None,
|
subdomain: t.Optional[str] = None,
|
||||||
url_defaults=None,
|
url_defaults: t.Optional[dict] = None,
|
||||||
root_path=None,
|
root_path: t.Optional[str] = None,
|
||||||
cli_group=_sentinel,
|
cli_group: t.Optional[str] = _sentinel, # type: ignore
|
||||||
):
|
):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
import_name=import_name,
|
import_name=import_name,
|
||||||
|
@ -163,19 +191,19 @@ class Blueprint(Scaffold):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.url_prefix = url_prefix
|
self.url_prefix = url_prefix
|
||||||
self.subdomain = subdomain
|
self.subdomain = subdomain
|
||||||
self.deferred_functions = []
|
self.deferred_functions: t.List[DeferredSetupFunction] = []
|
||||||
|
|
||||||
if url_defaults is None:
|
if url_defaults is None:
|
||||||
url_defaults = {}
|
url_defaults = {}
|
||||||
|
|
||||||
self.url_values_defaults = url_defaults
|
self.url_values_defaults = url_defaults
|
||||||
self.cli_group = cli_group
|
self.cli_group = cli_group
|
||||||
self._blueprints = []
|
self._blueprints: t.List[t.Tuple["Blueprint", dict]] = []
|
||||||
|
|
||||||
def _is_setup_finished(self):
|
def _is_setup_finished(self) -> bool:
|
||||||
return self.warn_on_modifications and self._got_registered_once
|
return self.warn_on_modifications and self._got_registered_once
|
||||||
|
|
||||||
def record(self, func):
|
def record(self, func: t.Callable) -> None:
|
||||||
"""Registers a function that is called when the blueprint is
|
"""Registers a function that is called when the blueprint is
|
||||||
registered on the application. This function is called with the
|
registered on the application. This function is called with the
|
||||||
state as argument as returned by the :meth:`make_setup_state`
|
state as argument as returned by the :meth:`make_setup_state`
|
||||||
|
@ -193,27 +221,29 @@ class Blueprint(Scaffold):
|
||||||
)
|
)
|
||||||
self.deferred_functions.append(func)
|
self.deferred_functions.append(func)
|
||||||
|
|
||||||
def record_once(self, func):
|
def record_once(self, func: t.Callable) -> None:
|
||||||
"""Works like :meth:`record` but wraps the function in another
|
"""Works like :meth:`record` but wraps the function in another
|
||||||
function that will ensure the function is only called once. If the
|
function that will ensure the function is only called once. If the
|
||||||
blueprint is registered a second time on the application, the
|
blueprint is registered a second time on the application, the
|
||||||
function passed is not called.
|
function passed is not called.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def wrapper(state):
|
def wrapper(state: BlueprintSetupState) -> None:
|
||||||
if state.first_registration:
|
if state.first_registration:
|
||||||
func(state)
|
func(state)
|
||||||
|
|
||||||
return self.record(update_wrapper(wrapper, func))
|
return self.record(update_wrapper(wrapper, func))
|
||||||
|
|
||||||
def make_setup_state(self, app, options, first_registration=False):
|
def make_setup_state(
|
||||||
|
self, app: "Flask", options: dict, first_registration: bool = False
|
||||||
|
) -> BlueprintSetupState:
|
||||||
"""Creates an instance of :meth:`~flask.blueprints.BlueprintSetupState`
|
"""Creates an instance of :meth:`~flask.blueprints.BlueprintSetupState`
|
||||||
object that is later passed to the register callback functions.
|
object that is later passed to the register callback functions.
|
||||||
Subclasses can override this to return a subclass of the setup state.
|
Subclasses can override this to return a subclass of the setup state.
|
||||||
"""
|
"""
|
||||||
return BlueprintSetupState(self, app, options, first_registration)
|
return BlueprintSetupState(self, app, options, first_registration)
|
||||||
|
|
||||||
def register_blueprint(self, blueprint, **options):
|
def register_blueprint(self, blueprint: "Blueprint", **options: t.Any) -> None:
|
||||||
"""Register a :class:`~flask.Blueprint` on this blueprint. Keyword
|
"""Register a :class:`~flask.Blueprint` on this blueprint. Keyword
|
||||||
arguments passed to this method will override the defaults set
|
arguments passed to this method will override the defaults set
|
||||||
on the blueprint.
|
on the blueprint.
|
||||||
|
@ -222,7 +252,7 @@ class Blueprint(Scaffold):
|
||||||
"""
|
"""
|
||||||
self._blueprints.append((blueprint, options))
|
self._blueprints.append((blueprint, options))
|
||||||
|
|
||||||
def register(self, app, options):
|
def register(self, app: "Flask", options: dict) -> None:
|
||||||
"""Called by :meth:`Flask.register_blueprint` to register all
|
"""Called by :meth:`Flask.register_blueprint` to register all
|
||||||
views and callbacks registered on the blueprint with the
|
views and callbacks registered on the blueprint with the
|
||||||
application. Creates a :class:`.BlueprintSetupState` and calls
|
application. Creates a :class:`.BlueprintSetupState` and calls
|
||||||
|
@ -327,7 +357,13 @@ class Blueprint(Scaffold):
|
||||||
bp_options["name_prefix"] = options.get("name_prefix", "") + self.name + "."
|
bp_options["name_prefix"] = options.get("name_prefix", "") + self.name + "."
|
||||||
blueprint.register(app, bp_options)
|
blueprint.register(app, bp_options)
|
||||||
|
|
||||||
def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
|
def add_url_rule(
|
||||||
|
self,
|
||||||
|
rule: str,
|
||||||
|
endpoint: t.Optional[str] = None,
|
||||||
|
view_func: t.Optional[t.Callable] = None,
|
||||||
|
**options: t.Any,
|
||||||
|
) -> None:
|
||||||
"""Like :meth:`Flask.add_url_rule` but for a blueprint. The endpoint for
|
"""Like :meth:`Flask.add_url_rule` but for a blueprint. The endpoint for
|
||||||
the :func:`url_for` function is prefixed with the name of the blueprint.
|
the :func:`url_for` function is prefixed with the name of the blueprint.
|
||||||
"""
|
"""
|
||||||
|
@ -339,7 +375,7 @@ class Blueprint(Scaffold):
|
||||||
), "Blueprint view function name should not contain dots"
|
), "Blueprint view function name should not contain dots"
|
||||||
self.record(lambda s: s.add_url_rule(rule, endpoint, view_func, **options))
|
self.record(lambda s: s.add_url_rule(rule, endpoint, view_func, **options))
|
||||||
|
|
||||||
def app_template_filter(self, name=None):
|
def app_template_filter(self, name: t.Optional[str] = None) -> t.Callable:
|
||||||
"""Register a custom template filter, available application wide. Like
|
"""Register a custom template filter, available application wide. Like
|
||||||
:meth:`Flask.template_filter` but for a blueprint.
|
:meth:`Flask.template_filter` but for a blueprint.
|
||||||
|
|
||||||
|
@ -347,13 +383,15 @@ class Blueprint(Scaffold):
|
||||||
function name will be used.
|
function name will be used.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def decorator(f):
|
def decorator(f: TemplateFilterCallable) -> TemplateFilterCallable:
|
||||||
self.add_app_template_filter(f, name=name)
|
self.add_app_template_filter(f, name=name)
|
||||||
return f
|
return f
|
||||||
|
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
def add_app_template_filter(self, f, name=None):
|
def add_app_template_filter(
|
||||||
|
self, f: TemplateFilterCallable, name: t.Optional[str] = None
|
||||||
|
) -> None:
|
||||||
"""Register a custom template filter, available application wide. Like
|
"""Register a custom template filter, available application wide. Like
|
||||||
:meth:`Flask.add_template_filter` but for a blueprint. Works exactly
|
:meth:`Flask.add_template_filter` but for a blueprint. Works exactly
|
||||||
like the :meth:`app_template_filter` decorator.
|
like the :meth:`app_template_filter` decorator.
|
||||||
|
@ -362,12 +400,12 @@ class Blueprint(Scaffold):
|
||||||
function name will be used.
|
function name will be used.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def register_template(state):
|
def register_template(state: BlueprintSetupState) -> None:
|
||||||
state.app.jinja_env.filters[name or f.__name__] = f
|
state.app.jinja_env.filters[name or f.__name__] = f
|
||||||
|
|
||||||
self.record_once(register_template)
|
self.record_once(register_template)
|
||||||
|
|
||||||
def app_template_test(self, name=None):
|
def app_template_test(self, name: t.Optional[str] = None) -> t.Callable:
|
||||||
"""Register a custom template test, available application wide. Like
|
"""Register a custom template test, available application wide. Like
|
||||||
:meth:`Flask.template_test` but for a blueprint.
|
:meth:`Flask.template_test` but for a blueprint.
|
||||||
|
|
||||||
|
@ -377,13 +415,15 @@ class Blueprint(Scaffold):
|
||||||
function name will be used.
|
function name will be used.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def decorator(f):
|
def decorator(f: TemplateTestCallable) -> TemplateTestCallable:
|
||||||
self.add_app_template_test(f, name=name)
|
self.add_app_template_test(f, name=name)
|
||||||
return f
|
return f
|
||||||
|
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
def add_app_template_test(self, f, name=None):
|
def add_app_template_test(
|
||||||
|
self, f: TemplateTestCallable, name: t.Optional[str] = None
|
||||||
|
) -> None:
|
||||||
"""Register a custom template test, available application wide. Like
|
"""Register a custom template test, available application wide. Like
|
||||||
:meth:`Flask.add_template_test` but for a blueprint. Works exactly
|
:meth:`Flask.add_template_test` but for a blueprint. Works exactly
|
||||||
like the :meth:`app_template_test` decorator.
|
like the :meth:`app_template_test` decorator.
|
||||||
|
@ -394,12 +434,12 @@ class Blueprint(Scaffold):
|
||||||
function name will be used.
|
function name will be used.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def register_template(state):
|
def register_template(state: BlueprintSetupState) -> None:
|
||||||
state.app.jinja_env.tests[name or f.__name__] = f
|
state.app.jinja_env.tests[name or f.__name__] = f
|
||||||
|
|
||||||
self.record_once(register_template)
|
self.record_once(register_template)
|
||||||
|
|
||||||
def app_template_global(self, name=None):
|
def app_template_global(self, name: t.Optional[str] = None) -> t.Callable:
|
||||||
"""Register a custom template global, available application wide. Like
|
"""Register a custom template global, available application wide. Like
|
||||||
:meth:`Flask.template_global` but for a blueprint.
|
:meth:`Flask.template_global` but for a blueprint.
|
||||||
|
|
||||||
|
@ -409,13 +449,15 @@ class Blueprint(Scaffold):
|
||||||
function name will be used.
|
function name will be used.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def decorator(f):
|
def decorator(f: TemplateGlobalCallable) -> TemplateGlobalCallable:
|
||||||
self.add_app_template_global(f, name=name)
|
self.add_app_template_global(f, name=name)
|
||||||
return f
|
return f
|
||||||
|
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
def add_app_template_global(self, f, name=None):
|
def add_app_template_global(
|
||||||
|
self, f: TemplateGlobalCallable, name: t.Optional[str] = None
|
||||||
|
) -> None:
|
||||||
"""Register a custom template global, available application wide. Like
|
"""Register a custom template global, available application wide. Like
|
||||||
:meth:`Flask.add_template_global` but for a blueprint. Works exactly
|
:meth:`Flask.add_template_global` but for a blueprint. Works exactly
|
||||||
like the :meth:`app_template_global` decorator.
|
like the :meth:`app_template_global` decorator.
|
||||||
|
@ -426,12 +468,12 @@ class Blueprint(Scaffold):
|
||||||
function name will be used.
|
function name will be used.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def register_template(state):
|
def register_template(state: BlueprintSetupState) -> None:
|
||||||
state.app.jinja_env.globals[name or f.__name__] = f
|
state.app.jinja_env.globals[name or f.__name__] = f
|
||||||
|
|
||||||
self.record_once(register_template)
|
self.record_once(register_template)
|
||||||
|
|
||||||
def before_app_request(self, f):
|
def before_app_request(self, f: BeforeRequestCallable) -> BeforeRequestCallable:
|
||||||
"""Like :meth:`Flask.before_request`. Such a function is executed
|
"""Like :meth:`Flask.before_request`. Such a function is executed
|
||||||
before each request, even if outside of a blueprint.
|
before each request, even if outside of a blueprint.
|
||||||
"""
|
"""
|
||||||
|
@ -442,7 +484,9 @@ class Blueprint(Scaffold):
|
||||||
)
|
)
|
||||||
return f
|
return f
|
||||||
|
|
||||||
def before_app_first_request(self, f):
|
def before_app_first_request(
|
||||||
|
self, f: BeforeRequestCallable
|
||||||
|
) -> BeforeRequestCallable:
|
||||||
"""Like :meth:`Flask.before_first_request`. Such a function is
|
"""Like :meth:`Flask.before_first_request`. Such a function is
|
||||||
executed before the first request to the application.
|
executed before the first request to the application.
|
||||||
"""
|
"""
|
||||||
|
@ -451,7 +495,7 @@ class Blueprint(Scaffold):
|
||||||
)
|
)
|
||||||
return f
|
return f
|
||||||
|
|
||||||
def after_app_request(self, f):
|
def after_app_request(self, f: AfterRequestCallable) -> AfterRequestCallable:
|
||||||
"""Like :meth:`Flask.after_request` but for a blueprint. Such a function
|
"""Like :meth:`Flask.after_request` but for a blueprint. Such a function
|
||||||
is executed after each request, even if outside of the blueprint.
|
is executed after each request, even if outside of the blueprint.
|
||||||
"""
|
"""
|
||||||
|
@ -462,7 +506,7 @@ class Blueprint(Scaffold):
|
||||||
)
|
)
|
||||||
return f
|
return f
|
||||||
|
|
||||||
def teardown_app_request(self, f):
|
def teardown_app_request(self, f: TeardownCallable) -> TeardownCallable:
|
||||||
"""Like :meth:`Flask.teardown_request` but for a blueprint. Such a
|
"""Like :meth:`Flask.teardown_request` but for a blueprint. Such a
|
||||||
function is executed when tearing down each request, even if outside of
|
function is executed when tearing down each request, even if outside of
|
||||||
the blueprint.
|
the blueprint.
|
||||||
|
@ -472,7 +516,9 @@ class Blueprint(Scaffold):
|
||||||
)
|
)
|
||||||
return f
|
return f
|
||||||
|
|
||||||
def app_context_processor(self, f):
|
def app_context_processor(
|
||||||
|
self, f: TemplateContextProcessorCallable
|
||||||
|
) -> TemplateContextProcessorCallable:
|
||||||
"""Like :meth:`Flask.context_processor` but for a blueprint. Such a
|
"""Like :meth:`Flask.context_processor` but for a blueprint. Such a
|
||||||
function is executed each request, even if outside of the blueprint.
|
function is executed each request, even if outside of the blueprint.
|
||||||
"""
|
"""
|
||||||
|
@ -481,32 +527,34 @@ class Blueprint(Scaffold):
|
||||||
)
|
)
|
||||||
return f
|
return f
|
||||||
|
|
||||||
def app_errorhandler(self, code):
|
def app_errorhandler(self, code: t.Union[t.Type[Exception], int]) -> t.Callable:
|
||||||
"""Like :meth:`Flask.errorhandler` but for a blueprint. This
|
"""Like :meth:`Flask.errorhandler` but for a blueprint. This
|
||||||
handler is used for all requests, even if outside of the blueprint.
|
handler is used for all requests, even if outside of the blueprint.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def decorator(f):
|
def decorator(f: ErrorHandlerCallable) -> ErrorHandlerCallable:
|
||||||
self.record_once(lambda s: s.app.errorhandler(code)(f))
|
self.record_once(lambda s: s.app.errorhandler(code)(f))
|
||||||
return f
|
return f
|
||||||
|
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
def app_url_value_preprocessor(self, f):
|
def app_url_value_preprocessor(
|
||||||
|
self, f: URLValuePreprocessorCallable
|
||||||
|
) -> URLValuePreprocessorCallable:
|
||||||
"""Same as :meth:`url_value_preprocessor` but application wide."""
|
"""Same as :meth:`url_value_preprocessor` but application wide."""
|
||||||
self.record_once(
|
self.record_once(
|
||||||
lambda s: s.app.url_value_preprocessors.setdefault(None, []).append(f)
|
lambda s: s.app.url_value_preprocessors.setdefault(None, []).append(f)
|
||||||
)
|
)
|
||||||
return f
|
return f
|
||||||
|
|
||||||
def app_url_defaults(self, f):
|
def app_url_defaults(self, f: URLDefaultCallable) -> URLDefaultCallable:
|
||||||
"""Same as :meth:`url_defaults` but application wide."""
|
"""Same as :meth:`url_defaults` but application wide."""
|
||||||
self.record_once(
|
self.record_once(
|
||||||
lambda s: s.app.url_default_functions.setdefault(None, []).append(f)
|
lambda s: s.app.url_default_functions.setdefault(None, []).append(f)
|
||||||
)
|
)
|
||||||
return f
|
return f
|
||||||
|
|
||||||
def ensure_sync(self, f):
|
def ensure_sync(self, f: t.Callable) -> t.Callable:
|
||||||
"""Ensure the function is synchronous.
|
"""Ensure the function is synchronous.
|
||||||
|
|
||||||
Override if you would like custom async to sync behaviour in
|
Override if you would like custom async to sync behaviour in
|
||||||
|
|
|
@ -27,7 +27,7 @@ except ImportError:
|
||||||
try:
|
try:
|
||||||
import ssl
|
import ssl
|
||||||
except ImportError:
|
except ImportError:
|
||||||
ssl = None
|
ssl = None # type: ignore
|
||||||
|
|
||||||
|
|
||||||
class NoAppException(click.UsageError):
|
class NoAppException(click.UsageError):
|
||||||
|
@ -860,7 +860,7 @@ def run_command(
|
||||||
|
|
||||||
@click.command("shell", short_help="Run a shell in the app context.")
|
@click.command("shell", short_help="Run a shell in the app context.")
|
||||||
@with_appcontext
|
@with_appcontext
|
||||||
def shell_command():
|
def shell_command() -> None:
|
||||||
"""Run an interactive Python shell in the context of a given
|
"""Run an interactive Python shell in the context of a given
|
||||||
Flask application. The application will populate the default
|
Flask application. The application will populate the default
|
||||||
namespace of this shell according to its configuration.
|
namespace of this shell according to its configuration.
|
||||||
|
@ -877,7 +877,7 @@ def shell_command():
|
||||||
f"App: {app.import_name} [{app.env}]\n"
|
f"App: {app.import_name} [{app.env}]\n"
|
||||||
f"Instance: {app.instance_path}"
|
f"Instance: {app.instance_path}"
|
||||||
)
|
)
|
||||||
ctx = {}
|
ctx: dict = {}
|
||||||
|
|
||||||
# Support the regular Python interpreter startup script if someone
|
# Support the regular Python interpreter startup script if someone
|
||||||
# is using it.
|
# is using it.
|
||||||
|
@ -922,7 +922,7 @@ def shell_command():
|
||||||
)
|
)
|
||||||
@click.option("--all-methods", is_flag=True, help="Show HEAD and OPTIONS methods.")
|
@click.option("--all-methods", is_flag=True, help="Show HEAD and OPTIONS methods.")
|
||||||
@with_appcontext
|
@with_appcontext
|
||||||
def routes_command(sort, all_methods):
|
def routes_command(sort: str, all_methods: bool) -> None:
|
||||||
"""Show all registered routes with endpoints and methods."""
|
"""Show all registered routes with endpoints and methods."""
|
||||||
|
|
||||||
rules = list(current_app.url_map.iter_rules())
|
rules = list(current_app.url_map.iter_rules())
|
||||||
|
@ -935,9 +935,12 @@ def routes_command(sort, all_methods):
|
||||||
if sort in ("endpoint", "rule"):
|
if sort in ("endpoint", "rule"):
|
||||||
rules = sorted(rules, key=attrgetter(sort))
|
rules = sorted(rules, key=attrgetter(sort))
|
||||||
elif sort == "methods":
|
elif sort == "methods":
|
||||||
rules = sorted(rules, key=lambda rule: sorted(rule.methods))
|
rules = sorted(rules, key=lambda rule: sorted(rule.methods)) # type: ignore
|
||||||
|
|
||||||
rule_methods = [", ".join(sorted(rule.methods - ignored_methods)) for rule in rules]
|
rule_methods = [
|
||||||
|
", ".join(sorted(rule.methods - ignored_methods)) # type: ignore
|
||||||
|
for rule in rules
|
||||||
|
]
|
||||||
|
|
||||||
headers = ("Endpoint", "Methods", "Rule")
|
headers = ("Endpoint", "Methods", "Rule")
|
||||||
widths = (
|
widths = (
|
||||||
|
@ -975,7 +978,7 @@ debug mode.
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main() -> None:
|
||||||
# TODO omit sys.argv once https://github.com/pallets/click/issues/536 is fixed
|
# TODO omit sys.argv once https://github.com/pallets/click/issues/536 is fixed
|
||||||
cli.main(args=sys.argv[1:])
|
cli.main(args=sys.argv[1:])
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import errno
|
import errno
|
||||||
import os
|
import os
|
||||||
import types
|
import types
|
||||||
|
import typing as t
|
||||||
|
|
||||||
from werkzeug.utils import import_string
|
from werkzeug.utils import import_string
|
||||||
|
|
||||||
|
@ -8,11 +9,11 @@ from werkzeug.utils import import_string
|
||||||
class ConfigAttribute:
|
class ConfigAttribute:
|
||||||
"""Makes an attribute forward to the config"""
|
"""Makes an attribute forward to the config"""
|
||||||
|
|
||||||
def __init__(self, name, get_converter=None):
|
def __init__(self, name: str, get_converter: t.Optional[t.Callable] = None) -> None:
|
||||||
self.__name__ = name
|
self.__name__ = name
|
||||||
self.get_converter = get_converter
|
self.get_converter = get_converter
|
||||||
|
|
||||||
def __get__(self, obj, type=None):
|
def __get__(self, obj: t.Any, owner: t.Any = None) -> t.Any:
|
||||||
if obj is None:
|
if obj is None:
|
||||||
return self
|
return self
|
||||||
rv = obj.config[self.__name__]
|
rv = obj.config[self.__name__]
|
||||||
|
@ -20,7 +21,7 @@ class ConfigAttribute:
|
||||||
rv = self.get_converter(rv)
|
rv = self.get_converter(rv)
|
||||||
return rv
|
return rv
|
||||||
|
|
||||||
def __set__(self, obj, value):
|
def __set__(self, obj: t.Any, value: t.Any) -> None:
|
||||||
obj.config[self.__name__] = value
|
obj.config[self.__name__] = value
|
||||||
|
|
||||||
|
|
||||||
|
@ -68,11 +69,11 @@ class Config(dict):
|
||||||
:param defaults: an optional dictionary of default values
|
:param defaults: an optional dictionary of default values
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, root_path, defaults=None):
|
def __init__(self, root_path: str, defaults: t.Optional[dict] = None) -> None:
|
||||||
dict.__init__(self, defaults or {})
|
dict.__init__(self, defaults or {})
|
||||||
self.root_path = root_path
|
self.root_path = root_path
|
||||||
|
|
||||||
def from_envvar(self, variable_name, silent=False):
|
def from_envvar(self, variable_name: str, silent: bool = False) -> bool:
|
||||||
"""Loads a configuration from an environment variable pointing to
|
"""Loads a configuration from an environment variable pointing to
|
||||||
a configuration file. This is basically just a shortcut with nicer
|
a configuration file. This is basically just a shortcut with nicer
|
||||||
error messages for this line of code::
|
error messages for this line of code::
|
||||||
|
@ -96,7 +97,7 @@ class Config(dict):
|
||||||
)
|
)
|
||||||
return self.from_pyfile(rv, silent=silent)
|
return self.from_pyfile(rv, silent=silent)
|
||||||
|
|
||||||
def from_pyfile(self, filename, silent=False):
|
def from_pyfile(self, filename: str, silent: bool = False) -> bool:
|
||||||
"""Updates the values in the config from a Python file. This function
|
"""Updates the values in the config from a Python file. This function
|
||||||
behaves as if the file was imported as module with the
|
behaves as if the file was imported as module with the
|
||||||
:meth:`from_object` function.
|
:meth:`from_object` function.
|
||||||
|
@ -124,7 +125,7 @@ class Config(dict):
|
||||||
self.from_object(d)
|
self.from_object(d)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def from_object(self, obj):
|
def from_object(self, obj: t.Union[object, str]) -> None:
|
||||||
"""Updates the values from the given object. An object can be of one
|
"""Updates the values from the given object. An object can be of one
|
||||||
of the following two types:
|
of the following two types:
|
||||||
|
|
||||||
|
@ -162,7 +163,12 @@ class Config(dict):
|
||||||
if key.isupper():
|
if key.isupper():
|
||||||
self[key] = getattr(obj, key)
|
self[key] = getattr(obj, key)
|
||||||
|
|
||||||
def from_file(self, filename, load, silent=False):
|
def from_file(
|
||||||
|
self,
|
||||||
|
filename: str,
|
||||||
|
load: t.Callable[[t.IO[t.Any]], t.Mapping],
|
||||||
|
silent: bool = False,
|
||||||
|
) -> bool:
|
||||||
"""Update the values in the config from a file that is loaded
|
"""Update the values in the config from a file that is loaded
|
||||||
using the ``load`` parameter. The loaded data is passed to the
|
using the ``load`` parameter. The loaded data is passed to the
|
||||||
:meth:`from_mapping` method.
|
:meth:`from_mapping` method.
|
||||||
|
@ -196,30 +202,26 @@ class Config(dict):
|
||||||
|
|
||||||
return self.from_mapping(obj)
|
return self.from_mapping(obj)
|
||||||
|
|
||||||
def from_mapping(self, *mapping, **kwargs):
|
def from_mapping(
|
||||||
|
self, mapping: t.Optional[t.Mapping[str, t.Any]] = None, **kwargs: t.Any
|
||||||
|
) -> bool:
|
||||||
"""Updates the config like :meth:`update` ignoring items with non-upper
|
"""Updates the config like :meth:`update` ignoring items with non-upper
|
||||||
keys.
|
keys.
|
||||||
|
|
||||||
.. versionadded:: 0.11
|
.. versionadded:: 0.11
|
||||||
"""
|
"""
|
||||||
mappings = []
|
mappings: t.Dict[str, t.Any] = {}
|
||||||
if len(mapping) == 1:
|
if mapping is not None:
|
||||||
if hasattr(mapping[0], "items"):
|
mappings.update(mapping)
|
||||||
mappings.append(mapping[0].items())
|
mappings.update(kwargs)
|
||||||
else:
|
for key, value in mappings.items():
|
||||||
mappings.append(mapping[0])
|
if key.isupper():
|
||||||
elif len(mapping) > 1:
|
self[key] = value
|
||||||
raise TypeError(
|
|
||||||
f"expected at most 1 positional argument, got {len(mapping)}"
|
|
||||||
)
|
|
||||||
mappings.append(kwargs.items())
|
|
||||||
for mapping in mappings:
|
|
||||||
for (key, value) in mapping:
|
|
||||||
if key.isupper():
|
|
||||||
self[key] = value
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def get_namespace(self, namespace, lowercase=True, trim_namespace=True):
|
def get_namespace(
|
||||||
|
self, namespace: str, lowercase: bool = True, trim_namespace: bool = True
|
||||||
|
) -> t.Dict[str, t.Any]:
|
||||||
"""Returns a dictionary containing a subset of configuration options
|
"""Returns a dictionary containing a subset of configuration options
|
||||||
that match the specified namespace/prefix. Example usage::
|
that match the specified namespace/prefix. Example usage::
|
||||||
|
|
||||||
|
@ -260,5 +262,5 @@ class Config(dict):
|
||||||
rv[key] = v
|
rv[key] = v
|
||||||
return rv
|
return rv
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self) -> str:
|
||||||
return f"<{type(self).__name__} {dict.__repr__(self)}>"
|
return f"<{type(self).__name__} {dict.__repr__(self)}>"
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import sys
|
import sys
|
||||||
|
import typing as t
|
||||||
from functools import update_wrapper
|
from functools import update_wrapper
|
||||||
|
from types import TracebackType
|
||||||
|
|
||||||
from werkzeug.exceptions import HTTPException
|
from werkzeug.exceptions import HTTPException
|
||||||
|
|
||||||
|
@ -7,6 +9,12 @@ from .globals import _app_ctx_stack
|
||||||
from .globals import _request_ctx_stack
|
from .globals import _request_ctx_stack
|
||||||
from .signals import appcontext_popped
|
from .signals import appcontext_popped
|
||||||
from .signals import appcontext_pushed
|
from .signals import appcontext_pushed
|
||||||
|
from .typing import AfterRequestCallable
|
||||||
|
|
||||||
|
if t.TYPE_CHECKING:
|
||||||
|
from .app import Flask
|
||||||
|
from .sessions import SessionMixin
|
||||||
|
from .wrappers import Request
|
||||||
|
|
||||||
|
|
||||||
# a singleton sentinel value for parameter defaults
|
# a singleton sentinel value for parameter defaults
|
||||||
|
@ -33,7 +41,7 @@ class _AppCtxGlobals:
|
||||||
.. versionadded:: 0.10
|
.. versionadded:: 0.10
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def get(self, name, default=None):
|
def get(self, name: str, default: t.Optional[t.Any] = None) -> t.Any:
|
||||||
"""Get an attribute by name, or a default value. Like
|
"""Get an attribute by name, or a default value. Like
|
||||||
:meth:`dict.get`.
|
:meth:`dict.get`.
|
||||||
|
|
||||||
|
@ -44,7 +52,7 @@ class _AppCtxGlobals:
|
||||||
"""
|
"""
|
||||||
return self.__dict__.get(name, default)
|
return self.__dict__.get(name, default)
|
||||||
|
|
||||||
def pop(self, name, default=_sentinel):
|
def pop(self, name: str, default: t.Any = _sentinel) -> t.Any:
|
||||||
"""Get and remove an attribute by name. Like :meth:`dict.pop`.
|
"""Get and remove an attribute by name. Like :meth:`dict.pop`.
|
||||||
|
|
||||||
:param name: Name of attribute to pop.
|
:param name: Name of attribute to pop.
|
||||||
|
@ -58,7 +66,7 @@ class _AppCtxGlobals:
|
||||||
else:
|
else:
|
||||||
return self.__dict__.pop(name, default)
|
return self.__dict__.pop(name, default)
|
||||||
|
|
||||||
def setdefault(self, name, default=None):
|
def setdefault(self, name: str, default: t.Any = None) -> t.Any:
|
||||||
"""Get the value of an attribute if it is present, otherwise
|
"""Get the value of an attribute if it is present, otherwise
|
||||||
set and return a default value. Like :meth:`dict.setdefault`.
|
set and return a default value. Like :meth:`dict.setdefault`.
|
||||||
|
|
||||||
|
@ -70,20 +78,20 @@ class _AppCtxGlobals:
|
||||||
"""
|
"""
|
||||||
return self.__dict__.setdefault(name, default)
|
return self.__dict__.setdefault(name, default)
|
||||||
|
|
||||||
def __contains__(self, item):
|
def __contains__(self, item: t.Any) -> bool:
|
||||||
return item in self.__dict__
|
return item in self.__dict__
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self) -> t.Iterator:
|
||||||
return iter(self.__dict__)
|
return iter(self.__dict__)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self) -> str:
|
||||||
top = _app_ctx_stack.top
|
top = _app_ctx_stack.top
|
||||||
if top is not None:
|
if top is not None:
|
||||||
return f"<flask.g of {top.app.name!r}>"
|
return f"<flask.g of {top.app.name!r}>"
|
||||||
return object.__repr__(self)
|
return object.__repr__(self)
|
||||||
|
|
||||||
|
|
||||||
def after_this_request(f):
|
def after_this_request(f: AfterRequestCallable) -> AfterRequestCallable:
|
||||||
"""Executes a function after this request. This is useful to modify
|
"""Executes a function after this request. This is useful to modify
|
||||||
response objects. The function is passed the response object and has
|
response objects. The function is passed the response object and has
|
||||||
to return the same or a new one.
|
to return the same or a new one.
|
||||||
|
@ -108,7 +116,7 @@ def after_this_request(f):
|
||||||
return f
|
return f
|
||||||
|
|
||||||
|
|
||||||
def copy_current_request_context(f):
|
def copy_current_request_context(f: t.Callable) -> t.Callable:
|
||||||
"""A helper function that decorates a function to retain the current
|
"""A helper function that decorates a function to retain the current
|
||||||
request context. This is useful when working with greenlets. The moment
|
request context. This is useful when working with greenlets. The moment
|
||||||
the function is decorated a copy of the request context is created and
|
the function is decorated a copy of the request context is created and
|
||||||
|
@ -148,7 +156,7 @@ def copy_current_request_context(f):
|
||||||
return update_wrapper(wrapper, f)
|
return update_wrapper(wrapper, f)
|
||||||
|
|
||||||
|
|
||||||
def has_request_context():
|
def has_request_context() -> bool:
|
||||||
"""If you have code that wants to test if a request context is there or
|
"""If you have code that wants to test if a request context is there or
|
||||||
not this function can be used. For instance, you may want to take advantage
|
not this function can be used. For instance, you may want to take advantage
|
||||||
of request information if the request object is available, but fail
|
of request information if the request object is available, but fail
|
||||||
|
@ -180,7 +188,7 @@ def has_request_context():
|
||||||
return _request_ctx_stack.top is not None
|
return _request_ctx_stack.top is not None
|
||||||
|
|
||||||
|
|
||||||
def has_app_context():
|
def has_app_context() -> bool:
|
||||||
"""Works like :func:`has_request_context` but for the application
|
"""Works like :func:`has_request_context` but for the application
|
||||||
context. You can also just do a boolean check on the
|
context. You can also just do a boolean check on the
|
||||||
:data:`current_app` object instead.
|
:data:`current_app` object instead.
|
||||||
|
@ -199,7 +207,7 @@ class AppContext:
|
||||||
context.
|
context.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, app):
|
def __init__(self, app: "Flask") -> None:
|
||||||
self.app = app
|
self.app = app
|
||||||
self.url_adapter = app.create_url_adapter(None)
|
self.url_adapter = app.create_url_adapter(None)
|
||||||
self.g = app.app_ctx_globals_class()
|
self.g = app.app_ctx_globals_class()
|
||||||
|
@ -208,13 +216,13 @@ class AppContext:
|
||||||
# but there a basic "refcount" is enough to track them.
|
# but there a basic "refcount" is enough to track them.
|
||||||
self._refcnt = 0
|
self._refcnt = 0
|
||||||
|
|
||||||
def push(self):
|
def push(self) -> None:
|
||||||
"""Binds the app context to the current context."""
|
"""Binds the app context to the current context."""
|
||||||
self._refcnt += 1
|
self._refcnt += 1
|
||||||
_app_ctx_stack.push(self)
|
_app_ctx_stack.push(self)
|
||||||
appcontext_pushed.send(self.app)
|
appcontext_pushed.send(self.app)
|
||||||
|
|
||||||
def pop(self, exc=_sentinel):
|
def pop(self, exc: t.Optional[BaseException] = _sentinel) -> None: # type: ignore
|
||||||
"""Pops the app context."""
|
"""Pops the app context."""
|
||||||
try:
|
try:
|
||||||
self._refcnt -= 1
|
self._refcnt -= 1
|
||||||
|
@ -227,11 +235,13 @@ class AppContext:
|
||||||
assert rv is self, f"Popped wrong app context. ({rv!r} instead of {self!r})"
|
assert rv is self, f"Popped wrong app context. ({rv!r} instead of {self!r})"
|
||||||
appcontext_popped.send(self.app)
|
appcontext_popped.send(self.app)
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self) -> "AppContext":
|
||||||
self.push()
|
self.push()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __exit__(self, exc_type, exc_value, tb):
|
def __exit__(
|
||||||
|
self, exc_type: type, exc_value: BaseException, tb: TracebackType
|
||||||
|
) -> None:
|
||||||
self.pop(exc_value)
|
self.pop(exc_value)
|
||||||
|
|
||||||
|
|
||||||
|
@ -265,7 +275,13 @@ class RequestContext:
|
||||||
that situation, otherwise your unittests will leak memory.
|
that situation, otherwise your unittests will leak memory.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, app, environ, request=None, session=None):
|
def __init__(
|
||||||
|
self,
|
||||||
|
app: "Flask",
|
||||||
|
environ: dict,
|
||||||
|
request: t.Optional["Request"] = None,
|
||||||
|
session: t.Optional["SessionMixin"] = None,
|
||||||
|
) -> None:
|
||||||
self.app = app
|
self.app = app
|
||||||
if request is None:
|
if request is None:
|
||||||
request = app.request_class(environ)
|
request = app.request_class(environ)
|
||||||
|
@ -282,7 +298,7 @@ class RequestContext:
|
||||||
# other request contexts. Now only if the last level is popped we
|
# other request contexts. Now only if the last level is popped we
|
||||||
# get rid of them. Additionally if an application context is missing
|
# get rid of them. Additionally if an application context is missing
|
||||||
# one is created implicitly so for each level we add this information
|
# one is created implicitly so for each level we add this information
|
||||||
self._implicit_app_ctx_stack = []
|
self._implicit_app_ctx_stack: t.List[t.Optional["AppContext"]] = []
|
||||||
|
|
||||||
# indicator if the context was preserved. Next time another context
|
# indicator if the context was preserved. Next time another context
|
||||||
# is pushed the preserved context is popped.
|
# is pushed the preserved context is popped.
|
||||||
|
@ -295,17 +311,17 @@ class RequestContext:
|
||||||
# Functions that should be executed after the request on the response
|
# Functions that should be executed after the request on the response
|
||||||
# object. These will be called before the regular "after_request"
|
# object. These will be called before the regular "after_request"
|
||||||
# functions.
|
# functions.
|
||||||
self._after_request_functions = []
|
self._after_request_functions: t.List[AfterRequestCallable] = []
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def g(self):
|
def g(self) -> AppContext:
|
||||||
return _app_ctx_stack.top.g
|
return _app_ctx_stack.top.g
|
||||||
|
|
||||||
@g.setter
|
@g.setter
|
||||||
def g(self, value):
|
def g(self, value: AppContext) -> None:
|
||||||
_app_ctx_stack.top.g = value
|
_app_ctx_stack.top.g = value
|
||||||
|
|
||||||
def copy(self):
|
def copy(self) -> "RequestContext":
|
||||||
"""Creates a copy of this request context with the same request object.
|
"""Creates a copy of this request context with the same request object.
|
||||||
This can be used to move a request context to a different greenlet.
|
This can be used to move a request context to a different greenlet.
|
||||||
Because the actual request object is the same this cannot be used to
|
Because the actual request object is the same this cannot be used to
|
||||||
|
@ -325,17 +341,17 @@ class RequestContext:
|
||||||
session=self.session,
|
session=self.session,
|
||||||
)
|
)
|
||||||
|
|
||||||
def match_request(self):
|
def match_request(self) -> None:
|
||||||
"""Can be overridden by a subclass to hook into the matching
|
"""Can be overridden by a subclass to hook into the matching
|
||||||
of the request.
|
of the request.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
result = self.url_adapter.match(return_rule=True)
|
result = self.url_adapter.match(return_rule=True) # type: ignore
|
||||||
self.request.url_rule, self.request.view_args = result
|
self.request.url_rule, self.request.view_args = result # type: ignore
|
||||||
except HTTPException as e:
|
except HTTPException as e:
|
||||||
self.request.routing_exception = e
|
self.request.routing_exception = e
|
||||||
|
|
||||||
def push(self):
|
def push(self) -> None:
|
||||||
"""Binds the request context to the current context."""
|
"""Binds the request context to the current context."""
|
||||||
# If an exception occurs in debug mode or if context preservation is
|
# If an exception occurs in debug mode or if context preservation is
|
||||||
# activated under exception situations exactly one context stays
|
# activated under exception situations exactly one context stays
|
||||||
|
@ -375,7 +391,7 @@ class RequestContext:
|
||||||
if self.session is None:
|
if self.session is None:
|
||||||
self.session = session_interface.make_null_session(self.app)
|
self.session = session_interface.make_null_session(self.app)
|
||||||
|
|
||||||
def pop(self, exc=_sentinel):
|
def pop(self, exc: t.Optional[BaseException] = _sentinel) -> None: # type: ignore
|
||||||
"""Pops the request context and unbinds it by doing that. This will
|
"""Pops the request context and unbinds it by doing that. This will
|
||||||
also trigger the execution of functions registered by the
|
also trigger the execution of functions registered by the
|
||||||
:meth:`~flask.Flask.teardown_request` decorator.
|
:meth:`~flask.Flask.teardown_request` decorator.
|
||||||
|
@ -414,20 +430,22 @@ class RequestContext:
|
||||||
rv is self
|
rv is self
|
||||||
), f"Popped wrong request context. ({rv!r} instead of {self!r})"
|
), f"Popped wrong request context. ({rv!r} instead of {self!r})"
|
||||||
|
|
||||||
def auto_pop(self, exc):
|
def auto_pop(self, exc: t.Optional[BaseException]) -> None:
|
||||||
if self.request.environ.get("flask._preserve_context") or (
|
if self.request.environ.get("flask._preserve_context") or (
|
||||||
exc is not None and self.app.preserve_context_on_exception
|
exc is not None and self.app.preserve_context_on_exception
|
||||||
):
|
):
|
||||||
self.preserved = True
|
self.preserved = True
|
||||||
self._preserved_exc = exc
|
self._preserved_exc = exc # type: ignore
|
||||||
else:
|
else:
|
||||||
self.pop(exc)
|
self.pop(exc)
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self) -> "RequestContext":
|
||||||
self.push()
|
self.push()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __exit__(self, exc_type, exc_value, tb):
|
def __exit__(
|
||||||
|
self, exc_type: type, exc_value: BaseException, tb: TracebackType
|
||||||
|
) -> None:
|
||||||
# do not pop the request stack if we are in debug mode and an
|
# do not pop the request stack if we are in debug mode and an
|
||||||
# exception happened. This will allow the debugger to still
|
# exception happened. This will allow the debugger to still
|
||||||
# access the request object in the interactive shell. Furthermore
|
# access the request object in the interactive shell. Furthermore
|
||||||
|
@ -435,7 +453,7 @@ class RequestContext:
|
||||||
# See flask.testing for how this works.
|
# See flask.testing for how this works.
|
||||||
self.auto_pop(exc_value)
|
self.auto_pop(exc_value)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self) -> str:
|
||||||
return (
|
return (
|
||||||
f"<{type(self).__name__} {self.request.url!r}"
|
f"<{type(self).__name__} {self.request.url!r}"
|
||||||
f" [{self.request.method}] of {self.app.name}>"
|
f" [{self.request.method}] of {self.app.name}>"
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import os
|
import os
|
||||||
|
import typing as t
|
||||||
from warnings import warn
|
from warnings import warn
|
||||||
|
|
||||||
from .app import Flask
|
from .app import Flask
|
||||||
|
@ -92,7 +93,7 @@ def attach_enctype_error_multidict(request):
|
||||||
request.files.__class__ = newcls
|
request.files.__class__ = newcls
|
||||||
|
|
||||||
|
|
||||||
def _dump_loader_info(loader):
|
def _dump_loader_info(loader) -> t.Generator:
|
||||||
yield f"class: {type(loader).__module__}.{type(loader).__name__}"
|
yield f"class: {type(loader).__module__}.{type(loader).__name__}"
|
||||||
for key, value in sorted(loader.__dict__.items()):
|
for key, value in sorted(loader.__dict__.items()):
|
||||||
if key.startswith("_"):
|
if key.startswith("_"):
|
||||||
|
@ -109,7 +110,7 @@ def _dump_loader_info(loader):
|
||||||
yield f"{key}: {value!r}"
|
yield f"{key}: {value!r}"
|
||||||
|
|
||||||
|
|
||||||
def explain_template_loading_attempts(app, template, attempts):
|
def explain_template_loading_attempts(app: Flask, template, attempts) -> None:
|
||||||
"""This should help developers understand what failed"""
|
"""This should help developers understand what failed"""
|
||||||
info = [f"Locating template {template!r}:"]
|
info = [f"Locating template {template!r}:"]
|
||||||
total_found = 0
|
total_found = 0
|
||||||
|
@ -157,7 +158,7 @@ def explain_template_loading_attempts(app, template, attempts):
|
||||||
app.logger.info("\n".join(info))
|
app.logger.info("\n".join(info))
|
||||||
|
|
||||||
|
|
||||||
def explain_ignored_app_run():
|
def explain_ignored_app_run() -> None:
|
||||||
if os.environ.get("WERKZEUG_RUN_MAIN") != "true":
|
if os.environ.get("WERKZEUG_RUN_MAIN") != "true":
|
||||||
warn(
|
warn(
|
||||||
Warning(
|
Warning(
|
||||||
|
|
|
@ -1,8 +1,14 @@
|
||||||
|
import typing as t
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
from werkzeug.local import LocalProxy
|
from werkzeug.local import LocalProxy
|
||||||
from werkzeug.local import LocalStack
|
from werkzeug.local import LocalStack
|
||||||
|
|
||||||
|
if t.TYPE_CHECKING:
|
||||||
|
from .app import Flask
|
||||||
|
from .ctx import AppContext
|
||||||
|
from .sessions import SessionMixin
|
||||||
|
from .wrappers import Request
|
||||||
|
|
||||||
_request_ctx_err_msg = """\
|
_request_ctx_err_msg = """\
|
||||||
Working outside of request context.
|
Working outside of request context.
|
||||||
|
@ -45,7 +51,7 @@ def _find_app():
|
||||||
# context locals
|
# context locals
|
||||||
_request_ctx_stack = LocalStack()
|
_request_ctx_stack = LocalStack()
|
||||||
_app_ctx_stack = LocalStack()
|
_app_ctx_stack = LocalStack()
|
||||||
current_app = LocalProxy(_find_app)
|
current_app: "Flask" = LocalProxy(_find_app) # type: ignore
|
||||||
request = LocalProxy(partial(_lookup_req_object, "request"))
|
request: "Request" = LocalProxy(partial(_lookup_req_object, "request")) # type: ignore
|
||||||
session = LocalProxy(partial(_lookup_req_object, "session"))
|
session: "SessionMixin" = LocalProxy(partial(_lookup_req_object, "session")) # type: ignore # noqa: B950
|
||||||
g = LocalProxy(partial(_lookup_app_object, "g"))
|
g: "AppContext" = LocalProxy(partial(_lookup_app_object, "g")) # type: ignore
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import os
|
import os
|
||||||
import socket
|
import socket
|
||||||
|
import typing as t
|
||||||
import warnings
|
import warnings
|
||||||
|
from datetime import timedelta
|
||||||
from functools import update_wrapper
|
from functools import update_wrapper
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from threading import RLock
|
from threading import RLock
|
||||||
|
@ -18,8 +20,11 @@ from .globals import request
|
||||||
from .globals import session
|
from .globals import session
|
||||||
from .signals import message_flashed
|
from .signals import message_flashed
|
||||||
|
|
||||||
|
if t.TYPE_CHECKING:
|
||||||
|
from .wrappers import Response
|
||||||
|
|
||||||
def get_env():
|
|
||||||
|
def get_env() -> str:
|
||||||
"""Get the environment the app is running in, indicated by the
|
"""Get the environment the app is running in, indicated by the
|
||||||
:envvar:`FLASK_ENV` environment variable. The default is
|
:envvar:`FLASK_ENV` environment variable. The default is
|
||||||
``'production'``.
|
``'production'``.
|
||||||
|
@ -27,7 +32,7 @@ def get_env():
|
||||||
return os.environ.get("FLASK_ENV") or "production"
|
return os.environ.get("FLASK_ENV") or "production"
|
||||||
|
|
||||||
|
|
||||||
def get_debug_flag():
|
def get_debug_flag() -> bool:
|
||||||
"""Get whether debug mode should be enabled for the app, indicated
|
"""Get whether debug mode should be enabled for the app, indicated
|
||||||
by the :envvar:`FLASK_DEBUG` environment variable. The default is
|
by the :envvar:`FLASK_DEBUG` environment variable. The default is
|
||||||
``True`` if :func:`.get_env` returns ``'development'``, or ``False``
|
``True`` if :func:`.get_env` returns ``'development'``, or ``False``
|
||||||
|
@ -41,7 +46,7 @@ def get_debug_flag():
|
||||||
return val.lower() not in ("0", "false", "no")
|
return val.lower() not in ("0", "false", "no")
|
||||||
|
|
||||||
|
|
||||||
def get_load_dotenv(default=True):
|
def get_load_dotenv(default: bool = True) -> bool:
|
||||||
"""Get whether the user has disabled loading dotenv files by setting
|
"""Get whether the user has disabled loading dotenv files by setting
|
||||||
:envvar:`FLASK_SKIP_DOTENV`. The default is ``True``, load the
|
:envvar:`FLASK_SKIP_DOTENV`. The default is ``True``, load the
|
||||||
files.
|
files.
|
||||||
|
@ -56,7 +61,9 @@ def get_load_dotenv(default=True):
|
||||||
return val.lower() in ("0", "false", "no")
|
return val.lower() in ("0", "false", "no")
|
||||||
|
|
||||||
|
|
||||||
def stream_with_context(generator_or_function):
|
def stream_with_context(
|
||||||
|
generator_or_function: t.Union[t.Generator, t.Callable]
|
||||||
|
) -> t.Generator:
|
||||||
"""Request contexts disappear when the response is started on the server.
|
"""Request contexts disappear when the response is started on the server.
|
||||||
This is done for efficiency reasons and to make it less likely to encounter
|
This is done for efficiency reasons and to make it less likely to encounter
|
||||||
memory leaks with badly written WSGI middlewares. The downside is that if
|
memory leaks with badly written WSGI middlewares. The downside is that if
|
||||||
|
@ -91,16 +98,16 @@ def stream_with_context(generator_or_function):
|
||||||
.. versionadded:: 0.9
|
.. versionadded:: 0.9
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
gen = iter(generator_or_function)
|
gen = iter(generator_or_function) # type: ignore
|
||||||
except TypeError:
|
except TypeError:
|
||||||
|
|
||||||
def decorator(*args, **kwargs):
|
def decorator(*args: t.Any, **kwargs: t.Any) -> t.Any:
|
||||||
gen = generator_or_function(*args, **kwargs)
|
gen = generator_or_function(*args, **kwargs) # type: ignore
|
||||||
return stream_with_context(gen)
|
return stream_with_context(gen)
|
||||||
|
|
||||||
return update_wrapper(decorator, generator_or_function)
|
return update_wrapper(decorator, generator_or_function) # type: ignore
|
||||||
|
|
||||||
def generator():
|
def generator() -> t.Generator:
|
||||||
ctx = _request_ctx_stack.top
|
ctx = _request_ctx_stack.top
|
||||||
if ctx is None:
|
if ctx is None:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
|
@ -120,7 +127,7 @@ def stream_with_context(generator_or_function):
|
||||||
yield from gen
|
yield from gen
|
||||||
finally:
|
finally:
|
||||||
if hasattr(gen, "close"):
|
if hasattr(gen, "close"):
|
||||||
gen.close()
|
gen.close() # type: ignore
|
||||||
|
|
||||||
# The trick is to start the generator. Then the code execution runs until
|
# The trick is to start the generator. Then the code execution runs until
|
||||||
# the first dummy None is yielded at which point the context was already
|
# the first dummy None is yielded at which point the context was already
|
||||||
|
@ -131,7 +138,7 @@ def stream_with_context(generator_or_function):
|
||||||
return wrapped_g
|
return wrapped_g
|
||||||
|
|
||||||
|
|
||||||
def make_response(*args):
|
def make_response(*args: t.Any) -> "Response":
|
||||||
"""Sometimes it is necessary to set additional headers in a view. Because
|
"""Sometimes it is necessary to set additional headers in a view. Because
|
||||||
views do not have to return response objects but can return a value that
|
views do not have to return response objects but can return a value that
|
||||||
is converted into a response object by Flask itself, it becomes tricky to
|
is converted into a response object by Flask itself, it becomes tricky to
|
||||||
|
@ -180,7 +187,7 @@ def make_response(*args):
|
||||||
return current_app.make_response(args)
|
return current_app.make_response(args)
|
||||||
|
|
||||||
|
|
||||||
def url_for(endpoint, **values):
|
def url_for(endpoint: str, **values: t.Any) -> str:
|
||||||
"""Generates a URL to the given endpoint with the method provided.
|
"""Generates a URL to the given endpoint with the method provided.
|
||||||
|
|
||||||
Variable arguments that are unknown to the target endpoint are appended
|
Variable arguments that are unknown to the target endpoint are appended
|
||||||
|
@ -331,7 +338,7 @@ def url_for(endpoint, **values):
|
||||||
return rv
|
return rv
|
||||||
|
|
||||||
|
|
||||||
def get_template_attribute(template_name, attribute):
|
def get_template_attribute(template_name: str, attribute: str) -> t.Any:
|
||||||
"""Loads a macro (or variable) a template exports. This can be used to
|
"""Loads a macro (or variable) a template exports. This can be used to
|
||||||
invoke a macro from within Python code. If you for example have a
|
invoke a macro from within Python code. If you for example have a
|
||||||
template named :file:`_cider.html` with the following contents:
|
template named :file:`_cider.html` with the following contents:
|
||||||
|
@ -353,7 +360,7 @@ def get_template_attribute(template_name, attribute):
|
||||||
return getattr(current_app.jinja_env.get_template(template_name).module, attribute)
|
return getattr(current_app.jinja_env.get_template(template_name).module, attribute)
|
||||||
|
|
||||||
|
|
||||||
def flash(message, category="message"):
|
def flash(message: str, category: str = "message") -> None:
|
||||||
"""Flashes a message to the next request. In order to remove the
|
"""Flashes a message to the next request. In order to remove the
|
||||||
flashed message from the session and to display it to the user,
|
flashed message from the session and to display it to the user,
|
||||||
the template has to call :func:`get_flashed_messages`.
|
the template has to call :func:`get_flashed_messages`.
|
||||||
|
@ -379,11 +386,15 @@ def flash(message, category="message"):
|
||||||
flashes.append((category, message))
|
flashes.append((category, message))
|
||||||
session["_flashes"] = flashes
|
session["_flashes"] = flashes
|
||||||
message_flashed.send(
|
message_flashed.send(
|
||||||
current_app._get_current_object(), message=message, category=category
|
current_app._get_current_object(), # type: ignore
|
||||||
|
message=message,
|
||||||
|
category=category,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_flashed_messages(with_categories=False, category_filter=()):
|
def get_flashed_messages(
|
||||||
|
with_categories: bool = False, category_filter: t.Iterable[str] = ()
|
||||||
|
) -> t.Union[t.List[str], t.List[t.Tuple[str, str]]]:
|
||||||
"""Pulls all flashed messages from the session and returns them.
|
"""Pulls all flashed messages from the session and returns them.
|
||||||
Further calls in the same request to the function will return
|
Further calls in the same request to the function will return
|
||||||
the same messages. By default just the messages are returned,
|
the same messages. By default just the messages are returned,
|
||||||
|
@ -608,7 +619,7 @@ def send_file(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def safe_join(directory, *pathnames):
|
def safe_join(directory: str, *pathnames: str) -> str:
|
||||||
"""Safely join zero or more untrusted path components to a base
|
"""Safely join zero or more untrusted path components to a base
|
||||||
directory to avoid escaping the base directory.
|
directory to avoid escaping the base directory.
|
||||||
|
|
||||||
|
@ -631,7 +642,7 @@ def safe_join(directory, *pathnames):
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
|
||||||
def send_from_directory(directory, path, **kwargs):
|
def send_from_directory(directory: str, path: str, **kwargs: t.Any) -> "Response":
|
||||||
"""Send a file from within a directory using :func:`send_file`.
|
"""Send a file from within a directory using :func:`send_file`.
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
@ -661,7 +672,7 @@ def send_from_directory(directory, path, **kwargs):
|
||||||
|
|
||||||
.. versionadded:: 0.5
|
.. versionadded:: 0.5
|
||||||
"""
|
"""
|
||||||
return werkzeug.utils.send_from_directory(
|
return werkzeug.utils.send_from_directory( # type: ignore
|
||||||
directory, path, **_prepare_send_file_kwargs(**kwargs)
|
directory, path, **_prepare_send_file_kwargs(**kwargs)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -675,27 +686,32 @@ class locked_cached_property(werkzeug.utils.cached_property):
|
||||||
Inherits from Werkzeug's ``cached_property`` (and ``property``).
|
Inherits from Werkzeug's ``cached_property`` (and ``property``).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, fget, name=None, doc=None):
|
def __init__(
|
||||||
|
self,
|
||||||
|
fget: t.Callable[[t.Any], t.Any],
|
||||||
|
name: t.Optional[str] = None,
|
||||||
|
doc: t.Optional[str] = None,
|
||||||
|
) -> None:
|
||||||
super().__init__(fget, name=name, doc=doc)
|
super().__init__(fget, name=name, doc=doc)
|
||||||
self.lock = RLock()
|
self.lock = RLock()
|
||||||
|
|
||||||
def __get__(self, obj, type=None):
|
def __get__(self, obj: object, type: type = None) -> t.Any: # type: ignore
|
||||||
if obj is None:
|
if obj is None:
|
||||||
return self
|
return self
|
||||||
|
|
||||||
with self.lock:
|
with self.lock:
|
||||||
return super().__get__(obj, type=type)
|
return super().__get__(obj, type=type)
|
||||||
|
|
||||||
def __set__(self, obj, value):
|
def __set__(self, obj: object, value: t.Any) -> None:
|
||||||
with self.lock:
|
with self.lock:
|
||||||
super().__set__(obj, value)
|
super().__set__(obj, value)
|
||||||
|
|
||||||
def __delete__(self, obj):
|
def __delete__(self, obj: object) -> None:
|
||||||
with self.lock:
|
with self.lock:
|
||||||
super().__delete__(obj)
|
super().__delete__(obj)
|
||||||
|
|
||||||
|
|
||||||
def total_seconds(td):
|
def total_seconds(td: timedelta) -> int:
|
||||||
"""Returns the total seconds from a timedelta object.
|
"""Returns the total seconds from a timedelta object.
|
||||||
|
|
||||||
:param timedelta td: the timedelta to be converted in seconds
|
:param timedelta td: the timedelta to be converted in seconds
|
||||||
|
@ -716,7 +732,7 @@ def total_seconds(td):
|
||||||
return td.days * 60 * 60 * 24 + td.seconds
|
return td.days * 60 * 60 * 24 + td.seconds
|
||||||
|
|
||||||
|
|
||||||
def is_ip(value):
|
def is_ip(value: str) -> bool:
|
||||||
"""Determine if the given string is an IP address.
|
"""Determine if the given string is an IP address.
|
||||||
|
|
||||||
:param value: value to check
|
:param value: value to check
|
||||||
|
@ -736,7 +752,7 @@ def is_ip(value):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def run_async(func):
|
def run_async(func: t.Callable[..., t.Coroutine]) -> t.Callable[..., t.Any]:
|
||||||
"""Return a sync function that will run the coroutine function *func*."""
|
"""Return a sync function that will run the coroutine function *func*."""
|
||||||
try:
|
try:
|
||||||
from asgiref.sync import async_to_sync
|
from asgiref.sync import async_to_sync
|
||||||
|
@ -752,7 +768,7 @@ def run_async(func):
|
||||||
)
|
)
|
||||||
|
|
||||||
@wraps(func)
|
@wraps(func)
|
||||||
def outer(*args, **kwargs):
|
def outer(*args: t.Any, **kwargs: t.Any) -> t.Any:
|
||||||
"""This function grabs the current context for the inner function.
|
"""This function grabs the current context for the inner function.
|
||||||
|
|
||||||
This is similar to the copy_current_xxx_context functions in the
|
This is similar to the copy_current_xxx_context functions in the
|
||||||
|
@ -764,7 +780,7 @@ def run_async(func):
|
||||||
ctx = _request_ctx_stack.top.copy()
|
ctx = _request_ctx_stack.top.copy()
|
||||||
|
|
||||||
@wraps(func)
|
@wraps(func)
|
||||||
async def inner(*a, **k):
|
async def inner(*a: t.Any, **k: t.Any) -> t.Any:
|
||||||
"""This restores the context before awaiting the func.
|
"""This restores the context before awaiting the func.
|
||||||
|
|
||||||
This is required as the function must be awaited within the
|
This is required as the function must be awaited within the
|
||||||
|
@ -780,5 +796,5 @@ def run_async(func):
|
||||||
|
|
||||||
return async_to_sync(inner)(*args, **kwargs)
|
return async_to_sync(inner)(*args, **kwargs)
|
||||||
|
|
||||||
outer._flask_sync_wrapper = True
|
outer._flask_sync_wrapper = True # type: ignore
|
||||||
return outer
|
return outer
|
||||||
|
|
|
@ -1,20 +1,25 @@
|
||||||
import io
|
import io
|
||||||
import json as _json
|
import json as _json
|
||||||
|
import typing as t
|
||||||
import uuid
|
import uuid
|
||||||
import warnings
|
import warnings
|
||||||
from datetime import date
|
from datetime import date
|
||||||
|
|
||||||
from jinja2.utils import htmlsafe_json_dumps as _jinja_htmlsafe_dumps
|
from jinja2.utils import htmlsafe_json_dumps as _jinja_htmlsafe_dumps # type: ignore
|
||||||
from werkzeug.http import http_date
|
from werkzeug.http import http_date
|
||||||
|
|
||||||
from ..globals import current_app
|
from ..globals import current_app
|
||||||
from ..globals import request
|
from ..globals import request
|
||||||
|
|
||||||
|
if t.TYPE_CHECKING:
|
||||||
|
from ..app import Flask
|
||||||
|
from ..wrappers import Response
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import dataclasses
|
import dataclasses
|
||||||
except ImportError:
|
except ImportError:
|
||||||
# Python < 3.7
|
# Python < 3.7
|
||||||
dataclasses = None
|
dataclasses = None # type: ignore
|
||||||
|
|
||||||
|
|
||||||
class JSONEncoder(_json.JSONEncoder):
|
class JSONEncoder(_json.JSONEncoder):
|
||||||
|
@ -34,7 +39,7 @@ class JSONEncoder(_json.JSONEncoder):
|
||||||
:attr:`flask.Blueprint.json_encoder` to override the default.
|
:attr:`flask.Blueprint.json_encoder` to override the default.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def default(self, o):
|
def default(self, o: t.Any) -> t.Any:
|
||||||
"""Convert ``o`` to a JSON serializable type. See
|
"""Convert ``o`` to a JSON serializable type. See
|
||||||
:meth:`json.JSONEncoder.default`. Python does not support
|
:meth:`json.JSONEncoder.default`. Python does not support
|
||||||
overriding how basic types like ``str`` or ``list`` are
|
overriding how basic types like ``str`` or ``list`` are
|
||||||
|
@ -48,7 +53,7 @@ class JSONEncoder(_json.JSONEncoder):
|
||||||
return dataclasses.asdict(o)
|
return dataclasses.asdict(o)
|
||||||
if hasattr(o, "__html__"):
|
if hasattr(o, "__html__"):
|
||||||
return str(o.__html__())
|
return str(o.__html__())
|
||||||
return super().default(self, o)
|
return super().default(o)
|
||||||
|
|
||||||
|
|
||||||
class JSONDecoder(_json.JSONDecoder):
|
class JSONDecoder(_json.JSONDecoder):
|
||||||
|
@ -62,14 +67,19 @@ class JSONDecoder(_json.JSONDecoder):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def _dump_arg_defaults(kwargs, app=None):
|
def _dump_arg_defaults(
|
||||||
|
kwargs: t.Dict[str, t.Any], app: t.Optional["Flask"] = None
|
||||||
|
) -> None:
|
||||||
"""Inject default arguments for dump functions."""
|
"""Inject default arguments for dump functions."""
|
||||||
if app is None:
|
if app is None:
|
||||||
app = current_app
|
app = current_app
|
||||||
|
|
||||||
if app:
|
if app:
|
||||||
bp = app.blueprints.get(request.blueprint) if request else None
|
cls = app.json_encoder
|
||||||
cls = bp.json_encoder if bp and bp.json_encoder else app.json_encoder
|
bp = app.blueprints.get(request.blueprint) if request else None # type: ignore
|
||||||
|
if bp is not None and bp.json_encoder is not None:
|
||||||
|
cls = bp.json_encoder
|
||||||
|
|
||||||
kwargs.setdefault("cls", cls)
|
kwargs.setdefault("cls", cls)
|
||||||
kwargs.setdefault("ensure_ascii", app.config["JSON_AS_ASCII"])
|
kwargs.setdefault("ensure_ascii", app.config["JSON_AS_ASCII"])
|
||||||
kwargs.setdefault("sort_keys", app.config["JSON_SORT_KEYS"])
|
kwargs.setdefault("sort_keys", app.config["JSON_SORT_KEYS"])
|
||||||
|
@ -78,20 +88,25 @@ def _dump_arg_defaults(kwargs, app=None):
|
||||||
kwargs.setdefault("cls", JSONEncoder)
|
kwargs.setdefault("cls", JSONEncoder)
|
||||||
|
|
||||||
|
|
||||||
def _load_arg_defaults(kwargs, app=None):
|
def _load_arg_defaults(
|
||||||
|
kwargs: t.Dict[str, t.Any], app: t.Optional["Flask"] = None
|
||||||
|
) -> None:
|
||||||
"""Inject default arguments for load functions."""
|
"""Inject default arguments for load functions."""
|
||||||
if app is None:
|
if app is None:
|
||||||
app = current_app
|
app = current_app
|
||||||
|
|
||||||
if app:
|
if app:
|
||||||
bp = app.blueprints.get(request.blueprint) if request else None
|
cls = app.json_decoder
|
||||||
cls = bp.json_decoder if bp and bp.json_decoder else app.json_decoder
|
bp = app.blueprints.get(request.blueprint) if request else None # type: ignore
|
||||||
|
if bp is not None and bp.json_decoder is not None:
|
||||||
|
cls = bp.json_decoder
|
||||||
|
|
||||||
kwargs.setdefault("cls", cls)
|
kwargs.setdefault("cls", cls)
|
||||||
else:
|
else:
|
||||||
kwargs.setdefault("cls", JSONDecoder)
|
kwargs.setdefault("cls", JSONDecoder)
|
||||||
|
|
||||||
|
|
||||||
def dumps(obj, app=None, **kwargs):
|
def dumps(obj: t.Any, app: t.Optional["Flask"] = None, **kwargs: t.Any) -> str:
|
||||||
"""Serialize an object to a string of JSON.
|
"""Serialize an object to a string of JSON.
|
||||||
|
|
||||||
Takes the same arguments as the built-in :func:`json.dumps`, with
|
Takes the same arguments as the built-in :func:`json.dumps`, with
|
||||||
|
@ -121,12 +136,14 @@ def dumps(obj, app=None, **kwargs):
|
||||||
)
|
)
|
||||||
|
|
||||||
if isinstance(rv, str):
|
if isinstance(rv, str):
|
||||||
return rv.encode(encoding)
|
return rv.encode(encoding) # type: ignore
|
||||||
|
|
||||||
return rv
|
return rv
|
||||||
|
|
||||||
|
|
||||||
def dump(obj, fp, app=None, **kwargs):
|
def dump(
|
||||||
|
obj: t.Any, fp: t.IO[str], app: t.Optional["Flask"] = None, **kwargs: t.Any
|
||||||
|
) -> None:
|
||||||
"""Serialize an object to JSON written to a file object.
|
"""Serialize an object to JSON written to a file object.
|
||||||
|
|
||||||
Takes the same arguments as the built-in :func:`json.dump`, with
|
Takes the same arguments as the built-in :func:`json.dump`, with
|
||||||
|
@ -150,7 +167,7 @@ def dump(obj, fp, app=None, **kwargs):
|
||||||
fp.write("")
|
fp.write("")
|
||||||
except TypeError:
|
except TypeError:
|
||||||
show_warning = True
|
show_warning = True
|
||||||
fp = io.TextIOWrapper(fp, encoding or "utf-8")
|
fp = io.TextIOWrapper(fp, encoding or "utf-8") # type: ignore
|
||||||
|
|
||||||
if show_warning:
|
if show_warning:
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
|
@ -163,7 +180,7 @@ def dump(obj, fp, app=None, **kwargs):
|
||||||
_json.dump(obj, fp, **kwargs)
|
_json.dump(obj, fp, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def loads(s, app=None, **kwargs):
|
def loads(s: str, app: t.Optional["Flask"] = None, **kwargs: t.Any) -> t.Any:
|
||||||
"""Deserialize an object from a string of JSON.
|
"""Deserialize an object from a string of JSON.
|
||||||
|
|
||||||
Takes the same arguments as the built-in :func:`json.loads`, with
|
Takes the same arguments as the built-in :func:`json.loads`, with
|
||||||
|
@ -199,7 +216,7 @@ def loads(s, app=None, **kwargs):
|
||||||
return _json.loads(s, **kwargs)
|
return _json.loads(s, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def load(fp, app=None, **kwargs):
|
def load(fp: t.IO[str], app: t.Optional["Flask"] = None, **kwargs: t.Any) -> t.Any:
|
||||||
"""Deserialize an object from JSON read from a file object.
|
"""Deserialize an object from JSON read from a file object.
|
||||||
|
|
||||||
Takes the same arguments as the built-in :func:`json.load`, with
|
Takes the same arguments as the built-in :func:`json.load`, with
|
||||||
|
@ -227,12 +244,12 @@ def load(fp, app=None, **kwargs):
|
||||||
)
|
)
|
||||||
|
|
||||||
if isinstance(fp.read(0), bytes):
|
if isinstance(fp.read(0), bytes):
|
||||||
fp = io.TextIOWrapper(fp, encoding)
|
fp = io.TextIOWrapper(fp, encoding) # type: ignore
|
||||||
|
|
||||||
return _json.load(fp, **kwargs)
|
return _json.load(fp, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def htmlsafe_dumps(obj, **kwargs):
|
def htmlsafe_dumps(obj: t.Any, **kwargs: t.Any) -> str:
|
||||||
"""Serialize an object to a string of JSON with :func:`dumps`, then
|
"""Serialize an object to a string of JSON with :func:`dumps`, then
|
||||||
replace HTML-unsafe characters with Unicode escapes and mark the
|
replace HTML-unsafe characters with Unicode escapes and mark the
|
||||||
result safe with :class:`~markupsafe.Markup`.
|
result safe with :class:`~markupsafe.Markup`.
|
||||||
|
@ -256,7 +273,7 @@ def htmlsafe_dumps(obj, **kwargs):
|
||||||
return _jinja_htmlsafe_dumps(obj, dumps=dumps, **kwargs)
|
return _jinja_htmlsafe_dumps(obj, dumps=dumps, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def htmlsafe_dump(obj, fp, **kwargs):
|
def htmlsafe_dump(obj: t.Any, fp: t.IO[str], **kwargs: t.Any) -> None:
|
||||||
"""Serialize an object to JSON written to a file object, replacing
|
"""Serialize an object to JSON written to a file object, replacing
|
||||||
HTML-unsafe characters with Unicode escapes. See
|
HTML-unsafe characters with Unicode escapes. See
|
||||||
:func:`htmlsafe_dumps` and :func:`dumps`.
|
:func:`htmlsafe_dumps` and :func:`dumps`.
|
||||||
|
@ -264,7 +281,7 @@ def htmlsafe_dump(obj, fp, **kwargs):
|
||||||
fp.write(htmlsafe_dumps(obj, **kwargs))
|
fp.write(htmlsafe_dumps(obj, **kwargs))
|
||||||
|
|
||||||
|
|
||||||
def jsonify(*args, **kwargs):
|
def jsonify(*args: t.Any, **kwargs: t.Any) -> "Response":
|
||||||
"""Serialize data to JSON and wrap it in a :class:`~flask.Response`
|
"""Serialize data to JSON and wrap it in a :class:`~flask.Response`
|
||||||
with the :mimetype:`application/json` mimetype.
|
with the :mimetype:`application/json` mimetype.
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,7 @@ be processed before ``dict``.
|
||||||
|
|
||||||
app.session_interface.serializer.register(TagOrderedDict, index=0)
|
app.session_interface.serializer.register(TagOrderedDict, index=0)
|
||||||
"""
|
"""
|
||||||
|
import typing as t
|
||||||
from base64 import b64decode
|
from base64 import b64decode
|
||||||
from base64 import b64encode
|
from base64 import b64encode
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
@ -60,27 +61,27 @@ class JSONTag:
|
||||||
|
|
||||||
#: The tag to mark the serialized object with. If ``None``, this tag is
|
#: The tag to mark the serialized object with. If ``None``, this tag is
|
||||||
#: only used as an intermediate step during tagging.
|
#: only used as an intermediate step during tagging.
|
||||||
key = None
|
key: t.Optional[str] = None
|
||||||
|
|
||||||
def __init__(self, serializer):
|
def __init__(self, serializer: "TaggedJSONSerializer") -> None:
|
||||||
"""Create a tagger for the given serializer."""
|
"""Create a tagger for the given serializer."""
|
||||||
self.serializer = serializer
|
self.serializer = serializer
|
||||||
|
|
||||||
def check(self, value):
|
def check(self, value: t.Any) -> bool:
|
||||||
"""Check if the given value should be tagged by this tag."""
|
"""Check if the given value should be tagged by this tag."""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def to_json(self, value):
|
def to_json(self, value: t.Any) -> t.Any:
|
||||||
"""Convert the Python object to an object that is a valid JSON type.
|
"""Convert the Python object to an object that is a valid JSON type.
|
||||||
The tag will be added later."""
|
The tag will be added later."""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def to_python(self, value):
|
def to_python(self, value: t.Any) -> t.Any:
|
||||||
"""Convert the JSON representation back to the correct type. The tag
|
"""Convert the JSON representation back to the correct type. The tag
|
||||||
will already be removed."""
|
will already be removed."""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def tag(self, value):
|
def tag(self, value: t.Any) -> t.Any:
|
||||||
"""Convert the value to a valid JSON type and add the tag structure
|
"""Convert the value to a valid JSON type and add the tag structure
|
||||||
around it."""
|
around it."""
|
||||||
return {self.key: self.to_json(value)}
|
return {self.key: self.to_json(value)}
|
||||||
|
@ -96,18 +97,18 @@ class TagDict(JSONTag):
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
key = " di"
|
key = " di"
|
||||||
|
|
||||||
def check(self, value):
|
def check(self, value: t.Any) -> bool:
|
||||||
return (
|
return (
|
||||||
isinstance(value, dict)
|
isinstance(value, dict)
|
||||||
and len(value) == 1
|
and len(value) == 1
|
||||||
and next(iter(value)) in self.serializer.tags
|
and next(iter(value)) in self.serializer.tags
|
||||||
)
|
)
|
||||||
|
|
||||||
def to_json(self, value):
|
def to_json(self, value: t.Any) -> t.Any:
|
||||||
key = next(iter(value))
|
key = next(iter(value))
|
||||||
return {f"{key}__": self.serializer.tag(value[key])}
|
return {f"{key}__": self.serializer.tag(value[key])}
|
||||||
|
|
||||||
def to_python(self, value):
|
def to_python(self, value: t.Any) -> t.Any:
|
||||||
key = next(iter(value))
|
key = next(iter(value))
|
||||||
return {key[:-2]: value[key]}
|
return {key[:-2]: value[key]}
|
||||||
|
|
||||||
|
@ -115,10 +116,10 @@ class TagDict(JSONTag):
|
||||||
class PassDict(JSONTag):
|
class PassDict(JSONTag):
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
|
|
||||||
def check(self, value):
|
def check(self, value: t.Any) -> bool:
|
||||||
return isinstance(value, dict)
|
return isinstance(value, dict)
|
||||||
|
|
||||||
def to_json(self, value):
|
def to_json(self, value: t.Any) -> t.Any:
|
||||||
# JSON objects may only have string keys, so don't bother tagging the
|
# JSON objects may only have string keys, so don't bother tagging the
|
||||||
# key here.
|
# key here.
|
||||||
return {k: self.serializer.tag(v) for k, v in value.items()}
|
return {k: self.serializer.tag(v) for k, v in value.items()}
|
||||||
|
@ -130,23 +131,23 @@ class TagTuple(JSONTag):
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
key = " t"
|
key = " t"
|
||||||
|
|
||||||
def check(self, value):
|
def check(self, value: t.Any) -> bool:
|
||||||
return isinstance(value, tuple)
|
return isinstance(value, tuple)
|
||||||
|
|
||||||
def to_json(self, value):
|
def to_json(self, value: t.Any) -> t.Any:
|
||||||
return [self.serializer.tag(item) for item in value]
|
return [self.serializer.tag(item) for item in value]
|
||||||
|
|
||||||
def to_python(self, value):
|
def to_python(self, value: t.Any) -> t.Any:
|
||||||
return tuple(value)
|
return tuple(value)
|
||||||
|
|
||||||
|
|
||||||
class PassList(JSONTag):
|
class PassList(JSONTag):
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
|
|
||||||
def check(self, value):
|
def check(self, value: t.Any) -> bool:
|
||||||
return isinstance(value, list)
|
return isinstance(value, list)
|
||||||
|
|
||||||
def to_json(self, value):
|
def to_json(self, value: t.Any) -> t.Any:
|
||||||
return [self.serializer.tag(item) for item in value]
|
return [self.serializer.tag(item) for item in value]
|
||||||
|
|
||||||
tag = to_json
|
tag = to_json
|
||||||
|
@ -156,13 +157,13 @@ class TagBytes(JSONTag):
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
key = " b"
|
key = " b"
|
||||||
|
|
||||||
def check(self, value):
|
def check(self, value: t.Any) -> bool:
|
||||||
return isinstance(value, bytes)
|
return isinstance(value, bytes)
|
||||||
|
|
||||||
def to_json(self, value):
|
def to_json(self, value: t.Any) -> t.Any:
|
||||||
return b64encode(value).decode("ascii")
|
return b64encode(value).decode("ascii")
|
||||||
|
|
||||||
def to_python(self, value):
|
def to_python(self, value: t.Any) -> t.Any:
|
||||||
return b64decode(value)
|
return b64decode(value)
|
||||||
|
|
||||||
|
|
||||||
|
@ -174,13 +175,13 @@ class TagMarkup(JSONTag):
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
key = " m"
|
key = " m"
|
||||||
|
|
||||||
def check(self, value):
|
def check(self, value: t.Any) -> bool:
|
||||||
return callable(getattr(value, "__html__", None))
|
return callable(getattr(value, "__html__", None))
|
||||||
|
|
||||||
def to_json(self, value):
|
def to_json(self, value: t.Any) -> t.Any:
|
||||||
return str(value.__html__())
|
return str(value.__html__())
|
||||||
|
|
||||||
def to_python(self, value):
|
def to_python(self, value: t.Any) -> t.Any:
|
||||||
return Markup(value)
|
return Markup(value)
|
||||||
|
|
||||||
|
|
||||||
|
@ -188,13 +189,13 @@ class TagUUID(JSONTag):
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
key = " u"
|
key = " u"
|
||||||
|
|
||||||
def check(self, value):
|
def check(self, value: t.Any) -> bool:
|
||||||
return isinstance(value, UUID)
|
return isinstance(value, UUID)
|
||||||
|
|
||||||
def to_json(self, value):
|
def to_json(self, value: t.Any) -> t.Any:
|
||||||
return value.hex
|
return value.hex
|
||||||
|
|
||||||
def to_python(self, value):
|
def to_python(self, value: t.Any) -> t.Any:
|
||||||
return UUID(value)
|
return UUID(value)
|
||||||
|
|
||||||
|
|
||||||
|
@ -202,13 +203,13 @@ class TagDateTime(JSONTag):
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
key = " d"
|
key = " d"
|
||||||
|
|
||||||
def check(self, value):
|
def check(self, value: t.Any) -> bool:
|
||||||
return isinstance(value, datetime)
|
return isinstance(value, datetime)
|
||||||
|
|
||||||
def to_json(self, value):
|
def to_json(self, value: t.Any) -> t.Any:
|
||||||
return http_date(value)
|
return http_date(value)
|
||||||
|
|
||||||
def to_python(self, value):
|
def to_python(self, value: t.Any) -> t.Any:
|
||||||
return parse_date(value)
|
return parse_date(value)
|
||||||
|
|
||||||
|
|
||||||
|
@ -242,14 +243,19 @@ class TaggedJSONSerializer:
|
||||||
TagDateTime,
|
TagDateTime,
|
||||||
]
|
]
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
self.tags = {}
|
self.tags: t.Dict[str, JSONTag] = {}
|
||||||
self.order = []
|
self.order: t.List[JSONTag] = []
|
||||||
|
|
||||||
for cls in self.default_tags:
|
for cls in self.default_tags:
|
||||||
self.register(cls)
|
self.register(cls)
|
||||||
|
|
||||||
def register(self, tag_class, force=False, index=None):
|
def register(
|
||||||
|
self,
|
||||||
|
tag_class: t.Type[JSONTag],
|
||||||
|
force: bool = False,
|
||||||
|
index: t.Optional[int] = None,
|
||||||
|
) -> None:
|
||||||
"""Register a new tag with this serializer.
|
"""Register a new tag with this serializer.
|
||||||
|
|
||||||
:param tag_class: tag class to register. Will be instantiated with this
|
:param tag_class: tag class to register. Will be instantiated with this
|
||||||
|
@ -277,7 +283,7 @@ class TaggedJSONSerializer:
|
||||||
else:
|
else:
|
||||||
self.order.insert(index, tag)
|
self.order.insert(index, tag)
|
||||||
|
|
||||||
def tag(self, value):
|
def tag(self, value: t.Any) -> t.Dict[str, t.Any]:
|
||||||
"""Convert a value to a tagged representation if necessary."""
|
"""Convert a value to a tagged representation if necessary."""
|
||||||
for tag in self.order:
|
for tag in self.order:
|
||||||
if tag.check(value):
|
if tag.check(value):
|
||||||
|
@ -285,7 +291,7 @@ class TaggedJSONSerializer:
|
||||||
|
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def untag(self, value):
|
def untag(self, value: t.Dict[str, t.Any]) -> t.Any:
|
||||||
"""Convert a tagged representation back to the original type."""
|
"""Convert a tagged representation back to the original type."""
|
||||||
if len(value) != 1:
|
if len(value) != 1:
|
||||||
return value
|
return value
|
||||||
|
@ -297,10 +303,10 @@ class TaggedJSONSerializer:
|
||||||
|
|
||||||
return self.tags[key].to_python(value[key])
|
return self.tags[key].to_python(value[key])
|
||||||
|
|
||||||
def dumps(self, value):
|
def dumps(self, value: t.Any) -> str:
|
||||||
"""Tag the value and dump it to a compact JSON string."""
|
"""Tag the value and dump it to a compact JSON string."""
|
||||||
return dumps(self.tag(value), separators=(",", ":"))
|
return dumps(self.tag(value), separators=(",", ":"))
|
||||||
|
|
||||||
def loads(self, value):
|
def loads(self, value: str) -> t.Any:
|
||||||
"""Load data from a JSON string and deserialized any tagged objects."""
|
"""Load data from a JSON string and deserialized any tagged objects."""
|
||||||
return loads(value, object_hook=self.untag)
|
return loads(value, object_hook=self.untag)
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
|
import typing as t
|
||||||
|
|
||||||
from werkzeug.local import LocalProxy
|
from werkzeug.local import LocalProxy
|
||||||
|
|
||||||
from .globals import request
|
from .globals import request
|
||||||
|
|
||||||
|
if t.TYPE_CHECKING:
|
||||||
|
from .app import Flask
|
||||||
|
|
||||||
|
|
||||||
@LocalProxy
|
@LocalProxy
|
||||||
def wsgi_errors_stream():
|
def wsgi_errors_stream() -> t.TextIO:
|
||||||
"""Find the most appropriate error stream for the application. If a request
|
"""Find the most appropriate error stream for the application. If a request
|
||||||
is active, log to ``wsgi.errors``, otherwise use ``sys.stderr``.
|
is active, log to ``wsgi.errors``, otherwise use ``sys.stderr``.
|
||||||
|
|
||||||
|
@ -19,7 +23,7 @@ def wsgi_errors_stream():
|
||||||
return request.environ["wsgi.errors"] if request else sys.stderr
|
return request.environ["wsgi.errors"] if request else sys.stderr
|
||||||
|
|
||||||
|
|
||||||
def has_level_handler(logger):
|
def has_level_handler(logger: logging.Logger) -> bool:
|
||||||
"""Check if there is a handler in the logging chain that will handle the
|
"""Check if there is a handler in the logging chain that will handle the
|
||||||
given logger's :meth:`effective level <~logging.Logger.getEffectiveLevel>`.
|
given logger's :meth:`effective level <~logging.Logger.getEffectiveLevel>`.
|
||||||
"""
|
"""
|
||||||
|
@ -33,20 +37,20 @@ def has_level_handler(logger):
|
||||||
if not current.propagate:
|
if not current.propagate:
|
||||||
break
|
break
|
||||||
|
|
||||||
current = current.parent
|
current = current.parent # type: ignore
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
#: Log messages to :func:`~flask.logging.wsgi_errors_stream` with the format
|
#: Log messages to :func:`~flask.logging.wsgi_errors_stream` with the format
|
||||||
#: ``[%(asctime)s] %(levelname)s in %(module)s: %(message)s``.
|
#: ``[%(asctime)s] %(levelname)s in %(module)s: %(message)s``.
|
||||||
default_handler = logging.StreamHandler(wsgi_errors_stream)
|
default_handler = logging.StreamHandler(wsgi_errors_stream) # type: ignore
|
||||||
default_handler.setFormatter(
|
default_handler.setFormatter(
|
||||||
logging.Formatter("[%(asctime)s] %(levelname)s in %(module)s: %(message)s")
|
logging.Formatter("[%(asctime)s] %(levelname)s in %(module)s: %(message)s")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def create_logger(app):
|
def create_logger(app: "Flask") -> logging.Logger:
|
||||||
"""Get the Flask app's logger and configure it if needed.
|
"""Get the Flask app's logger and configure it if needed.
|
||||||
|
|
||||||
The logger name will be the same as
|
The logger name will be the same as
|
||||||
|
|
|
@ -2,8 +2,11 @@ import importlib.util
|
||||||
import os
|
import os
|
||||||
import pkgutil
|
import pkgutil
|
||||||
import sys
|
import sys
|
||||||
|
import typing as t
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from functools import update_wrapper
|
from functools import update_wrapper
|
||||||
|
from json import JSONDecoder
|
||||||
|
from json import JSONEncoder
|
||||||
|
|
||||||
from jinja2 import FileSystemLoader
|
from jinja2 import FileSystemLoader
|
||||||
from werkzeug.exceptions import default_exceptions
|
from werkzeug.exceptions import default_exceptions
|
||||||
|
@ -14,17 +17,28 @@ from .globals import current_app
|
||||||
from .helpers import locked_cached_property
|
from .helpers import locked_cached_property
|
||||||
from .helpers import send_from_directory
|
from .helpers import send_from_directory
|
||||||
from .templating import _default_template_ctx_processor
|
from .templating import _default_template_ctx_processor
|
||||||
|
from .typing import AfterRequestCallable
|
||||||
|
from .typing import AppOrBlueprintKey
|
||||||
|
from .typing import BeforeRequestCallable
|
||||||
|
from .typing import ErrorHandlerCallable
|
||||||
|
from .typing import TeardownCallable
|
||||||
|
from .typing import TemplateContextProcessorCallable
|
||||||
|
from .typing import URLDefaultCallable
|
||||||
|
from .typing import URLValuePreprocessorCallable
|
||||||
|
|
||||||
|
if t.TYPE_CHECKING:
|
||||||
|
from .wrappers import Response
|
||||||
|
|
||||||
# a singleton sentinel value for parameter defaults
|
# a singleton sentinel value for parameter defaults
|
||||||
_sentinel = object()
|
_sentinel = object()
|
||||||
|
|
||||||
|
|
||||||
def setupmethod(f):
|
def setupmethod(f: t.Callable) -> t.Callable:
|
||||||
"""Wraps a method so that it performs a check in debug mode if the
|
"""Wraps a method so that it performs a check in debug mode if the
|
||||||
first request was already handled.
|
first request was already handled.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def wrapper_func(self, *args, **kwargs):
|
def wrapper_func(self, *args: t.Any, **kwargs: t.Any) -> t.Any:
|
||||||
if self._is_setup_finished():
|
if self._is_setup_finished():
|
||||||
raise AssertionError(
|
raise AssertionError(
|
||||||
"A setup function was called after the first request "
|
"A setup function was called after the first request "
|
||||||
|
@ -60,24 +74,24 @@ class Scaffold:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name: str
|
name: str
|
||||||
_static_folder = None
|
_static_folder: t.Optional[str] = None
|
||||||
_static_url_path = None
|
_static_url_path: t.Optional[str] = None
|
||||||
|
|
||||||
#: JSON encoder class used by :func:`flask.json.dumps`. If a
|
#: JSON encoder class used by :func:`flask.json.dumps`. If a
|
||||||
#: blueprint sets this, it will be used instead of the app's value.
|
#: blueprint sets this, it will be used instead of the app's value.
|
||||||
json_encoder = None
|
json_encoder: t.Optional[t.Type[JSONEncoder]] = None
|
||||||
|
|
||||||
#: JSON decoder class used by :func:`flask.json.loads`. If a
|
#: JSON decoder class used by :func:`flask.json.loads`. If a
|
||||||
#: blueprint sets this, it will be used instead of the app's value.
|
#: blueprint sets this, it will be used instead of the app's value.
|
||||||
json_decoder = None
|
json_decoder: t.Optional[t.Type[JSONDecoder]] = None
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
import_name,
|
import_name: str,
|
||||||
static_folder=None,
|
static_folder: t.Optional[str] = None,
|
||||||
static_url_path=None,
|
static_url_path: t.Optional[str] = None,
|
||||||
template_folder=None,
|
template_folder: t.Optional[str] = None,
|
||||||
root_path=None,
|
root_path: t.Optional[str] = None,
|
||||||
):
|
):
|
||||||
#: The name of the package or module that this object belongs
|
#: The name of the package or module that this object belongs
|
||||||
#: to. Do not change this once it is set by the constructor.
|
#: to. Do not change this once it is set by the constructor.
|
||||||
|
@ -110,7 +124,7 @@ class Scaffold:
|
||||||
#:
|
#:
|
||||||
#: This data structure is internal. It should not be modified
|
#: This data structure is internal. It should not be modified
|
||||||
#: directly and its format may change at any time.
|
#: directly and its format may change at any time.
|
||||||
self.view_functions = {}
|
self.view_functions: t.Dict[str, t.Callable] = {}
|
||||||
|
|
||||||
#: A data structure of registered error handlers, in the format
|
#: A data structure of registered error handlers, in the format
|
||||||
#: ``{scope: {code: {class: handler}}}```. The ``scope`` key is
|
#: ``{scope: {code: {class: handler}}}```. The ``scope`` key is
|
||||||
|
@ -125,7 +139,10 @@ class Scaffold:
|
||||||
#:
|
#:
|
||||||
#: This data structure is internal. It should not be modified
|
#: This data structure is internal. It should not be modified
|
||||||
#: directly and its format may change at any time.
|
#: directly and its format may change at any time.
|
||||||
self.error_handler_spec = defaultdict(lambda: defaultdict(dict))
|
self.error_handler_spec: t.Dict[
|
||||||
|
AppOrBlueprintKey,
|
||||||
|
t.Dict[t.Optional[int], t.Dict[t.Type[Exception], ErrorHandlerCallable]],
|
||||||
|
] = defaultdict(lambda: defaultdict(dict))
|
||||||
|
|
||||||
#: A data structure of functions to call at the beginning of
|
#: A data structure of functions to call at the beginning of
|
||||||
#: each request, in the format ``{scope: [functions]}``. The
|
#: each request, in the format ``{scope: [functions]}``. The
|
||||||
|
@ -137,7 +154,9 @@ class Scaffold:
|
||||||
#:
|
#:
|
||||||
#: This data structure is internal. It should not be modified
|
#: This data structure is internal. It should not be modified
|
||||||
#: directly and its format may change at any time.
|
#: directly and its format may change at any time.
|
||||||
self.before_request_funcs = defaultdict(list)
|
self.before_request_funcs: t.Dict[
|
||||||
|
AppOrBlueprintKey, t.List[BeforeRequestCallable]
|
||||||
|
] = defaultdict(list)
|
||||||
|
|
||||||
#: A data structure of functions to call at the end of each
|
#: A data structure of functions to call at the end of each
|
||||||
#: request, in the format ``{scope: [functions]}``. The
|
#: request, in the format ``{scope: [functions]}``. The
|
||||||
|
@ -149,7 +168,9 @@ class Scaffold:
|
||||||
#:
|
#:
|
||||||
#: This data structure is internal. It should not be modified
|
#: This data structure is internal. It should not be modified
|
||||||
#: directly and its format may change at any time.
|
#: directly and its format may change at any time.
|
||||||
self.after_request_funcs = defaultdict(list)
|
self.after_request_funcs: t.Dict[
|
||||||
|
AppOrBlueprintKey, t.List[AfterRequestCallable]
|
||||||
|
] = defaultdict(list)
|
||||||
|
|
||||||
#: A data structure of functions to call at the end of each
|
#: A data structure of functions to call at the end of each
|
||||||
#: request even if an exception is raised, in the format
|
#: request even if an exception is raised, in the format
|
||||||
|
@ -162,7 +183,9 @@ class Scaffold:
|
||||||
#:
|
#:
|
||||||
#: This data structure is internal. It should not be modified
|
#: This data structure is internal. It should not be modified
|
||||||
#: directly and its format may change at any time.
|
#: directly and its format may change at any time.
|
||||||
self.teardown_request_funcs = defaultdict(list)
|
self.teardown_request_funcs: t.Dict[
|
||||||
|
AppOrBlueprintKey, t.List[TeardownCallable]
|
||||||
|
] = defaultdict(list)
|
||||||
|
|
||||||
#: A data structure of functions to call to pass extra context
|
#: A data structure of functions to call to pass extra context
|
||||||
#: values when rendering templates, in the format
|
#: values when rendering templates, in the format
|
||||||
|
@ -175,9 +198,9 @@ class Scaffold:
|
||||||
#:
|
#:
|
||||||
#: This data structure is internal. It should not be modified
|
#: This data structure is internal. It should not be modified
|
||||||
#: directly and its format may change at any time.
|
#: directly and its format may change at any time.
|
||||||
self.template_context_processors = defaultdict(
|
self.template_context_processors: t.Dict[
|
||||||
list, {None: [_default_template_ctx_processor]}
|
AppOrBlueprintKey, t.List[TemplateContextProcessorCallable]
|
||||||
)
|
] = defaultdict(list, {None: [_default_template_ctx_processor]})
|
||||||
|
|
||||||
#: A data structure of functions to call to modify the keyword
|
#: A data structure of functions to call to modify the keyword
|
||||||
#: arguments passed to the view function, in the format
|
#: arguments passed to the view function, in the format
|
||||||
|
@ -190,7 +213,10 @@ class Scaffold:
|
||||||
#:
|
#:
|
||||||
#: This data structure is internal. It should not be modified
|
#: This data structure is internal. It should not be modified
|
||||||
#: directly and its format may change at any time.
|
#: directly and its format may change at any time.
|
||||||
self.url_value_preprocessors = defaultdict(list)
|
self.url_value_preprocessors: t.Dict[
|
||||||
|
AppOrBlueprintKey,
|
||||||
|
t.List[URLValuePreprocessorCallable],
|
||||||
|
] = defaultdict(list)
|
||||||
|
|
||||||
#: A data structure of functions to call to modify the keyword
|
#: A data structure of functions to call to modify the keyword
|
||||||
#: arguments when generating URLs, in the format
|
#: arguments when generating URLs, in the format
|
||||||
|
@ -203,31 +229,35 @@ class Scaffold:
|
||||||
#:
|
#:
|
||||||
#: This data structure is internal. It should not be modified
|
#: This data structure is internal. It should not be modified
|
||||||
#: directly and its format may change at any time.
|
#: directly and its format may change at any time.
|
||||||
self.url_default_functions = defaultdict(list)
|
self.url_default_functions: t.Dict[
|
||||||
|
AppOrBlueprintKey, t.List[URLDefaultCallable]
|
||||||
|
] = defaultdict(list)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self) -> str:
|
||||||
return f"<{type(self).__name__} {self.name!r}>"
|
return f"<{type(self).__name__} {self.name!r}>"
|
||||||
|
|
||||||
def _is_setup_finished(self):
|
def _is_setup_finished(self) -> bool:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def static_folder(self):
|
def static_folder(self) -> t.Optional[str]:
|
||||||
"""The absolute path to the configured static folder. ``None``
|
"""The absolute path to the configured static folder. ``None``
|
||||||
if no static folder is set.
|
if no static folder is set.
|
||||||
"""
|
"""
|
||||||
if self._static_folder is not None:
|
if self._static_folder is not None:
|
||||||
return os.path.join(self.root_path, self._static_folder)
|
return os.path.join(self.root_path, self._static_folder)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
@static_folder.setter
|
@static_folder.setter
|
||||||
def static_folder(self, value):
|
def static_folder(self, value: t.Optional[str]) -> None:
|
||||||
if value is not None:
|
if value is not None:
|
||||||
value = os.fspath(value).rstrip(r"\/")
|
value = os.fspath(value).rstrip(r"\/")
|
||||||
|
|
||||||
self._static_folder = value
|
self._static_folder = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def has_static_folder(self):
|
def has_static_folder(self) -> bool:
|
||||||
"""``True`` if :attr:`static_folder` is set.
|
"""``True`` if :attr:`static_folder` is set.
|
||||||
|
|
||||||
.. versionadded:: 0.5
|
.. versionadded:: 0.5
|
||||||
|
@ -235,7 +265,7 @@ class Scaffold:
|
||||||
return self.static_folder is not None
|
return self.static_folder is not None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def static_url_path(self):
|
def static_url_path(self) -> t.Optional[str]:
|
||||||
"""The URL prefix that the static route will be accessible from.
|
"""The URL prefix that the static route will be accessible from.
|
||||||
|
|
||||||
If it was not configured during init, it is derived from
|
If it was not configured during init, it is derived from
|
||||||
|
@ -248,14 +278,16 @@ class Scaffold:
|
||||||
basename = os.path.basename(self.static_folder)
|
basename = os.path.basename(self.static_folder)
|
||||||
return f"/{basename}".rstrip("/")
|
return f"/{basename}".rstrip("/")
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
@static_url_path.setter
|
@static_url_path.setter
|
||||||
def static_url_path(self, value):
|
def static_url_path(self, value: t.Optional[str]) -> None:
|
||||||
if value is not None:
|
if value is not None:
|
||||||
value = value.rstrip("/")
|
value = value.rstrip("/")
|
||||||
|
|
||||||
self._static_url_path = value
|
self._static_url_path = value
|
||||||
|
|
||||||
def get_send_file_max_age(self, filename):
|
def get_send_file_max_age(self, filename: str) -> t.Optional[int]:
|
||||||
"""Used by :func:`send_file` to determine the ``max_age`` cache
|
"""Used by :func:`send_file` to determine the ``max_age`` cache
|
||||||
value for a given file path if it wasn't passed.
|
value for a given file path if it wasn't passed.
|
||||||
|
|
||||||
|
@ -276,7 +308,7 @@ class Scaffold:
|
||||||
|
|
||||||
return int(value.total_seconds())
|
return int(value.total_seconds())
|
||||||
|
|
||||||
def send_static_file(self, filename):
|
def send_static_file(self, filename: str) -> "Response":
|
||||||
"""The view function used to serve files from
|
"""The view function used to serve files from
|
||||||
:attr:`static_folder`. A route is automatically registered for
|
:attr:`static_folder`. A route is automatically registered for
|
||||||
this view at :attr:`static_url_path` if :attr:`static_folder` is
|
this view at :attr:`static_url_path` if :attr:`static_folder` is
|
||||||
|
@ -290,10 +322,12 @@ class Scaffold:
|
||||||
# send_file only knows to call get_send_file_max_age on the app,
|
# send_file only knows to call get_send_file_max_age on the app,
|
||||||
# call it here so it works for blueprints too.
|
# call it here so it works for blueprints too.
|
||||||
max_age = self.get_send_file_max_age(filename)
|
max_age = self.get_send_file_max_age(filename)
|
||||||
return send_from_directory(self.static_folder, filename, max_age=max_age)
|
return send_from_directory(
|
||||||
|
t.cast(str, self.static_folder), filename, max_age=max_age
|
||||||
|
)
|
||||||
|
|
||||||
@locked_cached_property
|
@locked_cached_property
|
||||||
def jinja_loader(self):
|
def jinja_loader(self) -> t.Optional[FileSystemLoader]:
|
||||||
"""The Jinja loader for this object's templates. By default this
|
"""The Jinja loader for this object's templates. By default this
|
||||||
is a class :class:`jinja2.loaders.FileSystemLoader` to
|
is a class :class:`jinja2.loaders.FileSystemLoader` to
|
||||||
:attr:`template_folder` if it is set.
|
:attr:`template_folder` if it is set.
|
||||||
|
@ -302,8 +336,10 @@ class Scaffold:
|
||||||
"""
|
"""
|
||||||
if self.template_folder is not None:
|
if self.template_folder is not None:
|
||||||
return FileSystemLoader(os.path.join(self.root_path, self.template_folder))
|
return FileSystemLoader(os.path.join(self.root_path, self.template_folder))
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
def open_resource(self, resource, mode="rb"):
|
def open_resource(self, resource: str, mode: str = "rb") -> t.IO[t.AnyStr]:
|
||||||
"""Open a resource file relative to :attr:`root_path` for
|
"""Open a resource file relative to :attr:`root_path` for
|
||||||
reading.
|
reading.
|
||||||
|
|
||||||
|
@ -326,48 +362,48 @@ class Scaffold:
|
||||||
|
|
||||||
return open(os.path.join(self.root_path, resource), mode)
|
return open(os.path.join(self.root_path, resource), mode)
|
||||||
|
|
||||||
def _method_route(self, method, rule, options):
|
def _method_route(self, method: str, rule: str, options: dict) -> t.Callable:
|
||||||
if "methods" in options:
|
if "methods" in options:
|
||||||
raise TypeError("Use the 'route' decorator to use the 'methods' argument.")
|
raise TypeError("Use the 'route' decorator to use the 'methods' argument.")
|
||||||
|
|
||||||
return self.route(rule, methods=[method], **options)
|
return self.route(rule, methods=[method], **options)
|
||||||
|
|
||||||
def get(self, rule, **options):
|
def get(self, rule: str, **options: t.Any) -> t.Callable:
|
||||||
"""Shortcut for :meth:`route` with ``methods=["GET"]``.
|
"""Shortcut for :meth:`route` with ``methods=["GET"]``.
|
||||||
|
|
||||||
.. versionadded:: 2.0
|
.. versionadded:: 2.0
|
||||||
"""
|
"""
|
||||||
return self._method_route("GET", rule, options)
|
return self._method_route("GET", rule, options)
|
||||||
|
|
||||||
def post(self, rule, **options):
|
def post(self, rule: str, **options: t.Any) -> t.Callable:
|
||||||
"""Shortcut for :meth:`route` with ``methods=["POST"]``.
|
"""Shortcut for :meth:`route` with ``methods=["POST"]``.
|
||||||
|
|
||||||
.. versionadded:: 2.0
|
.. versionadded:: 2.0
|
||||||
"""
|
"""
|
||||||
return self._method_route("POST", rule, options)
|
return self._method_route("POST", rule, options)
|
||||||
|
|
||||||
def put(self, rule, **options):
|
def put(self, rule: str, **options: t.Any) -> t.Callable:
|
||||||
"""Shortcut for :meth:`route` with ``methods=["PUT"]``.
|
"""Shortcut for :meth:`route` with ``methods=["PUT"]``.
|
||||||
|
|
||||||
.. versionadded:: 2.0
|
.. versionadded:: 2.0
|
||||||
"""
|
"""
|
||||||
return self._method_route("PUT", rule, options)
|
return self._method_route("PUT", rule, options)
|
||||||
|
|
||||||
def delete(self, rule, **options):
|
def delete(self, rule: str, **options: t.Any) -> t.Callable:
|
||||||
"""Shortcut for :meth:`route` with ``methods=["DELETE"]``.
|
"""Shortcut for :meth:`route` with ``methods=["DELETE"]``.
|
||||||
|
|
||||||
.. versionadded:: 2.0
|
.. versionadded:: 2.0
|
||||||
"""
|
"""
|
||||||
return self._method_route("DELETE", rule, options)
|
return self._method_route("DELETE", rule, options)
|
||||||
|
|
||||||
def patch(self, rule, **options):
|
def patch(self, rule: str, **options: t.Any) -> t.Callable:
|
||||||
"""Shortcut for :meth:`route` with ``methods=["PATCH"]``.
|
"""Shortcut for :meth:`route` with ``methods=["PATCH"]``.
|
||||||
|
|
||||||
.. versionadded:: 2.0
|
.. versionadded:: 2.0
|
||||||
"""
|
"""
|
||||||
return self._method_route("PATCH", rule, options)
|
return self._method_route("PATCH", rule, options)
|
||||||
|
|
||||||
def route(self, rule, **options):
|
def route(self, rule: str, **options: t.Any) -> t.Callable:
|
||||||
"""Decorate a view function to register it with the given URL
|
"""Decorate a view function to register it with the given URL
|
||||||
rule and options. Calls :meth:`add_url_rule`, which has more
|
rule and options. Calls :meth:`add_url_rule`, which has more
|
||||||
details about the implementation.
|
details about the implementation.
|
||||||
|
@ -391,7 +427,7 @@ class Scaffold:
|
||||||
:class:`~werkzeug.routing.Rule` object.
|
:class:`~werkzeug.routing.Rule` object.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def decorator(f):
|
def decorator(f: t.Callable) -> t.Callable:
|
||||||
endpoint = options.pop("endpoint", None)
|
endpoint = options.pop("endpoint", None)
|
||||||
self.add_url_rule(rule, endpoint, f, **options)
|
self.add_url_rule(rule, endpoint, f, **options)
|
||||||
return f
|
return f
|
||||||
|
@ -401,12 +437,12 @@ class Scaffold:
|
||||||
@setupmethod
|
@setupmethod
|
||||||
def add_url_rule(
|
def add_url_rule(
|
||||||
self,
|
self,
|
||||||
rule,
|
rule: str,
|
||||||
endpoint=None,
|
endpoint: t.Optional[str] = None,
|
||||||
view_func=None,
|
view_func: t.Optional[t.Callable] = None,
|
||||||
provide_automatic_options=None,
|
provide_automatic_options: t.Optional[bool] = None,
|
||||||
**options,
|
**options: t.Any,
|
||||||
):
|
) -> t.Callable:
|
||||||
"""Register a rule for routing incoming requests and building
|
"""Register a rule for routing incoming requests and building
|
||||||
URLs. The :meth:`route` decorator is a shortcut to call this
|
URLs. The :meth:`route` decorator is a shortcut to call this
|
||||||
with the ``view_func`` argument. These are equivalent:
|
with the ``view_func`` argument. These are equivalent:
|
||||||
|
@ -466,7 +502,7 @@ class Scaffold:
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def endpoint(self, endpoint):
|
def endpoint(self, endpoint: str) -> t.Callable:
|
||||||
"""Decorate a view function to register it for the given
|
"""Decorate a view function to register it for the given
|
||||||
endpoint. Used if a rule is added without a ``view_func`` with
|
endpoint. Used if a rule is added without a ``view_func`` with
|
||||||
:meth:`add_url_rule`.
|
:meth:`add_url_rule`.
|
||||||
|
@ -490,7 +526,7 @@ class Scaffold:
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
@setupmethod
|
@setupmethod
|
||||||
def before_request(self, f):
|
def before_request(self, f: BeforeRequestCallable) -> BeforeRequestCallable:
|
||||||
"""Register a function to run before each request.
|
"""Register a function to run before each request.
|
||||||
|
|
||||||
For example, this can be used to open a database connection, or
|
For example, this can be used to open a database connection, or
|
||||||
|
@ -512,7 +548,7 @@ class Scaffold:
|
||||||
return f
|
return f
|
||||||
|
|
||||||
@setupmethod
|
@setupmethod
|
||||||
def after_request(self, f):
|
def after_request(self, f: AfterRequestCallable) -> AfterRequestCallable:
|
||||||
"""Register a function to run after each request to this object.
|
"""Register a function to run after each request to this object.
|
||||||
|
|
||||||
The function is called with the response object, and must return
|
The function is called with the response object, and must return
|
||||||
|
@ -528,7 +564,7 @@ class Scaffold:
|
||||||
return f
|
return f
|
||||||
|
|
||||||
@setupmethod
|
@setupmethod
|
||||||
def teardown_request(self, f):
|
def teardown_request(self, f: TeardownCallable) -> TeardownCallable:
|
||||||
"""Register a function to be run at the end of each request,
|
"""Register a function to be run at the end of each request,
|
||||||
regardless of whether there was an exception or not. These functions
|
regardless of whether there was an exception or not. These functions
|
||||||
are executed when the request context is popped, even if not an
|
are executed when the request context is popped, even if not an
|
||||||
|
@ -567,13 +603,17 @@ class Scaffold:
|
||||||
return f
|
return f
|
||||||
|
|
||||||
@setupmethod
|
@setupmethod
|
||||||
def context_processor(self, f):
|
def context_processor(
|
||||||
|
self, f: TemplateContextProcessorCallable
|
||||||
|
) -> TemplateContextProcessorCallable:
|
||||||
"""Registers a template context processor function."""
|
"""Registers a template context processor function."""
|
||||||
self.template_context_processors[None].append(f)
|
self.template_context_processors[None].append(f)
|
||||||
return f
|
return f
|
||||||
|
|
||||||
@setupmethod
|
@setupmethod
|
||||||
def url_value_preprocessor(self, f):
|
def url_value_preprocessor(
|
||||||
|
self, f: URLValuePreprocessorCallable
|
||||||
|
) -> URLValuePreprocessorCallable:
|
||||||
"""Register a URL value preprocessor function for all view
|
"""Register a URL value preprocessor function for all view
|
||||||
functions in the application. These functions will be called before the
|
functions in the application. These functions will be called before the
|
||||||
:meth:`before_request` functions.
|
:meth:`before_request` functions.
|
||||||
|
@ -590,7 +630,7 @@ class Scaffold:
|
||||||
return f
|
return f
|
||||||
|
|
||||||
@setupmethod
|
@setupmethod
|
||||||
def url_defaults(self, f):
|
def url_defaults(self, f: URLDefaultCallable) -> URLDefaultCallable:
|
||||||
"""Callback function for URL defaults for all view functions of the
|
"""Callback function for URL defaults for all view functions of the
|
||||||
application. It's called with the endpoint and values and should
|
application. It's called with the endpoint and values and should
|
||||||
update the values passed in place.
|
update the values passed in place.
|
||||||
|
@ -599,7 +639,9 @@ class Scaffold:
|
||||||
return f
|
return f
|
||||||
|
|
||||||
@setupmethod
|
@setupmethod
|
||||||
def errorhandler(self, code_or_exception):
|
def errorhandler(
|
||||||
|
self, code_or_exception: t.Union[t.Type[Exception], int]
|
||||||
|
) -> t.Callable:
|
||||||
"""Register a function to handle errors by code or exception class.
|
"""Register a function to handle errors by code or exception class.
|
||||||
|
|
||||||
A decorator that is used to register a function given an
|
A decorator that is used to register a function given an
|
||||||
|
@ -629,14 +671,18 @@ class Scaffold:
|
||||||
an arbitrary exception
|
an arbitrary exception
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def decorator(f):
|
def decorator(f: ErrorHandlerCallable) -> ErrorHandlerCallable:
|
||||||
self.register_error_handler(code_or_exception, f)
|
self.register_error_handler(code_or_exception, f)
|
||||||
return f
|
return f
|
||||||
|
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
@setupmethod
|
@setupmethod
|
||||||
def register_error_handler(self, code_or_exception, f):
|
def register_error_handler(
|
||||||
|
self,
|
||||||
|
code_or_exception: t.Union[t.Type[Exception], int],
|
||||||
|
f: ErrorHandlerCallable,
|
||||||
|
) -> None:
|
||||||
"""Alternative error attach function to the :meth:`errorhandler`
|
"""Alternative error attach function to the :meth:`errorhandler`
|
||||||
decorator that is more straightforward to use for non decorator
|
decorator that is more straightforward to use for non decorator
|
||||||
usage.
|
usage.
|
||||||
|
@ -662,7 +708,9 @@ class Scaffold:
|
||||||
self.error_handler_spec[None][code][exc_class] = self.ensure_sync(f)
|
self.error_handler_spec[None][code][exc_class] = self.ensure_sync(f)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_exc_class_and_code(exc_class_or_code):
|
def _get_exc_class_and_code(
|
||||||
|
exc_class_or_code: t.Union[t.Type[Exception], int]
|
||||||
|
) -> t.Tuple[t.Type[Exception], t.Optional[int]]:
|
||||||
"""Get the exception class being handled. For HTTP status codes
|
"""Get the exception class being handled. For HTTP status codes
|
||||||
or ``HTTPException`` subclasses, return both the exception and
|
or ``HTTPException`` subclasses, return both the exception and
|
||||||
status code.
|
status code.
|
||||||
|
@ -670,6 +718,7 @@ class Scaffold:
|
||||||
:param exc_class_or_code: Any exception class, or an HTTP status
|
:param exc_class_or_code: Any exception class, or an HTTP status
|
||||||
code as an integer.
|
code as an integer.
|
||||||
"""
|
"""
|
||||||
|
exc_class: t.Type[Exception]
|
||||||
if isinstance(exc_class_or_code, int):
|
if isinstance(exc_class_or_code, int):
|
||||||
exc_class = default_exceptions[exc_class_or_code]
|
exc_class = default_exceptions[exc_class_or_code]
|
||||||
else:
|
else:
|
||||||
|
@ -684,11 +733,11 @@ class Scaffold:
|
||||||
else:
|
else:
|
||||||
return exc_class, None
|
return exc_class, None
|
||||||
|
|
||||||
def ensure_sync(self, func):
|
def ensure_sync(self, func: t.Callable) -> t.Callable:
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|
||||||
def _endpoint_from_view_func(view_func):
|
def _endpoint_from_view_func(view_func: t.Callable) -> str:
|
||||||
"""Internal helper that returns the default endpoint for a given
|
"""Internal helper that returns the default endpoint for a given
|
||||||
function. This always is the function name.
|
function. This always is the function name.
|
||||||
"""
|
"""
|
||||||
|
@ -696,7 +745,7 @@ def _endpoint_from_view_func(view_func):
|
||||||
return view_func.__name__
|
return view_func.__name__
|
||||||
|
|
||||||
|
|
||||||
def get_root_path(import_name):
|
def get_root_path(import_name: str) -> str:
|
||||||
"""Find the root path of a package, or the path that contains a
|
"""Find the root path of a package, or the path that contains a
|
||||||
module. If it cannot be found, returns the current working
|
module. If it cannot be found, returns the current working
|
||||||
directory.
|
directory.
|
||||||
|
@ -721,7 +770,7 @@ def get_root_path(import_name):
|
||||||
return os.getcwd()
|
return os.getcwd()
|
||||||
|
|
||||||
if hasattr(loader, "get_filename"):
|
if hasattr(loader, "get_filename"):
|
||||||
filepath = loader.get_filename(import_name)
|
filepath = loader.get_filename(import_name) # type: ignore
|
||||||
else:
|
else:
|
||||||
# Fall back to imports.
|
# Fall back to imports.
|
||||||
__import__(import_name)
|
__import__(import_name)
|
||||||
|
@ -822,7 +871,7 @@ def _find_package_path(root_mod_name):
|
||||||
return package_path
|
return package_path
|
||||||
|
|
||||||
|
|
||||||
def find_package(import_name):
|
def find_package(import_name: str):
|
||||||
"""Find the prefix that a package is installed under, and the path
|
"""Find the prefix that a package is installed under, and the path
|
||||||
that it would be imported from.
|
that it would be imported from.
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import typing as t
|
||||||
import warnings
|
import warnings
|
||||||
from collections.abc import MutableMapping
|
from collections.abc import MutableMapping
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
@ -10,17 +11,21 @@ from werkzeug.datastructures import CallbackDict
|
||||||
from .helpers import is_ip
|
from .helpers import is_ip
|
||||||
from .json.tag import TaggedJSONSerializer
|
from .json.tag import TaggedJSONSerializer
|
||||||
|
|
||||||
|
if t.TYPE_CHECKING:
|
||||||
|
from .app import Flask
|
||||||
|
from .wrappers import Request, Response
|
||||||
|
|
||||||
|
|
||||||
class SessionMixin(MutableMapping):
|
class SessionMixin(MutableMapping):
|
||||||
"""Expands a basic dictionary with session attributes."""
|
"""Expands a basic dictionary with session attributes."""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def permanent(self):
|
def permanent(self) -> bool:
|
||||||
"""This reflects the ``'_permanent'`` key in the dict."""
|
"""This reflects the ``'_permanent'`` key in the dict."""
|
||||||
return self.get("_permanent", False)
|
return self.get("_permanent", False)
|
||||||
|
|
||||||
@permanent.setter
|
@permanent.setter
|
||||||
def permanent(self, value):
|
def permanent(self, value: bool) -> None:
|
||||||
self["_permanent"] = bool(value)
|
self["_permanent"] = bool(value)
|
||||||
|
|
||||||
#: Some implementations can detect whether a session is newly
|
#: Some implementations can detect whether a session is newly
|
||||||
|
@ -61,22 +66,22 @@ class SecureCookieSession(CallbackDict, SessionMixin):
|
||||||
#: different users.
|
#: different users.
|
||||||
accessed = False
|
accessed = False
|
||||||
|
|
||||||
def __init__(self, initial=None):
|
def __init__(self, initial: t.Any = None) -> None:
|
||||||
def on_update(self):
|
def on_update(self) -> None:
|
||||||
self.modified = True
|
self.modified = True
|
||||||
self.accessed = True
|
self.accessed = True
|
||||||
|
|
||||||
super().__init__(initial, on_update)
|
super().__init__(initial, on_update)
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key: str) -> t.Any:
|
||||||
self.accessed = True
|
self.accessed = True
|
||||||
return super().__getitem__(key)
|
return super().__getitem__(key)
|
||||||
|
|
||||||
def get(self, key, default=None):
|
def get(self, key: str, default: t.Any = None) -> t.Any:
|
||||||
self.accessed = True
|
self.accessed = True
|
||||||
return super().get(key, default)
|
return super().get(key, default)
|
||||||
|
|
||||||
def setdefault(self, key, default=None):
|
def setdefault(self, key: str, default: t.Any = None) -> t.Any:
|
||||||
self.accessed = True
|
self.accessed = True
|
||||||
return super().setdefault(key, default)
|
return super().setdefault(key, default)
|
||||||
|
|
||||||
|
@ -87,14 +92,14 @@ class NullSession(SecureCookieSession):
|
||||||
but fail on setting.
|
but fail on setting.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def _fail(self, *args, **kwargs):
|
def _fail(self, *args: t.Any, **kwargs: t.Any) -> t.NoReturn:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"The session is unavailable because no secret "
|
"The session is unavailable because no secret "
|
||||||
"key was set. Set the secret_key on the "
|
"key was set. Set the secret_key on the "
|
||||||
"application to something unique and secret."
|
"application to something unique and secret."
|
||||||
)
|
)
|
||||||
|
|
||||||
__setitem__ = __delitem__ = clear = pop = popitem = update = setdefault = _fail
|
__setitem__ = __delitem__ = clear = pop = popitem = update = setdefault = _fail # type: ignore # noqa: B950
|
||||||
del _fail
|
del _fail
|
||||||
|
|
||||||
|
|
||||||
|
@ -141,7 +146,7 @@ class SessionInterface:
|
||||||
#: .. versionadded:: 0.10
|
#: .. versionadded:: 0.10
|
||||||
pickle_based = False
|
pickle_based = False
|
||||||
|
|
||||||
def make_null_session(self, app):
|
def make_null_session(self, app: "Flask") -> NullSession:
|
||||||
"""Creates a null session which acts as a replacement object if the
|
"""Creates a null session which acts as a replacement object if the
|
||||||
real session support could not be loaded due to a configuration
|
real session support could not be loaded due to a configuration
|
||||||
error. This mainly aids the user experience because the job of the
|
error. This mainly aids the user experience because the job of the
|
||||||
|
@ -153,7 +158,7 @@ class SessionInterface:
|
||||||
"""
|
"""
|
||||||
return self.null_session_class()
|
return self.null_session_class()
|
||||||
|
|
||||||
def is_null_session(self, obj):
|
def is_null_session(self, obj: object) -> bool:
|
||||||
"""Checks if a given object is a null session. Null sessions are
|
"""Checks if a given object is a null session. Null sessions are
|
||||||
not asked to be saved.
|
not asked to be saved.
|
||||||
|
|
||||||
|
@ -162,14 +167,14 @@ class SessionInterface:
|
||||||
"""
|
"""
|
||||||
return isinstance(obj, self.null_session_class)
|
return isinstance(obj, self.null_session_class)
|
||||||
|
|
||||||
def get_cookie_name(self, app):
|
def get_cookie_name(self, app: "Flask") -> str:
|
||||||
"""Returns the name of the session cookie.
|
"""Returns the name of the session cookie.
|
||||||
|
|
||||||
Uses ``app.session_cookie_name`` which is set to ``SESSION_COOKIE_NAME``
|
Uses ``app.session_cookie_name`` which is set to ``SESSION_COOKIE_NAME``
|
||||||
"""
|
"""
|
||||||
return app.session_cookie_name
|
return app.session_cookie_name
|
||||||
|
|
||||||
def get_cookie_domain(self, app):
|
def get_cookie_domain(self, app: "Flask") -> t.Optional[str]:
|
||||||
"""Returns the domain that should be set for the session cookie.
|
"""Returns the domain that should be set for the session cookie.
|
||||||
|
|
||||||
Uses ``SESSION_COOKIE_DOMAIN`` if it is configured, otherwise
|
Uses ``SESSION_COOKIE_DOMAIN`` if it is configured, otherwise
|
||||||
|
@ -227,7 +232,7 @@ class SessionInterface:
|
||||||
app.config["SESSION_COOKIE_DOMAIN"] = rv
|
app.config["SESSION_COOKIE_DOMAIN"] = rv
|
||||||
return rv
|
return rv
|
||||||
|
|
||||||
def get_cookie_path(self, app):
|
def get_cookie_path(self, app: "Flask") -> str:
|
||||||
"""Returns the path for which the cookie should be valid. The
|
"""Returns the path for which the cookie should be valid. The
|
||||||
default implementation uses the value from the ``SESSION_COOKIE_PATH``
|
default implementation uses the value from the ``SESSION_COOKIE_PATH``
|
||||||
config var if it's set, and falls back to ``APPLICATION_ROOT`` or
|
config var if it's set, and falls back to ``APPLICATION_ROOT`` or
|
||||||
|
@ -235,27 +240,29 @@ class SessionInterface:
|
||||||
"""
|
"""
|
||||||
return app.config["SESSION_COOKIE_PATH"] or app.config["APPLICATION_ROOT"]
|
return app.config["SESSION_COOKIE_PATH"] or app.config["APPLICATION_ROOT"]
|
||||||
|
|
||||||
def get_cookie_httponly(self, app):
|
def get_cookie_httponly(self, app: "Flask") -> bool:
|
||||||
"""Returns True if the session cookie should be httponly. This
|
"""Returns True if the session cookie should be httponly. This
|
||||||
currently just returns the value of the ``SESSION_COOKIE_HTTPONLY``
|
currently just returns the value of the ``SESSION_COOKIE_HTTPONLY``
|
||||||
config var.
|
config var.
|
||||||
"""
|
"""
|
||||||
return app.config["SESSION_COOKIE_HTTPONLY"]
|
return app.config["SESSION_COOKIE_HTTPONLY"]
|
||||||
|
|
||||||
def get_cookie_secure(self, app):
|
def get_cookie_secure(self, app: "Flask") -> bool:
|
||||||
"""Returns True if the cookie should be secure. This currently
|
"""Returns True if the cookie should be secure. This currently
|
||||||
just returns the value of the ``SESSION_COOKIE_SECURE`` setting.
|
just returns the value of the ``SESSION_COOKIE_SECURE`` setting.
|
||||||
"""
|
"""
|
||||||
return app.config["SESSION_COOKIE_SECURE"]
|
return app.config["SESSION_COOKIE_SECURE"]
|
||||||
|
|
||||||
def get_cookie_samesite(self, app):
|
def get_cookie_samesite(self, app: "Flask") -> str:
|
||||||
"""Return ``'Strict'`` or ``'Lax'`` if the cookie should use the
|
"""Return ``'Strict'`` or ``'Lax'`` if the cookie should use the
|
||||||
``SameSite`` attribute. This currently just returns the value of
|
``SameSite`` attribute. This currently just returns the value of
|
||||||
the :data:`SESSION_COOKIE_SAMESITE` setting.
|
the :data:`SESSION_COOKIE_SAMESITE` setting.
|
||||||
"""
|
"""
|
||||||
return app.config["SESSION_COOKIE_SAMESITE"]
|
return app.config["SESSION_COOKIE_SAMESITE"]
|
||||||
|
|
||||||
def get_expiration_time(self, app, session):
|
def get_expiration_time(
|
||||||
|
self, app: "Flask", session: SessionMixin
|
||||||
|
) -> t.Optional[datetime]:
|
||||||
"""A helper method that returns an expiration date for the session
|
"""A helper method that returns an expiration date for the session
|
||||||
or ``None`` if the session is linked to the browser session. The
|
or ``None`` if the session is linked to the browser session. The
|
||||||
default implementation returns now + the permanent session
|
default implementation returns now + the permanent session
|
||||||
|
@ -263,8 +270,9 @@ class SessionInterface:
|
||||||
"""
|
"""
|
||||||
if session.permanent:
|
if session.permanent:
|
||||||
return datetime.utcnow() + app.permanent_session_lifetime
|
return datetime.utcnow() + app.permanent_session_lifetime
|
||||||
|
return None
|
||||||
|
|
||||||
def should_set_cookie(self, app, session):
|
def should_set_cookie(self, app: "Flask", session: SessionMixin) -> bool:
|
||||||
"""Used by session backends to determine if a ``Set-Cookie`` header
|
"""Used by session backends to determine if a ``Set-Cookie`` header
|
||||||
should be set for this session cookie for this response. If the session
|
should be set for this session cookie for this response. If the session
|
||||||
has been modified, the cookie is set. If the session is permanent and
|
has been modified, the cookie is set. If the session is permanent and
|
||||||
|
@ -280,7 +288,9 @@ class SessionInterface:
|
||||||
session.permanent and app.config["SESSION_REFRESH_EACH_REQUEST"]
|
session.permanent and app.config["SESSION_REFRESH_EACH_REQUEST"]
|
||||||
)
|
)
|
||||||
|
|
||||||
def open_session(self, app, request):
|
def open_session(
|
||||||
|
self, app: "Flask", request: "Request"
|
||||||
|
) -> t.Optional[SessionMixin]:
|
||||||
"""This method has to be implemented and must either return ``None``
|
"""This method has to be implemented and must either return ``None``
|
||||||
in case the loading failed because of a configuration error or an
|
in case the loading failed because of a configuration error or an
|
||||||
instance of a session object which implements a dictionary like
|
instance of a session object which implements a dictionary like
|
||||||
|
@ -288,7 +298,9 @@ class SessionInterface:
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def save_session(self, app, session, response):
|
def save_session(
|
||||||
|
self, app: "Flask", session: SessionMixin, response: "Response"
|
||||||
|
) -> None:
|
||||||
"""This is called for actual sessions returned by :meth:`open_session`
|
"""This is called for actual sessions returned by :meth:`open_session`
|
||||||
at the end of the request. This is still called during a request
|
at the end of the request. This is still called during a request
|
||||||
context so if you absolutely need access to the request you can do
|
context so if you absolutely need access to the request you can do
|
||||||
|
@ -319,7 +331,9 @@ class SecureCookieSessionInterface(SessionInterface):
|
||||||
serializer = session_json_serializer
|
serializer = session_json_serializer
|
||||||
session_class = SecureCookieSession
|
session_class = SecureCookieSession
|
||||||
|
|
||||||
def get_signing_serializer(self, app):
|
def get_signing_serializer(
|
||||||
|
self, app: "Flask"
|
||||||
|
) -> t.Optional[URLSafeTimedSerializer]:
|
||||||
if not app.secret_key:
|
if not app.secret_key:
|
||||||
return None
|
return None
|
||||||
signer_kwargs = dict(
|
signer_kwargs = dict(
|
||||||
|
@ -332,7 +346,9 @@ class SecureCookieSessionInterface(SessionInterface):
|
||||||
signer_kwargs=signer_kwargs,
|
signer_kwargs=signer_kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
def open_session(self, app, request):
|
def open_session(
|
||||||
|
self, app: "Flask", request: "Request"
|
||||||
|
) -> t.Optional[SecureCookieSession]:
|
||||||
s = self.get_signing_serializer(app)
|
s = self.get_signing_serializer(app)
|
||||||
if s is None:
|
if s is None:
|
||||||
return None
|
return None
|
||||||
|
@ -346,7 +362,9 @@ class SecureCookieSessionInterface(SessionInterface):
|
||||||
except BadSignature:
|
except BadSignature:
|
||||||
return self.session_class()
|
return self.session_class()
|
||||||
|
|
||||||
def save_session(self, app, session, response):
|
def save_session(
|
||||||
|
self, app: "Flask", session: SessionMixin, response: "Response"
|
||||||
|
) -> None:
|
||||||
name = self.get_cookie_name(app)
|
name = self.get_cookie_name(app)
|
||||||
domain = self.get_cookie_domain(app)
|
domain = self.get_cookie_domain(app)
|
||||||
path = self.get_cookie_path(app)
|
path = self.get_cookie_path(app)
|
||||||
|
@ -372,10 +390,10 @@ class SecureCookieSessionInterface(SessionInterface):
|
||||||
|
|
||||||
httponly = self.get_cookie_httponly(app)
|
httponly = self.get_cookie_httponly(app)
|
||||||
expires = self.get_expiration_time(app, session)
|
expires = self.get_expiration_time(app, session)
|
||||||
val = self.get_signing_serializer(app).dumps(dict(session))
|
val = self.get_signing_serializer(app).dumps(dict(session)) # type: ignore
|
||||||
response.set_cookie(
|
response.set_cookie(
|
||||||
name,
|
name,
|
||||||
val,
|
val, # type: ignore
|
||||||
expires=expires,
|
expires=expires,
|
||||||
httponly=httponly,
|
httponly=httponly,
|
||||||
domain=domain,
|
domain=domain,
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import typing as t
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from blinker import Namespace
|
from blinker import Namespace
|
||||||
|
|
||||||
|
@ -5,8 +7,8 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
signals_available = False
|
signals_available = False
|
||||||
|
|
||||||
class Namespace:
|
class Namespace: # type: ignore
|
||||||
def signal(self, name, doc=None):
|
def signal(self, name: str, doc: t.Optional[str] = None) -> "_FakeSignal":
|
||||||
return _FakeSignal(name, doc)
|
return _FakeSignal(name, doc)
|
||||||
|
|
||||||
class _FakeSignal:
|
class _FakeSignal:
|
||||||
|
@ -16,14 +18,14 @@ except ImportError:
|
||||||
will just ignore the arguments and do nothing instead.
|
will just ignore the arguments and do nothing instead.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name, doc=None):
|
def __init__(self, name: str, doc: t.Optional[str] = None) -> None:
|
||||||
self.name = name
|
self.name = name
|
||||||
self.__doc__ = doc
|
self.__doc__ = doc
|
||||||
|
|
||||||
def send(self, *args, **kwargs):
|
def send(self, *args: t.Any, **kwargs: t.Any) -> t.Any:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def _fail(self, *args, **kwargs):
|
def _fail(self, *args: t.Any, **kwargs: t.Any) -> t.Any:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"Signalling support is unavailable because the blinker"
|
"Signalling support is unavailable because the blinker"
|
||||||
" library is not installed."
|
" library is not installed."
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
|
import typing as t
|
||||||
|
|
||||||
from jinja2 import BaseLoader
|
from jinja2 import BaseLoader
|
||||||
from jinja2 import Environment as BaseEnvironment
|
from jinja2 import Environment as BaseEnvironment
|
||||||
|
from jinja2 import Template
|
||||||
from jinja2 import TemplateNotFound
|
from jinja2 import TemplateNotFound
|
||||||
|
|
||||||
from .globals import _app_ctx_stack
|
from .globals import _app_ctx_stack
|
||||||
|
@ -7,8 +10,12 @@ from .globals import _request_ctx_stack
|
||||||
from .signals import before_render_template
|
from .signals import before_render_template
|
||||||
from .signals import template_rendered
|
from .signals import template_rendered
|
||||||
|
|
||||||
|
if t.TYPE_CHECKING:
|
||||||
|
from .app import Flask
|
||||||
|
from .scaffold import Scaffold
|
||||||
|
|
||||||
def _default_template_ctx_processor():
|
|
||||||
|
def _default_template_ctx_processor() -> t.Dict[str, t.Any]:
|
||||||
"""Default template context processor. Injects `request`,
|
"""Default template context processor. Injects `request`,
|
||||||
`session` and `g`.
|
`session` and `g`.
|
||||||
"""
|
"""
|
||||||
|
@ -29,7 +36,7 @@ class Environment(BaseEnvironment):
|
||||||
name of the blueprint to referenced templates if necessary.
|
name of the blueprint to referenced templates if necessary.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, app, **options):
|
def __init__(self, app: "Flask", **options: t.Any) -> None:
|
||||||
if "loader" not in options:
|
if "loader" not in options:
|
||||||
options["loader"] = app.create_global_jinja_loader()
|
options["loader"] = app.create_global_jinja_loader()
|
||||||
BaseEnvironment.__init__(self, **options)
|
BaseEnvironment.__init__(self, **options)
|
||||||
|
@ -41,15 +48,19 @@ class DispatchingJinjaLoader(BaseLoader):
|
||||||
the blueprint folders.
|
the blueprint folders.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, app):
|
def __init__(self, app: "Flask") -> None:
|
||||||
self.app = app
|
self.app = app
|
||||||
|
|
||||||
def get_source(self, environment, template):
|
def get_source(
|
||||||
|
self, environment: Environment, template: str
|
||||||
|
) -> t.Tuple[str, t.Optional[str], t.Callable]:
|
||||||
if self.app.config["EXPLAIN_TEMPLATE_LOADING"]:
|
if self.app.config["EXPLAIN_TEMPLATE_LOADING"]:
|
||||||
return self._get_source_explained(environment, template)
|
return self._get_source_explained(environment, template)
|
||||||
return self._get_source_fast(environment, template)
|
return self._get_source_fast(environment, template)
|
||||||
|
|
||||||
def _get_source_explained(self, environment, template):
|
def _get_source_explained(
|
||||||
|
self, environment: Environment, template: str
|
||||||
|
) -> t.Tuple[str, t.Optional[str], t.Callable]:
|
||||||
attempts = []
|
attempts = []
|
||||||
trv = None
|
trv = None
|
||||||
|
|
||||||
|
@ -70,7 +81,9 @@ class DispatchingJinjaLoader(BaseLoader):
|
||||||
return trv
|
return trv
|
||||||
raise TemplateNotFound(template)
|
raise TemplateNotFound(template)
|
||||||
|
|
||||||
def _get_source_fast(self, environment, template):
|
def _get_source_fast(
|
||||||
|
self, environment: Environment, template: str
|
||||||
|
) -> t.Tuple[str, t.Optional[str], t.Callable]:
|
||||||
for _srcobj, loader in self._iter_loaders(template):
|
for _srcobj, loader in self._iter_loaders(template):
|
||||||
try:
|
try:
|
||||||
return loader.get_source(environment, template)
|
return loader.get_source(environment, template)
|
||||||
|
@ -78,7 +91,9 @@ class DispatchingJinjaLoader(BaseLoader):
|
||||||
continue
|
continue
|
||||||
raise TemplateNotFound(template)
|
raise TemplateNotFound(template)
|
||||||
|
|
||||||
def _iter_loaders(self, template):
|
def _iter_loaders(
|
||||||
|
self, template: str
|
||||||
|
) -> t.Generator[t.Tuple["Scaffold", BaseLoader], None, None]:
|
||||||
loader = self.app.jinja_loader
|
loader = self.app.jinja_loader
|
||||||
if loader is not None:
|
if loader is not None:
|
||||||
yield self.app, loader
|
yield self.app, loader
|
||||||
|
@ -88,7 +103,7 @@ class DispatchingJinjaLoader(BaseLoader):
|
||||||
if loader is not None:
|
if loader is not None:
|
||||||
yield blueprint, loader
|
yield blueprint, loader
|
||||||
|
|
||||||
def list_templates(self):
|
def list_templates(self) -> t.List[str]:
|
||||||
result = set()
|
result = set()
|
||||||
loader = self.app.jinja_loader
|
loader = self.app.jinja_loader
|
||||||
if loader is not None:
|
if loader is not None:
|
||||||
|
@ -103,7 +118,7 @@ class DispatchingJinjaLoader(BaseLoader):
|
||||||
return list(result)
|
return list(result)
|
||||||
|
|
||||||
|
|
||||||
def _render(template, context, app):
|
def _render(template: Template, context: dict, app: "Flask") -> str:
|
||||||
"""Renders the template and fires the signal"""
|
"""Renders the template and fires the signal"""
|
||||||
|
|
||||||
before_render_template.send(app, template=template, context=context)
|
before_render_template.send(app, template=template, context=context)
|
||||||
|
@ -112,7 +127,9 @@ def _render(template, context, app):
|
||||||
return rv
|
return rv
|
||||||
|
|
||||||
|
|
||||||
def render_template(template_name_or_list, **context):
|
def render_template(
|
||||||
|
template_name_or_list: t.Union[str, t.List[str]], **context: t.Any
|
||||||
|
) -> str:
|
||||||
"""Renders a template from the template folder with the given
|
"""Renders a template from the template folder with the given
|
||||||
context.
|
context.
|
||||||
|
|
||||||
|
@ -131,7 +148,7 @@ def render_template(template_name_or_list, **context):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def render_template_string(source, **context):
|
def render_template_string(source: str, **context: t.Any) -> str:
|
||||||
"""Renders a template from the given template source string
|
"""Renders a template from the given template source string
|
||||||
with the given context. Template variables will be autoescaped.
|
with the given context. Template variables will be autoescaped.
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
|
import typing as t
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from copy import copy
|
from copy import copy
|
||||||
|
from types import TracebackType
|
||||||
|
|
||||||
import werkzeug.test
|
import werkzeug.test
|
||||||
from click.testing import CliRunner
|
from click.testing import CliRunner
|
||||||
|
@ -10,6 +12,11 @@ from werkzeug.wrappers import Request as BaseRequest
|
||||||
from . import _request_ctx_stack
|
from . import _request_ctx_stack
|
||||||
from .cli import ScriptInfo
|
from .cli import ScriptInfo
|
||||||
from .json import dumps as json_dumps
|
from .json import dumps as json_dumps
|
||||||
|
from .sessions import SessionMixin
|
||||||
|
|
||||||
|
if t.TYPE_CHECKING:
|
||||||
|
from .app import Flask
|
||||||
|
from .wrappers import Response
|
||||||
|
|
||||||
|
|
||||||
class EnvironBuilder(werkzeug.test.EnvironBuilder):
|
class EnvironBuilder(werkzeug.test.EnvironBuilder):
|
||||||
|
@ -36,14 +43,14 @@ class EnvironBuilder(werkzeug.test.EnvironBuilder):
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
app,
|
app: "Flask",
|
||||||
path="/",
|
path: str = "/",
|
||||||
base_url=None,
|
base_url: t.Optional[str] = None,
|
||||||
subdomain=None,
|
subdomain: t.Optional[str] = None,
|
||||||
url_scheme=None,
|
url_scheme: t.Optional[str] = None,
|
||||||
*args,
|
*args: t.Any,
|
||||||
**kwargs,
|
**kwargs: t.Any,
|
||||||
):
|
) -> None:
|
||||||
assert not (base_url or subdomain or url_scheme) or (
|
assert not (base_url or subdomain or url_scheme) or (
|
||||||
base_url is not None
|
base_url is not None
|
||||||
) != bool(
|
) != bool(
|
||||||
|
@ -74,7 +81,7 @@ class EnvironBuilder(werkzeug.test.EnvironBuilder):
|
||||||
self.app = app
|
self.app = app
|
||||||
super().__init__(path, base_url, *args, **kwargs)
|
super().__init__(path, base_url, *args, **kwargs)
|
||||||
|
|
||||||
def json_dumps(self, obj, **kwargs):
|
def json_dumps(self, obj: t.Any, **kwargs: t.Any) -> str: # type: ignore
|
||||||
"""Serialize ``obj`` to a JSON-formatted string.
|
"""Serialize ``obj`` to a JSON-formatted string.
|
||||||
|
|
||||||
The serialization will be configured according to the config associated
|
The serialization will be configured according to the config associated
|
||||||
|
@ -99,9 +106,10 @@ class FlaskClient(Client):
|
||||||
Basic usage is outlined in the :doc:`/testing` chapter.
|
Basic usage is outlined in the :doc:`/testing` chapter.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
application: "Flask"
|
||||||
preserve_context = False
|
preserve_context = False
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args: t.Any, **kwargs: t.Any) -> None:
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.environ_base = {
|
self.environ_base = {
|
||||||
"REMOTE_ADDR": "127.0.0.1",
|
"REMOTE_ADDR": "127.0.0.1",
|
||||||
|
@ -109,7 +117,9 @@ class FlaskClient(Client):
|
||||||
}
|
}
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def session_transaction(self, *args, **kwargs):
|
def session_transaction(
|
||||||
|
self, *args: t.Any, **kwargs: t.Any
|
||||||
|
) -> t.Generator[SessionMixin, None, None]:
|
||||||
"""When used in combination with a ``with`` statement this opens a
|
"""When used in combination with a ``with`` statement this opens a
|
||||||
session transaction. This can be used to modify the session that
|
session transaction. This can be used to modify the session that
|
||||||
the test client uses. Once the ``with`` block is left the session is
|
the test client uses. Once the ``with`` block is left the session is
|
||||||
|
@ -161,9 +171,14 @@ class FlaskClient(Client):
|
||||||
headers = resp.get_wsgi_headers(c.request.environ)
|
headers = resp.get_wsgi_headers(c.request.environ)
|
||||||
self.cookie_jar.extract_wsgi(c.request.environ, headers)
|
self.cookie_jar.extract_wsgi(c.request.environ, headers)
|
||||||
|
|
||||||
def open(
|
def open( # type: ignore
|
||||||
self, *args, as_tuple=False, buffered=False, follow_redirects=False, **kwargs
|
self,
|
||||||
):
|
*args: t.Any,
|
||||||
|
as_tuple: bool = False,
|
||||||
|
buffered: bool = False,
|
||||||
|
follow_redirects: bool = False,
|
||||||
|
**kwargs: t.Any,
|
||||||
|
) -> "Response":
|
||||||
# Same logic as super.open, but apply environ_base and preserve_context.
|
# Same logic as super.open, but apply environ_base and preserve_context.
|
||||||
request = None
|
request = None
|
||||||
|
|
||||||
|
@ -198,20 +213,22 @@ class FlaskClient(Client):
|
||||||
finally:
|
finally:
|
||||||
builder.close()
|
builder.close()
|
||||||
|
|
||||||
return super().open(
|
return super().open( # type: ignore
|
||||||
request,
|
request,
|
||||||
as_tuple=as_tuple,
|
as_tuple=as_tuple,
|
||||||
buffered=buffered,
|
buffered=buffered,
|
||||||
follow_redirects=follow_redirects,
|
follow_redirects=follow_redirects,
|
||||||
)
|
)
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self) -> "FlaskClient":
|
||||||
if self.preserve_context:
|
if self.preserve_context:
|
||||||
raise RuntimeError("Cannot nest client invocations")
|
raise RuntimeError("Cannot nest client invocations")
|
||||||
self.preserve_context = True
|
self.preserve_context = True
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __exit__(self, exc_type, exc_value, tb):
|
def __exit__(
|
||||||
|
self, exc_type: type, exc_value: BaseException, tb: TracebackType
|
||||||
|
) -> None:
|
||||||
self.preserve_context = False
|
self.preserve_context = False
|
||||||
|
|
||||||
# Normally the request context is preserved until the next
|
# Normally the request context is preserved until the next
|
||||||
|
@ -233,11 +250,13 @@ class FlaskCliRunner(CliRunner):
|
||||||
:meth:`~flask.Flask.test_cli_runner`. See :ref:`testing-cli`.
|
:meth:`~flask.Flask.test_cli_runner`. See :ref:`testing-cli`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, app, **kwargs):
|
def __init__(self, app: "Flask", **kwargs: t.Any) -> None:
|
||||||
self.app = app
|
self.app = app
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
def invoke(self, cli=None, args=None, **kwargs):
|
def invoke( # type: ignore
|
||||||
|
self, cli: t.Any = None, args: t.Any = None, **kwargs: t.Any
|
||||||
|
) -> t.Any:
|
||||||
"""Invokes a CLI command in an isolated environment. See
|
"""Invokes a CLI command in an isolated environment. See
|
||||||
:meth:`CliRunner.invoke <click.testing.CliRunner.invoke>` for
|
:meth:`CliRunner.invoke <click.testing.CliRunner.invoke>` for
|
||||||
full method documentation. See :ref:`testing-cli` for examples.
|
full method documentation. See :ref:`testing-cli` for examples.
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
import typing as t
|
||||||
|
|
||||||
|
|
||||||
|
if t.TYPE_CHECKING:
|
||||||
|
from werkzeug.datastructures import Headers # noqa: F401
|
||||||
|
from wsgiref.types import WSGIApplication # noqa: F401
|
||||||
|
from .wrappers import Response # noqa: F401
|
||||||
|
|
||||||
|
# The possible types that are directly convertible or are a Response object.
|
||||||
|
ResponseValue = t.Union[
|
||||||
|
"Response",
|
||||||
|
t.AnyStr,
|
||||||
|
t.Dict[str, t.Any], # any jsonify-able dict
|
||||||
|
t.Generator[t.AnyStr, None, None],
|
||||||
|
]
|
||||||
|
StatusCode = int
|
||||||
|
|
||||||
|
# the possible types for an individual HTTP header
|
||||||
|
HeaderName = str
|
||||||
|
HeaderValue = t.Union[str, t.List[str], t.Tuple[str, ...]]
|
||||||
|
|
||||||
|
# the possible types for HTTP headers
|
||||||
|
HeadersValue = t.Union[
|
||||||
|
"Headers", t.Dict[HeaderName, HeaderValue], t.List[t.Tuple[HeaderName, HeaderValue]]
|
||||||
|
]
|
||||||
|
|
||||||
|
# The possible types returned by a route function.
|
||||||
|
ResponseReturnValue = t.Union[
|
||||||
|
ResponseValue,
|
||||||
|
t.Tuple[ResponseValue, HeadersValue],
|
||||||
|
t.Tuple[ResponseValue, StatusCode],
|
||||||
|
t.Tuple[ResponseValue, StatusCode, HeadersValue],
|
||||||
|
"WSGIApplication",
|
||||||
|
]
|
||||||
|
|
||||||
|
AppOrBlueprintKey = t.Optional[str] # The App key is None, whereas blueprints are named
|
||||||
|
AfterRequestCallable = t.Callable[["Response"], "Response"]
|
||||||
|
BeforeRequestCallable = t.Callable[[], None]
|
||||||
|
ErrorHandlerCallable = t.Callable[[Exception], ResponseReturnValue]
|
||||||
|
TeardownCallable = t.Callable[[t.Optional[BaseException]], "Response"]
|
||||||
|
TemplateContextProcessorCallable = t.Callable[[], t.Dict[str, t.Any]]
|
||||||
|
TemplateFilterCallable = t.Callable[[t.Any], str]
|
||||||
|
TemplateGlobalCallable = t.Callable[[], t.Any]
|
||||||
|
TemplateTestCallable = t.Callable[[t.Any], bool]
|
||||||
|
URLDefaultCallable = t.Callable[[str, dict], None]
|
||||||
|
URLValuePreprocessorCallable = t.Callable[[t.Optional[str], t.Optional[dict]], None]
|
|
@ -1,4 +1,7 @@
|
||||||
|
import typing as t
|
||||||
|
|
||||||
from .globals import request
|
from .globals import request
|
||||||
|
from .typing import ResponseReturnValue
|
||||||
|
|
||||||
|
|
||||||
http_method_funcs = frozenset(
|
http_method_funcs = frozenset(
|
||||||
|
@ -39,10 +42,10 @@ class View:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
#: A list of methods this view can handle.
|
#: A list of methods this view can handle.
|
||||||
methods = None
|
methods: t.Optional[t.List[str]] = None
|
||||||
|
|
||||||
#: Setting this disables or force-enables the automatic options handling.
|
#: Setting this disables or force-enables the automatic options handling.
|
||||||
provide_automatic_options = None
|
provide_automatic_options: t.Optional[bool] = None
|
||||||
|
|
||||||
#: The canonical way to decorate class-based views is to decorate the
|
#: The canonical way to decorate class-based views is to decorate the
|
||||||
#: return value of as_view(). However since this moves parts of the
|
#: return value of as_view(). However since this moves parts of the
|
||||||
|
@ -53,9 +56,9 @@ class View:
|
||||||
#: view function is created the result is automatically decorated.
|
#: view function is created the result is automatically decorated.
|
||||||
#:
|
#:
|
||||||
#: .. versionadded:: 0.8
|
#: .. versionadded:: 0.8
|
||||||
decorators = ()
|
decorators: t.List[t.Callable] = []
|
||||||
|
|
||||||
def dispatch_request(self):
|
def dispatch_request(self) -> ResponseReturnValue:
|
||||||
"""Subclasses have to override this method to implement the
|
"""Subclasses have to override this method to implement the
|
||||||
actual view function code. This method is called with all
|
actual view function code. This method is called with all
|
||||||
the arguments from the URL rule.
|
the arguments from the URL rule.
|
||||||
|
@ -63,7 +66,9 @@ class View:
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def as_view(cls, name, *class_args, **class_kwargs):
|
def as_view(
|
||||||
|
cls, name: str, *class_args: t.Any, **class_kwargs: t.Any
|
||||||
|
) -> t.Callable:
|
||||||
"""Converts the class into an actual view function that can be used
|
"""Converts the class into an actual view function that can be used
|
||||||
with the routing system. Internally this generates a function on the
|
with the routing system. Internally this generates a function on the
|
||||||
fly which will instantiate the :class:`View` on each request and call
|
fly which will instantiate the :class:`View` on each request and call
|
||||||
|
@ -73,8 +78,8 @@ class View:
|
||||||
constructor of the class.
|
constructor of the class.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def view(*args, **kwargs):
|
def view(*args: t.Any, **kwargs: t.Any) -> ResponseReturnValue:
|
||||||
self = view.view_class(*class_args, **class_kwargs)
|
self = view.view_class(*class_args, **class_kwargs) # type: ignore
|
||||||
return self.dispatch_request(*args, **kwargs)
|
return self.dispatch_request(*args, **kwargs)
|
||||||
|
|
||||||
if cls.decorators:
|
if cls.decorators:
|
||||||
|
@ -88,12 +93,12 @@ class View:
|
||||||
# view this thing came from, secondly it's also used for instantiating
|
# view this thing came from, secondly it's also used for instantiating
|
||||||
# the view class so you can actually replace it with something else
|
# the view class so you can actually replace it with something else
|
||||||
# for testing purposes and debugging.
|
# for testing purposes and debugging.
|
||||||
view.view_class = cls
|
view.view_class = cls # type: ignore
|
||||||
view.__name__ = name
|
view.__name__ = name
|
||||||
view.__doc__ = cls.__doc__
|
view.__doc__ = cls.__doc__
|
||||||
view.__module__ = cls.__module__
|
view.__module__ = cls.__module__
|
||||||
view.methods = cls.methods
|
view.methods = cls.methods # type: ignore
|
||||||
view.provide_automatic_options = cls.provide_automatic_options
|
view.provide_automatic_options = cls.provide_automatic_options # type: ignore
|
||||||
return view
|
return view
|
||||||
|
|
||||||
|
|
||||||
|
@ -140,7 +145,7 @@ class MethodView(View, metaclass=MethodViewType):
|
||||||
app.add_url_rule('/counter', view_func=CounterAPI.as_view('counter'))
|
app.add_url_rule('/counter', view_func=CounterAPI.as_view('counter'))
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def dispatch_request(self, *args, **kwargs):
|
def dispatch_request(self, *args: t.Any, **kwargs: t.Any) -> ResponseReturnValue:
|
||||||
meth = getattr(self, request.method.lower(), None)
|
meth = getattr(self, request.method.lower(), None)
|
||||||
|
|
||||||
# If the request method is HEAD and we don't have a handler for it
|
# If the request method is HEAD and we don't have a handler for it
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import typing as t
|
||||||
|
|
||||||
from werkzeug.exceptions import BadRequest
|
from werkzeug.exceptions import BadRequest
|
||||||
from werkzeug.wrappers import Request as RequestBase
|
from werkzeug.wrappers import Request as RequestBase
|
||||||
from werkzeug.wrappers import Response as ResponseBase
|
from werkzeug.wrappers import Response as ResponseBase
|
||||||
|
@ -5,6 +7,9 @@ from werkzeug.wrappers import Response as ResponseBase
|
||||||
from . import json
|
from . import json
|
||||||
from .globals import current_app
|
from .globals import current_app
|
||||||
|
|
||||||
|
if t.TYPE_CHECKING:
|
||||||
|
from werkzeug.routing import Rule
|
||||||
|
|
||||||
|
|
||||||
class Request(RequestBase):
|
class Request(RequestBase):
|
||||||
"""The request object used by default in Flask. Remembers the
|
"""The request object used by default in Flask. Remembers the
|
||||||
|
@ -31,26 +36,28 @@ class Request(RequestBase):
|
||||||
#: because the request was never internally bound.
|
#: because the request was never internally bound.
|
||||||
#:
|
#:
|
||||||
#: .. versionadded:: 0.6
|
#: .. versionadded:: 0.6
|
||||||
url_rule = None
|
url_rule: t.Optional["Rule"] = None
|
||||||
|
|
||||||
#: A dict of view arguments that matched the request. If an exception
|
#: A dict of view arguments that matched the request. If an exception
|
||||||
#: happened when matching, this will be ``None``.
|
#: happened when matching, this will be ``None``.
|
||||||
view_args = None
|
view_args: t.Optional[t.Dict[str, t.Any]] = None
|
||||||
|
|
||||||
#: If matching the URL failed, this is the exception that will be
|
#: If matching the URL failed, this is the exception that will be
|
||||||
#: raised / was raised as part of the request handling. This is
|
#: raised / was raised as part of the request handling. This is
|
||||||
#: usually a :exc:`~werkzeug.exceptions.NotFound` exception or
|
#: usually a :exc:`~werkzeug.exceptions.NotFound` exception or
|
||||||
#: something similar.
|
#: something similar.
|
||||||
routing_exception = None
|
routing_exception: t.Optional[Exception] = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def max_content_length(self):
|
def max_content_length(self) -> t.Optional[int]: # type: ignore
|
||||||
"""Read-only view of the ``MAX_CONTENT_LENGTH`` config key."""
|
"""Read-only view of the ``MAX_CONTENT_LENGTH`` config key."""
|
||||||
if current_app:
|
if current_app:
|
||||||
return current_app.config["MAX_CONTENT_LENGTH"]
|
return current_app.config["MAX_CONTENT_LENGTH"]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def endpoint(self):
|
def endpoint(self) -> t.Optional[str]:
|
||||||
"""The endpoint that matched the request. This in combination with
|
"""The endpoint that matched the request. This in combination with
|
||||||
:attr:`view_args` can be used to reconstruct the same or a
|
:attr:`view_args` can be used to reconstruct the same or a
|
||||||
modified URL. If an exception happened when matching, this will
|
modified URL. If an exception happened when matching, this will
|
||||||
|
@ -58,14 +65,18 @@ class Request(RequestBase):
|
||||||
"""
|
"""
|
||||||
if self.url_rule is not None:
|
if self.url_rule is not None:
|
||||||
return self.url_rule.endpoint
|
return self.url_rule.endpoint
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def blueprint(self):
|
def blueprint(self) -> t.Optional[str]:
|
||||||
"""The name of the current blueprint"""
|
"""The name of the current blueprint"""
|
||||||
if self.url_rule and "." in self.url_rule.endpoint:
|
if self.url_rule and "." in self.url_rule.endpoint:
|
||||||
return self.url_rule.endpoint.rsplit(".", 1)[0]
|
return self.url_rule.endpoint.rsplit(".", 1)[0]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
def _load_form_data(self):
|
def _load_form_data(self) -> None:
|
||||||
RequestBase._load_form_data(self)
|
RequestBase._load_form_data(self)
|
||||||
|
|
||||||
# In debug mode we're replacing the files multidict with an ad-hoc
|
# In debug mode we're replacing the files multidict with an ad-hoc
|
||||||
|
@ -80,7 +91,7 @@ class Request(RequestBase):
|
||||||
|
|
||||||
attach_enctype_error_multidict(self)
|
attach_enctype_error_multidict(self)
|
||||||
|
|
||||||
def on_json_loading_failed(self, e):
|
def on_json_loading_failed(self, e: Exception) -> t.NoReturn:
|
||||||
if current_app and current_app.debug:
|
if current_app and current_app.debug:
|
||||||
raise BadRequest(f"Failed to decode JSON object: {e}")
|
raise BadRequest(f"Failed to decode JSON object: {e}")
|
||||||
|
|
||||||
|
@ -110,7 +121,7 @@ class Response(ResponseBase):
|
||||||
json_module = json
|
json_module = json
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def max_cookie_size(self):
|
def max_cookie_size(self) -> int: # type: ignore
|
||||||
"""Read-only view of the :data:`MAX_COOKIE_SIZE` config key.
|
"""Read-only view of the :data:`MAX_COOKIE_SIZE` config key.
|
||||||
|
|
||||||
See :attr:`~werkzeug.wrappers.Response.max_cookie_size` in
|
See :attr:`~werkzeug.wrappers.Response.max_cookie_size` in
|
||||||
|
|
Loading…
Reference in New Issue