Merge branch 'main' into pr/emisargent/4969
This commit is contained in:
commit
58c52e84cd
38 changed files with 282 additions and 1063 deletions
2
.github/workflows/publish.yaml
vendored
2
.github/workflows/publish.yaml
vendored
|
|
@ -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:
|
||||
|
|
|
|||
2
.github/workflows/tests.yaml
vendored
2
.github/workflows/tests.yaml
vendored
|
|
@ -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') }}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
45
CHANGES.rst
45
CHANGES.rst
|
|
@ -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`
|
||||
|
||||
|
|
|
|||
10
docs/api.rst
10
docs/api.rst
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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/>`__
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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/
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -2,4 +2,4 @@ pytest
|
|||
asgiref
|
||||
blinker
|
||||
greenlet ; python_version < "3.11"
|
||||
python-dotenv
|
||||
python-dotenv>=1; python_version >= "3.8"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
369
src/flask/app.py
369
src/flask/app.py
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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``
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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('/')}"
|
||||
|
|
|
|||
|
|
@ -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
2
tests/static/config.toml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
TEST_KEY="foo"
|
||||
SECRET_KEY="config"
|
||||
|
|
@ -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/")
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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__)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue