Merge branch 'main' into pr/emisargent/4969

This commit is contained in:
David Lord 2023-03-12 08:13:33 -07:00
commit 58c52e84cd
No known key found for this signature in database
GPG key ID: 7A1C87E3F5BC42A8
38 changed files with 282 additions and 1063 deletions

View file

@ -33,7 +33,7 @@ jobs:
id-token: write
contents: write
# Can't pin with hash due to how this workflow works.
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v1.4.0
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v1.5.0
with:
base64-subjects: ${{ needs.build.outputs.hash }}
create-release:

View file

@ -49,7 +49,7 @@ jobs:
pip install -U setuptools
python -m pip install -U pip
- name: cache mypy
uses: actions/cache@627f0f41f6904a5b1efbaed9f96d9eb58e92e920
uses: actions/cache@69d9d449aced6a2ede0bc19182fadc3a0a42d2b0
with:
path: ./.mypy_cache
key: mypy|${{ matrix.python }}|${{ hashFiles('pyproject.toml') }}

View file

@ -26,7 +26,7 @@ repos:
- flake8-bugbear
- flake8-implicit-str-concat
- repo: https://github.com/peterdemin/pip-compile-multi
rev: v2.6.1
rev: v2.6.2
hooks:
- id: pip-compile-multi-verify
- repo: https://github.com/pre-commit/pre-commit-hooks

View file

@ -3,19 +3,56 @@ Version 2.3.0
Unreleased
- Remove previously deprecated code. :pr:`4995`
- The ``push`` and ``pop`` methods of the deprecated ``_app_ctx_stack`` and
``_request_ctx_stack`` objects are removed. ``top`` still exists to give
extensions more time to update, but it will be removed.
- The ``FLASK_ENV`` environment variable, ``ENV`` config key, and ``app.env``
property are removed.
- The ``session_cookie_name``, ``send_file_max_age_default``, ``use_x_sendfile``,
``propagate_exceptions``, and ``templates_auto_reload`` properties on ``app``
are removed.
- The ``JSON_AS_ASCII``, ``JSON_SORT_KEYS``, ``JSONIFY_MIMETYPE``, and
``JSONIFY_PRETTYPRINT_REGULAR`` config keys are removed.
- The ``app.before_first_request`` and ``bp.before_app_first_request`` decorators
are removed.
- ``json_encoder`` and ``json_decoder`` attributes on app and blueprint, and the
corresponding ``json.JSONEncoder`` and ``JSONDecoder`` classes, are removed.
- The ``json.htmlsafe_dumps`` and ``htmlsafe_dump`` functions are removed.
- Calling setup methods on blueprints after registration is an error instead of a
warning. :pr:`4997`
- Importing ``escape`` and ``Markup`` from ``flask`` is deprecated. Import them
directly from ``markupsafe`` instead. :pr:`4996`
- The ``app.got_first_request`` property is deprecated. :pr:`4997`
- The ``locked_cached_property`` decorator is deprecated. Use a lock inside the
decorated function if locking is needed. :issue:`4993`
- Remove uses of locks that could cause requests to block each other very briefly.
:issue:`4993`
- Use modern packaging metadata with ``pyproject.toml`` instead of ``setup.cfg``.
:pr:`4947`
- Ensure subdomains are applied with nested blueprints. :issue:`4834`
- ``config.from_file`` can use ``text=False`` to indicate that the parser wants a
binary file instead. :issue:`4989`
- If a blueprint is created with an empty name it raises a ``ValueError``.
:issue:`5010`
Version 2.2.4
-------------
Unreleased
- Update for compatibility with Werkzeug 2.3.
Version 2.2.3
-------------
Unreleased
Released 2023-02-15
- Autoescaping is now enabled by default for ``.svg`` files. Inside
templates this behavior can be changed with the ``autoescape`` tag.
:issue:`4831`
- Autoescape is enabled by default for ``.svg`` template files. :issue:`4831`
- Fix the type of ``template_folder`` to accept ``pathlib.Path``. :issue:`4892`
- Add ``--debug`` option to the ``flask run`` command. :issue:`4777`

View file

@ -217,10 +217,6 @@ Useful Functions and Classes
.. autofunction:: send_from_directory
.. autofunction:: escape
.. autoclass:: Markup
:members: escape, unescape, striptags
Message Flashing
----------------
@ -270,12 +266,6 @@ HTML ``<script>`` tags.
:members:
:member-order: bysource
.. autoclass:: JSONEncoder
:members:
.. autoclass:: JSONDecoder
:members:
.. automodule:: flask.json.tag

View file

@ -103,6 +103,14 @@ the ``--debug`` option.
* Debugger is active!
* Debugger PIN: 223-456-919
The ``--debug`` option can also be passed to the top level ``flask`` command to enable
debug mode for any command. The following two ``run`` calls are equivalent.
.. code-block:: console
$ flask --app hello --debug run
$ flask --app hello run --debug
Watch and Ignore Files with the Reloader
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View file

@ -47,17 +47,17 @@ Debug Mode
The :data:`DEBUG` config value is special because it may behave inconsistently if
changed after the app has begun setting up. In order to set debug mode reliably, use the
``--debug`` option on the ``flask run`` command. ``flask run`` will use the interactive
debugger and reloader by default in debug mode.
``--debug`` option on the ``flask`` or ``flask run`` command. ``flask run`` will use the
interactive debugger and reloader by default in debug mode.
.. code-block:: text
$ flask --app hello run --debug
Using the option is recommended. While it is possible to set :data:`DEBUG` in your
config or code, this is strongly discouraged. It can't be read early by the ``flask run``
command, and some systems or extensions may have already configured themselves based on
a previous value.
config or code, this is strongly discouraged. It can't be read early by the
``flask run`` command, and some systems or extensions may have already configured
themselves based on a previous value.
Builtin Configuration Values
@ -65,18 +65,6 @@ Builtin Configuration Values
The following configuration values are used internally by Flask:
.. py:data:: ENV
What environment the app is running in. The :attr:`~flask.Flask.env` attribute maps
to this config key.
Default: ``'production'``
.. deprecated:: 2.2
Will be removed in Flask 2.3. Use ``--debug`` instead.
.. versionadded:: 1.0
.. py:data:: DEBUG
Whether debug mode is enabled. When using ``flask run`` to start the development
@ -271,52 +259,6 @@ The following configuration values are used internally by Flask:
Default: ``None``
.. py:data:: JSON_AS_ASCII
Serialize objects to ASCII-encoded JSON. If this is disabled, the
JSON returned from ``jsonify`` will contain Unicode characters. This
has security implications when rendering the JSON into JavaScript in
templates, and should typically remain enabled.
Default: ``True``
.. deprecated:: 2.2
Will be removed in Flask 2.3. Set ``app.json.ensure_ascii``
instead.
.. py:data:: JSON_SORT_KEYS
Sort the keys of JSON objects alphabetically. This is useful for caching
because it ensures the data is serialized the same way no matter what
Python's hash seed is. While not recommended, you can disable this for a
possible performance improvement at the cost of caching.
Default: ``True``
.. deprecated:: 2.2
Will be removed in Flask 2.3. Set ``app.json.sort_keys``
instead.
.. py:data:: JSONIFY_PRETTYPRINT_REGULAR
:func:`~flask.jsonify` responses will be output with newlines,
spaces, and indentation for easier reading by humans. Always enabled
in debug mode.
Default: ``False``
.. deprecated:: 2.2
Will be removed in Flask 2.3. Set ``app.json.compact`` instead.
.. py:data:: JSONIFY_MIMETYPE
The mimetype of ``jsonify`` responses.
Default: ``'application/json'``
.. deprecated:: 2.2
Will be removed in Flask 2.3. Set ``app.json.mimetype`` instead.
.. py:data:: TEMPLATES_AUTO_RELOAD
Reload templates when they are changed. If not set, it will be enabled in
@ -381,14 +323,13 @@ The following configuration values are used internally by Flask:
.. versionchanged:: 2.2
Removed ``PRESERVE_CONTEXT_ON_EXCEPTION``.
.. versionchanged:: 2.2
``JSON_AS_ASCII``, ``JSON_SORT_KEYS``,
``JSONIFY_MIMETYPE``, and ``JSONIFY_PRETTYPRINT_REGULAR`` will be
removed in Flask 2.3. The default ``app.json`` provider has
.. versionchanged:: 2.3
``JSON_AS_ASCII``, ``JSON_SORT_KEYS``, ``JSONIFY_MIMETYPE``, and
``JSONIFY_PRETTYPRINT_REGULAR`` were removed. The default ``app.json`` provider has
equivalent attributes instead.
.. versionchanged:: 2.2
``ENV`` will be removed in Flask 2.3. Use ``--debug`` instead.
.. versionchanged:: 2.3
``ENV`` was removed.
Configuring from Python Files

View file

@ -69,7 +69,6 @@ See also:
- Sentry also supports catching errors from a worker queue
(RQ, Celery, etc.) in a similar fashion. See the `Python SDK docs
<https://docs.sentry.io/platforms/python/>`__ for more information.
- `Getting started with Sentry <https://docs.sentry.io/quickstart/?platform=python>`__
- `Flask-specific documentation <https://docs.sentry.io/platforms/python/guides/flask/>`__

