flask
Advanced tools
+2
-2
@@ -79,4 +79,4 @@ .. currentmodule:: flask | ||
| macOS Monterey and later automatically starts a service that uses port | ||
| 5000. To disable the service, go to System Preferences, Sharing, and | ||
| disable "AirPlay Receiver". | ||
| 5000. You can choose to disable this service instead of using a different port by | ||
| searching for "AirPlay Receiver" in System Preferences and toggling it off. | ||
@@ -83,0 +83,0 @@ |
@@ -15,1 +15,4 @@ [project] | ||
| name = "task_app" | ||
| [tool.ruff] | ||
| src = ["src"] |
@@ -5,3 +5,3 @@ from flask import jsonify | ||
| from js_example import app | ||
| from . import app | ||
@@ -8,0 +8,0 @@ |
@@ -30,1 +30,4 @@ [project] | ||
| source = ["js_example", "tests"] | ||
| [tool.ruff] | ||
| src = ["src"] |
@@ -34,3 +34,3 @@ import os | ||
| # register the database commands | ||
| from flaskr import db | ||
| from . import db | ||
@@ -40,3 +40,4 @@ db.init_app(app) | ||
| # apply the blueprints to the app | ||
| from flaskr import auth, blog | ||
| from . import auth | ||
| from . import blog | ||
@@ -43,0 +44,0 @@ app.register_blueprint(auth.bp) |
@@ -14,3 +14,3 @@ import functools | ||
| from flaskr.db import get_db | ||
| from .db import get_db | ||
@@ -17,0 +17,0 @@ bp = Blueprint("auth", __name__, url_prefix="/auth") |
@@ -10,4 +10,4 @@ from flask import Blueprint | ||
| from flaskr.auth import login_required | ||
| from flaskr.db import get_db | ||
| from .auth import login_required | ||
| from .db import get_db | ||
@@ -14,0 +14,0 @@ bp = Blueprint("blog", __name__) |
@@ -37,1 +37,4 @@ [project] | ||
| source = ["flaskr", "tests"] | ||
| [tool.ruff] | ||
| src = ["src"] |
+1
-1
| Metadata-Version: 2.1 | ||
| Name: Flask | ||
| Version: 3.0.0 | ||
| Version: 3.0.1 | ||
| Summary: A simple framework for building complex web applications. | ||
@@ -5,0 +5,0 @@ Maintainer-email: Pallets <contact@palletsprojects.com> |
+24
-17
| [project] | ||
| name = "Flask" | ||
| version = "3.0.0" | ||
| version = "3.0.1" | ||
| description = "A simple framework for building complex web applications." | ||
@@ -82,20 +82,6 @@ readme = "README.rst" | ||
| python_version = "3.8" | ||
| files = ["src/flask"] | ||
| files = ["src/flask", "tests/typing"] | ||
| show_error_codes = true | ||
| pretty = true | ||
| #strict = true | ||
| allow_redefinition = true | ||
| disallow_subclassing_any = true | ||
| #disallow_untyped_calls = true | ||
| #disallow_untyped_defs = true | ||
| #disallow_incomplete_defs = true | ||
| no_implicit_optional = true | ||
| local_partial_types = true | ||
| #no_implicit_reexport = true | ||
| strict_equality = true | ||
| warn_redundant_casts = true | ||
| warn_unused_configs = true | ||
| warn_unused_ignores = true | ||
| #warn_return_any = true | ||
| #warn_unreachable = true | ||
| strict = true | ||
@@ -110,1 +96,22 @@ [[tool.mypy.overrides]] | ||
| ignore_missing_imports = true | ||
| [tool.ruff] | ||
| src = ["src"] | ||
| fix = true | ||
| show-fixes = true | ||
| show-source = true | ||
| [tool.ruff.lint] | ||
| select = [ | ||
| "B", # flake8-bugbear | ||
| "E", # pycodestyle error | ||
| "F", # pyflakes | ||
| "I", # isort | ||
| "UP", # pyupgrade | ||
| "W", # pycodestyle warning | ||
| ] | ||
| ignore-init-module-imports = true | ||
| [tool.ruff.lint.isort] | ||
| force-single-line = true | ||
| order-by-type = false |
@@ -1,13 +0,12 @@ | ||
| # SHA1:80754af91bfb6d1073585b046fe0a474ce868509 | ||
| # | ||
| # This file is autogenerated by pip-compile-multi | ||
| # To update, run: | ||
| # This file is autogenerated by pip-compile with Python 3.11 | ||
| # by the following command: | ||
| # | ||
| # pip-compile-multi | ||
| # pip-compile build.in | ||
| # | ||
| build==0.10.0 | ||
| # via -r requirements/build.in | ||
| packaging==23.1 | ||
| build==1.0.3 | ||
| # via -r build.in | ||
| packaging==23.2 | ||
| # via build | ||
| pyproject-hooks==1.0.0 | ||
| # via build |
| -r docs.in | ||
| -r tests.in | ||
| -r typing.in | ||
| pip-compile-multi | ||
| pip-tools | ||
| pre-commit | ||
| tox |
+116
-29
@@ -1,15 +0,23 @@ | ||
| # SHA1:54b5b77ec8c7a0064ffa93b2fd16cb0130ba177c | ||
| # | ||
| # This file is autogenerated by pip-compile-multi | ||
| # To update, run: | ||
| # This file is autogenerated by pip-compile with Python 3.11 | ||
| # by the following command: | ||
| # | ||
| # pip-compile-multi | ||
| # pip-compile dev.in | ||
| # | ||
| -r docs.txt | ||
| -r tests.txt | ||
| -r typing.txt | ||
| build==0.10.0 | ||
| alabaster==0.7.16 | ||
| # via sphinx | ||
| asgiref==3.7.2 | ||
| # via | ||
| # -r tests.in | ||
| # -r typing.in | ||
| babel==2.14.0 | ||
| # via sphinx | ||
| build==1.0.3 | ||
| # via pip-tools | ||
| cachetools==5.3.1 | ||
| cachetools==5.3.2 | ||
| # via tox | ||
| certifi==2023.11.17 | ||
| # via requests | ||
| cffi==1.16.0 | ||
| # via cryptography | ||
| cfgv==3.4.0 | ||
@@ -19,43 +27,122 @@ # via pre-commit | ||
| # via tox | ||
| click==8.1.6 | ||
| # via | ||
| # pip-compile-multi | ||
| # pip-tools | ||
| charset-normalizer==3.3.2 | ||
| # via requests | ||
| click==8.1.7 | ||
| # via pip-tools | ||
| colorama==0.4.6 | ||
| # via tox | ||
| distlib==0.3.7 | ||
| cryptography==41.0.7 | ||
| # via -r typing.in | ||
| distlib==0.3.8 | ||
| # via virtualenv | ||
| filelock==3.12.2 | ||
| docutils==0.18.1 | ||
| # via | ||
| # sphinx | ||
| # sphinx-tabs | ||
| filelock==3.13.1 | ||
| # via | ||
| # tox | ||
| # virtualenv | ||
| identify==2.5.26 | ||
| identify==2.5.33 | ||
| # via pre-commit | ||
| idna==3.6 | ||
| # via requests | ||
| imagesize==1.4.1 | ||
| # via sphinx | ||
| iniconfig==2.0.0 | ||
| # via pytest | ||
| jinja2==3.1.3 | ||
| # via sphinx | ||
| markupsafe==2.1.3 | ||
| # via jinja2 | ||
| mypy==1.8.0 | ||
| # via -r typing.in | ||
| mypy-extensions==1.0.0 | ||
| # via mypy | ||
| nodeenv==1.8.0 | ||
| # via pre-commit | ||
| pip-compile-multi==2.6.3 | ||
| # via -r requirements/dev.in | ||
| packaging==23.2 | ||
| # via | ||
| # build | ||
| # pallets-sphinx-themes | ||
| # pyproject-api | ||
| # pytest | ||
| # sphinx | ||
| # tox | ||
| pallets-sphinx-themes==2.1.1 | ||
| # via -r docs.in | ||
| pip-tools==7.3.0 | ||
| # via pip-compile-multi | ||
| platformdirs==3.10.0 | ||
| # via -r dev.in | ||
| platformdirs==4.1.0 | ||
| # via | ||
| # tox | ||
| # virtualenv | ||
| pre-commit==3.3.3 | ||
| # via -r requirements/dev.in | ||
| pyproject-api==1.5.3 | ||
| pluggy==1.3.0 | ||
| # via | ||
| # pytest | ||
| # tox | ||
| pre-commit==3.6.0 | ||
| # via -r dev.in | ||
| pycparser==2.21 | ||
| # via cffi | ||
| pygments==2.17.2 | ||
| # via | ||
| # sphinx | ||
| # sphinx-tabs | ||
| pyproject-api==1.6.1 | ||
| # via tox | ||
| pyproject-hooks==1.0.0 | ||
| # via build | ||
| pytest==7.4.4 | ||
| # via -r tests.in | ||
| python-dotenv==1.0.0 | ||
| # via | ||
| # -r tests.in | ||
| # -r typing.in | ||
| pyyaml==6.0.1 | ||
| # via pre-commit | ||
| toposort==1.10 | ||
| # via pip-compile-multi | ||
| tox==4.9.0 | ||
| # via -r requirements/dev.in | ||
| virtualenv==20.24.3 | ||
| requests==2.31.0 | ||
| # via sphinx | ||
| snowballstemmer==2.2.0 | ||
| # via sphinx | ||
| sphinx==7.2.6 | ||
| # via | ||
| # -r docs.in | ||
| # pallets-sphinx-themes | ||
| # sphinx-issues | ||
| # sphinx-tabs | ||
| # sphinxcontrib-log-cabinet | ||
| sphinx-issues==3.0.1 | ||
| # via -r docs.in | ||
| sphinx-tabs==3.4.4 | ||
| # via -r docs.in | ||
| sphinxcontrib-applehelp==1.0.8 | ||
| # via sphinx | ||
| sphinxcontrib-devhelp==1.0.6 | ||
| # via sphinx | ||
| sphinxcontrib-htmlhelp==2.0.5 | ||
| # via sphinx | ||
| sphinxcontrib-jsmath==1.0.1 | ||
| # via sphinx | ||
| sphinxcontrib-log-cabinet==1.0.1 | ||
| # via -r docs.in | ||
| sphinxcontrib-qthelp==1.0.7 | ||
| # via sphinx | ||
| sphinxcontrib-serializinghtml==1.1.10 | ||
| # via sphinx | ||
| tox==4.12.0 | ||
| # via -r dev.in | ||
| types-contextvars==2.4.7.3 | ||
| # via -r typing.in | ||
| types-dataclasses==0.6.6 | ||
| # via -r typing.in | ||
| typing-extensions==4.9.0 | ||
| # via mypy | ||
| urllib3==2.1.0 | ||
| # via requests | ||
| virtualenv==20.25.0 | ||
| # via | ||
| # pre-commit | ||
| # tox | ||
| wheel==0.41.1 | ||
| wheel==0.42.0 | ||
| # via pip-tools | ||
@@ -62,0 +149,0 @@ |
@@ -1,5 +0,5 @@ | ||
| Pallets-Sphinx-Themes | ||
| Sphinx | ||
| pallets-sphinx-themes | ||
| sphinx | ||
| sphinx-issues | ||
| sphinxcontrib-log-cabinet | ||
| sphinx-tabs |
+24
-30
@@ -1,15 +0,14 @@ | ||
| # SHA1:34fd4ca6516e97c7348e6facdd9c4ebb68209d1c | ||
| # | ||
| # This file is autogenerated by pip-compile-multi | ||
| # To update, run: | ||
| # This file is autogenerated by pip-compile with Python 3.11 | ||
| # by the following command: | ||
| # | ||
| # pip-compile-multi | ||
| # pip-compile docs.in | ||
| # | ||
| alabaster==0.7.13 | ||
| alabaster==0.7.16 | ||
| # via sphinx | ||
| babel==2.12.1 | ||
| babel==2.14.0 | ||
| # via sphinx | ||
| certifi==2023.7.22 | ||
| certifi==2023.11.17 | ||
| # via requests | ||
| charset-normalizer==3.2.0 | ||
| charset-normalizer==3.3.2 | ||
| # via requests | ||
@@ -20,11 +19,11 @@ docutils==0.18.1 | ||
| # sphinx-tabs | ||
| idna==3.4 | ||
| idna==3.6 | ||
| # via requests | ||
| imagesize==1.4.1 | ||
| # via sphinx | ||
| jinja2==3.1.2 | ||
| jinja2==3.1.3 | ||
| # via sphinx | ||
| markupsafe==2.1.3 | ||
| # via jinja2 | ||
| packaging==23.1 | ||
| packaging==23.2 | ||
| # via | ||
@@ -34,4 +33,4 @@ # pallets-sphinx-themes | ||
| pallets-sphinx-themes==2.1.1 | ||
| # via -r requirements/docs.in | ||
| pygments==2.16.1 | ||
| # via -r docs.in | ||
| pygments==2.17.2 | ||
| # via | ||
@@ -44,23 +43,18 @@ # sphinx | ||
| # via sphinx | ||
| sphinx==7.1.2 | ||
| sphinx==7.2.6 | ||
| # via | ||
| # -r requirements/docs.in | ||
| # -r docs.in | ||
| # pallets-sphinx-themes | ||
| # sphinx-issues | ||
| # sphinx-tabs | ||
| # sphinxcontrib-applehelp | ||
| # sphinxcontrib-devhelp | ||
| # sphinxcontrib-htmlhelp | ||
| # sphinxcontrib-log-cabinet | ||
| # sphinxcontrib-qthelp | ||
| # sphinxcontrib-serializinghtml | ||
| sphinx-issues==3.0.1 | ||
| # via -r requirements/docs.in | ||
| sphinx-tabs==3.4.1 | ||
| # via -r requirements/docs.in | ||
| sphinxcontrib-applehelp==1.0.7 | ||
| # via -r docs.in | ||
| sphinx-tabs==3.4.4 | ||
| # via -r docs.in | ||
| sphinxcontrib-applehelp==1.0.8 | ||
| # via sphinx | ||
| sphinxcontrib-devhelp==1.0.5 | ||
| sphinxcontrib-devhelp==1.0.6 | ||
| # via sphinx | ||
| sphinxcontrib-htmlhelp==2.0.4 | ||
| sphinxcontrib-htmlhelp==2.0.5 | ||
| # via sphinx | ||
@@ -70,8 +64,8 @@ sphinxcontrib-jsmath==1.0.1 | ||
| sphinxcontrib-log-cabinet==1.0.1 | ||
| # via -r requirements/docs.in | ||
| sphinxcontrib-qthelp==1.0.6 | ||
| # via -r docs.in | ||
| sphinxcontrib-qthelp==1.0.7 | ||
| # via sphinx | ||
| sphinxcontrib-serializinghtml==1.1.8 | ||
| sphinxcontrib-serializinghtml==1.1.10 | ||
| # via sphinx | ||
| urllib3==2.0.4 | ||
| urllib3==2.1.0 | ||
| # via requests |
@@ -1,19 +0,18 @@ | ||
| # SHA1:42d37aff22e2f1fc447e20d483e13d6d4e066b10 | ||
| # | ||
| # This file is autogenerated by pip-compile-multi | ||
| # To update, run: | ||
| # This file is autogenerated by pip-compile with Python 3.11 | ||
| # by the following command: | ||
| # | ||
| # pip-compile-multi | ||
| # pip-compile tests.in | ||
| # | ||
| asgiref==3.7.2 | ||
| # via -r requirements/tests.in | ||
| # via -r tests.in | ||
| iniconfig==2.0.0 | ||
| # via pytest | ||
| packaging==23.1 | ||
| packaging==23.2 | ||
| # via pytest | ||
| pluggy==1.2.0 | ||
| pluggy==1.3.0 | ||
| # via pytest | ||
| pytest==7.4.0 | ||
| # via -r requirements/tests.in | ||
| pytest==7.4.4 | ||
| # via -r tests.in | ||
| python-dotenv==1.0.0 | ||
| # via -r requirements/tests.in | ||
| # via -r tests.in |
| mypy | ||
| types-contextvars | ||
| types-dataclasses | ||
| asgiref | ||
| cryptography | ||
| python-dotenv |
+15
-12
@@ -1,14 +0,15 @@ | ||
| # SHA1:6a354b832686fd3ec017455769a0270953a1e225 | ||
| # | ||
| # This file is autogenerated by pip-compile-multi | ||
| # To update, run: | ||
| # This file is autogenerated by pip-compile with Python 3.11 | ||
| # by the following command: | ||
| # | ||
| # pip-compile-multi | ||
| # pip-compile typing.in | ||
| # | ||
| cffi==1.15.1 | ||
| asgiref==3.7.2 | ||
| # via -r typing.in | ||
| cffi==1.16.0 | ||
| # via cryptography | ||
| cryptography==41.0.3 | ||
| # via -r requirements/typing.in | ||
| mypy==1.5.1 | ||
| # via -r requirements/typing.in | ||
| cryptography==41.0.7 | ||
| # via -r typing.in | ||
| mypy==1.8.0 | ||
| # via -r typing.in | ||
| mypy-extensions==1.0.0 | ||
@@ -18,7 +19,9 @@ # via mypy | ||
| # via cffi | ||
| python-dotenv==1.0.0 | ||
| # via -r typing.in | ||
| types-contextvars==2.4.7.3 | ||
| # via -r requirements/typing.in | ||
| # via -r typing.in | ||
| types-dataclasses==0.6.6 | ||
| # via -r requirements/typing.in | ||
| typing-extensions==4.7.1 | ||
| # via -r typing.in | ||
| typing-extensions==4.9.0 | ||
| # via mypy |
+32
-22
| from __future__ import annotations | ||
| import collections.abc as cabc | ||
| import os | ||
@@ -7,3 +8,2 @@ import sys | ||
| import weakref | ||
| from collections.abc import Iterator as _abc_Iterator | ||
| from datetime import timedelta | ||
@@ -58,2 +58,5 @@ from inspect import iscoroutinefunction | ||
| if t.TYPE_CHECKING: # pragma: no cover | ||
| from _typeshed.wsgi import StartResponse | ||
| from _typeshed.wsgi import WSGIEnvironment | ||
| from .testing import FlaskClient | ||
@@ -205,7 +208,7 @@ from .testing import FlaskCliRunner | ||
| #: for more information. | ||
| request_class = Request | ||
| request_class: type[Request] = Request | ||
| #: The class that is used for response objects. See | ||
| #: :class:`~flask.Response` for more information. | ||
| response_class = Response | ||
| response_class: type[Response] = Response | ||
@@ -222,7 +225,7 @@ #: the session interface to use. By default an instance of | ||
| static_url_path: str | None = None, | ||
| static_folder: str | os.PathLike | None = "static", | ||
| static_folder: str | os.PathLike[str] | None = "static", | ||
| static_host: str | None = None, | ||
| host_matching: bool = False, | ||
| subdomain_matching: bool = False, | ||
| template_folder: str | os.PathLike | None = "templates", | ||
| template_folder: str | os.PathLike[str] | None = "templates", | ||
| instance_path: str | None = None, | ||
@@ -289,3 +292,3 @@ instance_relative_config: bool = False, | ||
| return value | ||
| return value # type: ignore[no-any-return] | ||
@@ -455,3 +458,3 @@ def send_static_file(self, filename: str) -> Response: | ||
| ): | ||
| raise request.routing_exception # type: ignore | ||
| raise request.routing_exception # type: ignore[misc] | ||
@@ -462,3 +465,3 @@ from .debughelpers import FormDataRoutingRedirect | ||
| def update_template_context(self, context: dict) -> None: | ||
| def update_template_context(self, context: dict[str, t.Any]) -> None: | ||
| """Update the template context with some commonly used variables. | ||
@@ -491,3 +494,3 @@ This injects request, session, config and g into the template | ||
| def make_shell_context(self) -> dict: | ||
| def make_shell_context(self) -> dict[str, t.Any]: | ||
| """Returns the shell context for an interactive shell for this | ||
@@ -735,3 +738,3 @@ application. This runs all the registered shell context | ||
| return e | ||
| return self.ensure_sync(handler)(e) | ||
| return self.ensure_sync(handler)(e) # type: ignore[no-any-return] | ||
@@ -768,3 +771,3 @@ def handle_user_exception( | ||
| return self.ensure_sync(handler)(e) | ||
| return self.ensure_sync(handler)(e) # type: ignore[no-any-return] | ||
@@ -862,3 +865,3 @@ def handle_exception(self, e: Exception) -> Response: | ||
| view_args: dict[str, t.Any] = req.view_args # type: ignore[assignment] | ||
| return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) | ||
| return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return] | ||
@@ -927,3 +930,3 @@ def full_dispatch_request(self) -> Response: | ||
| def ensure_sync(self, func: t.Callable) -> t.Callable: | ||
| def ensure_sync(self, func: t.Callable[..., t.Any]) -> t.Callable[..., t.Any]: | ||
| """Ensure that the function is synchronous for WSGI workers. | ||
@@ -943,3 +946,3 @@ Plain ``def`` functions are returned as-is. ``async def`` | ||
| def async_to_sync( | ||
| self, func: t.Callable[..., t.Coroutine] | ||
| self, func: t.Callable[..., t.Coroutine[t.Any, t.Any, t.Any]] | ||
| ) -> t.Callable[..., t.Any]: | ||
@@ -1182,3 +1185,3 @@ """Return a sync function that will run the coroutine function. | ||
| if not isinstance(rv, self.response_class): | ||
| if isinstance(rv, (str, bytes, bytearray)) or isinstance(rv, _abc_Iterator): | ||
| if isinstance(rv, (str, bytes, bytearray)) or isinstance(rv, cabc.Iterator): | ||
| # let the response class set the status and headers instead of | ||
@@ -1200,3 +1203,4 @@ # waiting to do it manually, so that the class can handle any | ||
| rv = self.response_class.force_type( | ||
| rv, request.environ # type: ignore[arg-type] | ||
| rv, # type: ignore[arg-type] | ||
| request.environ, | ||
| ) | ||
@@ -1257,3 +1261,3 @@ except TypeError as e: | ||
| if rv is not None: | ||
| return rv | ||
| return rv # type: ignore[no-any-return] | ||
@@ -1291,3 +1295,4 @@ return None | ||
| def do_teardown_request( | ||
| self, exc: BaseException | None = _sentinel # type: ignore | ||
| self, | ||
| exc: BaseException | None = _sentinel, # type: ignore[assignment] | ||
| ) -> None: | ||
@@ -1325,3 +1330,4 @@ """Called after the request is dispatched and the response is | ||
| def do_teardown_appcontext( | ||
| self, exc: BaseException | None = _sentinel # type: ignore | ||
| self, | ||
| exc: BaseException | None = _sentinel, # type: ignore[assignment] | ||
| ) -> None: | ||
@@ -1371,3 +1377,3 @@ """Called right before the application context is popped. | ||
| def request_context(self, environ: dict) -> RequestContext: | ||
| def request_context(self, environ: WSGIEnvironment) -> RequestContext: | ||
| """Create a :class:`~flask.ctx.RequestContext` representing a | ||
@@ -1444,3 +1450,5 @@ WSGI environment. Use a ``with`` block to push the context, | ||
| def wsgi_app(self, environ: dict, start_response: t.Callable) -> t.Any: | ||
| def wsgi_app( | ||
| self, environ: WSGIEnvironment, start_response: StartResponse | ||
| ) -> cabc.Iterable[bytes]: | ||
| """The actual WSGI application. This is not implemented in | ||
@@ -1493,3 +1501,5 @@ :meth:`__call__` so that middlewares can be applied without | ||
| def __call__(self, environ: dict, start_response: t.Callable) -> t.Any: | ||
| def __call__( | ||
| self, environ: WSGIEnvironment, start_response: StartResponse | ||
| ) -> cabc.Iterable[bytes]: | ||
| """The WSGI server calls the Flask application object as the | ||
@@ -1496,0 +1506,0 @@ WSGI application. This calls :meth:`wsgi_app`, which can be |
@@ -42,3 +42,3 @@ from __future__ import annotations | ||
| return value | ||
| return value # type: ignore[no-any-return] | ||
@@ -45,0 +45,0 @@ def send_static_file(self, filename: str) -> Response: |
+91
-50
| from __future__ import annotations | ||
| import ast | ||
| import collections.abc as cabc | ||
| import importlib.metadata | ||
@@ -14,2 +15,3 @@ import inspect | ||
| from operator import itemgetter | ||
| from types import ModuleType | ||
@@ -27,2 +29,8 @@ import click | ||
| if t.TYPE_CHECKING: | ||
| import ssl | ||
| from _typeshed.wsgi import StartResponse | ||
| from _typeshed.wsgi import WSGIApplication | ||
| from _typeshed.wsgi import WSGIEnvironment | ||
| from .app import Flask | ||
@@ -35,3 +43,3 @@ | ||
| def find_best_app(module): | ||
| def find_best_app(module: ModuleType) -> Flask: | ||
| """Given a module instance this tries to find the best possible | ||
@@ -89,3 +97,3 @@ application in the module or raises an exception. | ||
| def _called_with_wrong_args(f): | ||
| def _called_with_wrong_args(f: t.Callable[..., Flask]) -> bool: | ||
| """Check whether calling a function raised a ``TypeError`` because | ||
@@ -116,3 +124,3 @@ the call failed or because something in the factory raised the | ||
| def find_app_by_string(module, app_name): | ||
| def find_app_by_string(module: ModuleType, app_name: str) -> Flask: | ||
| """Check if the given string is a variable name or a function. Call | ||
@@ -148,3 +156,7 @@ a function to get the app instance, or return the variable directly. | ||
| args = [ast.literal_eval(arg) for arg in expr.args] | ||
| kwargs = {kw.arg: ast.literal_eval(kw.value) for kw in expr.keywords} | ||
| kwargs = { | ||
| kw.arg: ast.literal_eval(kw.value) | ||
| for kw in expr.keywords | ||
| if kw.arg is not None | ||
| } | ||
| except ValueError: | ||
@@ -194,3 +206,3 @@ # literal_eval gives cryptic error messages, show a generic | ||
| def prepare_import(path): | ||
| def prepare_import(path: str) -> str: | ||
| """Given a filename this will try to calculate the python path, add it | ||
@@ -224,3 +236,19 @@ to the search path and return the actual module name that is expected. | ||
| def locate_app(module_name, app_name, raise_if_not_found=True): | ||
| @t.overload | ||
| def locate_app( | ||
| module_name: str, app_name: str | None, raise_if_not_found: t.Literal[True] = True | ||
| ) -> Flask: | ||
| ... | ||
| @t.overload | ||
| def locate_app( | ||
| module_name: str, app_name: str | None, raise_if_not_found: t.Literal[False] = ... | ||
| ) -> Flask | None: | ||
| ... | ||
| def locate_app( | ||
| module_name: str, app_name: str | None, raise_if_not_found: bool = True | ||
| ) -> Flask | None: | ||
| try: | ||
@@ -231,3 +259,3 @@ __import__(module_name) | ||
| # Determine this by checking whether the trace has a depth > 1. | ||
| if sys.exc_info()[2].tb_next: | ||
| if sys.exc_info()[2].tb_next: # type: ignore[union-attr] | ||
| raise NoAppException( | ||
@@ -240,3 +268,3 @@ f"While importing {module_name!r}, an ImportError was" | ||
| else: | ||
| return | ||
| return None | ||
@@ -251,3 +279,3 @@ module = sys.modules[module_name] | ||
| def get_version(ctx, param, value): | ||
| def get_version(ctx: click.Context, param: click.Parameter, value: t.Any) -> None: | ||
| if not value or ctx.resilient_parsing: | ||
@@ -313,3 +341,3 @@ return | ||
| if self.create_app is not None: | ||
| app = self.create_app() | ||
| app: Flask | None = self.create_app() | ||
| else: | ||
@@ -327,6 +355,6 @@ if self.app_import_path: | ||
| if app: | ||
| if app is not None: | ||
| break | ||
| if not app: | ||
| if app is None: | ||
| raise NoAppException( | ||
@@ -350,4 +378,6 @@ "Could not locate a Flask application. Use the" | ||
| F = t.TypeVar("F", bound=t.Callable[..., t.Any]) | ||
| def with_appcontext(f): | ||
| def with_appcontext(f: F) -> F: | ||
| """Wraps a callback so that it's guaranteed to be executed with the | ||
@@ -367,10 +397,10 @@ script's application context. | ||
| @click.pass_context | ||
| def decorator(__ctx, *args, **kwargs): | ||
| def decorator(ctx: click.Context, /, *args: t.Any, **kwargs: t.Any) -> t.Any: | ||
| if not current_app: | ||
| app = __ctx.ensure_object(ScriptInfo).load_app() | ||
| __ctx.with_resource(app.app_context()) | ||
| app = ctx.ensure_object(ScriptInfo).load_app() | ||
| ctx.with_resource(app.app_context()) | ||
| return __ctx.invoke(f, *args, **kwargs) | ||
| return ctx.invoke(f, *args, **kwargs) | ||
| return update_wrapper(decorator, f) | ||
| return update_wrapper(decorator, f) # type: ignore[return-value] | ||
@@ -386,3 +416,5 @@ | ||
| def command(self, *args, **kwargs): | ||
| def command( # type: ignore[override] | ||
| self, *args: t.Any, **kwargs: t.Any | ||
| ) -> t.Callable[[t.Callable[..., t.Any]], click.Command]: | ||
| """This works exactly like the method of the same name on a regular | ||
@@ -394,10 +426,12 @@ :class:`click.Group` but it wraps callbacks in :func:`with_appcontext` | ||
| def decorator(f): | ||
| def decorator(f: t.Callable[..., t.Any]) -> click.Command: | ||
| if wrap_for_ctx: | ||
| f = with_appcontext(f) | ||
| return click.Group.command(self, *args, **kwargs)(f) | ||
| return super(AppGroup, self).command(*args, **kwargs)(f) # type: ignore[no-any-return] | ||
| return decorator | ||
| def group(self, *args, **kwargs): | ||
| def group( # type: ignore[override] | ||
| self, *args: t.Any, **kwargs: t.Any | ||
| ) -> t.Callable[[t.Callable[..., t.Any]], click.Group]: | ||
| """This works exactly like the method of the same name on a regular | ||
@@ -408,3 +442,3 @@ :class:`click.Group` but it defaults the group class to | ||
| kwargs.setdefault("cls", AppGroup) | ||
| return click.Group.group(self, *args, **kwargs) | ||
| return super().group(*args, **kwargs) # type: ignore[no-any-return] | ||
@@ -566,3 +600,3 @@ | ||
| def _load_plugin_commands(self): | ||
| def _load_plugin_commands(self) -> None: | ||
| if self._loaded_plugin_commands: | ||
@@ -584,3 +618,3 @@ return | ||
| def get_command(self, ctx, name): | ||
| def get_command(self, ctx: click.Context, name: str) -> click.Command | None: | ||
| self._load_plugin_commands() | ||
@@ -607,3 +641,3 @@ # Look up built-in and plugin commands, which should be | ||
| # and command callbacks without needing @with_appcontext. | ||
| if not current_app or current_app._get_current_object() is not app: | ||
| if not current_app or current_app._get_current_object() is not app: # type: ignore[attr-defined] | ||
| ctx.with_resource(app.app_context()) | ||
@@ -613,3 +647,3 @@ | ||
| def list_commands(self, ctx): | ||
| def list_commands(self, ctx: click.Context) -> list[str]: | ||
| self._load_plugin_commands() | ||
@@ -670,3 +704,3 @@ # Start with the built-in and plugin commands. | ||
| def _path_is_ancestor(path, other): | ||
| def _path_is_ancestor(path: str, other: str) -> bool: | ||
| """Take ``other`` and remove the length of ``path`` from it. Then join it | ||
@@ -678,3 +712,3 @@ to ``path``. If it is the original value, ``path`` is an ancestor of | ||
| def load_dotenv(path: str | os.PathLike | None = None) -> bool: | ||
| def load_dotenv(path: str | os.PathLike[str] | None = None) -> bool: | ||
| """Load "dotenv" files in order of precedence to set environment variables. | ||
@@ -740,3 +774,3 @@ | ||
| def show_server_banner(debug, app_import_path): | ||
| def show_server_banner(debug: bool, app_import_path: str | None) -> None: | ||
| """Show extra startup messages the first time the server is run, | ||
@@ -763,6 +797,8 @@ ignoring the reloader. | ||
| def __init__(self): | ||
| def __init__(self) -> None: | ||
| self.path_type = click.Path(exists=True, dir_okay=False, resolve_path=True) | ||
| def convert(self, value, param, ctx): | ||
| def convert( | ||
| self, value: t.Any, param: click.Parameter | None, ctx: click.Context | None | ||
| ) -> t.Any: | ||
| try: | ||
@@ -802,3 +838,3 @@ import ssl | ||
| def _validate_key(ctx, param, value): | ||
| def _validate_key(ctx: click.Context, param: click.Parameter, value: t.Any) -> t.Any: | ||
| """The ``--key`` option must be specified when ``--cert`` is a file. | ||
@@ -825,3 +861,5 @@ Modifies the ``cert`` param to be a ``(cert, key)`` pair if needed. | ||
| raise click.BadParameter( | ||
| 'When "--cert" is an SSLContext object, "--key is not used.', ctx, param | ||
| 'When "--cert" is an SSLContext object, "--key" is not used.', | ||
| ctx, | ||
| param, | ||
| ) | ||
@@ -847,6 +885,7 @@ | ||
| def convert(self, value, param, ctx): | ||
| def convert( | ||
| self, value: t.Any, param: click.Parameter | None, ctx: click.Context | None | ||
| ) -> t.Any: | ||
| items = self.split_envvar_value(value) | ||
| super_convert = super().convert | ||
| return [super_convert(item, param, ctx) for item in items] | ||
| return [super().convert(item, param, ctx) for item in items] | ||
@@ -908,12 +947,12 @@ | ||
| def run_command( | ||
| info, | ||
| host, | ||
| port, | ||
| reload, | ||
| debugger, | ||
| with_threads, | ||
| cert, | ||
| extra_files, | ||
| exclude_patterns, | ||
| ): | ||
| info: ScriptInfo, | ||
| host: str, | ||
| port: int, | ||
| reload: bool, | ||
| debugger: bool, | ||
| with_threads: bool, | ||
| cert: ssl.SSLContext | tuple[str, str | None] | t.Literal["adhoc"] | None, | ||
| extra_files: list[str] | None, | ||
| exclude_patterns: list[str] | None, | ||
| ) -> None: | ||
| """Run a local development server. | ||
@@ -928,3 +967,3 @@ | ||
| try: | ||
| app = info.load_app() | ||
| app: WSGIApplication = info.load_app() | ||
| except Exception as e: | ||
@@ -937,3 +976,5 @@ if is_running_from_reloader(): | ||
| def app(environ, start_response): | ||
| def app( | ||
| environ: WSGIEnvironment, start_response: StartResponse | ||
| ) -> cabc.Iterable[bytes]: | ||
| raise err from None | ||
@@ -989,3 +1030,3 @@ | ||
| ) | ||
| ctx: dict = {} | ||
| ctx: dict[str, t.Any] = {} | ||
@@ -992,0 +1033,0 @@ # Support the regular Python interpreter startup script if someone |
+35
-10
@@ -11,23 +11,44 @@ from __future__ import annotations | ||
| if t.TYPE_CHECKING: | ||
| import typing_extensions as te | ||
| class ConfigAttribute: | ||
| from .sansio.app import App | ||
| T = t.TypeVar("T") | ||
| class ConfigAttribute(t.Generic[T]): | ||
| """Makes an attribute forward to the config""" | ||
| def __init__(self, name: str, get_converter: t.Callable | None = None) -> None: | ||
| def __init__( | ||
| self, name: str, get_converter: t.Callable[[t.Any], T] | None = None | ||
| ) -> None: | ||
| self.__name__ = name | ||
| self.get_converter = get_converter | ||
| def __get__(self, obj: t.Any, owner: t.Any = None) -> t.Any: | ||
| @t.overload | ||
| def __get__(self, obj: None, owner: None) -> te.Self: | ||
| ... | ||
| @t.overload | ||
| def __get__(self, obj: App, owner: type[App]) -> T: | ||
| ... | ||
| def __get__(self, obj: App | None, owner: type[App] | None = None) -> T | te.Self: | ||
| if obj is None: | ||
| return self | ||
| rv = obj.config[self.__name__] | ||
| if self.get_converter is not None: | ||
| rv = self.get_converter(rv) | ||
| return rv | ||
| def __set__(self, obj: t.Any, value: t.Any) -> None: | ||
| return rv # type: ignore[no-any-return] | ||
| def __set__(self, obj: App, value: t.Any) -> None: | ||
| obj.config[self.__name__] = value | ||
| class Config(dict): | ||
| class Config(dict): # type: ignore[type-arg] | ||
| """Works exactly like a dict but provides ways to fill it from files | ||
@@ -77,3 +98,5 @@ or special dictionaries. There are two common patterns to populate the | ||
| def __init__( | ||
| self, root_path: str | os.PathLike, defaults: dict | None = None | ||
| self, | ||
| root_path: str | os.PathLike[str], | ||
| defaults: dict[str, t.Any] | None = None, | ||
| ) -> None: | ||
@@ -171,3 +194,5 @@ super().__init__(defaults or {}) | ||
| def from_pyfile(self, filename: str | os.PathLike, silent: bool = False) -> bool: | ||
| def from_pyfile( | ||
| self, filename: str | os.PathLike[str], silent: bool = False | ||
| ) -> bool: | ||
| """Updates the values in the config from a Python file. This function | ||
@@ -241,4 +266,4 @@ behaves as if the file was imported as module with the | ||
| self, | ||
| filename: str | os.PathLike, | ||
| load: t.Callable[[t.IO[t.Any]], t.Mapping], | ||
| filename: str | os.PathLike[str], | ||
| load: t.Callable[[t.IO[t.Any]], t.Mapping[str, t.Any]], | ||
| silent: bool = False, | ||
@@ -245,0 +270,0 @@ text: bool = True, |
+19
-10
@@ -18,2 +18,4 @@ from __future__ import annotations | ||
| if t.TYPE_CHECKING: # pragma: no cover | ||
| from _typeshed.wsgi import WSGIEnvironment | ||
| from .app import Flask | ||
@@ -116,3 +118,5 @@ from .sessions import SessionMixin | ||
| def after_this_request(f: ft.AfterRequestCallable) -> ft.AfterRequestCallable: | ||
| def after_this_request( | ||
| f: ft.AfterRequestCallable[t.Any], | ||
| ) -> ft.AfterRequestCallable[t.Any]: | ||
| """Executes a function after this request. This is useful to modify | ||
@@ -150,3 +154,6 @@ response objects. The function is passed the response object and has | ||
| def copy_current_request_context(f: t.Callable) -> t.Callable: | ||
| F = t.TypeVar("F", bound=t.Callable[..., t.Any]) | ||
| def copy_current_request_context(f: F) -> F: | ||
| """A helper function that decorates a function to retain the current | ||
@@ -185,7 +192,7 @@ request context. This is useful when working with greenlets. The moment | ||
| def wrapper(*args, **kwargs): | ||
| with ctx: | ||
| return ctx.app.ensure_sync(f)(*args, **kwargs) | ||
| def wrapper(*args: t.Any, **kwargs: t.Any) -> t.Any: | ||
| with ctx: # type: ignore[union-attr] | ||
| return ctx.app.ensure_sync(f)(*args, **kwargs) # type: ignore[union-attr] | ||
| return update_wrapper(wrapper, f) | ||
| return update_wrapper(wrapper, f) # type: ignore[return-value] | ||
@@ -246,3 +253,3 @@ | ||
| self.g: _AppCtxGlobals = app.app_ctx_globals_class() | ||
| self._cv_tokens: list[contextvars.Token] = [] | ||
| self._cv_tokens: list[contextvars.Token[AppContext]] = [] | ||
@@ -310,3 +317,3 @@ def push(self) -> None: | ||
| app: Flask, | ||
| environ: dict, | ||
| environ: WSGIEnvironment, | ||
| request: Request | None = None, | ||
@@ -330,5 +337,7 @@ session: SessionMixin | None = None, | ||
| # functions. | ||
| self._after_request_functions: list[ft.AfterRequestCallable] = [] | ||
| self._after_request_functions: list[ft.AfterRequestCallable[t.Any]] = [] | ||
| self._cv_tokens: list[tuple[contextvars.Token, AppContext | None]] = [] | ||
| self._cv_tokens: list[ | ||
| tuple[contextvars.Token[RequestContext], AppContext | None] | ||
| ] = [] | ||
@@ -335,0 +344,0 @@ def copy(self) -> RequestContext: |
@@ -5,2 +5,5 @@ from __future__ import annotations | ||
| from jinja2.loaders import BaseLoader | ||
| from werkzeug.routing import RequestRedirect | ||
| from .blueprints import Blueprint | ||
@@ -10,3 +13,7 @@ from .globals import request_ctx | ||
| if t.TYPE_CHECKING: | ||
| from .sansio.scaffold import Scaffold | ||
| from .wrappers import Request | ||
| class UnexpectedUnicodeError(AssertionError, UnicodeError): | ||
@@ -23,3 +30,3 @@ """Raised in places where we want some better error reporting for | ||
| def __init__(self, request, key): | ||
| def __init__(self, request: Request, key: str) -> None: | ||
| form_matches = request.form.getlist(key) | ||
@@ -42,3 +49,3 @@ buf = [ | ||
| def __str__(self): | ||
| def __str__(self) -> str: | ||
| return self.msg | ||
@@ -54,4 +61,5 @@ | ||
| def __init__(self, request): | ||
| def __init__(self, request: Request) -> None: | ||
| exc = request.routing_exception | ||
| assert isinstance(exc, RequestRedirect) | ||
| buf = [ | ||
@@ -78,3 +86,3 @@ f"A request was sent to '{request.url}', but routing issued" | ||
| def attach_enctype_error_multidict(request): | ||
| def attach_enctype_error_multidict(request: Request) -> None: | ||
| """Patch ``request.files.__getitem__`` to raise a descriptive error | ||
@@ -88,4 +96,4 @@ about ``enctype=multipart/form-data``. | ||
| class newcls(oldcls): | ||
| def __getitem__(self, key): | ||
| class newcls(oldcls): # type: ignore[valid-type, misc] | ||
| def __getitem__(self, key: str) -> t.Any: | ||
| try: | ||
@@ -106,3 +114,3 @@ return super().__getitem__(key) | ||
| def _dump_loader_info(loader) -> t.Generator: | ||
| def _dump_loader_info(loader: BaseLoader) -> t.Iterator[str]: | ||
| yield f"class: {type(loader).__module__}.{type(loader).__name__}" | ||
@@ -124,3 +132,13 @@ for key, value in sorted(loader.__dict__.items()): | ||
| def explain_template_loading_attempts(app: App, template, attempts) -> None: | ||
| def explain_template_loading_attempts( | ||
| app: App, | ||
| template: str, | ||
| attempts: list[ | ||
| tuple[ | ||
| BaseLoader, | ||
| Scaffold, | ||
| tuple[str, str | None, t.Callable[[], bool] | None] | None, | ||
| ] | ||
| ], | ||
| ) -> None: | ||
| """This should help developers understand what failed""" | ||
@@ -127,0 +145,0 @@ info = [f"Locating template {template!r}:"] |
+12
-14
@@ -14,2 +14,3 @@ from __future__ import annotations | ||
| from werkzeug.utils import redirect as _wz_redirect | ||
| from werkzeug.wrappers import Response as BaseResponse | ||
@@ -24,3 +25,2 @@ from .globals import _cv_request | ||
| if t.TYPE_CHECKING: # pragma: no cover | ||
| from werkzeug.wrappers import Response as BaseResponse | ||
| from .wrappers import Response | ||
@@ -53,5 +53,3 @@ | ||
| def stream_with_context( | ||
| generator_or_function: ( | ||
| t.Iterator[t.AnyStr] | t.Callable[..., t.Iterator[t.AnyStr]] | ||
| ) | ||
| generator_or_function: t.Iterator[t.AnyStr] | t.Callable[..., t.Iterator[t.AnyStr]], | ||
| ) -> t.Iterator[t.AnyStr]: | ||
@@ -92,12 +90,12 @@ """Request contexts disappear when the response is started on the server. | ||
| try: | ||
| gen = iter(generator_or_function) # type: ignore | ||
| gen = iter(generator_or_function) # type: ignore[arg-type] | ||
| except TypeError: | ||
| def decorator(*args: t.Any, **kwargs: t.Any) -> t.Any: | ||
| gen = generator_or_function(*args, **kwargs) # type: ignore | ||
| gen = generator_or_function(*args, **kwargs) # type: ignore[operator] | ||
| return stream_with_context(gen) | ||
| return update_wrapper(decorator, generator_or_function) # type: ignore | ||
| return update_wrapper(decorator, generator_or_function) # type: ignore[arg-type] | ||
| def generator() -> t.Generator: | ||
| def generator() -> t.Iterator[t.AnyStr | None]: | ||
| ctx = _cv_request.get(None) | ||
@@ -130,3 +128,3 @@ if ctx is None: | ||
| next(wrapped_g) | ||
| return wrapped_g | ||
| return wrapped_g # type: ignore[return-value] | ||
@@ -180,3 +178,3 @@ | ||
| args = args[0] | ||
| return current_app.make_response(args) # type: ignore | ||
| return current_app.make_response(args) | ||
@@ -397,3 +395,3 @@ | ||
| def send_file( | ||
| path_or_file: os.PathLike | str | t.BinaryIO, | ||
| path_or_file: os.PathLike[t.AnyStr] | str | t.BinaryIO, | ||
| mimetype: str | None = None, | ||
@@ -524,4 +522,4 @@ as_attachment: bool = False, | ||
| def send_from_directory( | ||
| directory: os.PathLike | str, | ||
| path: os.PathLike | str, | ||
| directory: os.PathLike[str] | str, | ||
| path: os.PathLike[str] | str, | ||
| **kwargs: t.Any, | ||
@@ -621,3 +619,3 @@ ) -> Response: | ||
| # filepath is import_name.py for a module, or __init__.py for a package. | ||
| return os.path.dirname(os.path.abspath(filepath)) | ||
| return os.path.dirname(os.path.abspath(filepath)) # type: ignore[no-any-return] | ||
@@ -624,0 +622,0 @@ |
@@ -170,2 +170,2 @@ from __future__ import annotations | ||
| """ | ||
| return current_app.json.response(*args, **kwargs) | ||
| return current_app.json.response(*args, **kwargs) # type: ignore[return-value] |
@@ -14,4 +14,5 @@ from __future__ import annotations | ||
| if t.TYPE_CHECKING: # pragma: no cover | ||
| from werkzeug.sansio.response import Response | ||
| from ..sansio.app import App | ||
| from ..wrappers import Response | ||
@@ -39,3 +40,3 @@ | ||
| def __init__(self, app: App) -> None: | ||
| self._app = weakref.proxy(app) | ||
| self._app: App = weakref.proxy(app) | ||
@@ -139,5 +140,3 @@ def dumps(self, obj: t.Any, **kwargs: t.Any) -> str: | ||
| default: t.Callable[[t.Any], t.Any] = staticmethod( | ||
| _default | ||
| ) # type: ignore[assignment] | ||
| default: t.Callable[[t.Any], t.Any] = staticmethod(_default) # type: ignore[assignment] | ||
| """Apply this function to any object that :meth:`json.dumps` does | ||
@@ -144,0 +143,0 @@ not know how to serialize. It should return a valid JSON type or |
@@ -64,5 +64,5 @@ """ | ||
| #: The tag to mark the serialized object with. If ``None``, this tag is | ||
| #: The tag to mark the serialized object with. If empty, this tag is | ||
| #: only used as an intermediate step during tagging. | ||
| key: str | None = None | ||
| key: str = "" | ||
@@ -87,3 +87,3 @@ def __init__(self, serializer: TaggedJSONSerializer) -> None: | ||
| def tag(self, value: t.Any) -> t.Any: | ||
| def tag(self, value: t.Any) -> dict[str, t.Any]: | ||
| """Convert the value to a valid JSON type and add the tag structure | ||
@@ -279,3 +279,3 @@ around it.""" | ||
| if key is not None: | ||
| if key: | ||
| if not force and key in self.tags: | ||
@@ -291,3 +291,3 @@ raise KeyError(f"Tag '{key}' is already registered.") | ||
| def tag(self, value: t.Any) -> dict[str, t.Any]: | ||
| def tag(self, value: t.Any) -> t.Any: | ||
| """Convert a value to a tagged representation if necessary.""" | ||
@@ -312,2 +312,14 @@ for tag in self.order: | ||
| def _untag_scan(self, value: t.Any) -> t.Any: | ||
| if isinstance(value, dict): | ||
| # untag each item recursively | ||
| value = {k: self._untag_scan(v) for k, v in value.items()} | ||
| # untag the dict itself | ||
| value = self.untag(value) | ||
| elif isinstance(value, list): | ||
| # untag each item recursively | ||
| value = [self._untag_scan(item) for item in value] | ||
| return value | ||
| def dumps(self, value: t.Any) -> str: | ||
@@ -319,2 +331,2 @@ """Tag the value and dump it to a compact JSON string.""" | ||
| """Load data from a JSON string and deserialized any tagged objects.""" | ||
| return loads(value, object_hook=self.untag) | ||
| return self._untag_scan(loads(value)) |
@@ -25,5 +25,8 @@ from __future__ import annotations | ||
| """ | ||
| return request.environ["wsgi.errors"] if request else sys.stderr | ||
| if request: | ||
| return request.environ["wsgi.errors"] # type: ignore[no-any-return] | ||
| return sys.stderr | ||
| def has_level_handler(logger: logging.Logger) -> bool: | ||
@@ -30,0 +33,0 @@ """Check if there is a handler in the logging chain that will handle the |
+21
-17
@@ -38,5 +38,6 @@ from __future__ import annotations | ||
| from werkzeug.wrappers import Response as BaseResponse | ||
| from .blueprints import Blueprint | ||
| from ..testing import FlaskClient | ||
| from ..testing import FlaskCliRunner | ||
| from .blueprints import Blueprint | ||
@@ -208,3 +209,3 @@ T_shell_context_processor = t.TypeVar( | ||
| #: ``TESTING`` configuration key. Defaults to ``False``. | ||
| testing = ConfigAttribute("TESTING") | ||
| testing = ConfigAttribute[bool]("TESTING") | ||
@@ -217,3 +218,3 @@ #: If a secret key is set, cryptographic components can use this to | ||
| #: :data:`SECRET_KEY` configuration key. Defaults to ``None``. | ||
| secret_key = ConfigAttribute("SECRET_KEY") | ||
| secret_key = ConfigAttribute[t.Union[str, bytes, None]]("SECRET_KEY") | ||
@@ -227,4 +228,5 @@ #: A :class:`~datetime.timedelta` which is used to set the expiration | ||
| #: ``timedelta(days=31)`` | ||
| permanent_session_lifetime = ConfigAttribute( | ||
| "PERMANENT_SESSION_LIFETIME", get_converter=_make_timedelta | ||
| permanent_session_lifetime = ConfigAttribute[timedelta]( | ||
| "PERMANENT_SESSION_LIFETIME", | ||
| get_converter=_make_timedelta, # type: ignore[arg-type] | ||
| ) | ||
@@ -253,3 +255,3 @@ | ||
| #: | ||
| jinja_options: dict = {} | ||
| jinja_options: dict[str, t.Any] = {} | ||
@@ -282,3 +284,3 @@ #: The rule object to use for URL rules created. This is used by | ||
| default_config: dict | ||
| default_config: dict[str, t.Any] | ||
| response_class: type[Response] | ||
@@ -290,7 +292,7 @@ | ||
| static_url_path: str | None = None, | ||
| static_folder: str | os.PathLike | None = "static", | ||
| static_folder: str | os.PathLike[str] | None = "static", | ||
| static_host: str | None = None, | ||
| host_matching: bool = False, | ||
| subdomain_matching: bool = False, | ||
| template_folder: str | os.PathLike | None = "templates", | ||
| template_folder: str | os.PathLike[str] | None = "templates", | ||
| instance_path: str | None = None, | ||
@@ -393,3 +395,3 @@ instance_relative_config: bool = False, | ||
| #: .. versionadded:: 0.7 | ||
| self.extensions: dict = {} | ||
| self.extensions: dict[str, t.Any] = {} | ||
@@ -446,3 +448,3 @@ #: The :class:`~werkzeug.routing.Map` for this instance. You can use | ||
| if self.import_name == "__main__": | ||
| fn = getattr(sys.modules["__main__"], "__file__", None) | ||
| fn: str | None = getattr(sys.modules["__main__"], "__file__", None) | ||
| if fn is None: | ||
@@ -571,3 +573,3 @@ return "__main__" | ||
| """ | ||
| return self.config["DEBUG"] | ||
| return self.config["DEBUG"] # type: ignore[no-any-return] | ||
@@ -662,6 +664,6 @@ @debug.setter | ||
| rule = self.url_rule_class(rule, methods=methods, **options) | ||
| rule.provide_automatic_options = provide_automatic_options # type: ignore | ||
| rule_obj = self.url_rule_class(rule, methods=methods, **options) | ||
| rule_obj.provide_automatic_options = provide_automatic_options # type: ignore[attr-defined] | ||
| self.url_map.add(rule) | ||
| self.url_map.add(rule_obj) | ||
| if view_func is not None: | ||
@@ -919,6 +921,8 @@ old_func = self.view_functions.get(endpoint) | ||
| return _wz_redirect( | ||
| location, code=code, Response=self.response_class # type: ignore[arg-type] | ||
| location, | ||
| code=code, | ||
| Response=self.response_class, # type: ignore[arg-type] | ||
| ) | ||
| def inject_url_defaults(self, endpoint: str, values: dict) -> None: | ||
| def inject_url_defaults(self, endpoint: str, values: dict[str, t.Any]) -> None: | ||
| """Injects the URL defaults for the given endpoint directly into | ||
@@ -925,0 +929,0 @@ the values dictionary passed. This is used internally and |
@@ -17,4 +17,4 @@ from __future__ import annotations | ||
| DeferredSetupFunction = t.Callable[["BlueprintSetupState"], t.Callable] | ||
| T_after_request = t.TypeVar("T_after_request", bound=ft.AfterRequestCallable) | ||
| DeferredSetupFunction = t.Callable[["BlueprintSetupState"], None] | ||
| T_after_request = t.TypeVar("T_after_request", bound=ft.AfterRequestCallable[t.Any]) | ||
| T_before_request = t.TypeVar("T_before_request", bound=ft.BeforeRequestCallable) | ||
@@ -92,3 +92,3 @@ T_error_handler = t.TypeVar("T_error_handler", bound=ft.ErrorHandlerCallable) | ||
| endpoint: str | None = None, | ||
| view_func: t.Callable | None = None, | ||
| view_func: ft.RouteCallable | None = None, | ||
| **options: t.Any, | ||
@@ -180,10 +180,10 @@ ) -> None: | ||
| import_name: str, | ||
| static_folder: str | os.PathLike | None = None, | ||
| static_folder: str | os.PathLike[str] | None = None, | ||
| static_url_path: str | None = None, | ||
| template_folder: str | os.PathLike | None = None, | ||
| template_folder: str | os.PathLike[str] | None = None, | ||
| url_prefix: str | None = None, | ||
| subdomain: str | None = None, | ||
| url_defaults: dict | None = None, | ||
| url_defaults: dict[str, t.Any] | None = None, | ||
| root_path: str | None = None, | ||
| cli_group: str | None = _sentinel, # type: ignore | ||
| cli_group: str | None = _sentinel, # type: ignore[assignment] | ||
| ): | ||
@@ -214,3 +214,3 @@ super().__init__( | ||
| self.cli_group = cli_group | ||
| self._blueprints: list[tuple[Blueprint, dict]] = [] | ||
| self._blueprints: list[tuple[Blueprint, dict[str, t.Any]]] = [] | ||
@@ -228,3 +228,3 @@ def _check_setup_finished(self, f_name: str) -> None: | ||
| @setupmethod | ||
| def record(self, func: t.Callable) -> None: | ||
| def record(self, func: DeferredSetupFunction) -> None: | ||
| """Registers a function that is called when the blueprint is | ||
@@ -238,3 +238,3 @@ registered on the application. This function is called with the | ||
| @setupmethod | ||
| def record_once(self, func: t.Callable) -> None: | ||
| def record_once(self, func: DeferredSetupFunction) -> None: | ||
| """Works like :meth:`record` but wraps the function in another | ||
@@ -253,3 +253,3 @@ function that will ensure the function is only called once. If the | ||
| def make_setup_state( | ||
| self, app: App, options: dict, first_registration: bool = False | ||
| self, app: App, options: dict[str, t.Any], first_registration: bool = False | ||
| ) -> BlueprintSetupState: | ||
@@ -280,3 +280,3 @@ """Creates an instance of :meth:`~flask.blueprints.BlueprintSetupState` | ||
| def register(self, app: App, options: dict) -> None: | ||
| def register(self, app: App, options: dict[str, t.Any]) -> None: | ||
| """Called by :meth:`Flask.register_blueprint` to register all | ||
@@ -388,3 +388,6 @@ views and callbacks registered on the blueprint with the | ||
| def _merge_blueprint_funcs(self, app: App, name: str) -> None: | ||
| def extend(bp_dict, parent_dict): | ||
| def extend( | ||
| bp_dict: dict[ft.AppOrBlueprintKey, list[t.Any]], | ||
| parent_dict: dict[ft.AppOrBlueprintKey, list[t.Any]], | ||
| ) -> None: | ||
| for key, values in bp_dict.items(): | ||
@@ -610,3 +613,6 @@ key = name if key is None else f"{name}.{key}" | ||
| def decorator(f: T_error_handler) -> T_error_handler: | ||
| self.record_once(lambda s: s.app.errorhandler(code)(f)) | ||
| def from_blueprint(state: BlueprintSetupState) -> None: | ||
| state.app.errorhandler(code)(f) | ||
| self.record_once(from_blueprint) | ||
| return f | ||
@@ -613,0 +619,0 @@ |
@@ -11,2 +11,3 @@ from __future__ import annotations | ||
| import click | ||
| from jinja2 import FileSystemLoader | ||
@@ -26,3 +27,3 @@ from werkzeug.exceptions import default_exceptions | ||
| F = t.TypeVar("F", bound=t.Callable[..., t.Any]) | ||
| T_after_request = t.TypeVar("T_after_request", bound=ft.AfterRequestCallable) | ||
| T_after_request = t.TypeVar("T_after_request", bound=ft.AfterRequestCallable[t.Any]) | ||
| T_before_request = t.TypeVar("T_before_request", bound=ft.BeforeRequestCallable) | ||
@@ -44,3 +45,3 @@ T_error_handler = t.TypeVar("T_error_handler", bound=ft.ErrorHandlerCallable) | ||
| def wrapper_func(self, *args: t.Any, **kwargs: t.Any) -> t.Any: | ||
| def wrapper_func(self: Scaffold, *args: t.Any, **kwargs: t.Any) -> t.Any: | ||
| self._check_setup_finished(f_name) | ||
@@ -77,5 +78,5 @@ return f(self, *args, **kwargs) | ||
| import_name: str, | ||
| static_folder: str | os.PathLike | None = None, | ||
| static_folder: str | os.PathLike[str] | None = None, | ||
| static_url_path: str | None = None, | ||
| template_folder: str | os.PathLike | None = None, | ||
| template_folder: str | os.PathLike[str] | None = None, | ||
| root_path: str | None = None, | ||
@@ -106,3 +107,3 @@ ): | ||
| #: been registered. | ||
| self.cli = AppGroup() | ||
| self.cli: click.Group = AppGroup() | ||
@@ -115,3 +116,3 @@ #: A dictionary mapping endpoint names to view functions. | ||
| #: directly and its format may change at any time. | ||
| self.view_functions: dict[str, t.Callable] = {} | ||
| self.view_functions: dict[str, ft.RouteCallable] = {} | ||
@@ -161,3 +162,3 @@ #: A data structure of registered error handlers, in the format | ||
| self.after_request_funcs: dict[ | ||
| ft.AppOrBlueprintKey, list[ft.AfterRequestCallable] | ||
| ft.AppOrBlueprintKey, list[ft.AfterRequestCallable[t.Any]] | ||
| ] = defaultdict(list) | ||
@@ -243,3 +244,3 @@ | ||
| @static_folder.setter | ||
| def static_folder(self, value: str | os.PathLike | None) -> None: | ||
| def static_folder(self, value: str | os.PathLike[str] | None) -> None: | ||
| if value is not None: | ||
@@ -298,3 +299,3 @@ value = os.fspath(value).rstrip(r"\/") | ||
| rule: str, | ||
| options: dict, | ||
| options: dict[str, t.Any], | ||
| ) -> t.Callable[[T_route], T_route]: | ||
@@ -712,3 +713,3 @@ if "methods" in options: | ||
| def _endpoint_from_view_func(view_func: t.Callable) -> str: | ||
| def _endpoint_from_view_func(view_func: ft.RouteCallable) -> str: | ||
| """Internal helper that returns the default endpoint for a given | ||
@@ -730,3 +731,3 @@ function. This always is the function name. | ||
| def _find_package_path(import_name): | ||
| def _find_package_path(import_name: str) -> str: | ||
| """Find the path that contains the package or module.""" | ||
@@ -748,30 +749,31 @@ root_mod_name, _, _ = import_name.partition(".") | ||
| if root_spec.origin in {"namespace", None}: | ||
| # namespace package | ||
| package_spec = importlib.util.find_spec(import_name) | ||
| if root_spec.submodule_search_locations: | ||
| if root_spec.origin is None or root_spec.origin == "namespace": | ||
| # namespace package | ||
| package_spec = importlib.util.find_spec(import_name) | ||
| if package_spec is not None and package_spec.submodule_search_locations: | ||
| # Pick the path in the namespace that contains the submodule. | ||
| package_path = pathlib.Path( | ||
| os.path.commonpath(package_spec.submodule_search_locations) | ||
| ) | ||
| search_location = next( | ||
| location | ||
| for location in root_spec.submodule_search_locations | ||
| if _path_is_relative_to(package_path, location) | ||
| ) | ||
| if package_spec is not None and package_spec.submodule_search_locations: | ||
| # Pick the path in the namespace that contains the submodule. | ||
| package_path = pathlib.Path( | ||
| os.path.commonpath(package_spec.submodule_search_locations) | ||
| ) | ||
| search_location = next( | ||
| location | ||
| for location in root_spec.submodule_search_locations | ||
| if _path_is_relative_to(package_path, location) | ||
| ) | ||
| else: | ||
| # Pick the first path. | ||
| search_location = root_spec.submodule_search_locations[0] | ||
| return os.path.dirname(search_location) | ||
| else: | ||
| # Pick the first path. | ||
| search_location = root_spec.submodule_search_locations[0] | ||
| return os.path.dirname(search_location) | ||
| elif root_spec.submodule_search_locations: | ||
| # package with __init__.py | ||
| return os.path.dirname(os.path.dirname(root_spec.origin)) | ||
| # package with __init__.py | ||
| return os.path.dirname(os.path.dirname(root_spec.origin)) | ||
| else: | ||
| # module | ||
| return os.path.dirname(root_spec.origin) | ||
| return os.path.dirname(root_spec.origin) # type: ignore[type-var, return-value] | ||
| def find_package(import_name: str): | ||
| def find_package(import_name: str) -> tuple[str | None, str]: | ||
| """Find the prefix that a package is installed under, and the path | ||
@@ -778,0 +780,0 @@ that it would be imported from. |
+16
-12
@@ -16,7 +16,11 @@ from __future__ import annotations | ||
| if t.TYPE_CHECKING: # pragma: no cover | ||
| import typing_extensions as te | ||
| from .app import Flask | ||
| from .wrappers import Request, Response | ||
| from .wrappers import Request | ||
| from .wrappers import Response | ||
| class SessionMixin(MutableMapping): | ||
| # TODO generic when Python > 3.8 | ||
| class SessionMixin(MutableMapping): # type: ignore[type-arg] | ||
| """Expands a basic dictionary with session attributes.""" | ||
@@ -49,3 +53,4 @@ | ||
| class SecureCookieSession(CallbackDict, SessionMixin): | ||
| # TODO generic when Python > 3.8 | ||
| class SecureCookieSession(CallbackDict, SessionMixin): # type: ignore[type-arg] | ||
| """Base class for sessions based on signed cookies. | ||
@@ -73,3 +78,3 @@ | ||
| def __init__(self, initial: t.Any = None) -> None: | ||
| def on_update(self) -> None: | ||
| def on_update(self: te.Self) -> None: | ||
| self.modified = True | ||
@@ -183,3 +188,3 @@ self.accessed = True | ||
| """The name of the session cookie. Uses``app.config["SESSION_COOKIE_NAME"]``.""" | ||
| return app.config["SESSION_COOKIE_NAME"] | ||
| return app.config["SESSION_COOKIE_NAME"] # type: ignore[no-any-return] | ||
@@ -196,4 +201,3 @@ def get_cookie_domain(self, app: Flask) -> str | None: | ||
| """ | ||
| rv = app.config["SESSION_COOKIE_DOMAIN"] | ||
| return rv if rv else None | ||
| return app.config["SESSION_COOKIE_DOMAIN"] # type: ignore[no-any-return] | ||
@@ -206,3 +210,3 @@ def get_cookie_path(self, app: Flask) -> str: | ||
| """ | ||
| return app.config["SESSION_COOKIE_PATH"] or app.config["APPLICATION_ROOT"] | ||
| return app.config["SESSION_COOKIE_PATH"] or app.config["APPLICATION_ROOT"] # type: ignore[no-any-return] | ||
@@ -214,3 +218,3 @@ def get_cookie_httponly(self, app: Flask) -> bool: | ||
| """ | ||
| return app.config["SESSION_COOKIE_HTTPONLY"] | ||
| return app.config["SESSION_COOKIE_HTTPONLY"] # type: ignore[no-any-return] | ||
@@ -221,5 +225,5 @@ def get_cookie_secure(self, app: Flask) -> bool: | ||
| """ | ||
| return app.config["SESSION_COOKIE_SECURE"] | ||
| return app.config["SESSION_COOKIE_SECURE"] # type: ignore[no-any-return] | ||
| def get_cookie_samesite(self, app: Flask) -> str: | ||
| def get_cookie_samesite(self, app: Flask) -> str | None: | ||
| """Return ``'Strict'`` or ``'Lax'`` if the cookie should use the | ||
@@ -229,3 +233,3 @@ ``SameSite`` attribute. This currently just returns the value of | ||
| """ | ||
| return app.config["SESSION_COOKIE_SAMESITE"] | ||
| return app.config["SESSION_COOKIE_SAMESITE"] # type: ignore[no-any-return] | ||
@@ -232,0 +236,0 @@ def get_expiration_time(self, app: Flask, session: SessionMixin) -> datetime | None: |
@@ -60,5 +60,5 @@ from __future__ import annotations | ||
| def get_source( # type: ignore | ||
| self, environment: Environment, template: str | ||
| ) -> tuple[str, str | None, t.Callable | None]: | ||
| def get_source( | ||
| self, environment: BaseEnvironment, template: str | ||
| ) -> tuple[str, str | None, t.Callable[[], bool] | None]: | ||
| if self.app.config["EXPLAIN_TEMPLATE_LOADING"]: | ||
@@ -69,4 +69,4 @@ return self._get_source_explained(environment, template) | ||
| def _get_source_explained( | ||
| self, environment: Environment, template: str | ||
| ) -> tuple[str, str | None, t.Callable | None]: | ||
| self, environment: BaseEnvironment, template: str | ||
| ) -> tuple[str, str | None, t.Callable[[], bool] | None]: | ||
| attempts = [] | ||
@@ -94,4 +94,4 @@ rv: tuple[str, str | None, t.Callable[[], bool] | None] | None | ||
| def _get_source_fast( | ||
| self, environment: Environment, template: str | ||
| ) -> tuple[str, str | None, t.Callable | None]: | ||
| self, environment: BaseEnvironment, template: str | ||
| ) -> tuple[str, str | None, t.Callable[[], bool] | None]: | ||
| for _srcobj, loader in self._iter_loaders(template): | ||
@@ -104,5 +104,3 @@ try: | ||
| def _iter_loaders( | ||
| self, template: str | ||
| ) -> t.Generator[tuple[Scaffold, BaseLoader], None, None]: | ||
| def _iter_loaders(self, template: str) -> t.Iterator[tuple[Scaffold, BaseLoader]]: | ||
| loader = self.app.jinja_loader | ||
@@ -109,0 +107,0 @@ if loader is not None: |
@@ -20,2 +20,3 @@ from __future__ import annotations | ||
| if t.TYPE_CHECKING: # pragma: no cover | ||
| from _typeshed.wsgi import WSGIEnvironment | ||
| from werkzeug.test import TestResponse | ||
@@ -138,3 +139,3 @@ | ||
| self, *args: t.Any, **kwargs: t.Any | ||
| ) -> t.Generator[SessionMixin, None, None]: | ||
| ) -> t.Iterator[SessionMixin]: | ||
| """When used in combination with a ``with`` statement this opens a | ||
@@ -186,3 +187,3 @@ session transaction. This can be used to modify the session that | ||
| def _copy_environ(self, other): | ||
| def _copy_environ(self, other: WSGIEnvironment) -> WSGIEnvironment: | ||
| out = {**self.environ_base, **other} | ||
@@ -195,3 +196,5 @@ | ||
| def _request_from_builder_args(self, args, kwargs): | ||
| def _request_from_builder_args( | ||
| self, args: tuple[t.Any, ...], kwargs: dict[str, t.Any] | ||
| ) -> BaseRequest: | ||
| kwargs["environ_base"] = self._copy_environ(kwargs.get("environ_base", {})) | ||
@@ -217,3 +220,3 @@ builder = EnvironBuilder(self.application, *args, **kwargs) | ||
| builder = copy(args[0]) | ||
| builder.environ_base = self._copy_environ(builder.environ_base or {}) | ||
| builder.environ_base = self._copy_environ(builder.environ_base or {}) # type: ignore[arg-type] | ||
| request = builder.get_request() | ||
@@ -295,3 +298,3 @@ elif isinstance(args[0], dict): | ||
| if cli is None: | ||
| cli = self.app.cli # type: ignore | ||
| cli = self.app.cli | ||
@@ -298,0 +301,0 @@ if "obj" not in kwargs: |
@@ -71,4 +71,6 @@ from __future__ import annotations | ||
| TemplateTestCallable = t.Callable[..., bool] | ||
| URLDefaultCallable = t.Callable[[str, dict], None] | ||
| URLValuePreprocessorCallable = t.Callable[[t.Optional[str], t.Optional[dict]], None] | ||
| URLDefaultCallable = t.Callable[[str, t.Dict[str, t.Any]], None] | ||
| URLValuePreprocessorCallable = t.Callable[ | ||
| [t.Optional[str], t.Optional[t.Dict[str, t.Any]]], None | ||
| ] | ||
@@ -75,0 +77,0 @@ # This should take Exception, but that either breaks typing the argument |
@@ -9,2 +9,3 @@ from __future__ import annotations | ||
| F = t.TypeVar("F", bound=t.Callable[..., t.Any]) | ||
@@ -64,3 +65,3 @@ http_method_funcs = frozenset( | ||
| #: .. versionadded:: 0.8 | ||
| decorators: t.ClassVar[list[t.Callable]] = [] | ||
| decorators: t.ClassVar[list[t.Callable[[F], F]]] = [] | ||
@@ -111,3 +112,3 @@ #: Create a new instance of this view class for every request by | ||
| ) | ||
| return current_app.ensure_sync(self.dispatch_request)(**kwargs) | ||
| return current_app.ensure_sync(self.dispatch_request)(**kwargs) # type: ignore[no-any-return] | ||
@@ -118,3 +119,3 @@ else: | ||
| def view(**kwargs: t.Any) -> ft.ResponseReturnValue: | ||
| return current_app.ensure_sync(self.dispatch_request)(**kwargs) | ||
| return current_app.ensure_sync(self.dispatch_request)(**kwargs) # type: ignore[no-any-return] | ||
@@ -194,2 +195,2 @@ if cls.decorators: | ||
| assert meth is not None, f"Unimplemented method {request.method!r}" | ||
| return current_app.ensure_sync(meth)(**kwargs) | ||
| return current_app.ensure_sync(meth)(**kwargs) # type: ignore[no-any-return] |
@@ -6,2 +6,3 @@ from __future__ import annotations | ||
| from werkzeug.exceptions import BadRequest | ||
| from werkzeug.exceptions import HTTPException | ||
| from werkzeug.wrappers import Request as RequestBase | ||
@@ -53,9 +54,9 @@ from werkzeug.wrappers import Response as ResponseBase | ||
| #: something similar. | ||
| routing_exception: Exception | None = None | ||
| routing_exception: HTTPException | None = None | ||
| @property | ||
| def max_content_length(self) -> int | None: # type: ignore | ||
| def max_content_length(self) -> int | None: # type: ignore[override] | ||
| """Read-only view of the ``MAX_CONTENT_LENGTH`` config key.""" | ||
| if current_app: | ||
| return current_app.config["MAX_CONTENT_LENGTH"] | ||
| return current_app.config["MAX_CONTENT_LENGTH"] # type: ignore[no-any-return] | ||
| else: | ||
@@ -172,5 +173,5 @@ return None | ||
| if current_app: | ||
| return current_app.config["MAX_COOKIE_SIZE"] | ||
| return current_app.config["MAX_COOKIE_SIZE"] # type: ignore[no-any-return] | ||
| # return Werkzeug's default when not in an app context | ||
| return super().max_cookie_size |
@@ -199,3 +199,3 @@ import pytest | ||
| def teardown_req(error=None): | ||
| 1 / 0 | ||
| raise ZeroDivisionError | ||
@@ -206,9 +206,6 @@ @app.teardown_appcontext | ||
| try: | ||
| with app.test_request_context(): | ||
| called.append(flask.current_app.name) | ||
| except ZeroDivisionError: | ||
| pass | ||
| with app.app_context(): | ||
| called.append(flask.current_app.name) | ||
| assert called == ["flask_test", "TEARDOWN"] | ||
| assert not flask.current_app |
| from flask import Module | ||
| mod = Module(__name__, "foo", subdomain="foo") |
+13
-11
@@ -22,3 +22,2 @@ import gc | ||
| require_cpython_gc = pytest.mark.skipif( | ||
@@ -194,3 +193,4 @@ python_implementation() != "CPython", | ||
| def test_werkzeug_routing(app, client): | ||
| from werkzeug.routing import Submount, Rule | ||
| from werkzeug.routing import Rule | ||
| from werkzeug.routing import Submount | ||
@@ -215,3 +215,4 @@ app.url_map.add( | ||
| def test_endpoint_decorator(app, client): | ||
| from werkzeug.routing import Submount, Rule | ||
| from werkzeug.routing import Rule | ||
| from werkzeug.routing import Submount | ||
@@ -437,5 +438,5 @@ app.url_map.add( | ||
| assert s["t"] == (1, 2, 3) | ||
| assert type(s["b"]) is bytes | ||
| assert type(s["b"]) is bytes # noqa: E721 | ||
| assert s["b"] == b"\xff" | ||
| assert type(s["m"]) is Markup | ||
| assert type(s["m"]) is Markup # noqa: E721 | ||
| assert s["m"] == Markup("<html>") | ||
@@ -791,3 +792,3 @@ assert s["u"] == the_uuid | ||
| def fails(): | ||
| 1 // 0 | ||
| raise ZeroDivisionError | ||
@@ -859,3 +860,3 @@ rv = client.get("/") | ||
| def error(): | ||
| 1 // 0 | ||
| raise ZeroDivisionError | ||
@@ -886,3 +887,3 @@ @app.route("/forbidden") | ||
| def broken_func(): | ||
| 1 // 0 | ||
| raise ZeroDivisionError | ||
@@ -1057,3 +1058,3 @@ @app.after_request | ||
| if _trigger == "before": | ||
| 1 // 0 | ||
| raise ZeroDivisionError | ||
@@ -1063,3 +1064,4 @@ @app.after_request | ||
| if _trigger == "after": | ||
| 1 // 0 | ||
| raise ZeroDivisionError | ||
| return response | ||
@@ -1519,3 +1521,3 @@ | ||
| def index(): | ||
| 1 // 0 | ||
| raise ZeroDivisionError | ||
@@ -1522,0 +1524,0 @@ if key is not None: |
@@ -637,6 +637,7 @@ import pytest | ||
| template_string = lambda: flask.render_template_string( # noqa: E731 | ||
| "{% if notanswer %}{{ notanswer }} is not the answer. {% endif %}" | ||
| "{% if answer %}{{ answer }} is the answer.{% endif %}" | ||
| ) | ||
| def template_string(): | ||
| return flask.render_template_string( | ||
| "{% if notanswer %}{{ notanswer }} is not the answer. {% endif %}" | ||
| "{% if answer %}{{ answer }} is the answer.{% endif %}" | ||
| ) | ||
@@ -643,0 +644,0 @@ # App global context processor |
@@ -101,3 +101,3 @@ import flask | ||
| def index(): | ||
| 1 // 0 | ||
| raise ZeroDivisionError | ||
@@ -173,3 +173,3 @@ def record(sender, exception): | ||
| def index(): | ||
| 1 // 0 | ||
| raise ZeroDivisionError | ||
@@ -176,0 +176,0 @@ flask.appcontext_tearing_down.connect(record_teardown, app) |
@@ -222,3 +222,3 @@ import importlib.metadata | ||
| def other(): | ||
| 1 // 0 | ||
| raise ZeroDivisionError | ||
@@ -231,2 +231,3 @@ with client: | ||
| with client: | ||
| resp = client.get("/other") | ||
@@ -238,8 +239,4 @@ assert not hasattr(flask.g, "value") | ||
| try: | ||
| flask.g.value | ||
| except (AttributeError, RuntimeError): | ||
| pass | ||
| else: | ||
| raise AssertionError("some kind of exception expected") | ||
| with pytest.raises(RuntimeError): | ||
| flask.g.value # noqa: B018 | ||
@@ -246,0 +243,0 @@ |
@@ -44,6 +44,6 @@ import pytest | ||
| def get(self): | ||
| 1 // 0 | ||
| raise ZeroDivisionError | ||
| def post(self): | ||
| 1 // 0 | ||
| raise ZeroDivisionError | ||
@@ -50,0 +50,0 @@ class Other(Index): |
| from __future__ import annotations | ||
| import typing as t | ||
| from flask import Flask | ||
@@ -32,3 +30,3 @@ from flask import Response | ||
| @app.teardown_appcontext | ||
| def teardown_sync(exc: t.Optional[BaseException]) -> None: | ||
| def teardown_sync(exc: BaseException | None) -> None: | ||
| ... | ||
@@ -38,3 +36,3 @@ | ||
| @app.teardown_appcontext | ||
| async def teardown_async(exc: t.Optional[BaseException]) -> None: | ||
| async def teardown_async(exc: BaseException | None) -> None: | ||
| ... |
@@ -32,3 +32,3 @@ from __future__ import annotations | ||
| @app.route("/json/dict") | ||
| def hello_json_dict() -> t.Dict[str, t.Any]: | ||
| def hello_json_dict() -> dict[str, t.Any]: | ||
| return {"response": "Hello, World!"} | ||
@@ -38,3 +38,3 @@ | ||
| @app.route("/json/dict") | ||
| def hello_json_list() -> t.List[t.Any]: | ||
| def hello_json_list() -> list[t.Any]: | ||
| return [{"message": "Hello"}, {"message": "World"}] | ||
@@ -41,0 +41,0 @@ |
+2
-9
@@ -20,11 +20,4 @@ [tox] | ||
| -r requirements/tests.txt | ||
| min: -r requirements/tests-pallets-min.txt | ||
| dev: https://github.com/pallets/werkzeug/archive/refs/heads/main.tar.gz | ||
| dev: https://github.com/pallets/jinja/archive/refs/heads/main.tar.gz | ||
| dev: https://github.com/pallets/markupsafe/archive/refs/heads/main.tar.gz | ||
| dev: https://github.com/pallets/itsdangerous/archive/refs/heads/main.tar.gz | ||
| dev: https://github.com/pallets/click/archive/refs/heads/main.tar.gz | ||
| # examples/tutorial[test] | ||
| # examples/javascript[test] | ||
| # commands = pytest -v --tb=short --basetemp={envtmpdir} {posargs:tests examples} | ||
| min: -r requirements-skip/tests-min.txt | ||
| dev: -r requirements-skip/tests-dev.txt | ||
| commands = pytest -v --tb=short --basetemp={envtmpdir} {posargs:tests} | ||
@@ -31,0 +24,0 @@ |
| Werkzeug==3.0.0 | ||
| Jinja2==3.1.2 | ||
| MarkupSafe==2.1.1 | ||
| itsdangerous==2.1.2 | ||
| click==8.1.3 | ||
| blinker==1.6.2 |
| # SHA1:fbb9fae044c2e7d895de9b3d7cbb40a11a822f04 | ||
| # | ||
| # This file is autogenerated by pip-compile-multi | ||
| # To update, run: | ||
| # | ||
| # pip-compile-multi | ||
| # | ||
| blinker==1.6.2 | ||
| # via -r requirements/tests-pallets-min.in | ||
| click==8.1.3 | ||
| # via -r requirements/tests-pallets-min.in | ||
| itsdangerous==2.1.2 | ||
| # via -r requirements/tests-pallets-min.in | ||
| jinja2==3.1.2 | ||
| # via -r requirements/tests-pallets-min.in | ||
| markupsafe==2.1.1 | ||
| # via | ||
| # -r requirements/tests-pallets-min.in | ||
| # jinja2 | ||
| # werkzeug | ||
| werkzeug==3.0.0 | ||
| # via -r requirements/tests-pallets-min.in |
Sorry, the diff of this file is too big to display
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
1507876
0.45%13589
0.77%231
-0.86%