Latest Threat Research:SANDWORM_MODE: Shai-Hulud-Style npm Worm Hijacks CI Workflows and Poisons AI Toolchains.Details
Socket
Book a DemoSign in
Socket

flask

Package Overview
Dependencies
Maintainers
0
Versions
64
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

flask - pypi Package Compare versions

Comparing version
3.0.0
to
3.0.1
+2
-2
docs/server.rst

@@ -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"]
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>

[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

@@ -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

@@ -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

@@ -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
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:

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

@@ -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,

@@ -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}:"]

@@ -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

@@ -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,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")

@@ -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 @@

@@ -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