View file

@ -44,7 +44,7 @@ use them if you install them.
* `Watchdog`_ provides a faster, more efficient reloader for the development
server.
.. _Blinker: https://pythonhosted.org/blinker/
.. _Blinker: https://blinker.readthedocs.io/en/stable/
.. _python-dotenv: https://github.com/theskumar/python-dotenv#readme
.. _watchdog: https://pythonhosted.org/watchdog/

View file

@ -23,7 +23,7 @@ in templates, but there are still other places where you have to be
careful:
- generating HTML without the help of Jinja2
- calling :class:`~flask.Markup` on data submitted by users
- calling :class:`~markupsafe.Markup` on data submitted by users
- sending out HTML from uploaded files, never do that, use the
``Content-Disposition: attachment`` header to prevent that problem.
- sending out textfiles from uploaded files. Some browsers are using

View file

@ -115,7 +115,7 @@ markdown to HTML converter.
There are three ways to accomplish that:
- In the Python code, wrap the HTML string in a :class:`~flask.Markup`
- In the Python code, wrap the HTML string in a :class:`~markupsafe.Markup`
object before passing it to the template. This is in general the
recommended way.
- Inside the template, use the ``|safe`` filter to explicitly mark a

View file

@ -19,7 +19,7 @@ In a separate terminal, activate the virtualenv and run the Flask development se
```shell
$ . ./.venv/bin/activate
$ flask -A task_app --debug run
$ flask -A task_app run --debug
```
Go to http://localhost:5000/ and use the forms to submit tasks. You can see the polling

View file

@ -2,7 +2,7 @@
# This file is autogenerated by pip-compile with Python 3.10
# by the following command:
#
# pip-compile pyproject.toml
# pip-compile --resolver=backtracking pyproject.toml
#
amqp==5.1.1
# via kombu
@ -25,7 +25,7 @@ click-plugins==1.1.1
# via celery
click-repl==0.2.0
# via celery
flask==2.2.2
flask==2.2.3
# via flask-example-celery (pyproject.toml)
itsdangerous==2.1.2
# via flask
@ -37,7 +37,7 @@ markupsafe==2.1.2
# via
# jinja2
# werkzeug
prompt-toolkit==3.0.36
prompt-toolkit==3.0.37
# via click-repl
pytz==2022.7.1
# via celery
@ -52,5 +52,5 @@ vine==5.0.0
# kombu
wcwidth==0.2.6
# via prompt-toolkit
werkzeug==2.2.2
werkzeug==2.2.3
# via flask

View file

@ -5,13 +5,11 @@
#
# pip-compile-multi
#
build==0.9.0
build==0.10.0
# via -r requirements/build.in
packaging==23.0
# via build
pep517==0.13.0
pyproject-hooks==1.0.0
# via build
tomli==2.0.1
# via
# build
# pep517
# via build

View file

@ -8,9 +8,9 @@
-r docs.txt
-r tests.txt
-r typing.txt
build==0.9.0
build==0.10.0
# via pip-tools
cachetools==5.2.0
cachetools==5.3.0
# via tox
cfgv==3.3.1
# via pre-commit
@ -24,37 +24,35 @@ colorama==0.4.6
# via tox
distlib==0.3.6
# via virtualenv
filelock==3.8.2
filelock==3.9.0
# via
# tox
# virtualenv
identify==2.5.11
identify==2.5.19
# via pre-commit
nodeenv==1.7.0
# via pre-commit
pep517==0.13.0
# via build
pip-compile-multi==2.6.1
pip-compile-multi==2.6.2
# via -r requirements/dev.in
pip-tools==6.12.1
pip-tools==6.12.3
# via pip-compile-multi
platformdirs==2.6.0
platformdirs==3.1.1
# via
# tox
# virtualenv
pre-commit==2.20.0
pre-commit==3.1.1
# via -r requirements/dev.in
pyproject-api==1.2.1
pyproject-api==1.5.0
# via tox
pyproject-hooks==1.0.0
# via build
pyyaml==6.0
# via pre-commit
toml==0.10.2
# via pre-commit
toposort==1.7
toposort==1.10
# via pip-compile-multi
tox==4.0.16
tox==4.4.6
# via -r requirements/dev.in
virtualenv==20.17.1
virtualenv==20.20.0
# via
# pre-commit
# tox

View file

@ -5,13 +5,13 @@
#
# pip-compile-multi
#
alabaster==0.7.12
alabaster==0.7.13
# via sphinx
babel==2.11.0
babel==2.12.1
# via sphinx
certifi==2022.12.7
# via requests
charset-normalizer==2.1.1
charset-normalizer==3.1.0
# via requests
docutils==0.17.1
# via
@ -23,21 +23,19 @@ imagesize==1.4.1
# via sphinx
jinja2==3.1.2
# via sphinx
markupsafe==2.1.1
markupsafe==2.1.2
# via jinja2
packaging==22.0
packaging==23.0
# via
# pallets-sphinx-themes
# sphinx
pallets-sphinx-themes==2.0.3
# via -r requirements/docs.in
pygments==2.13.0
pygments==2.14.0
# via
# sphinx
# sphinx-tabs
pytz==2022.7
# via babel
requests==2.28.1
requests==2.28.2
# via sphinx
snowballstemmer==2.2.0
# via sphinx
@ -52,11 +50,11 @@ sphinx-issues==3.0.1
# via -r requirements/docs.in
sphinx-tabs==3.3.1
# via -r requirements/docs.in
sphinxcontrib-applehelp==1.0.2
sphinxcontrib-applehelp==1.0.4
# via sphinx
sphinxcontrib-devhelp==1.0.2
# via sphinx
sphinxcontrib-htmlhelp==2.0.0
sphinxcontrib-htmlhelp==2.0.1
# via sphinx
sphinxcontrib-jsmath==1.0.1
# via sphinx
@ -66,5 +64,5 @@ sphinxcontrib-qthelp==1.0.3
# via sphinx
sphinxcontrib-serializinghtml==1.1.5
# via sphinx
urllib3==1.26.13
urllib3==1.26.15
# via requests

View file

@ -2,4 +2,4 @@ pytest
asgiref
blinker
greenlet ; python_version < "3.11"
python-dotenv
python-dotenv>=1; python_version >= "3.8"

View file

@ -1,4 +1,4 @@
# SHA1:69cf1e101a60350e9933c6f1f3b129bd9ed1ea7c
# SHA1:30698f5f4f9cba5088318306829a15b0dc123b38
#
# This file is autogenerated by pip-compile-multi
# To update, run:
@ -11,13 +11,19 @@ attrs==22.2.0
# via pytest
blinker==1.5
# via -r requirements/tests.in
iniconfig==1.1.1
exceptiongroup==1.1.0
# via pytest
packaging==22.0
greenlet==2.0.2 ; python_version < "3.11"
# via -r requirements/tests.in
iniconfig==2.0.0
# via pytest
packaging==23.0
# via pytest
pluggy==1.0.0
# via pytest
pytest==7.2.0
pytest==7.2.2
# via -r requirements/tests.in
python-dotenv==0.21.0
python-dotenv==1.0.0 ; python_version >= "3.8"
# via -r requirements/tests.in
tomli==2.0.1
# via pytest

View file

@ -7,19 +7,21 @@
#
cffi==1.15.1
# via cryptography
cryptography==38.0.4
cryptography==39.0.2
# via -r requirements/typing.in
mypy==0.991
mypy==1.1.1
# via -r requirements/typing.in
mypy-extensions==0.4.3
mypy-extensions==1.0.0
# via mypy
pycparser==2.21
# via cffi
types-contextvars==2.4.7
tomli==2.0.1
# via mypy
types-contextvars==2.4.7.1
# via -r requirements/typing.in
types-dataclasses==0.6.6
# via -r requirements/typing.in
types-setuptools==65.6.0.2
types-setuptools==67.6.0.0
# via -r requirements/typing.in
typing-extensions==4.4.0
typing-extensions==4.5.0
# via mypy

View file

@ -1,6 +1,3 @@
from markupsafe import escape
from markupsafe import Markup
from . import json as json
from .app import Flask as Flask
from .app import Request as Request
@ -51,7 +48,7 @@ def __getattr__(name):
from .globals import __app_ctx_stack
warnings.warn(
"'_app_ctx_stack' is deprecated and will be removed in Flask 2.3.",
"'_app_ctx_stack' is deprecated and will be removed in Flask 2.4.",
DeprecationWarning,
stacklevel=2,
)
@ -62,10 +59,34 @@ def __getattr__(name):
from .globals import __request_ctx_stack
warnings.warn(
"'_request_ctx_stack' is deprecated and will be removed in Flask 2.3.",
"'_request_ctx_stack' is deprecated and will be removed in Flask 2.4.",
DeprecationWarning,
stacklevel=2,
)
return __request_ctx_stack
if name == "escape":
import warnings
from markupsafe import escape
warnings.warn(
"'flask.escape' is deprecated and will be removed in Flask 2.4. Import"
" 'markupsafe.escape' instead.",
DeprecationWarning,
stacklevel=2,
)
return escape
if name == "escape":
import warnings
from markupsafe import Markup
warnings.warn(
"'flask.Markup' is deprecated and will be removed in Flask 2.4. Import"
" 'markupsafe.Markup' instead.",
DeprecationWarning,
stacklevel=2,
)
return Markup
raise AttributeError(name)

View file

@ -1,6 +1,5 @@
import functools
import inspect
import json
import logging
import os
import sys
@ -9,8 +8,8 @@ import weakref
from collections.abc import Iterator as _abc_Iterator
from datetime import timedelta
from itertools import chain
from threading import Lock
from types import TracebackType
from urllib.parse import quote as _url_quote
import click
from werkzeug.datastructures import Headers
@ -27,7 +26,7 @@ from werkzeug.routing import RequestRedirect
from werkzeug.routing import RoutingException
from werkzeug.routing import Rule
from werkzeug.serving import is_running_from_reloader
from werkzeug.urls import url_quote
from werkzeug.utils import cached_property
from werkzeug.utils import redirect as _wz_redirect
from werkzeug.wrappers import Response as BaseResponse
@ -48,7 +47,6 @@ from .helpers import _split_blueprint_path
from .helpers import get_debug_flag
from .helpers import get_flashed_messages
from .helpers import get_load_dotenv
from .helpers import locked_cached_property
from .json.provider import DefaultJSONProvider
from .json.provider import JSONProvider
from .logging import create_logger
@ -75,9 +73,6 @@ if t.TYPE_CHECKING: # pragma: no cover
from .testing import FlaskClient
from .testing import FlaskCliRunner
T_before_first_request = t.TypeVar(
"T_before_first_request", bound=ft.BeforeFirstRequestCallable
)
T_shell_context_processor = t.TypeVar(
"T_shell_context_processor", bound=ft.ShellContextProcessorCallable
)
@ -274,36 +269,6 @@ class Flask(Scaffold):
#: :data:`SECRET_KEY` configuration key. Defaults to ``None``.
secret_key = ConfigAttribute("SECRET_KEY")
@property
def session_cookie_name(self) -> str:
"""The name of the cookie set by the session interface.
.. deprecated:: 2.2
Will be removed in Flask 2.3. Use ``app.config["SESSION_COOKIE_NAME"]``
instead.
"""
import warnings
warnings.warn(
"'session_cookie_name' is deprecated and will be removed in Flask 2.3. Use"
" 'SESSION_COOKIE_NAME' in 'app.config' instead.",
DeprecationWarning,
stacklevel=2,
)
return self.config["SESSION_COOKIE_NAME"]
@session_cookie_name.setter
def session_cookie_name(self, value: str) -> None:
import warnings
warnings.warn(
"'session_cookie_name' is deprecated and will be removed in Flask 2.3. Use"
" 'SESSION_COOKIE_NAME' in 'app.config' instead.",
DeprecationWarning,
stacklevel=2,
)
self.config["SESSION_COOKIE_NAME"] = value
#: A :class:`~datetime.timedelta` which is used to set the expiration
#: date of a permanent session. The default is 31 days which makes a
#: permanent session survive for roughly one month.
@ -315,152 +280,6 @@ class Flask(Scaffold):
"PERMANENT_SESSION_LIFETIME", get_converter=_make_timedelta
)
@property
def send_file_max_age_default(self) -> t.Optional[timedelta]:
"""The default value for ``max_age`` for :func:`~flask.send_file`. The default
is ``None``, which tells the browser to use conditional requests instead of a
timed cache.
.. deprecated:: 2.2
Will be removed in Flask 2.3. Use
``app.config["SEND_FILE_MAX_AGE_DEFAULT"]`` instead.
.. versionchanged:: 2.0
Defaults to ``None`` instead of 12 hours.
"""
import warnings
warnings.warn(
"'send_file_max_age_default' is deprecated and will be removed in Flask"
" 2.3. Use 'SEND_FILE_MAX_AGE_DEFAULT' in 'app.config' instead.",
DeprecationWarning,
stacklevel=2,
)
return _make_timedelta(self.config["SEND_FILE_MAX_AGE_DEFAULT"])
@send_file_max_age_default.setter
def send_file_max_age_default(self, value: t.Union[int, timedelta, None]) -> None:
import warnings
warnings.warn(
"'send_file_max_age_default' is deprecated and will be removed in Flask"
" 2.3. Use 'SEND_FILE_MAX_AGE_DEFAULT' in 'app.config' instead.",
DeprecationWarning,
stacklevel=2,
)
self.config["SEND_FILE_MAX_AGE_DEFAULT"] = _make_timedelta(value)
@property
def use_x_sendfile(self) -> bool:
"""Enable this to use the ``X-Sendfile`` feature, assuming the server supports
it, from :func:`~flask.send_file`.
.. deprecated:: 2.2
Will be removed in Flask 2.3. Use ``app.config["USE_X_SENDFILE"]`` instead.
"""
import warnings
warnings.warn(
"'use_x_sendfile' is deprecated and will be removed in Flask 2.3. Use"
" 'USE_X_SENDFILE' in 'app.config' instead.",
DeprecationWarning,
stacklevel=2,
)
return self.config["USE_X_SENDFILE"]
@use_x_sendfile.setter
def use_x_sendfile(self, value: bool) -> None:
import warnings
warnings.warn(
"'use_x_sendfile' is deprecated and will be removed in Flask 2.3. Use"
" 'USE_X_SENDFILE' in 'app.config' instead.",
DeprecationWarning,
stacklevel=2,
)
self.config["USE_X_SENDFILE"] = value
_json_encoder: t.Union[t.Type[json.JSONEncoder], None] = None
_json_decoder: t.Union[t.Type[json.JSONDecoder], None] = None
@property # type: ignore[override]
def json_encoder(self) -> t.Type[json.JSONEncoder]:
"""The JSON encoder class to use. Defaults to
:class:`~flask.json.JSONEncoder`.
.. deprecated:: 2.2
Will be removed in Flask 2.3. Customize
:attr:`json_provider_class` instead.
.. versionadded:: 0.10
"""
import warnings
warnings.warn(
"'app.json_encoder' is deprecated and will be removed in Flask 2.3."
" Customize 'app.json_provider_class' or 'app.json' instead.",
DeprecationWarning,
stacklevel=2,
)
if self._json_encoder is None:
from . import json
return json.JSONEncoder
return self._json_encoder
@json_encoder.setter
def json_encoder(self, value: t.Type[json.JSONEncoder]) -> None:
import warnings
warnings.warn(
"'app.json_encoder' is deprecated and will be removed in Flask 2.3."
" Customize 'app.json_provider_class' or 'app.json' instead.",
DeprecationWarning,
stacklevel=2,
)
self._json_encoder = value
@property # type: ignore[override]
def json_decoder(self) -> t.Type[json.JSONDecoder]:
"""The JSON decoder class to use. Defaults to
:class:`~flask.json.JSONDecoder`.
.. deprecated:: 2.2
Will be removed in Flask 2.3. Customize
:attr:`json_provider_class` instead.
.. versionadded:: 0.10
"""
import warnings
warnings.warn(
"'app.json_decoder' is deprecated and will be removed in Flask 2.3."
" Customize 'app.json_provider_class' or 'app.json' instead.",
DeprecationWarning,
stacklevel=2,
)
if self._json_decoder is None:
from . import json
return json.JSONDecoder
return self._json_decoder
@json_decoder.setter
def json_decoder(self, value: t.Type[json.JSONDecoder]) -> None:
import warnings
warnings.warn(
"'app.json_decoder' is deprecated and will be removed in Flask 2.3."
" Customize 'app.json_provider_class' or 'app.json' instead.",
DeprecationWarning,
stacklevel=2,
)
self._json_decoder = value
json_provider_class: t.Type[JSONProvider] = DefaultJSONProvider
"""A subclass of :class:`~flask.json.provider.JSONProvider`. An
instance is created and assigned to :attr:`app.json` when creating
@ -487,7 +306,6 @@ class Flask(Scaffold):
#: Default configuration parameters.
default_config = ImmutableDict(
{
"ENV": None,
"DEBUG": None,
"TESTING": False,
"PROPAGATE_EXCEPTIONS": None,
@ -509,10 +327,6 @@ class Flask(Scaffold):
"TRAP_HTTP_EXCEPTIONS": False,
"EXPLAIN_TEMPLATE_LOADING": False,
"PREFERRED_URL_SCHEME": "http",
"JSON_AS_ASCII": None,
"JSON_SORT_KEYS": None,
"JSONIFY_PRETTYPRINT_REGULAR": None,
"JSONIFY_MIMETYPE": None,
"TEMPLATES_AUTO_RELOAD": None,
"MAX_COOKIE_SIZE": 4093,
}
@ -625,17 +439,6 @@ class Flask(Scaffold):
t.Callable[[Exception, str, t.Dict[str, t.Any]], str]
] = []
#: A list of functions that will be called at the beginning of the
#: first request to this instance. To register a function, use the
#: :meth:`before_first_request` decorator.
#:
#: .. deprecated:: 2.2
#: Will be removed in Flask 2.3. Run setup code when
#: creating the application instead.
#:
#: .. versionadded:: 0.8
self.before_first_request_funcs: t.List[ft.BeforeFirstRequestCallable] = []
#: A list of functions that are called when the application context
#: is destroyed. Since the application context is also torn down
#: if the request ends this is the place to store code that disconnects
@ -692,7 +495,6 @@ class Flask(Scaffold):
# tracks internally if the application already handled at least one
# request.
self._got_first_request = False
self._before_request_lock = Lock()
# Add a static route using the provided static_url_path, static_host,
# and static_folder if there is a configured static_folder.
@ -729,7 +531,7 @@ class Flask(Scaffold):
" running it."
)
@locked_cached_property
@cached_property
def name(self) -> str: # type: ignore
"""The name of the application. This is usually the import name
with the difference that it's guessed from the run file if the
@ -746,29 +548,7 @@ class Flask(Scaffold):
return os.path.splitext(os.path.basename(fn))[0]
return self.import_name
@property
def propagate_exceptions(self) -> bool:
"""Returns the value of the ``PROPAGATE_EXCEPTIONS`` configuration
value in case it's set, otherwise a sensible default is returned.
.. deprecated:: 2.2
Will be removed in Flask 2.3.
.. versionadded:: 0.7
"""
import warnings
warnings.warn(
"'propagate_exceptions' is deprecated and will be removed in Flask 2.3.",
DeprecationWarning,
stacklevel=2,
)
rv = self.config["PROPAGATE_EXCEPTIONS"]
if rv is not None:
return rv
return self.testing or self.debug
@locked_cached_property
@cached_property
def logger(self) -> logging.Logger:
"""A standard Python :class:`~logging.Logger` for the app, with
the same name as :attr:`name`.
@ -795,7 +575,7 @@ class Flask(Scaffold):
"""
return create_logger(self)
@locked_cached_property
@cached_property
def jinja_env(self) -> Environment:
"""The Jinja environment used to load templates.
@ -810,8 +590,18 @@ class Flask(Scaffold):
"""This attribute is set to ``True`` if the application started
handling the first request.
.. deprecated:: 2.3
Will be removed in Flask 2.4.
.. versionadded:: 0.8
"""
import warnings
warnings.warn(
"'got_first_request' is deprecated and will be removed in Flask 2.4.",
DeprecationWarning,
stacklevel=2,
)
return self._got_first_request
def make_config(self, instance_relative: bool = False) -> Config:
@ -827,7 +617,6 @@ class Flask(Scaffold):
if instance_relative:
root_path = self.instance_path
defaults = dict(self.default_config)
defaults["ENV"] = os.environ.get("FLASK_ENV") or "production"
defaults["DEBUG"] = get_debug_flag()
return self.config_class(root_path, defaults)
@ -868,42 +657,6 @@ class Flask(Scaffold):
"""
return open(os.path.join(self.instance_path, resource), mode)
@property
def templates_auto_reload(self) -> bool:
"""Reload templates when they are changed. Used by
:meth:`create_jinja_environment`. It is enabled by default in debug mode.
.. deprecated:: 2.2
Will be removed in Flask 2.3. Use ``app.config["TEMPLATES_AUTO_RELOAD"]``
instead.
.. versionadded:: 1.0
This property was added but the underlying config and behavior
already existed.
"""
import warnings
warnings.warn(
"'templates_auto_reload' is deprecated and will be removed in Flask 2.3."
" Use 'TEMPLATES_AUTO_RELOAD' in 'app.config' instead.",
DeprecationWarning,
stacklevel=2,
)
rv = self.config["TEMPLATES_AUTO_RELOAD"]
return rv if rv is not None else self.debug
@templates_auto_reload.setter
def templates_auto_reload(self, value: bool) -> None:
import warnings
warnings.warn(
"'templates_auto_reload' is deprecated and will be removed in Flask 2.3."
" Use 'TEMPLATES_AUTO_RELOAD' in 'app.config' instead.",
DeprecationWarning,
stacklevel=2,
)
self.config["TEMPLATES_AUTO_RELOAD"] = value
def create_jinja_environment(self) -> Environment:
"""Create the Jinja environment based on :attr:`jinja_options`
and the various Jinja-related methods of the app. Changing
@ -1010,40 +763,6 @@ class Flask(Scaffold):
rv.update(processor())
return rv
@property
def env(self) -> str:
"""What environment the app is running in. This maps to the :data:`ENV` config
key.
**Do not enable development when deploying in production.**
Default: ``'production'``
.. deprecated:: 2.2
Will be removed in Flask 2.3.
"""
import warnings
warnings.warn(
"'app.env' is deprecated and will be removed in Flask 2.3."
" Use 'app.debug' instead.",
DeprecationWarning,
stacklevel=2,
)
return self.config["ENV"]
@env.setter
def env(self, value: str) -> None:
import warnings
warnings.warn(
"'app.env' is deprecated and will be removed in Flask 2.3."
" Use 'app.debug' instead.",
DeprecationWarning,
stacklevel=2,
)
self.config["ENV"] = value
@property
def debug(self) -> bool:
"""Whether debug mode is enabled. When using ``flask run`` to start the
@ -1144,16 +863,8 @@ class Flask(Scaffold):
if get_load_dotenv(load_dotenv):
cli.load_dotenv()
# if set, let env vars override previous values
if "FLASK_ENV" in os.environ:
print(
"'FLASK_ENV' is deprecated and will not be used in"
" Flask 2.3. Use 'FLASK_DEBUG' instead.",
file=sys.stderr,
)
self.config["ENV"] = os.environ.get("FLASK_ENV") or "production"
self.debug = get_debug_flag()
elif "FLASK_DEBUG" in os.environ:
# if set, env var overrides existing value
if "FLASK_DEBUG" in os.environ:
self.debug = get_debug_flag()
# debug passed to method overrides all other sources
@ -1248,7 +959,7 @@ class Flask(Scaffold):
"""
cls = self.test_client_class
if cls is None:
from .testing import FlaskClient as cls # type: ignore
from .testing import FlaskClient as cls
return cls( # type: ignore
self, self.response_class, use_cookies=use_cookies, **kwargs
)
@ -1266,7 +977,7 @@ class Flask(Scaffold):
cls = self.test_cli_runner_class
if cls is None:
from .testing import FlaskCliRunner as cls # type: ignore
from .testing import FlaskCliRunner as cls
return cls(self, **kwargs) # type: ignore
@ -1479,32 +1190,6 @@ class Flask(Scaffold):
"""
self.jinja_env.globals[name or f.__name__] = f
@setupmethod
def before_first_request(self, f: T_before_first_request) -> T_before_first_request:
"""Registers a function to be run before the first request to this
instance of the application.
The function will be called without any arguments and its return
value is ignored.
.. deprecated:: 2.2
Will be removed in Flask 2.3. Run setup code when creating
the application instead.
.. versionadded:: 0.8
"""
import warnings
warnings.warn(
"'before_first_request' is deprecated and will be removed"
" in Flask 2.3. Run setup code while creating the"
" application instead.",
DeprecationWarning,
stacklevel=2,
)
self.before_first_request_funcs.append(f)
return f
@setupmethod
def teardown_appcontext(self, f: T_teardown) -> T_teardown:
"""Registers a function to be called when the application
@ -1682,7 +1367,7 @@ class Flask(Scaffold):
Always sends the :data:`got_request_exception` signal.
If :attr:`propagate_exceptions` is ``True``, such as in debug
If :data:`PROPAGATE_EXCEPTIONS` is ``True``, such as in debug
mode, the error will be re-raised so that the debugger can
display it. Otherwise, the original exception is logged, and
an :exc:`~werkzeug.exceptions.InternalServerError` is returned.
@ -1805,16 +1490,7 @@ class Flask(Scaffold):
.. versionadded:: 0.7
"""
# Run before_first_request functions if this is the thread's first request.
# Inlined to avoid a method call on subsequent requests.
# This is deprecated, will be removed in Flask 2.3.
if not self._got_first_request:
with self._before_request_lock:
if not self._got_first_request:
for func in self.before_first_request_funcs:
self.ensure_sync(func)()
self._got_first_request = True
self._got_first_request = True
try:
request_started.send(self)
@ -2034,7 +1710,8 @@ class Flask(Scaffold):
return self.handle_url_build_error(error, endpoint, values)
if _anchor is not None:
rv = f"{rv}#{url_quote(_anchor)}"
_anchor = _url_quote(_anchor, safe="%!#$&'()*+,/:;=?@")
rv = f"{rv}#{_anchor}"
return rv

View file

@ -1,4 +1,3 @@
import json
import os
import typing as t
from collections import defaultdict
@ -15,9 +14,6 @@ if t.TYPE_CHECKING: # pragma: no cover
DeferredSetupFunction = t.Callable[["BlueprintSetupState"], t.Callable]
T_after_request = t.TypeVar("T_after_request", bound=ft.AfterRequestCallable)
T_before_first_request = t.TypeVar(
"T_before_first_request", bound=ft.BeforeFirstRequestCallable
)
T_before_request = t.TypeVar("T_before_request", bound=ft.BeforeRequestCallable)
T_error_handler = t.TypeVar("T_error_handler", bound=ft.ErrorHandlerCallable)
T_teardown = t.TypeVar("T_teardown", bound=ft.TeardownCallable)
@ -173,77 +169,6 @@ class Blueprint(Scaffold):
_got_registered_once = False
_json_encoder: t.Union[t.Type[json.JSONEncoder], None] = None
_json_decoder: t.Union[t.Type[json.JSONDecoder], None] = None
@property
def json_encoder(
self,
) -> t.Union[t.Type[json.JSONEncoder], None]:
"""Blueprint-local JSON encoder class to use. Set to ``None`` to use the app's.
.. deprecated:: 2.2
Will be removed in Flask 2.3. Customize
:attr:`json_provider_class` instead.
.. versionadded:: 0.10
"""
import warnings
warnings.warn(
"'bp.json_encoder' is deprecated and will be removed in Flask 2.3."
" Customize 'app.json_provider_class' or 'app.json' instead.",
DeprecationWarning,
stacklevel=2,
)
return self._json_encoder
@json_encoder.setter
def json_encoder(self, value: t.Union[t.Type[json.JSONEncoder], None]) -> None:
import warnings
warnings.warn(
"'bp.json_encoder' is deprecated and will be removed in Flask 2.3."
" Customize 'app.json_provider_class' or 'app.json' instead.",
DeprecationWarning,
stacklevel=2,
)
self._json_encoder = value
@property
def json_decoder(
self,
) -> t.Union[t.Type[json.JSONDecoder], None]:
"""Blueprint-local JSON decoder class to use. Set to ``None`` to use the app's.
.. deprecated:: 2.2
Will be removed in Flask 2.3. Customize
:attr:`json_provider_class` instead.
.. versionadded:: 0.10
"""
import warnings
warnings.warn(
"'bp.json_decoder' is deprecated and will be removed in Flask 2.3."
" Customize 'app.json_provider_class' or 'app.json' instead.",
DeprecationWarning,
stacklevel=2,
)
return self._json_decoder
@json_decoder.setter
def json_decoder(self, value: t.Union[t.Type[json.JSONDecoder], None]) -> None:
import warnings
warnings.warn(
"'bp.json_decoder' is deprecated and will be removed in Flask 2.3."
" Customize 'app.json_provider_class' or 'app.json' instead.",
DeprecationWarning,
stacklevel=2,
)
self._json_decoder = value
def __init__(
self,
name: str,
@ -265,6 +190,9 @@ class Blueprint(Scaffold):
root_path=root_path,
)
if not name:
raise ValueError("'name' may not be empty.")
if "." in name:
raise ValueError("'name' may not contain a dot '.' character.")
@ -282,19 +210,12 @@ class Blueprint(Scaffold):
def _check_setup_finished(self, f_name: str) -> None:
if self._got_registered_once:
import warnings
warnings.warn(
f"The setup method '{f_name}' can no longer be called on"
f" the blueprint '{self.name}'. It has already been"
" registered at least once, any changes will not be"
" applied consistently.\n"
"Make sure all imports, decorators, functions, etc."
" needed to set up the blueprint are done before"
" registering it.\n"
"This warning will become an exception in Flask 2.3.",
UserWarning,
stacklevel=3,
raise AssertionError(
f"The setup method '{f_name}' can no longer be called on the blueprint"
f" '{self.name}'. It has already been registered at least once, any"
" changes will not be applied consistently.\n"
"Make sure all imports, decorators, functions, etc. needed to set up"
" the blueprint are done before registering it."
)
@setupmethod
@ -361,6 +282,10 @@ class Blueprint(Scaffold):
.. versionchanged:: 2.3
Nested blueprints now correctly apply subdomains.
.. versionchanged:: 2.1
Registering the same blueprint with the same name multiple
times is an error.
.. versionchanged:: 2.0.1
Nested blueprints are registered with their dotted name.
This allows different blueprints with the same name to be
@ -371,10 +296,6 @@ class Blueprint(Scaffold):
name the blueprint is registered with. This allows the same
blueprint to be registered multiple times with unique names
for ``url_for``.
.. versionchanged:: 2.0.1
Registering the same blueprint with the same name multiple
times is deprecated and will become an error in Flask 2.1.
"""
name_prefix = options.get("name_prefix", "")
self_name = options.get("name", self.name)
@ -634,29 +555,6 @@ class Blueprint(Scaffold):
)
return f
@setupmethod
def before_app_first_request(
self, f: T_before_first_request
) -> T_before_first_request:
"""Register a function to run before the first request to the application is
handled by the worker. Equivalent to :meth:`.Flask.before_first_request`.
.. deprecated:: 2.2
Will be removed in Flask 2.3. Run setup code when creating
the application instead.
"""
import warnings
warnings.warn(
"'before_app_first_request' is deprecated and will be"
" removed in Flask 2.3. Use 'record_once' instead to run"
" setup code when registering the blueprint.",
DeprecationWarning,
stacklevel=2,
)
self.record_once(lambda s: s.app.before_first_request_funcs.append(f))
return f
@setupmethod
def after_app_request(self, f: T_after_request) -> T_after_request:
"""Like :meth:`after_request`, but after every request, not only those handled

View file

@ -234,6 +234,7 @@ class Config(dict):
filename: str,
load: t.Callable[[t.IO[t.Any]], t.Mapping],
silent: bool = False,
text: bool = True,
) -> bool:
"""Update the values in the config from a file that is loaded
using the ``load`` parameter. The loaded data is passed to the
@ -244,8 +245,8 @@ class Config(dict):
import json
app.config.from_file("config.json", load=json.load)
import toml
app.config.from_file("config.toml", load=toml.load)
import tomllib
app.config.from_file("config.toml", load=tomllib.load, text=False)
:param filename: The path to the data file. This can be an
absolute path or relative to the config root path.
@ -254,14 +255,18 @@ class Config(dict):
:type load: ``Callable[[Reader], Mapping]`` where ``Reader``
implements a ``read`` method.
:param silent: Ignore the file if it doesn't exist.
:param text: Open the file in text or binary mode.
:return: ``True`` if the file was loaded successfully.
.. versionchanged:: 2.3
The ``text`` parameter was added.
.. versionadded:: 2.0
"""
filename = os.path.join(self.root_path, filename)
try:
with open(filename) as f:
with open(filename, "r" if text else "rb") as f:
obj = load(f)
except OSError as e:
if silent and e.errno in (errno.ENOENT, errno.EISDIR):

View file

@ -17,30 +17,17 @@ class _FakeStack:
self.name = name
self.cv = cv
def _warn(self):
@property
def top(self) -> t.Optional[t.Any]:
import warnings
warnings.warn(
f"'_{self.name}_ctx_stack' is deprecated and will be"
" removed in Flask 2.3. Use 'g' to store data, or"
f" '{self.name}_ctx' to access the current context.",
f"'_{self.name}_ctx_stack' is deprecated and will be removed in Flask 2.4."
f" Use 'g' to store data, or '{self.name}_ctx' to access the current"
" context.",
DeprecationWarning,
stacklevel=3,
stacklevel=2,
)
def push(self, obj: t.Any) -> None:
self._warn()
self.cv.set(obj)
def pop(self) -> t.Any:
self._warn()
ctx = self.cv.get(None)
self.cv.set(None)
return ctx
@property
def top(self) -> t.Optional[t.Any]:
self._warn()
return self.cv.get(None)
@ -88,7 +75,7 @@ def __getattr__(name: str) -> t.Any:
import warnings
warnings.warn(
"'_app_ctx_stack' is deprecated and will be removed in Flask 2.3.",
"'_app_ctx_stack' is deprecated and will be removed in Flask 2.4.",
DeprecationWarning,
stacklevel=2,
)
@ -98,7 +85,7 @@ def __getattr__(name: str) -> t.Any:
import warnings
warnings.warn(
"'_request_ctx_stack' is deprecated and will be removed in Flask 2.3.",
"'_request_ctx_stack' is deprecated and will be removed in Flask 2.4.",
DeprecationWarning,
stacklevel=2,
)

View file

@ -25,45 +25,12 @@ if t.TYPE_CHECKING: # pragma: no cover
import typing_extensions as te
def get_env() -> str:
"""Get the environment the app is running in, indicated by the
:envvar:`FLASK_ENV` environment variable. The default is
``'production'``.
.. deprecated:: 2.2
Will be removed in Flask 2.3.
"""
import warnings
warnings.warn(
"'FLASK_ENV' and 'get_env' are deprecated and will be removed"
" in Flask 2.3. Use 'FLASK_DEBUG' instead.",
DeprecationWarning,
stacklevel=2,
)
return os.environ.get("FLASK_ENV") or "production"
def get_debug_flag() -> bool:
"""Get whether debug mode should be enabled for the app, indicated by the
:envvar:`FLASK_DEBUG` environment variable. The default is ``False``.
"""
val = os.environ.get("FLASK_DEBUG")
if not val:
env = os.environ.get("FLASK_ENV")
if env is not None:
print(
"'FLASK_ENV' is deprecated and will not be used in"
" Flask 2.3. Use 'FLASK_DEBUG' instead.",
file=sys.stderr,
)
return env == "development"
return False
return val.lower() not in {"0", "false", "no"}
return bool(val and val.lower() not in {"0", "false", "no"})
def get_load_dotenv(default: bool = True) -> bool:
@ -646,6 +613,10 @@ class locked_cached_property(werkzeug.utils.cached_property):
:class:`werkzeug.utils.cached_property` except access uses a lock
for thread safety.
.. deprecated:: 2.3
Will be removed in Flask 2.4. Use a lock inside the decorated function if
locking is needed.
.. versionchanged:: 2.0
Inherits from Werkzeug's ``cached_property`` (and ``property``).
"""
@ -656,6 +627,14 @@ class locked_cached_property(werkzeug.utils.cached_property):
name: t.Optional[str] = None,
doc: t.Optional[str] = None,
) -> None:
import warnings
warnings.warn(
"'locked_cached_property' is deprecated and will be removed in Flask 2.4."
" Use a lock inside the decorated function if locking is needed.",
DeprecationWarning,
stacklevel=2,
)
super().__init__(fget, name=name, doc=doc)
self.lock = RLock()

View file

@ -3,85 +3,14 @@ from __future__ import annotations
import json as _json
import typing as t
from jinja2.utils import htmlsafe_json_dumps as _jinja_htmlsafe_dumps
from ..globals import current_app
from .provider import _default
if t.TYPE_CHECKING: # pragma: no cover
from ..app import Flask
from ..wrappers import Response
class JSONEncoder(_json.JSONEncoder):
"""The default JSON encoder. Handles extra types compared to the
built-in :class:`json.JSONEncoder`.
- :class:`datetime.datetime` and :class:`datetime.date` are
serialized to :rfc:`822` strings. This is the same as the HTTP
date format.
- :class:`decimal.Decimal` is serialized to a string.
- :class:`uuid.UUID` is serialized to a string.
- :class:`dataclasses.dataclass` is passed to
:func:`dataclasses.asdict`.
- :class:`~markupsafe.Markup` (or any object with a ``__html__``
method) will call the ``__html__`` method to get a string.
Assign a subclass of this to :attr:`flask.Flask.json_encoder` or
:attr:`flask.Blueprint.json_encoder` to override the default.
.. deprecated:: 2.2
Will be removed in Flask 2.3. Use ``app.json`` instead.
"""
def __init__(self, **kwargs) -> None:
import warnings
warnings.warn(
"'JSONEncoder' is deprecated and will be removed in"
" Flask 2.3. Use 'Flask.json' to provide an alternate"
" JSON implementation instead.",
DeprecationWarning,
stacklevel=3,
)
super().__init__(**kwargs)
def default(self, o: t.Any) -> t.Any:
"""Convert ``o`` to a JSON serializable type. See
:meth:`json.JSONEncoder.default`. Python does not support
overriding how basic types like ``str`` or ``list`` are
serialized, they are handled before this method.
"""
return _default(o)
class JSONDecoder(_json.JSONDecoder):
"""The default JSON decoder.
This does not change any behavior from the built-in
:class:`json.JSONDecoder`.
Assign a subclass of this to :attr:`flask.Flask.json_decoder` or
:attr:`flask.Blueprint.json_decoder` to override the default.
.. deprecated:: 2.2
Will be removed in Flask 2.3. Use ``app.json`` instead.
"""
def __init__(self, **kwargs) -> None:
import warnings
warnings.warn(
"'JSONDecoder' is deprecated and will be removed in"
" Flask 2.3. Use 'Flask.json' to provide an alternate"
" JSON implementation instead.",
DeprecationWarning,
stacklevel=3,
)
super().__init__(**kwargs)
def dumps(obj: t.Any, *, app: Flask | None = None, **kwargs: t.Any) -> str:
def dumps(obj: t.Any, **kwargs: t.Any) -> str:
"""Serialize data as JSON.
If :data:`~flask.current_app` is available, it will use its
@ -91,13 +20,13 @@ def dumps(obj: t.Any, *, app: Flask | None = None, **kwargs: t.Any) -> str:
:param obj: The data to serialize.
:param kwargs: Arguments passed to the ``dumps`` implementation.
.. versionchanged:: 2.3
The ``app`` parameter was removed.
.. versionchanged:: 2.2
Calls ``current_app.json.dumps``, allowing an app to override
the behavior.
.. versionchanged:: 2.2
The ``app`` parameter will be removed in Flask 2.3.
.. versionchanged:: 2.0.2
:class:`decimal.Decimal` is supported by converting to a string.
@ -108,28 +37,14 @@ def dumps(obj: t.Any, *, app: Flask | None = None, **kwargs: t.Any) -> str:
``app`` can be passed directly, rather than requiring an app
context for configuration.
"""
if app is not None:
import warnings
warnings.warn(
"The 'app' parameter is deprecated and will be removed in"
" Flask 2.3. Call 'app.json.dumps' directly instead.",
DeprecationWarning,
stacklevel=2,
)
else:
app = current_app
if app:
return app.json.dumps(obj, **kwargs)
if current_app:
return current_app.json.dumps(obj, **kwargs)
kwargs.setdefault("default", _default)
return _json.dumps(obj, **kwargs)
def dump(
obj: t.Any, fp: t.IO[str], *, app: Flask | None = None, **kwargs: t.Any
) -> None:
def dump(obj: t.Any, fp: t.IO[str], **kwargs: t.Any) -> None:
"""Serialize data as JSON and write to a file.
If :data:`~flask.current_app` is available, it will use its
@ -141,37 +56,25 @@ def dump(
encoding to be valid JSON.
:param kwargs: Arguments passed to the ``dump`` implementation.
.. versionchanged:: 2.3
The ``app`` parameter was removed.
.. versionchanged:: 2.2
Calls ``current_app.json.dump``, allowing an app to override
the behavior.
.. versionchanged:: 2.2
The ``app`` parameter will be removed in Flask 2.3.
.. versionchanged:: 2.0
Writing to a binary file, and the ``encoding`` argument, will be
removed in Flask 2.1.
"""
if app is not None:
import warnings
warnings.warn(
"The 'app' parameter is deprecated and will be removed in"
" Flask 2.3. Call 'app.json.dump' directly instead.",
DeprecationWarning,
stacklevel=2,
)
else:
app = current_app
if app:
app.json.dump(obj, fp, **kwargs)
if current_app:
current_app.json.dump(obj, fp, **kwargs)
else:
kwargs.setdefault("default", _default)
_json.dump(obj, fp, **kwargs)
def loads(s: str | bytes, *, app: Flask | None = None, **kwargs: t.Any) -> t.Any:
def loads(s: str | bytes, **kwargs: t.Any) -> t.Any:
"""Deserialize data as JSON.
If :data:`~flask.current_app` is available, it will use its
@ -181,13 +84,13 @@ def loads(s: str | bytes, *, app: Flask | None = None, **kwargs: t.Any) -> t.Any
:param s: Text or UTF-8 bytes.
:param kwargs: Arguments passed to the ``loads`` implementation.
.. versionchanged:: 2.3
The ``app`` parameter was removed.
.. versionchanged:: 2.2
Calls ``current_app.json.loads``, allowing an app to override
the behavior.
.. versionchanged:: 2.2
The ``app`` parameter will be removed in Flask 2.3.
.. versionchanged:: 2.0
``encoding`` will be removed in Flask 2.1. The data must be a
string or UTF-8 bytes.
@ -196,25 +99,13 @@ def loads(s: str | bytes, *, app: Flask | None = None, **kwargs: t.Any) -> t.Any
``app`` can be passed directly, rather than requiring an app
context for configuration.
"""
if app is not None:
import warnings
warnings.warn(
"The 'app' parameter is deprecated and will be removed in"
" Flask 2.3. Call 'app.json.loads' directly instead.",
DeprecationWarning,
stacklevel=2,
)
else:
app = current_app
if app:
return app.json.loads(s, **kwargs)
if current_app:
return current_app.json.loads(s, **kwargs)
return _json.loads(s, **kwargs)
def load(fp: t.IO[t.AnyStr], *, app: Flask | None = None, **kwargs: t.Any) -> t.Any:
def load(fp: t.IO[t.AnyStr], **kwargs: t.Any) -> t.Any:
"""Deserialize data as JSON read from a file.
If :data:`~flask.current_app` is available, it will use its
@ -224,6 +115,9 @@ def load(fp: t.IO[t.AnyStr], *, app: Flask | None = None, **kwargs: t.Any) -> t.
:param fp: A file opened for reading text or UTF-8 bytes.
:param kwargs: Arguments passed to the ``load`` implementation.
.. versionchanged:: 2.3
The ``app`` parameter was removed.
.. versionchanged:: 2.2
Calls ``current_app.json.load``, allowing an app to override
the behavior.
@ -235,78 +129,12 @@ def load(fp: t.IO[t.AnyStr], *, app: Flask | None = None, **kwargs: t.Any) -> t.
``encoding`` will be removed in Flask 2.1. The file must be text
mode, or binary mode with UTF-8 bytes.
"""
if app is not None:
import warnings
warnings.warn(
"The 'app' parameter is deprecated and will be removed in"
" Flask 2.3. Call 'app.json.load' directly instead.",
DeprecationWarning,
stacklevel=2,
)
else:
app = current_app
if app:
return app.json.load(fp, **kwargs)
if current_app:
return current_app.json.load(fp, **kwargs)
return _json.load(fp, **kwargs)
def htmlsafe_dumps(obj: t.Any, **kwargs: t.Any) -> str:
"""Serialize an object to a string of JSON with :func:`dumps`, then
replace HTML-unsafe characters with Unicode escapes and mark the
result safe with :class:`~markupsafe.Markup`.
This is available in templates as the ``|tojson`` filter.
The returned string is safe to render in HTML documents and
``<script>`` tags. The exception is in HTML attributes that are
double quoted; either use single quotes or the ``|forceescape``
filter.
.. deprecated:: 2.2
Will be removed in Flask 2.3. This is built-in to Jinja now.
.. versionchanged:: 2.0
Uses :func:`jinja2.utils.htmlsafe_json_dumps`. The returned
value is marked safe by wrapping in :class:`~markupsafe.Markup`.
.. versionchanged:: 0.10
Single quotes are escaped, making this safe to use in HTML,
``<script>`` tags, and single-quoted attributes without further
escaping.
"""
import warnings
warnings.warn(
"'htmlsafe_dumps' is deprecated and will be removed in Flask"
" 2.3. Use 'jinja2.utils.htmlsafe_json_dumps' instead.",
DeprecationWarning,
stacklevel=2,
)
return _jinja_htmlsafe_dumps(obj, dumps=dumps, **kwargs)
def htmlsafe_dump(obj: t.Any, fp: t.IO[str], **kwargs: t.Any) -> None:
"""Serialize an object to JSON written to a file object, replacing
HTML-unsafe characters with Unicode escapes. See
:func:`htmlsafe_dumps` and :func:`dumps`.
.. deprecated:: 2.2
Will be removed in Flask 2.3.
"""
import warnings
warnings.warn(
"'htmlsafe_dump' is deprecated and will be removed in Flask"
" 2.3. Use 'jinja2.utils.htmlsafe_json_dumps' instead.",
DeprecationWarning,
stacklevel=2,
)
fp.write(htmlsafe_dumps(obj, **kwargs))
def jsonify(*args: t.Any, **kwargs: t.Any) -> Response:
"""Serialize the given arguments as JSON, and return a
:class:`~flask.Response` object with the ``application/json``

View file

@ -10,8 +10,6 @@ from datetime import date
from werkzeug.http import http_date
from ..globals import request
if t.TYPE_CHECKING: # pragma: no cover
from ..app import Flask
from ..wrappers import Response
@ -176,57 +174,9 @@ class DefaultJSONProvider(JSONProvider):
:param obj: The data to serialize.
:param kwargs: Passed to :func:`json.dumps`.
"""
cls = self._app._json_encoder
bp = self._app.blueprints.get(request.blueprint) if request else None
if bp is not None and bp._json_encoder is not None:
cls = bp._json_encoder
if cls is not None:
import warnings
warnings.warn(
"Setting 'json_encoder' on the app or a blueprint is"
" deprecated and will be removed in Flask 2.3."
" Customize 'app.json' instead.",
DeprecationWarning,
)
kwargs.setdefault("cls", cls)
if "default" not in cls.__dict__:
kwargs.setdefault("default", self.default)
else:
kwargs.setdefault("default", self.default)
ensure_ascii = self._app.config["JSON_AS_ASCII"]
sort_keys = self._app.config["JSON_SORT_KEYS"]
if ensure_ascii is not None:
import warnings
warnings.warn(
"The 'JSON_AS_ASCII' config key is deprecated and will"
" be removed in Flask 2.3. Set 'app.json.ensure_ascii'"
" instead.",
DeprecationWarning,
)
else:
ensure_ascii = self.ensure_ascii
if sort_keys is not None:
import warnings
warnings.warn(
"The 'JSON_SORT_KEYS' config key is deprecated and will"
" be removed in Flask 2.3. Set 'app.json.sort_keys'"
" instead.",
DeprecationWarning,
)
else:
sort_keys = self.sort_keys
kwargs.setdefault("ensure_ascii", ensure_ascii)
kwargs.setdefault("sort_keys", sort_keys)
kwargs.setdefault("default", self.default)
kwargs.setdefault("ensure_ascii", self.ensure_ascii)
kwargs.setdefault("sort_keys", self.sort_keys)
return json.dumps(obj, **kwargs)
def loads(self, s: str | bytes, **kwargs: t.Any) -> t.Any:
@ -235,23 +185,6 @@ class DefaultJSONProvider(JSONProvider):
:param s: Text or UTF-8 bytes.
:param kwargs: Passed to :func:`json.loads`.
"""
cls = self._app._json_decoder
bp = self._app.blueprints.get(request.blueprint) if request else None
if bp is not None and bp._json_decoder is not None:
cls = bp._json_decoder
if cls is not None:
import warnings
warnings.warn(
"Setting 'json_decoder' on the app or a blueprint is"
" deprecated and will be removed in Flask 2.3."
" Customize 'app.json' instead.",
DeprecationWarning,
)
kwargs.setdefault("cls", cls)
return json.loads(s, **kwargs)
def response(self, *args: t.Any, **kwargs: t.Any) -> Response:
@ -272,39 +205,12 @@ class DefaultJSONProvider(JSONProvider):
"""
obj = self._prepare_response_obj(args, kwargs)
dump_args: t.Dict[str, t.Any] = {}
pretty = self._app.config["JSONIFY_PRETTYPRINT_REGULAR"]
mimetype = self._app.config["JSONIFY_MIMETYPE"]
if pretty is not None:
import warnings
warnings.warn(
"The 'JSONIFY_PRETTYPRINT_REGULAR' config key is"
" deprecated and will be removed in Flask 2.3. Set"
" 'app.json.compact' instead.",
DeprecationWarning,
)
compact: bool | None = not pretty
else:
compact = self.compact
if (compact is None and self._app.debug) or compact is False:
if (self.compact is None and self._app.debug) or self.compact is False:
dump_args.setdefault("indent", 2)
else:
dump_args.setdefault("separators", (",", ":"))
if mimetype is not None:
import warnings
warnings.warn(
"The 'JSONIFY_MIMETYPE' config key is deprecated and"
" will be removed in Flask 2.3. Set 'app.json.mimetype'"
" instead.",
DeprecationWarning,
)
else:
mimetype = self.mimetype
return self._app.response_class(
f"{self.dumps(obj, **dump_args)}\n", mimetype=mimetype
f"{self.dumps(obj, **dump_args)}\n", mimetype=self.mimetype
)

View file

@ -1,5 +1,4 @@
import importlib.util
import json
import os
import pathlib
import pkgutil
@ -12,12 +11,12 @@ from functools import update_wrapper
from jinja2 import FileSystemLoader
from werkzeug.exceptions import default_exceptions
from werkzeug.exceptions import HTTPException
from werkzeug.utils import cached_property
from . import typing as ft
from .cli import AppGroup
from .globals import current_app
from .helpers import get_root_path
from .helpers import locked_cached_property
from .helpers import send_from_directory
from .templating import _default_template_ctx_processor
@ -74,20 +73,6 @@ class Scaffold:
_static_folder: t.Optional[str] = None
_static_url_path: t.Optional[str] = None
#: JSON encoder class used by :func:`flask.json.dumps`. If a
#: blueprint sets this, it will be used instead of the app's value.
#:
#: .. deprecated:: 2.2
#: Will be removed in Flask 2.3.
json_encoder: t.Union[t.Type[json.JSONEncoder], None] = None
#: JSON decoder class used by :func:`flask.json.loads`. If a
#: blueprint sets this, it will be used instead of the app's value.
#:
#: .. deprecated:: 2.2
#: Will be removed in Flask 2.3.
json_decoder: t.Union[t.Type[json.JSONDecoder], None] = None
def __init__(
self,
import_name: str,
@ -332,7 +317,7 @@ class Scaffold:
t.cast(str, self.static_folder), filename, max_age=max_age
)
@locked_cached_property
@cached_property
def jinja_loader(self) -> t.Optional[FileSystemLoader]:
"""The Jinja loader for this object's templates. By default this
is a class :class:`jinja2.loaders.FileSystemLoader` to

View file

@ -3,11 +3,11 @@ from contextlib import contextmanager
from contextlib import ExitStack
from copy import copy
from types import TracebackType
from urllib.parse import urlsplit
import werkzeug.test
from click.testing import CliRunner
from werkzeug.test import Client
from werkzeug.urls import url_parse
from werkzeug.wrappers import Request as BaseRequest
from .cli import ScriptInfo
@ -68,7 +68,7 @@ class EnvironBuilder(werkzeug.test.EnvironBuilder):
if url_scheme is None:
url_scheme = app.config["PREFERRED_URL_SCHEME"]
url = url_parse(path)
url = urlsplit(path)
base_url = (
f"{url.scheme or url_scheme}://{url.netloc or http_host}"
f"/{app_root.lstrip('/')}"

View file

@ -20,7 +20,6 @@ def _standard_os_environ():
out = (
(os.environ, "FLASK_ENV_FILE", monkeypatch.notset),
(os.environ, "FLASK_APP", monkeypatch.notset),
(os.environ, "FLASK_ENV", monkeypatch.notset),
(os.environ, "FLASK_DEBUG", monkeypatch.notset),
(os.environ, "FLASK_RUN_FROM_CLI", monkeypatch.notset),
(os.environ, "WERKZEUG_RUN_MAIN", monkeypatch.notset),

2
tests/static/config.toml Normal file
View file

@ -0,0 +1,2 @@
TEST_KEY="foo"
SECRET_KEY="config"

View file

@ -95,7 +95,6 @@ def test_async_error_handler(path, async_app):
def test_async_before_after_request():
app_first_called = False
app_before_called = False
app_after_called = False
bp_before_called = False
@ -107,13 +106,6 @@ def test_async_before_after_request():
def index():
return ""
with pytest.deprecated_call():
@app.before_first_request
async def before_first():
nonlocal app_first_called
app_first_called = True
@app.before_request
async def before():
nonlocal app_before_called
@ -146,7 +138,6 @@ def test_async_before_after_request():
test_client = app.test_client()
test_client.get("/")
assert app_first_called
assert app_before_called
assert app_after_called
test_client.get("/bp/")

View file

@ -1,16 +1,15 @@
import gc
import re
import time
import uuid
import warnings
import weakref
from datetime import datetime
from datetime import timezone
from platform import python_implementation
from threading import Thread
import pytest
import werkzeug.serving
from markupsafe import Markup
from werkzeug.exceptions import BadRequest
from werkzeug.exceptions import Forbidden
from werkzeug.exceptions import NotFound
@ -474,7 +473,7 @@ def test_session_special_types(app, client):
def dump_session_contents():
flask.session["t"] = (1, 2, 3)
flask.session["b"] = b"\xff"
flask.session["m"] = flask.Markup("<html>")
flask.session["m"] = Markup("<html>")
flask.session["u"] = the_uuid
flask.session["d"] = now
flask.session["t_tag"] = {" t": "not-a-tuple"}
@ -488,8 +487,8 @@ def test_session_special_types(app, client):
assert s["t"] == (1, 2, 3)
assert type(s["b"]) == bytes
assert s["b"] == b"\xff"
assert type(s["m"]) == flask.Markup
assert s["m"] == flask.Markup("<html>")
assert type(s["m"]) == Markup
assert s["m"] == Markup("<html>")
assert s["u"] == the_uuid
assert s["d"] == now
assert s["t_tag"] == {" t": "not-a-tuple"}
@ -613,7 +612,7 @@ def test_extended_flashing(app):
def index():
flask.flash("Hello World")
flask.flash("Hello World", "error")
flask.flash(flask.Markup("<em>Testing</em>"), "warning")
flask.flash(Markup("<em>Testing</em>"), "warning")
return ""
@app.route("/test/")
@ -622,7 +621,7 @@ def test_extended_flashing(app):
assert list(messages) == [
"Hello World",
"Hello World",
flask.Markup("<em>Testing</em>"),
Markup("<em>Testing</em>"),
]
return ""
@ -633,7 +632,7 @@ def test_extended_flashing(app):
assert list(messages) == [
("message", "Hello World"),
("error", "Hello World"),
("warning", flask.Markup("<em>Testing</em>")),
("warning", Markup("<em>Testing</em>")),
]
return ""
@ -652,7 +651,7 @@ def test_extended_flashing(app):
)
assert list(messages) == [
("message", "Hello World"),
("warning", flask.Markup("<em>Testing</em>")),
("warning", Markup("<em>Testing</em>")),
]
return ""
@ -661,7 +660,7 @@ def test_extended_flashing(app):
messages = flask.get_flashed_messages(category_filter=["message", "warning"])
assert len(messages) == 2
assert messages[0] == "Hello World"
assert messages[1] == flask.Markup("<em>Testing</em>")
assert messages[1] == Markup("<em>Testing</em>")
return ""
# Create new test client on each test to clean flashed messages.
@ -1658,7 +1657,6 @@ def test_no_setup_after_first_request(app, client):
def index():
return "Awesome"
assert not app.got_first_request
assert client.get("/").data == b"Awesome"
with pytest.raises(AssertionError) as exc_info:
@ -1667,43 +1665,6 @@ def test_no_setup_after_first_request(app, client):
assert "setup method 'add_url_rule'" in str(exc_info.value)
def test_before_first_request_functions(app, client):
got = []
with pytest.deprecated_call():
@app.before_first_request
def foo():
got.append(42)
client.get("/")
assert got == [42]
client.get("/")
assert got == [42]
assert app.got_first_request
def test_before_first_request_functions_concurrent(app, client):
got = []
with pytest.deprecated_call():
@app.before_first_request
def foo():
time.sleep(0.2)
got.append(42)
def get_and_assert():
client.get("/")
assert got == [42]
t = Thread(target=get_and_assert)
t.start()
get_and_assert()
t.join()
assert app.got_first_request
def test_routing_redirect_debugging(monkeypatch, app, client):
app.config["DEBUG"] = True

View file

@ -256,6 +256,11 @@ def test_dotted_name_not_allowed(app, client):
flask.Blueprint("app.ui", __name__)
def test_empty_name_not_allowed(app, client):
with pytest.raises(ValueError):
flask.Blueprint("", __name__)
def test_dotted_names_from_app(app, client):
test = flask.Blueprint("test", __name__)
@ -722,12 +727,6 @@ def test_app_request_processing(app, client):
bp = flask.Blueprint("bp", __name__)
evts = []
with pytest.deprecated_call():
@bp.before_app_first_request
def before_first_request():
evts.append("first")
@bp.before_app_request
def before_app():
evts.append("before")
@ -755,12 +754,12 @@ def test_app_request_processing(app, client):
# first request
resp = client.get("/").data
assert resp == b"request|after"
assert evts == ["first", "before", "after", "teardown"]
assert evts == ["before", "after", "teardown"]
# second request
resp = client.get("/").data
assert resp == b"request|after"
assert evts == ["first"] + ["before", "after", "teardown"] * 2
assert evts == ["before", "after", "teardown"] * 2
def test_app_url_processors(app, client):

View file

@ -6,7 +6,6 @@ import pytest
import flask
# config keys used for the TestConfig
TEST_KEY = "foo"
SECRET_KEY = "config"
@ -30,13 +29,23 @@ def test_config_from_object():
common_object_test(app)
def test_config_from_file():
def test_config_from_file_json():
app = flask.Flask(__name__)
current_dir = os.path.dirname(os.path.abspath(__file__))
app.config.from_file(os.path.join(current_dir, "static", "config.json"), json.load)
common_object_test(app)
def test_config_from_file_toml():
tomllib = pytest.importorskip("tomllib", reason="tomllib added in 3.11")
app = flask.Flask(__name__)
current_dir = os.path.dirname(os.path.abspath(__file__))
app.config.from_file(
os.path.join(current_dir, "static", "config.toml"), tomllib.load, text=False
)
common_object_test(app)
def test_from_prefixed_env(monkeypatch):
monkeypatch.setenv("FLASK_STRING", "value")
monkeypatch.setenv("FLASK_BOOL", "true")

View file

@ -302,24 +302,18 @@ class TestStreaming:
class TestHelpers:
@pytest.mark.parametrize(
"debug, expected_flag, expected_default_flag",
("debug", "expect"),
[
("", False, False),
("0", False, False),
("False", False, False),
("No", False, False),
("True", True, True),
("", False),
("0", False),
("False", False),
("No", False),
("True", True),
],
)
def test_get_debug_flag(
self, monkeypatch, debug, expected_flag, expected_default_flag
):
def test_get_debug_flag(self, monkeypatch, debug, expect):
monkeypatch.setenv("FLASK_DEBUG", debug)
if expected_flag is None:
assert get_debug_flag() is None
else:
assert get_debug_flag() == expected_flag
assert get_debug_flag() == expected_default_flag
assert get_debug_flag() == expect
def test_make_response(self):
app = flask.Flask(__name__)

View file

@ -3,8 +3,8 @@ from datetime import timezone
from uuid import uuid4
import pytest
from markupsafe import Markup
from flask import Markup
from flask.json.tag import JSONTag
from flask.json.tag import TaggedJSONSerializer

View file

@ -3,6 +3,7 @@ import logging
import pytest
import werkzeug.serving
from jinja2 import TemplateNotFound
from markupsafe import Markup
import flask
@ -73,7 +74,7 @@ def test_escaping(app, client):
@app.route("/")
def index():
return flask.render_template(
"escaping_template.html", text=text, html=flask.Markup(text)
"escaping_template.html", text=text, html=Markup(text)
)
lines = client.get("/").data.splitlines()
@ -93,7 +94,7 @@ def test_no_escaping(app, client):
@app.route("/")
def index():
return flask.render_template(
"non_escaping_template.txt", text=text, html=flask.Markup(text)
"non_escaping_template.txt", text=text, html=Markup(text)
)
lines = client.get("/").data.splitlines()