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

flask

Package Overview
Dependencies
Maintainers
0
Versions
64
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

flask - pypi Package Compare versions

Comparing version
2.2.5
to
2.3.3
docs/_static/flask-horizontal.png

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

+29
[project]
name = "js_example"
version = "1.1.0"
description = "Demonstrates making AJAX requests to Flask."
readme = "README.rst"
license = {file = "LICENSE.rst"}
maintainers = [{name = "Pallets", email = "contact@palletsprojects.com"}]
dependencies = ["flask"]
[project.urls]
Documentation = "https://flask.palletsprojects.com/patterns/jquery/"
[project.optional-dependencies]
test = ["pytest"]
[build-system]
requires = ["flit_core<4"]
build-backend = "flit_core.buildapi"
[tool.flit.module]
name = "js_example"
[tool.pytest.ini_options]
testpaths = ["tests"]
filterwarnings = ["error"]
[tool.coverage.run]
branch = true
source = ["js_example", "tests"]
[project]
name = "flaskr"
version = "1.0.0"
description = "The basic blog app built in the Flask tutorial."
readme = "README.rst"
license = {text = "BSD-3-Clause"}
maintainers = [{name = "Pallets", email = "contact@palletsprojects.com"}]
dependencies = [
"flask",
]
[project.urls]
Documentation = "https://flask.palletsprojects.com/tutorial/"
[project.optional-dependencies]
test = ["pytest"]
[build-system]
requires = ["flit_core<4"]
build-backend = "flit_core.buildapi"
[tool.flit.module]
name = "flaskr"
[tool.flit.sdist]
include = [
"tests/",
]
[tool.pytest.ini_options]
testpaths = ["tests"]
filterwarnings = ["error"]
[tool.coverage.run]
branch = true
source = ["flaskr", "tests"]
[project]
name = "Flask"
description = "A simple framework for building complex web applications."
readme = "README.rst"
license = {file = "LICENSE.rst"}
maintainers = [{name = "Pallets", email = "contact@palletsprojects.com"}]
classifiers = [
"Development Status :: 5 - Production/Stable",
"Environment :: Web Environment",
"Framework :: Flask",
"Intended Audience :: Developers",
"License :: OSI Approved :: BSD License",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Topic :: Internet :: WWW/HTTP :: Dynamic Content",
"Topic :: Internet :: WWW/HTTP :: WSGI",
"Topic :: Internet :: WWW/HTTP :: WSGI :: Application",
"Topic :: Software Development :: Libraries :: Application Frameworks",
]
requires-python = ">=3.8"
dependencies = [
"Werkzeug>=2.3.7",
"Jinja2>=3.1.2",
"itsdangerous>=2.1.2",
"click>=8.1.3",
"blinker>=1.6.2",
"importlib-metadata>=3.6.0; python_version < '3.10'",
]
dynamic = ["version"]
[project.urls]
Donate = "https://palletsprojects.com/donate"
Documentation = "https://flask.palletsprojects.com/"
Changes = "https://flask.palletsprojects.com/changes/"
"Source Code" = "https://github.com/pallets/flask/"
"Issue Tracker" = "https://github.com/pallets/flask/issues/"
Chat = "https://discord.gg/pallets"
[project.optional-dependencies]
async = ["asgiref>=3.2"]
dotenv = ["python-dotenv"]
[project.scripts]
flask = "flask.cli:main"
[build-system]
requires = ["flit_core<4"]
build-backend = "flit_core.buildapi"
[tool.flit.module]
name = "flask"
[tool.flit.sdist]
include = [
"docs/",
"examples/",
"requirements/",
"tests/",
"CHANGES.rst",
"CONTRIBUTING.rst",
"tox.ini",
]
exclude = [
"docs/_build/",
]
[tool.pytest.ini_options]
testpaths = ["tests"]
filterwarnings = [
"error",
]
[tool.coverage.run]
branch = true
source = ["flask", "tests"]
[tool.coverage.paths]
source = ["src", "*/site-packages"]
[tool.mypy]
python_version = "3.8"
files = ["src/flask"]
show_error_codes = true
pretty = true
#strict = true
allow_redefinition = true
disallow_subclassing_any = true
#disallow_untyped_calls = true
#disallow_untyped_defs = true
#disallow_incomplete_defs = true
no_implicit_optional = true
local_partial_types = true
#no_implicit_reexport = true
strict_equality = true
warn_redundant_casts = true
warn_unused_configs = true
warn_unused_ignores = true
#warn_return_any = true
#warn_unreachable = true
[[tool.mypy.overrides]]
module = [
"asgiref.*",
"dotenv.*",
"cryptography.*",
"importlib_metadata",
]
ignore_missing_imports = true
-r docs.in
-r tests.in
-r typing.in
pip-compile-multi
pre-commit
tox
Pallets-Sphinx-Themes
Sphinx
sphinx-issues
sphinxcontrib-log-cabinet
sphinx-tabs
Werkzeug==2.3.7
Jinja2==3.1.2
MarkupSafe==2.1.1
itsdangerous==2.1.2
click==8.1.3
blinker==1.6.2
pytest
asgiref
greenlet ; python_version < "3.11"
python-dotenv
mypy
types-contextvars
types-dataclasses
cryptography
TEST_KEY="foo"
SECRET_KEY="config"
+53
-42

@@ -70,5 +70,25 @@ How to contribute to Flask

First time setup
~~~~~~~~~~~~~~~~
First time setup using GitHub Codespaces
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
`GitHub Codespaces`_ creates a development environment that is already set up for the
project. By default it opens in Visual Studio Code for the Web, but this can
be changed in your GitHub profile settings to use Visual Studio Code or JetBrains
PyCharm on your local computer.
- Make sure you have a `GitHub account`_.
- From the project's repository page, click the green "Code" button and then "Create
codespace on main".
- The codespace will be set up, then Visual Studio Code will open. However, you'll
need to wait a bit longer for the Python extension to be installed. You'll know it's
ready when the terminal at the bottom shows that the virtualenv was activated.
- Check out a branch and `start coding`_.
.. _GitHub Codespaces: https://docs.github.com/en/codespaces
.. _devcontainer: https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/adding-a-dev-container-configuration/introduction-to-dev-containers
First time setup in your local environment
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Make sure you have a `GitHub account`_.
- Download and install the `latest version of git`_.

@@ -82,22 +102,13 @@ - Configure git with your `username`_ and `email`_.

- Make sure you have a `GitHub account`_.
- Fork Flask to your GitHub account by clicking the `Fork`_ button.
- `Clone`_ the main repository locally.
- `Clone`_ your fork locally, replacing ``your-username`` in the command below with
your actual username.
.. code-block:: text
$ git clone https://github.com/pallets/flask
$ git clone https://github.com/your-username/flask
$ cd flask
- Add your fork as a remote to push your work to. Replace
``{username}`` with your username. This names the remote "fork", the
default Pallets remote is "origin".
- Create a virtualenv. Use the latest version of Python.
.. code-block:: text
$ git remote add fork https://github.com/{username}/flask
- Create a virtualenv.
- Linux/macOS

@@ -107,4 +118,4 @@

$ python3 -m venv env
$ . env/bin/activate
$ python3 -m venv .venv
$ . .venv/bin/activate

@@ -115,16 +126,10 @@ - Windows

> py -3 -m venv env
> env\Scripts\activate
> py -3 -m venv .venv
> .venv\Scripts\activate
- Upgrade pip and setuptools.
- Install the development dependencies, then install Flask in editable mode.
.. code-block:: text
$ python -m pip install --upgrade pip setuptools
- Install the development dependencies, then install Flask in editable
mode.
.. code-block:: text
$ python -m pip install -U pip
$ pip install -r requirements/dev.txt && pip install -e .

@@ -136,11 +141,12 @@

$ pre-commit install
$ pre-commit install --install-hooks
.. _GitHub account: https://github.com/join
.. _latest version of git: https://git-scm.com/downloads
.. _username: https://docs.github.com/en/github/using-git/setting-your-username-in-git
.. _email: https://docs.github.com/en/github/setting-up-and-managing-your-github-user-account/setting-your-commit-email-address
.. _GitHub account: https://github.com/join
.. _Fork: https://github.com/pallets/flask/fork
.. _Clone: https://docs.github.com/en/github/getting-started-with-github/fork-a-repo#step-2-create-a-local-clone-of-your-fork
.. _start coding:

@@ -150,5 +156,4 @@ Start coding

- Create a branch to identify the issue you would like to work on. If
you're submitting a bug or documentation fix, branch off of the
latest ".x" branch.
- Create a branch to identify the issue you would like to work on. If you're
submitting a bug or documentation fix, branch off of the latest ".x" branch.

@@ -160,4 +165,3 @@ .. code-block:: text

If you're submitting a feature addition or change, branch off of the
"main" branch.
If you're submitting a feature addition or change, branch off of the "main" branch.

@@ -169,17 +173,21 @@ .. code-block:: text

- Using your favorite editor, make your changes,
`committing as you go`_.
- Include tests that cover any code changes you make. Make sure the
test fails without your patch. Run the tests as described below.
- Push your commits to your fork on GitHub and
`create a pull request`_. Link to the issue being addressed with
``fixes #123`` in the pull request.
- Using your favorite editor, make your changes, `committing as you go`_.
- If you are in a codespace, you will be prompted to `create a fork`_ the first
time you make a commit. Enter ``Y`` to continue.
- Include tests that cover any code changes you make. Make sure the test fails without
your patch. Run the tests as described below.
- Push your commits to your fork on GitHub and `create a pull request`_. Link to the
issue being addressed with ``fixes #123`` in the pull request description.
.. code-block:: text
$ git push --set-upstream fork your-branch-name
$ git push --set-upstream origin your-branch-name
.. _committing as you go: https://afraid-to-commit.readthedocs.io/en/latest/git/commandlinegit.html#commit-your-changes
.. _create a fork: https://docs.github.com/en/codespaces/developing-in-codespaces/using-source-control-in-your-codespace#about-automatic-forking
.. _create a pull request: https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request
.. _Running the tests:

@@ -212,2 +220,5 @@ Running the tests

If you are using GitHub Codespaces, ``coverage`` is already installed
so you can skip the installation command.
.. code-block:: text

@@ -214,0 +225,0 @@

@@ -220,7 +220,3 @@ API

.. autofunction:: escape
.. autoclass:: Markup
:members: escape, unescape, striptags
Message Flashing

@@ -274,8 +270,2 @@ ----------------

.. autoclass:: JSONEncoder
:members:
.. autoclass:: JSONDecoder
:members:
.. automodule:: flask.json.tag

@@ -348,11 +338,6 @@

.. versionadded:: 0.6
Signals are provided by the `Blinker`_ library. See :doc:`signals` for an introduction.
.. data:: signals.signals_available
.. _blinker: https://blinker.readthedocs.io/
``True`` if the signaling system is available. This is the case
when `blinker`_ is installed.
The following signals exist in Flask:
.. data:: template_rendered

@@ -523,3 +508,2 @@

.. data:: message_flashed

@@ -542,20 +526,8 @@

.. class:: signals.Namespace
.. data:: signals.signals_available
An alias for :class:`blinker.base.Namespace` if blinker is available,
otherwise a dummy class that creates fake signals. This class is
available for Flask extensions that want to provide the same fallback
system as Flask itself.
.. deprecated:: 2.3
Will be removed in Flask 2.4. Signals are always available
.. method:: signal(name, doc=None)
Creates a new signal for this namespace if blinker is available,
otherwise returns a fake signal that has a send method that will
do nothing but will fail with a :exc:`RuntimeError` for all other
operations, including connecting.
.. _blinker: https://pypi.org/project/blinker/
Class-Based Views

@@ -562,0 +534,0 @@ -----------------

@@ -143,8 +143,6 @@ .. currentmodule:: flask

The application will call functions registered with
:meth:`~Flask.teardown_appcontext` when the application context is
popped.
The application will call functions registered with :meth:`~Flask.teardown_appcontext`
when the application context is popped.
If :data:`~signals.signals_available` is true, the following signals are
sent: :data:`appcontext_pushed`, :data:`appcontext_tearing_down`, and
:data:`appcontext_popped`.
The following signals are sent: :data:`appcontext_pushed`,
:data:`appcontext_tearing_down`, and :data:`appcontext_popped`.

@@ -143,2 +143,15 @@ Modular Applications with Blueprints

In addition a child blueprint's will gain their parent's subdomain,
with their subdomain as prefix if present i.e.
.. code-block:: python
parent = Blueprint('parent', __name__, subdomain='parent')
child = Blueprint('child', __name__, subdomain='child')
parent.register_blueprint(child)
app.register_blueprint(parent)
url_for('parent.child.create', _external=True)
"child.parent.domain.tld"
Blueprint-specific before request functions, etc. registered with the

@@ -145,0 +158,0 @@ parent will trigger for the child. If a child does not have an error

@@ -291,3 +291,3 @@ .. currentmodule:: flask

Unix Bash, :file:`venv/bin/activate`::
Unix Bash, :file:`.venv/bin/activate`::

@@ -298,3 +298,3 @@ $ export FLASK_APP=hello

Fish, :file:`venv/bin/activate.fish`::
Fish, :file:`.venv/bin/activate.fish`::

@@ -305,3 +305,3 @@ $ set -x FLASK_APP hello

Windows CMD, :file:`venv\\Scripts\\activate.bat`::
Windows CMD, :file:`.venv\\Scripts\\activate.bat`::

@@ -312,3 +312,3 @@ > set FLASK_APP=hello

Windows Powershell, :file:`venv\\Scripts\\activate.ps1`::
Windows Powershell, :file:`.venv\\Scripts\\activate.ps1`::

@@ -453,20 +453,12 @@ > $env:FLASK_APP = "hello"

`entry point`_. This is useful for extensions that want to add commands when
they are installed. Entry points are specified in :file:`setup.py` ::
they are installed. Entry points are specified in :file:`pyproject.toml`:
from setuptools import setup
.. code-block:: toml
setup(
name='flask-my-extension',
...,
entry_points={
'flask.commands': [
'my-command=flask_my_extension.commands:cli'
],
},
)
[project.entry-points."flask.commands"]
my-command = "my_extension.commands:cli"
.. _entry point: https://packaging.python.org/tutorials/packaging-projects/#entry-points
Inside :file:`flask_my_extension/commands.py` you can then export a Click
Inside :file:`my_extension/commands.py` you can then export a Click
object::

@@ -509,15 +501,8 @@

Define the entry point in :file:`setup.py`::
Define the entry point in :file:`pyproject.toml`:
from setuptools import setup
.. code-block:: toml
setup(
name='flask-my-extension',
...,
entry_points={
'console_scripts': [
'wiki=wiki:cli'
],
},
)
[project.scripts]
wiki = "wiki:cli"

@@ -524,0 +509,0 @@ Install the application in the virtualenv in editable mode and the custom

@@ -46,4 +46,2 @@ import packaging.version

ProjectLink("Issue Tracker", "https://github.com/pallets/flask/issues/"),
ProjectLink("Website", "https://palletsprojects.com/p/flask/"),
ProjectLink("Twitter", "https://twitter.com/PalletsTeam"),
ProjectLink("Chat", "https://discord.gg/pallets"),

@@ -58,4 +56,4 @@ ]

html_static_path = ["_static"]
html_favicon = "_static/flask-icon.png"
html_logo = "_static/flask-icon.png"
html_favicon = "_static/shortcut-icon.png"
html_logo = "_static/flask-vertical.png"
html_title = f"Flask Documentation ({version})"

@@ -62,0 +60,0 @@ html_show_sourcelink = False

@@ -68,14 +68,2 @@ Configuration Handling

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

@@ -150,8 +138,13 @@

The domain match rule that the session cookie will be valid for. If not
set, the cookie will be valid for all subdomains of :data:`SERVER_NAME`.
If ``False``, the cookie's domain will not be set.
The value of the ``Domain`` parameter on the session cookie. If not set, browsers
will only send the cookie to the exact domain it was set from. Otherwise, they
will send it to any subdomain of the given value as well.
Not setting this value is more restricted and secure than setting it.
Default: ``None``
.. versionchanged:: 2.3
Not set by default, does not fall back to ``SERVER_NAME``.
.. py:data:: SESSION_COOKIE_PATH

@@ -236,10 +229,2 @@

If set, will be used for the session cookie domain if
:data:`SESSION_COOKIE_DOMAIN` is not set. Modern web browsers will
not allow setting cookies for domains without a dot. To use a domain
locally, add any names that should route to the app to your
``hosts`` file. ::
127.0.0.1 localhost.dev
If set, ``url_for`` can generate external URLs with only an application

@@ -250,2 +235,5 @@ context instead of a request context.

.. versionchanged:: 2.3
Does not affect ``SESSION_COOKIE_DOMAIN``.
.. py:data:: APPLICATION_ROOT

@@ -278,48 +266,2 @@

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

@@ -389,10 +331,9 @@

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

@@ -478,4 +419,4 @@

import toml
app.config.from_file("config.toml", load=toml.load)
import tomllib
app.config.from_file("config.toml", load=tomllib.load, text=False)

@@ -482,0 +423,0 @@ Or from a JSON file:

@@ -37,4 +37,4 @@ eventlet

$ cd hello-app
$ python -m venv venv
$ . venv/bin/activate
$ python -m venv .venv
$ . .venv/bin/activate
$ pip install . # install your application

@@ -41,0 +41,0 @@ $ pip install eventlet

@@ -36,4 +36,4 @@ gevent

$ cd hello-app
$ python -m venv venv
$ . venv/bin/activate
$ python -m venv .venv
$ . .venv/bin/activate
$ pip install . # install your application

@@ -40,0 +40,0 @@ $ pip install gevent

@@ -33,4 +33,4 @@ Gunicorn

$ cd hello-app
$ python -m venv venv
$ . venv/bin/activate
$ python -m venv .venv
$ . .venv/bin/activate
$ pip install . # install your application

@@ -37,0 +37,0 @@ $ pip install gunicorn

@@ -36,4 +36,4 @@ mod_wsgi

$ cd hello-app
$ python -m venv venv
$ . venv/bin/activate
$ python -m venv .venv
$ . .venv/bin/activate
$ pip install . # install your application

@@ -93,4 +93,4 @@ $ pip install mod_wsgi

$ sudo /home/hello/venv/bin/mod_wsgi-express start-server \
$ sudo /home/hello/.venv/bin/mod_wsgi-express start-server \
/home/hello/wsgi.py \
--user hello --group hello --port 80 --processes 4

@@ -32,4 +32,4 @@ uWSGI

$ cd hello-app
$ python -m venv venv
$ . venv/bin/activate
$ python -m venv .venv
$ . .venv/bin/activate
$ pip install . # install your application

@@ -36,0 +36,0 @@ $ pip install pyuwsgi

@@ -30,4 +30,4 @@ Waitress

$ cd hello-app
$ python -m venv venv
$ . venv/bin/activate
$ python -m venv .venv
$ . .venv/bin/activate
$ pip install . # install your application

@@ -34,0 +34,0 @@ $ pip install waitress

@@ -234,3 +234,3 @@ Handling Application Errors

``Exception`` handler will not handle ``HTTPException`` subclasses
because it the ``HTTPException`` handler is more specific.
because the ``HTTPException`` handler is more specific.

@@ -237,0 +237,0 @@

@@ -296,5 +296,4 @@ Flask Extension Development

``sqlalchemy>=1.4``.
9. Indicate the versions of Python supported using
``python_requires=">=version"``. Flask itself supports Python >=3.7
as of December 2021, but this will update over time.
9. Indicate the versions of Python supported using ``python_requires=">=version"``.
Flask itself supports Python >=3.8 as of April 2023, but this will update over time.

@@ -301,0 +300,0 @@ .. _PyPI: https://pypi.org/search/?c=Framework+%3A%3A+Flask

@@ -6,6 +6,4 @@ .. rst-class:: hide-header

.. image:: _static/flask-logo.png
:alt: Flask: web development, one drop at a time
.. image:: _static/flask-horizontal.png
:align: center
:target: https://palletsprojects.com/p/flask/

@@ -20,12 +18,11 @@ Welcome to Flask's documentation. Get started with :doc:`installation`

Flask depends on the `Jinja`_ template engine and the `Werkzeug`_ WSGI
toolkit. The documentation for these libraries can be found at:
Flask depends on the `Werkzeug`_ WSGI toolkit, the `Jinja`_ template engine, and the
`Click`_ CLI toolkit. Be sure to check their documentation as well as Flask's when
looking for information.
- `Jinja documentation <https://jinja.palletsprojects.com/>`_
- `Werkzeug documentation <https://werkzeug.palletsprojects.com/>`_
.. _Werkzeug: https://werkzeug.palletsprojects.com
.. _Jinja: https://jinja.palletsprojects.com
.. _Click: https://click.palletsprojects.com
.. _Jinja: https://www.palletsprojects.com/p/jinja/
.. _Werkzeug: https://www.palletsprojects.com/p/werkzeug/
User's Guide

@@ -32,0 +29,0 @@ ------------

@@ -8,4 +8,3 @@ Installation

We recommend using the latest version of Python. Flask supports Python
3.7 and newer.
We recommend using the latest version of Python. Flask supports Python 3.8 and newer.

@@ -28,2 +27,3 @@

the ``flask`` command and allows adding custom management commands.
* `Blinker`_ provides support for :doc:`signals`.

@@ -35,2 +35,3 @@ .. _Werkzeug: https://palletsprojects.com/p/werkzeug/

.. _Click: https://palletsprojects.com/p/click/
.. _Blinker: https://blinker.readthedocs.io/

@@ -44,3 +45,2 @@

* `Blinker`_ provides support for :doc:`signals`.
* `python-dotenv`_ enables support for :ref:`dotenv` when running ``flask``

@@ -51,3 +51,2 @@ commands.

.. _Blinker: https://blinker.readthedocs.io/en/stable/
.. _python-dotenv: https://github.com/theskumar/python-dotenv#readme

@@ -93,3 +92,3 @@ .. _watchdog: https://pythonhosted.org/watchdog/

Create a project folder and a :file:`venv` folder within:
Create a project folder and a :file:`.venv` folder within:

@@ -104,3 +103,3 @@ .. tabs::

$ cd myproject
$ python3 -m venv venv
$ python3 -m venv .venv

@@ -113,3 +112,3 @@ .. group-tab:: Windows

> cd myproject
> py -3 -m venv venv
> py -3 -m venv .venv

@@ -130,3 +129,3 @@

$ . venv/bin/activate
$ . .venv/bin/activate

@@ -137,3 +136,3 @@ .. group-tab:: Windows

> venv\Scripts\activate
> .venv\Scripts\activate

@@ -140,0 +139,0 @@ Your shell prompt will change to show the name of the activated

@@ -1,19 +0,4 @@

License
=======
BSD-3-Clause License
====================
BSD-3-Clause Source License
---------------------------
The BSD-3-Clause license applies to all files in the Flask repository
and source distribution. This includes Flask's source code, the
examples, and tests, as well as the documentation.
.. include:: ../LICENSE.rst
Artwork License
---------------
This license applies to Flask's logo.
.. include:: ../artwork/LICENSE.rst

@@ -21,21 +21,11 @@ Application Dispatching

Each of the techniques and examples below results in an ``application``
object that can be run with any WSGI server. For production, see
:doc:`/deploying/index`. For development, Werkzeug provides a server
through :func:`werkzeug.serving.run_simple`::
object that can be run with any WSGI server. For development, use the
``flask run`` command to start a development server. For production, see
:doc:`/deploying/index`.
from werkzeug.serving import run_simple
run_simple('localhost', 5000, application, use_reloader=True)
.. code-block:: python
Note that :func:`run_simple <werkzeug.serving.run_simple>` is not intended for
use in production. Use a production WSGI server. See :doc:`/deploying/index`.
In order to use the interactive debugger, debugging must be enabled both on
the application and the simple server. Here is the "hello world" example with
debugging and :func:`run_simple <werkzeug.serving.run_simple>`::
from flask import Flask
from werkzeug.serving import run_simple
app = Flask(__name__)
app.debug = True

@@ -46,7 +36,3 @@ @app.route('/')

if __name__ == '__main__':
run_simple('localhost', 5000, app,
use_reloader=True, use_debugger=True, use_evalex=True)
Combining Applications

@@ -63,4 +49,6 @@ ----------------------

For example you could have your main application run on ``/`` and your
backend interface on ``/backend``::
backend interface on ``/backend``.
.. code-block:: python
from werkzeug.middleware.dispatcher import DispatcherMiddleware

@@ -95,4 +83,6 @@ from frontend_app import application as frontend

delegates it to your Flask application. If that application does not
exist yet, it is dynamically created and remembered::
exist yet, it is dynamically created and remembered.
.. code-block:: python
from threading import Lock

@@ -124,4 +114,6 @@

This dispatcher can then be used like this::
This dispatcher can then be used like this:
.. code-block:: python
from myapplication import create_app, get_user_for_subdomain

@@ -151,6 +143,8 @@ from werkzeug.exceptions import NotFound

the ``Host`` header to figure out the subdomain one simply looks at the
request path up to the first slash::
request path up to the first slash.
.. code-block:: python
from threading import Lock
from werkzeug.wsgi import pop_path_info, peek_path_info
from wsgiref.util import shift_path_info

@@ -175,5 +169,5 @@ class PathDispatcher:

def __call__(self, environ, start_response):
app = self.get_application(peek_path_info(environ))
app = self.get_application(_peek_path_info(environ))
if app is not None:
pop_path_info(environ)
shift_path_info(environ)
else:

@@ -183,5 +177,14 @@ app = self.default_app

def _peek_path_info(environ):
segments = environ.get("PATH_INFO", "").lstrip("/").split("/", 1)
if segments:
return segments[0]
return None
The big difference between this and the subdomain one is that this one
falls back to another application if the creator function returns ``None``::
falls back to another application if the creator function returns ``None``.
.. code-block:: python
from myapplication import create_app, default_app, get_user_for_prefix

@@ -188,0 +191,0 @@

@@ -102,3 +102,3 @@ Application Factories

$ flask --app hello:create_app(local_auth=True) run``
$ flask --app hello:create_app(local_auth=True) run

@@ -105,0 +105,0 @@ Then the ``create_app`` factory in ``myapp`` is called with the keyword

@@ -128,4 +128,4 @@ JavaScript, ``fetch``, and JSON

let data = new FormData()
data.append("name": "Flask Room")
data.append("description": "Talk about Flask here.")
data.append("name", "Flask Room")
data.append("description", "Talk about Flask here.")
fetch(room_url, {

@@ -132,0 +132,0 @@ "method": "POST",

@@ -45,16 +45,17 @@ Large Applications as Packages

does not want modules in packages to be the startup file. But that is not
a big problem, just add a new file called :file:`setup.py` next to the inner
:file:`yourapplication` folder with the following contents::
a big problem, just add a new file called :file:`pyproject.toml` next to the inner
:file:`yourapplication` folder with the following contents:
from setuptools import setup
.. code-block:: toml
setup(
name='yourapplication',
packages=['yourapplication'],
include_package_data=True,
install_requires=[
'flask',
],
)
[project]
name = "yourapplication"
dependencies = [
"flask",
]
[build-system]
requires = ["flit_core<4"]
build-backend = "flit_core.buildapi"
Install your application so it is importable:

@@ -104,3 +105,3 @@

/yourapplication
setup.py
pyproject.toml
/yourapplication

@@ -107,0 +108,0 @@ __init__.py

@@ -37,4 +37,3 @@ SQLAlchemy in Flask

from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import scoped_session, sessionmaker, declarative_base

@@ -41,0 +40,0 @@ engine = create_engine('sqlite:////tmp/test.db')

@@ -362,3 +362,3 @@ Quickstart

primarily be generating HTML pages, but you can also generate markdown, plain text for
emails, any anything else.
emails, and anything else.

@@ -365,0 +365,0 @@ For a reference to HTML, CSS, and other web APIs, use the `MDN Web Docs`_.

@@ -207,19 +207,14 @@ .. currentmodule:: flask

If :data:`~signals.signals_available` is true, the following signals are
sent:
The following signals are sent:
#. :data:`request_started` is sent before the
:meth:`~Flask.before_request` functions are called.
#. :data:`request_started` is sent before the :meth:`~Flask.before_request` functions
are called.
#. :data:`request_finished` is sent after the :meth:`~Flask.after_request` functions
are called.
#. :data:`got_request_exception` is sent when an exception begins to be handled, but
before an :meth:`~Flask.errorhandler` is looked up or called.
#. :data:`request_tearing_down` is sent after the :meth:`~Flask.teardown_request`
functions are called.
#. :data:`request_finished` is sent after the
:meth:`~Flask.after_request` functions are called.
#. :data:`got_request_exception` is sent when an exception begins to
be handled, but before an :meth:`~Flask.errorhandler` is looked up or
called.
#. :data:`request_tearing_down` is sent after the
:meth:`~Flask.teardown_request` functions are called.
.. _notes-on-proxies:

@@ -226,0 +221,0 @@

@@ -26,3 +26,3 @@ Security Considerations

- 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

@@ -29,0 +29,0 @@ ``Content-Disposition: attachment`` header to prevent that problem.

Signals
=======
.. versionadded:: 0.6
Signals are a lightweight way to notify subscribers of certain events during the
lifecycle of the application and each request. When an event occurs, it emits the
signal, which calls each subscriber.
Starting with Flask 0.6, there is integrated support for signalling in
Flask. This support is provided by the excellent `blinker`_ library and
will gracefully fall back if it is not available.
Signals are implemented by the `Blinker`_ library. See its documentation for detailed
information. Flask provides some built-in signals. Extensions may provide their own.
What are signals? Signals help you decouple applications by sending
notifications when actions occur elsewhere in the core framework or
another Flask extensions. In short, signals allow certain senders to
notify subscribers that something happened.
Many signals mirror Flask's decorator-based callbacks with similar names. For example,
the :data:`.request_started` signal is similar to the :meth:`~.Flask.before_request`
decorator. The advantage of signals over handlers is that they can be subscribed to
temporarily, and can't directly affect the application. This is useful for testing,
metrics, auditing, and more. For example, if you want to know what templates were
rendered at what parts of what requests, there is a signal that will notify you of that
information.
Flask comes with a couple of signals and other extensions might provide
more. Also keep in mind that signals are intended to notify subscribers
and should not encourage subscribers to modify data. You will notice that
there are signals that appear to do the same thing like some of the
builtin decorators do (eg: :data:`~flask.request_started` is very similar
to :meth:`~flask.Flask.before_request`). However, there are differences in
how they work. The core :meth:`~flask.Flask.before_request` handler, for
example, is executed in a specific order and is able to abort the request
early by returning a response. In contrast all signal handlers are
executed in undefined order and do not modify any data.
The big advantage of signals over handlers is that you can safely
subscribe to them for just a split second. These temporary
subscriptions are helpful for unit testing for example. Say you want to
know what templates were rendered as part of a request: signals allow you
to do exactly that.
Core Signals
------------
See :ref:`core-signals-list` for a list of all built-in signals. The :doc:`lifecycle`
page also describes the order that signals and decorators execute.
Subscribing to Signals

@@ -102,7 +97,2 @@ ----------------------

.. admonition:: Blinker API Changes
The :meth:`~blinker.base.Signal.connected_to` method arrived in Blinker
with version 1.1.
Creating Signals

@@ -113,3 +103,3 @@ ----------------

blinker library directly. The most common use case are named signals in a
custom :class:`~blinker.base.Namespace`.. This is what is recommended
custom :class:`~blinker.base.Namespace`. This is what is recommended
most of the time::

@@ -128,8 +118,2 @@

.. admonition:: For Extension Developers
If you are writing a Flask extension and you want to gracefully degrade for
missing blinker installations, you can do so by using the
:class:`flask.signals.Namespace` class.
.. _signals-sending:

@@ -176,3 +160,3 @@

With Blinker 1.1 you can also easily subscribe to signals by using the new
You can also easily subscribe to signals by using the
:meth:`~blinker.base.NamedSignal.connect_via` decorator::

@@ -186,8 +170,3 @@

Core Signals
------------
Take a look at :ref:`core-signals-list` for a list of all builtin signals.
.. _blinker: https://pypi.org/project/blinker/

@@ -118,3 +118,3 @@ Templates

- 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

@@ -121,0 +121,0 @@ recommended way.

@@ -17,19 +17,10 @@ Deploy to Production

When you want to deploy your application elsewhere, you build a
distribution file. The current standard for Python distribution is the
*wheel* format, with the ``.whl`` extension. Make sure the wheel library
is installed first:
When you want to deploy your application elsewhere, you build a *wheel*
(``.whl``) file. Install and use the ``build`` tool to do this.
.. code-block:: none
$ pip install wheel
$ pip install build
$ python -m build --wheel
Running ``setup.py`` with Python gives you a command line tool to issue
build-related commands. The ``bdist_wheel`` command will build a wheel
distribution file.
.. code-block:: none
$ python setup.py bdist_wheel
You can find the file in ``dist/flaskr-1.0.0-py3-none-any.whl``. The

@@ -58,3 +49,3 @@ file name is in the format of {project name}-{version}-{python tag}

a different directory for the instance folder. You can find it at
``venv/var/flaskr-instance`` instead.
``.venv/var/flaskr-instance`` instead.

@@ -82,3 +73,3 @@

.. code-block:: python
:caption: ``venv/var/flaskr-instance/config.py``
:caption: ``.venv/var/flaskr-instance/config.py``

@@ -85,0 +76,0 @@ SECRET_KEY = '192b9bdd22ab9ed4d12e236c78afcb9a393ec15f71bbf5dc987d54727823bcbf'

Make the Project Installable
============================
Making your project installable means that you can build a
*distribution* file and install that in another environment, just like
you installed Flask in your project's environment. This makes deploying
your project the same as installing any other library, so you're using
all the standard Python tools to manage everything.
Making your project installable means that you can build a *wheel* file and install that
in another environment, just like you installed Flask in your project's environment.
This makes deploying your project the same as installing any other library, so you're
using all the standard Python tools to manage everything.

@@ -31,46 +30,23 @@ Installing also comes with other benefits that might not be obvious from

The ``setup.py`` file describes your project and the files that belong
to it.
The ``pyproject.toml`` file describes your project and how to build it.
.. code-block:: python
:caption: ``setup.py``
.. code-block:: toml
:caption: ``pyproject.toml``
from setuptools import find_packages, setup
[project]
name = "flaskr"
version = "1.0.0"
description = "The basic blog app built in the Flask tutorial."
dependencies = [
"flask",
]
setup(
name='flaskr',
version='1.0.0',
packages=find_packages(),
include_package_data=True,
install_requires=[
'flask',
],
)
[build-system]
requires = ["flit_core<4"]
build-backend = "flit_core.buildapi"
See the official `Packaging tutorial <packaging tutorial_>`_ for more
explanation of the files and options used.
``packages`` tells Python what package directories (and the Python files
they contain) to include. ``find_packages()`` finds these directories
automatically so you don't have to type them out. To include other
files, such as the static and templates directories,
``include_package_data`` is set. Python needs another file named
``MANIFEST.in`` to tell what this other data is.
.. code-block:: none
:caption: ``MANIFEST.in``
include flaskr/schema.sql
graft flaskr/static
graft flaskr/templates
global-exclude *.pyc
This tells Python to copy everything in the ``static`` and ``templates``
directories, and the ``schema.sql`` file, but to exclude all bytecode
files.
See the official `Packaging tutorial <packaging tutorial_>`_ and
`detailed guide <packaging guide_>`_ for more explanation of the files
and options used.
.. _packaging tutorial: https://packaging.python.org/tutorials/packaging-projects/
.. _packaging guide: https://packaging.python.org/guides/distributing-packages-using-setuptools/

@@ -87,6 +63,6 @@

This tells pip to find ``setup.py`` in the current directory and install
it in *editable* or *development* mode. Editable mode means that as you
make changes to your local code, you'll only need to re-install if you
change the metadata about the project, such as its dependencies.
This tells pip to find ``pyproject.toml`` in the current directory and install the
project in *editable* or *development* mode. Editable mode means that as you make
changes to your local code, you'll only need to re-install if you change the metadata
about the project, such as its dependencies.

@@ -108,5 +84,3 @@ You can observe that the project is now installed with ``pip list``.

pip 9.0.3
setuptools 39.0.1
Werkzeug 0.14.1
wheel 0.30.0

@@ -113,0 +87,0 @@ Nothing changes from how you've been running your project so far.

@@ -44,3 +44,3 @@ Project Layout

* ``tests/``, a directory containing test modules.
* ``venv/``, a Python virtual environment where Flask and other
* ``.venv/``, a Python virtual environment where Flask and other
dependencies are installed.

@@ -84,4 +84,4 @@ * Installation files telling Python how to install your project.

│ └── test_blog.py
├── venv/
├── setup.py
├── .venv/
├── pyproject.toml
└── MANIFEST.in

@@ -97,3 +97,3 @@

venv/
.venv/

@@ -100,0 +100,0 @@ *.pyc

@@ -493,16 +493,14 @@ .. currentmodule:: flask

Some extra configuration, which is not required but makes running
tests with coverage less verbose, can be added to the project's
``setup.cfg`` file.
Some extra configuration, which is not required but makes running tests with coverage
less verbose, can be added to the project's ``pyproject.toml`` file.
.. code-block:: none
:caption: ``setup.cfg``
.. code-block:: toml
:caption: ``pyproject.toml``
[tool:pytest]
testpaths = tests
[tool.pytest.ini_options]
testpaths = ["tests"]
[coverage:run]
branch = True
source =
flaskr
[tool.coverage.run]
branch = true
source = ["flaskr"]

@@ -518,3 +516,3 @@ To run the tests, use the ``pytest`` command. It will find and run all

platform linux -- Python 3.6.4, pytest-3.5.0, py-1.5.3, pluggy-0.6.0
rootdir: /home/user/Projects/flask-tutorial, inifile: setup.cfg
rootdir: /home/user/Projects/flask-tutorial
collected 23 items

@@ -521,0 +519,0 @@

@@ -252,3 +252,3 @@ Class-based Views

def __init__(self, model):
self.model
self.model = model
self.validator = generate_validator(model)

@@ -255,0 +255,0 @@

@@ -6,7 +6,10 @@ [project]

readme = "README.md"
requires-python = ">=3.7"
requires-python = ">=3.8"
dependencies = ["flask>=2.2.2", "celery[redis]>=5.2.7"]
[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"
requires = ["flit_core<4"]
build-backend = "flit_core.buildapi"
[tool.flit.module]
name = "task_app"
#
# This file is autogenerated by pip-compile with Python 3.10
# This file is autogenerated by pip-compile with Python 3.11
# by the following command:

@@ -13,2 +13,4 @@ #

# via celery
blinker==1.6.2
# via flask
celery[redis]==5.2.7

@@ -29,3 +31,3 @@ # via flask-example-celery (pyproject.toml)

# via celery
flask==2.2.3
flask==2.3.2
# via flask-example-celery (pyproject.toml)

@@ -42,7 +44,7 @@ itsdangerous==2.1.2

# werkzeug
prompt-toolkit==3.0.37
prompt-toolkit==3.0.38
# via click-repl
pytz==2022.7.1
pytz==2023.3
# via celery
redis==4.5.1
redis==4.5.4
# via celery

@@ -58,3 +60,3 @@ six==1.16.0

# via prompt-toolkit
werkzeug==2.2.3
werkzeug==2.3.3
# via flask

@@ -1,2 +0,2 @@

venv/
.venv/
*.pyc

@@ -3,0 +3,0 @@ __pycache__/

@@ -26,4 +26,4 @@ JavaScript Ajax Example

$ python3 -m venv venv
$ . venv/bin/activate
$ python3 -m venv .venv
$ . .venv/bin/activate
$ pip install -e .

@@ -30,0 +30,0 @@

@@ -1,2 +0,2 @@

venv/
.venv/
*.pyc

@@ -3,0 +3,0 @@ __pycache__/

@@ -26,9 +26,9 @@ Flaskr

$ python3 -m venv venv
$ . venv/bin/activate
$ python3 -m venv .venv
$ . .venv/bin/activate
Or on Windows cmd::
$ py -3 -m venv venv
$ venv\Scripts\activate.bat
$ py -3 -m venv .venv
$ .venv\Scripts\activate.bat

@@ -35,0 +35,0 @@ Install Flaskr::

+19
-19
Metadata-Version: 2.1
Name: Flask
Version: 2.2.5
Version: 2.3.3
Summary: A simple framework for building complex web applications.
Home-page: https://palletsprojects.com/p/flask
Author: Armin Ronacher
Author-email: armin.ronacher@active-4.com
Maintainer: Pallets
Maintainer-email: contact@palletsprojects.com
License: BSD-3-Clause
Project-URL: Donate, https://palletsprojects.com/donate
Project-URL: Documentation, https://flask.palletsprojects.com/
Project-URL: Changes, https://flask.palletsprojects.com/changes/
Project-URL: Source Code, https://github.com/pallets/flask/
Project-URL: Issue Tracker, https://github.com/pallets/flask/issues/
Project-URL: Twitter, https://twitter.com/PalletsTeam
Project-URL: Chat, https://discord.gg/pallets
Maintainer-email: Pallets <contact@palletsprojects.com>
Requires-Python: >=3.8
Description-Content-Type: text/x-rst
Classifier: Development Status :: 5 - Production/Stable

@@ -29,7 +19,18 @@ Classifier: Environment :: Web Environment

Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Requires-Python: >=3.7
Description-Content-Type: text/x-rst
Requires-Dist: Werkzeug>=2.3.7
Requires-Dist: Jinja2>=3.1.2
Requires-Dist: itsdangerous>=2.1.2
Requires-Dist: click>=8.1.3
Requires-Dist: blinker>=1.6.2
Requires-Dist: importlib-metadata>=3.6.0; python_version < '3.10'
Requires-Dist: asgiref>=3.2 ; extra == "async"
Requires-Dist: python-dotenv ; extra == "dotenv"
Project-URL: Changes, https://flask.palletsprojects.com/changes/
Project-URL: Chat, https://discord.gg/pallets
Project-URL: Documentation, https://flask.palletsprojects.com/
Project-URL: Donate, https://palletsprojects.com/donate
Project-URL: Issue Tracker, https://github.com/pallets/flask/issues/
Project-URL: Source Code, https://github.com/pallets/flask/
Provides-Extra: async
Provides-Extra: dotenv
License-File: LICENSE.rst

@@ -115,4 +116,3 @@ Flask

- Issue Tracker: https://github.com/pallets/flask/issues/
- Website: https://palletsprojects.com/p/flask/
- Twitter: https://twitter.com/PalletsTeam
- Chat: https://discord.gg/pallets

@@ -80,4 +80,2 @@ Flask

- Issue Tracker: https://github.com/pallets/flask/issues/
- Website: https://palletsprojects.com/p/flask/
- Twitter: https://twitter.com/PalletsTeam
- Chat: https://discord.gg/pallets

@@ -13,3 +13,3 @@ # SHA1:54b5b77ec8c7a0064ffa93b2fd16cb0130ba177c

# via pip-tools
cachetools==5.3.0
cachetools==5.3.1
# via tox

@@ -28,21 +28,21 @@ cfgv==3.3.1

# via virtualenv
filelock==3.12.0
filelock==3.12.2
# via
# tox
# virtualenv
identify==2.5.22
identify==2.5.24
# via pre-commit
nodeenv==1.7.0
nodeenv==1.8.0
# via pre-commit
pip-compile-multi==2.6.2
pip-compile-multi==2.6.3
# via -r requirements/dev.in
pip-tools==6.13.0
# via pip-compile-multi
platformdirs==3.3.0
platformdirs==3.8.0
# via
# tox
# virtualenv
pre-commit==3.2.2
pre-commit==3.3.3
# via -r requirements/dev.in
pyproject-api==1.5.1
pyproject-api==1.5.2
# via tox

@@ -55,5 +55,5 @@ pyproject-hooks==1.0.0

# via pip-compile-multi
tox==4.5.0
tox==4.6.3
# via -r requirements/dev.in
virtualenv==20.22.0
virtualenv==20.23.1
# via

@@ -60,0 +60,0 @@ # pre-commit

@@ -1,2 +0,2 @@

# SHA1:323f1c1134d78952ea63131c187303def63b56bd
# SHA1:34fd4ca6516e97c7348e6facdd9c4ebb68209d1c
#

@@ -12,7 +12,7 @@ # This file is autogenerated by pip-compile-multi

# via sphinx
certifi==2022.12.7
certifi==2023.5.7
# via requests
charset-normalizer==3.1.0
# via requests
docutils==0.17.1
docutils==0.18.1
# via

@@ -27,3 +27,3 @@ # sphinx

# via sphinx
markupsafe==2.1.2
markupsafe==2.1.3
# via jinja2

@@ -34,3 +34,3 @@ packaging==23.1

# sphinx
pallets-sphinx-themes==2.1.0
pallets-sphinx-themes==2.1.1
# via -r requirements/docs.in

@@ -41,7 +41,7 @@ pygments==2.15.1

# sphinx-tabs
requests==2.28.2
requests==2.31.0
# via sphinx
snowballstemmer==2.2.0
# via sphinx
sphinx==4.5.0
sphinx==7.0.1
# via

@@ -55,3 +55,3 @@ # -r requirements/docs.in

# via -r requirements/docs.in
sphinx-tabs==3.3.1
sphinx-tabs==3.4.1
# via -r requirements/docs.in

@@ -72,3 +72,3 @@ sphinxcontrib-applehelp==1.0.4

# via sphinx
urllib3==1.26.15
urllib3==2.0.3
# via requests

@@ -1,2 +0,2 @@

# SHA1:f7109e66098c9e4cb68dc5f0d8e14f429ceed12c
# SHA1:fe057f95a98251b053eec8fa27df0feb722c70e8
#

@@ -8,8 +8,10 @@ # This file is autogenerated by pip-compile-multi

#
click==8.0.0
blinker==1.6.2
# via -r requirements/tests-pallets-min.in
itsdangerous==2.0.0
click==8.1.3
# via -r requirements/tests-pallets-min.in
jinja2==3.0.0
itsdangerous==2.1.2
# via -r requirements/tests-pallets-min.in
jinja2==3.1.2
# via -r requirements/tests-pallets-min.in
markupsafe==2.1.1

@@ -20,3 +22,3 @@ # via

# werkzeug
werkzeug==2.2.2
werkzeug==2.3.7
# via -r requirements/tests-pallets-min.in

@@ -1,2 +0,2 @@

# SHA1:30698f5f4f9cba5088318306829a15b0dc123b38
# SHA1:42d37aff22e2f1fc447e20d483e13d6d4e066b10
#

@@ -8,6 +8,4 @@ # This file is autogenerated by pip-compile-multi

#
asgiref==3.6.0
asgiref==3.7.2
# via -r requirements/tests.in
blinker==1.6.2
# via -r requirements/tests.in
iniconfig==2.0.0

@@ -17,7 +15,7 @@ # via pytest

# via pytest
pluggy==1.0.0
pluggy==1.2.0
# via pytest
pytest==7.3.1
pytest==7.4.0
# via -r requirements/tests.in
python-dotenv==1.0.0 ; python_version >= "3.8"
python-dotenv==1.0.0
# via -r requirements/tests.in

@@ -1,2 +0,2 @@

# SHA1:7cc3f64d4e78db89d81680ac81503d5ac35d31a9
# SHA1:6a354b832686fd3ec017455769a0270953a1e225
#

@@ -10,5 +10,5 @@ # This file is autogenerated by pip-compile-multi

# via cryptography
cryptography==40.0.2
cryptography==41.0.1
# via -r requirements/typing.in
mypy==1.2.0
mypy==1.4.1
# via -r requirements/typing.in

@@ -23,5 +23,3 @@ mypy-extensions==1.0.0

# via -r requirements/typing.in
types-setuptools==67.7.0.0
# via -r requirements/typing.in
typing-extensions==4.5.0
typing-extensions==4.6.3
# via mypy

@@ -1,4 +0,1 @@

from markupsafe import escape
from markupsafe import Markup
from . import json as json

@@ -38,3 +35,2 @@ from .app import Flask as Flask

from .signals import request_tearing_down as request_tearing_down
from .signals import signals_available as signals_available
from .signals import template_rendered as template_rendered

@@ -46,3 +42,3 @@ from .templating import render_template as render_template

__version__ = "2.2.5"
__version__ = "2.3.3"

@@ -56,3 +52,3 @@

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,

@@ -68,3 +64,3 @@ stacklevel=2,

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,

@@ -75,2 +71,37 @@ stacklevel=2,

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 == "Markup":
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
if name == "signals_available":
import warnings
warnings.warn(
"'signals_available' is deprecated and will be removed in Flask 2.4."
" Signals are always available",
DeprecationWarning,
stacklevel=2,
)
return True
raise AttributeError(name)

@@ -1,2 +0,3 @@

import json
from __future__ import annotations
import os

@@ -18,5 +19,2 @@ import typing as t

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)

@@ -46,4 +44,4 @@ T_error_handler = t.TypeVar("T_error_handler", bound=ft.ErrorHandlerCallable)

self,
blueprint: "Blueprint",
app: "Flask",
blueprint: Blueprint,
app: Flask,
options: t.Any,

@@ -94,4 +92,4 @@ first_registration: bool,

rule: str,
endpoint: t.Optional[str] = None,
view_func: t.Optional[t.Callable] = None,
endpoint: str | None = None,
view_func: t.Callable | None = None,
**options: t.Any,

@@ -179,73 +177,2 @@ ) -> None:

_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__(

@@ -255,10 +182,10 @@ self,

import_name: str,
static_folder: t.Optional[t.Union[str, os.PathLike]] = None,
static_url_path: t.Optional[str] = None,
template_folder: t.Optional[t.Union[str, os.PathLike]] = None,
url_prefix: t.Optional[str] = None,
subdomain: t.Optional[str] = None,
url_defaults: t.Optional[dict] = None,
root_path: t.Optional[str] = None,
cli_group: t.Optional[str] = _sentinel, # type: ignore
static_folder: str | os.PathLike | None = None,
static_url_path: str | None = None,
template_folder: str | os.PathLike | None = None,
url_prefix: str | None = None,
subdomain: str | None = None,
url_defaults: dict | None = None,
root_path: str | None = None,
cli_group: str | None = _sentinel, # type: ignore
):

@@ -273,2 +200,5 @@ super().__init__(

if not name:
raise ValueError("'name' may not be empty.")
if "." in name:

@@ -280,3 +210,3 @@ raise ValueError("'name' may not contain a dot '.' character.")

self.subdomain = subdomain
self.deferred_functions: t.List[DeferredSetupFunction] = []
self.deferred_functions: list[DeferredSetupFunction] = []

@@ -288,19 +218,12 @@ if url_defaults is None:

self.cli_group = cli_group
self._blueprints: t.List[t.Tuple["Blueprint", dict]] = []
self._blueprints: list[tuple[Blueprint, dict]] = []
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."
)

@@ -332,3 +255,3 @@

def make_setup_state(
self, app: "Flask", options: dict, first_registration: bool = False
self, app: Flask, options: dict, first_registration: bool = False
) -> BlueprintSetupState:

@@ -342,3 +265,3 @@ """Creates an instance of :meth:`~flask.blueprints.BlueprintSetupState`

@setupmethod
def register_blueprint(self, blueprint: "Blueprint", **options: t.Any) -> None:
def register_blueprint(self, blueprint: Blueprint, **options: t.Any) -> None:
"""Register a :class:`~flask.Blueprint` on this blueprint. Keyword

@@ -360,3 +283,3 @@ arguments passed to this method will override the defaults set

def register(self, app: "Flask", options: dict) -> None:
def register(self, app: Flask, options: dict) -> None:
"""Called by :meth:`Flask.register_blueprint` to register all

@@ -372,2 +295,9 @@ views and callbacks registered on the blueprint with the

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

@@ -383,6 +313,2 @@ Nested blueprints are registered with their dotted name.

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.
"""

@@ -469,3 +395,14 @@ name_prefix = options.get("name_prefix", "")

bp_url_prefix = bp_options.get("url_prefix")
bp_subdomain = bp_options.get("subdomain")
if bp_subdomain is None:
bp_subdomain = blueprint.subdomain
if state.subdomain is not None and bp_subdomain is not None:
bp_options["subdomain"] = bp_subdomain + "." + state.subdomain
elif bp_subdomain is not None:
bp_options["subdomain"] = bp_subdomain
elif state.subdomain is not None:
bp_options["subdomain"] = state.subdomain
if bp_url_prefix is None:

@@ -490,5 +427,5 @@ bp_url_prefix = blueprint.url_prefix

rule: str,
endpoint: t.Optional[str] = None,
view_func: t.Optional[ft.RouteCallable] = None,
provide_automatic_options: t.Optional[bool] = None,
endpoint: str | None = None,
view_func: ft.RouteCallable | None = None,
provide_automatic_options: bool | None = None,
**options: t.Any,

@@ -520,3 +457,3 @@ ) -> None:

def app_template_filter(
self, name: t.Optional[str] = None
self, name: str | None = None
) -> t.Callable[[T_template_filter], T_template_filter]:

@@ -538,3 +475,3 @@ """Register a template filter, available in any template rendered by the

def add_app_template_filter(
self, f: ft.TemplateFilterCallable, name: t.Optional[str] = None
self, f: ft.TemplateFilterCallable, name: str | None = None
) -> None:

@@ -556,3 +493,3 @@ """Register a template filter, available in any template rendered by the

def app_template_test(
self, name: t.Optional[str] = None
self, name: str | None = None
) -> t.Callable[[T_template_test], T_template_test]:

@@ -576,3 +513,3 @@ """Register a template test, available in any template rendered by the

def add_app_template_test(
self, f: ft.TemplateTestCallable, name: t.Optional[str] = None
self, f: ft.TemplateTestCallable, name: str | None = None
) -> None:

@@ -596,3 +533,3 @@ """Register a template test, available in any template rendered by the

def app_template_global(
self, name: t.Optional[str] = None
self, name: str | None = None
) -> t.Callable[[T_template_global], T_template_global]:

@@ -616,3 +553,3 @@ """Register a template global, available in any template rendered by the

def add_app_template_global(
self, f: ft.TemplateGlobalCallable, name: t.Optional[str] = None
self, f: ft.TemplateGlobalCallable, name: str | None = None
) -> None:

@@ -645,25 +582,2 @@ """Register a template global, available in any template rendered by the

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

@@ -702,3 +616,3 @@ """Like :meth:`after_request`, but after every request, not only those handled

def app_errorhandler(
self, code: t.Union[t.Type[Exception], int]
self, code: type[Exception] | int
) -> t.Callable[[T_error_handler], T_error_handler]:

@@ -705,0 +619,0 @@ """Like :meth:`errorhandler`, but for every request, not only those handled by

from __future__ import annotations
import ast
import importlib.metadata
import inspect

@@ -12,3 +13,3 @@ import os

from functools import update_wrapper
from operator import attrgetter
from operator import itemgetter

@@ -245,9 +246,9 @@ import click

import werkzeug
from . import __version__
flask_version = importlib.metadata.version("flask")
werkzeug_version = importlib.metadata.version("werkzeug")
click.echo(
f"Python {platform.python_version()}\n"
f"Flask {__version__}\n"
f"Werkzeug {werkzeug.__version__}",
f"Flask {flask_version}\n"
f"Werkzeug {werkzeug_version}",
color=ctx.color,

@@ -290,3 +291,3 @@ )

#: this script info.
self.data: t.Dict[t.Any, t.Any] = {}
self.data: dict[t.Any, t.Any] = {}
self.set_debug_flag = set_debug_flag

@@ -308,3 +309,3 @@ self._loaded_app: Flask | None = None

path, name = (
re.split(r":(?![\\/])", self.app_import_path, 1) + [None]
re.split(r":(?![\\/])", self.app_import_path, maxsplit=1) + [None]
)[:2]

@@ -996,7 +997,7 @@ import_name = prepare_import(path)

"-s",
type=click.Choice(("endpoint", "methods", "rule", "match")),
type=click.Choice(("endpoint", "methods", "domain", "rule", "match")),
default="endpoint",
help=(
'Method to sort routes by. "match" is the order that Flask will match '
"routes when dispatching a request."
"Method to sort routes by. 'match' is the order that Flask will match routes"
" when dispatching a request."
),

@@ -1008,4 +1009,4 @@ )

"""Show all registered routes with endpoints and methods."""
rules = list(current_app.url_map.iter_rules())
rules = list(current_app.url_map.iter_rules())
if not rules:

@@ -1015,30 +1016,43 @@ click.echo("No routes were registered.")

ignored_methods = set(() if all_methods else ("HEAD", "OPTIONS"))
ignored_methods = set() if all_methods else {"HEAD", "OPTIONS"}
host_matching = current_app.url_map.host_matching
has_domain = any(rule.host if host_matching else rule.subdomain for rule in rules)
rows = []
if sort in ("endpoint", "rule"):
rules = sorted(rules, key=attrgetter(sort))
elif sort == "methods":
rules = sorted(rules, key=lambda rule: sorted(rule.methods)) # type: ignore
for rule in rules:
row = [
rule.endpoint,
", ".join(sorted((rule.methods or set()) - ignored_methods)),
]
rule_methods = [
", ".join(sorted(rule.methods - ignored_methods)) # type: ignore
for rule in rules
]
if has_domain:
row.append((rule.host if host_matching else rule.subdomain) or "")
headers = ("Endpoint", "Methods", "Rule")
widths = (
max(len(rule.endpoint) for rule in rules),
max(len(methods) for methods in rule_methods),
max(len(rule.rule) for rule in rules),
)
widths = [max(len(h), w) for h, w in zip(headers, widths)]
row = "{{0:<{0}}} {{1:<{1}}} {{2:<{2}}}".format(*widths)
row.append(rule.rule)
rows.append(row)
click.echo(row.format(*headers).strip())
click.echo(row.format(*("-" * width for width in widths)))
headers = ["Endpoint", "Methods"]
sorts = ["endpoint", "methods"]
for rule, methods in zip(rules, rule_methods):
click.echo(row.format(rule.endpoint, methods, rule.rule).rstrip())
if has_domain:
headers.append("Host" if host_matching else "Subdomain")
sorts.append("domain")
headers.append("Rule")
sorts.append("rule")
try:
rows.sort(key=itemgetter(sorts.index(sort)))
except ValueError:
pass
rows.insert(0, headers)
widths = [max(len(row[i]) for row in rows) for i in range(len(headers))]
rows.insert(1, ["-" * w for w in widths])
template = " ".join(f"{{{i}:<{w}}}" for i, w in enumerate(widths))
for row in rows:
click.echo(template.format(*row))
cli = FlaskGroup(

@@ -1045,0 +1059,0 @@ name="flask",

@@ -0,1 +1,3 @@

from __future__ import annotations
import errno

@@ -13,3 +15,3 @@ import json

def __init__(self, name: str, get_converter: t.Optional[t.Callable] = None) -> None:
def __init__(self, name: str, get_converter: t.Callable | None = None) -> None:
self.__name__ = name

@@ -74,3 +76,5 @@ self.get_converter = get_converter

def __init__(self, root_path: str, defaults: t.Optional[dict] = None) -> None:
def __init__(
self, root_path: str | os.PathLike, defaults: dict | None = None
) -> None:
super().__init__(defaults or {})

@@ -167,3 +171,3 @@ self.root_path = root_path

def from_pyfile(self, filename: str, silent: bool = False) -> bool:
def from_pyfile(self, filename: str | os.PathLike, silent: bool = False) -> bool:
"""Updates the values in the config from a Python file. This function

@@ -197,3 +201,3 @@ behaves as if the file was imported as module with the

def from_object(self, obj: t.Union[object, str]) -> None:
def from_object(self, obj: object | str) -> None:
"""Updates the values from the given object. An object can be of one

@@ -238,5 +242,6 @@ of the following two types:

self,
filename: str,
filename: str | os.PathLike,
load: t.Callable[[t.IO[t.Any]], t.Mapping],
silent: bool = False,
text: bool = True,
) -> bool:

@@ -252,4 +257,4 @@ """Update the values in the config from a file that is loaded

import toml
app.config.from_file("config.toml", load=toml.load)
import tomllib
app.config.from_file("config.toml", load=tomllib.load, text=False)

@@ -263,4 +268,8 @@ :param filename: The path to the data file. This can be an

: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

@@ -271,3 +280,3 @@ """

try:
with open(filename) as f:
with open(filename, "r" if text else "rb") as f:
obj = load(f)

@@ -284,3 +293,3 @@ except OSError as e:

def from_mapping(
self, mapping: t.Optional[t.Mapping[str, t.Any]] = None, **kwargs: t.Any
self, mapping: t.Mapping[str, t.Any] | None = None, **kwargs: t.Any
) -> bool:

@@ -294,3 +303,3 @@ """Updates the config like :meth:`update` ignoring items with

"""
mappings: t.Dict[str, t.Any] = {}
mappings: dict[str, t.Any] = {}
if mapping is not None:

@@ -306,3 +315,3 @@ mappings.update(mapping)

self, namespace: str, lowercase: bool = True, trim_namespace: bool = True
) -> t.Dict[str, t.Any]:
) -> dict[str, t.Any]:
"""Returns a dictionary containing a subset of configuration options

@@ -309,0 +318,0 @@ that match the specified namespace/prefix. Example usage::

@@ -0,1 +1,3 @@

from __future__ import annotations
import contextvars

@@ -63,3 +65,3 @@ import sys

def get(self, name: str, default: t.Optional[t.Any] = None) -> t.Any:
def get(self, name: str, default: t.Any | None = None) -> t.Any:
"""Get an attribute by name, or a default value. Like

@@ -237,7 +239,7 @@ :meth:`dict.get`.

def __init__(self, app: "Flask") -> None:
def __init__(self, app: Flask) -> None:
self.app = app
self.url_adapter = app.create_url_adapter(None)
self.g: _AppCtxGlobals = app.app_ctx_globals_class()
self._cv_tokens: t.List[contextvars.Token] = []
self._cv_tokens: list[contextvars.Token] = []

@@ -247,5 +249,5 @@ def push(self) -> None:

self._cv_tokens.append(_cv_app.set(self))
appcontext_pushed.send(self.app)
appcontext_pushed.send(self.app, _async_wrapper=self.app.ensure_sync)
def pop(self, exc: t.Optional[BaseException] = _sentinel) -> None: # type: ignore
def pop(self, exc: BaseException | None = _sentinel) -> None: # type: ignore
"""Pops the app context."""

@@ -266,5 +268,5 @@ try:

appcontext_popped.send(self.app)
appcontext_popped.send(self.app, _async_wrapper=self.app.ensure_sync)
def __enter__(self) -> "AppContext":
def __enter__(self) -> AppContext:
self.push()

@@ -275,5 +277,5 @@ return self

self,
exc_type: t.Optional[type],
exc_value: t.Optional[BaseException],
tb: t.Optional[TracebackType],
exc_type: type | None,
exc_value: BaseException | None,
tb: TracebackType | None,
) -> None:

@@ -307,6 +309,6 @@ self.pop(exc_value)

self,
app: "Flask",
app: Flask,
environ: dict,
request: t.Optional["Request"] = None,
session: t.Optional["SessionMixin"] = None,
request: Request | None = None,
session: SessionMixin | None = None,
) -> None:

@@ -323,12 +325,12 @@ self.app = app

self.request.routing_exception = e
self.flashes: t.Optional[t.List[t.Tuple[str, str]]] = None
self.session: t.Optional["SessionMixin"] = session
self.flashes: list[tuple[str, str]] | None = None
self.session: SessionMixin | None = session
# Functions that should be executed after the request on the response
# object. These will be called before the regular "after_request"
# functions.
self._after_request_functions: t.List[ft.AfterRequestCallable] = []
self._after_request_functions: list[ft.AfterRequestCallable] = []
self._cv_tokens: t.List[t.Tuple[contextvars.Token, t.Optional[AppContext]]] = []
self._cv_tokens: list[tuple[contextvars.Token, AppContext | None]] = []
def copy(self) -> "RequestContext":
def copy(self) -> RequestContext:
"""Creates a copy of this request context with the same request object.

@@ -392,3 +394,3 @@ This can be used to move a request context to a different greenlet.

def pop(self, exc: t.Optional[BaseException] = _sentinel) -> None: # type: ignore
def pop(self, exc: BaseException | None = _sentinel) -> None: # type: ignore
"""Pops the request context and unbinds it by doing that. This will

@@ -430,3 +432,3 @@ also trigger the execution of functions registered by the

def __enter__(self) -> "RequestContext":
def __enter__(self) -> RequestContext:
self.push()

@@ -437,5 +439,5 @@ return self

self,
exc_type: t.Optional[type],
exc_value: t.Optional[BaseException],
tb: t.Optional[TracebackType],
exc_type: type | None,
exc_value: BaseException | None,
tb: TracebackType | None,
) -> None:

@@ -442,0 +444,0 @@ self.pop(exc_value)

@@ -0,1 +1,3 @@

from __future__ import annotations
import typing as t

@@ -2,0 +4,0 @@

@@ -0,1 +1,3 @@

from __future__ import annotations
import typing as t

@@ -20,26 +22,13 @@ from contextvars import ContextVar

def _warn(self):
@property
def top(self) -> t.Any | None:
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)

@@ -55,11 +44,11 @@

"""
_cv_app: ContextVar["AppContext"] = ContextVar("flask.app_ctx")
_cv_app: ContextVar[AppContext] = ContextVar("flask.app_ctx")
__app_ctx_stack = _FakeStack("app", _cv_app)
app_ctx: "AppContext" = LocalProxy( # type: ignore[assignment]
app_ctx: AppContext = LocalProxy( # type: ignore[assignment]
_cv_app, unbound_message=_no_app_msg
)
current_app: "Flask" = LocalProxy( # type: ignore[assignment]
current_app: Flask = LocalProxy( # type: ignore[assignment]
_cv_app, "app", unbound_message=_no_app_msg
)
g: "_AppCtxGlobals" = LocalProxy( # type: ignore[assignment]
g: _AppCtxGlobals = LocalProxy( # type: ignore[assignment]
_cv_app, "g", unbound_message=_no_app_msg

@@ -75,11 +64,11 @@ )

"""
_cv_request: ContextVar["RequestContext"] = ContextVar("flask.request_ctx")
_cv_request: ContextVar[RequestContext] = ContextVar("flask.request_ctx")
__request_ctx_stack = _FakeStack("request", _cv_request)
request_ctx: "RequestContext" = LocalProxy( # type: ignore[assignment]
request_ctx: RequestContext = LocalProxy( # type: ignore[assignment]
_cv_request, unbound_message=_no_req_msg
)
request: "Request" = LocalProxy( # type: ignore[assignment]
request: Request = LocalProxy( # type: ignore[assignment]
_cv_request, "request", unbound_message=_no_req_msg
)
session: "SessionMixin" = LocalProxy( # type: ignore[assignment]
session: SessionMixin = LocalProxy( # type: ignore[assignment]
_cv_request, "session", unbound_message=_no_req_msg

@@ -94,3 +83,3 @@ )

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,

@@ -105,3 +94,3 @@ stacklevel=2,

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,

@@ -108,0 +97,0 @@ stacklevel=2,

@@ -0,6 +1,9 @@

from __future__ import annotations
import importlib.util
import os
import pkgutil
import socket
import sys
import typing as t
import warnings
from datetime import datetime

@@ -25,24 +28,4 @@ from functools import lru_cache

from .wrappers import Response
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:

@@ -53,19 +36,5 @@ """Get whether debug mode should be enabled for the app, indicated by the

val = os.environ.get("FLASK_DEBUG")
return bool(val and val.lower() not in {"0", "false", "no"})
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"}
def get_load_dotenv(default: bool = True) -> bool:

@@ -87,5 +56,5 @@ """Get whether the user has disabled loading default dotenv files by

def stream_with_context(
generator_or_function: t.Union[
t.Iterator[t.AnyStr], t.Callable[..., t.Iterator[t.AnyStr]]
]
generator_or_function: (
t.Iterator[t.AnyStr] | t.Callable[..., t.Iterator[t.AnyStr]]
)
) -> t.Iterator[t.AnyStr]:

@@ -166,3 +135,3 @@ """Request contexts disappear when the response is started on the server.

def make_response(*args: t.Any) -> "Response":
def make_response(*args: t.Any) -> Response:
"""Sometimes it is necessary to set additional headers in a view. Because

@@ -219,6 +188,6 @@ views do not have to return response objects but can return a value that

*,
_anchor: t.Optional[str] = None,
_method: t.Optional[str] = None,
_scheme: t.Optional[str] = None,
_external: t.Optional[bool] = None,
_anchor: str | None = None,
_method: str | None = None,
_scheme: str | None = None,
_external: bool | None = None,
**values: t.Any,

@@ -272,4 +241,4 @@ ) -> str:

def redirect(
location: str, code: int = 302, Response: t.Optional[t.Type["BaseResponse"]] = None
) -> "BaseResponse":
location: str, code: int = 302, Response: type[BaseResponse] | None = None
) -> BaseResponse:
"""Create a redirect response object.

@@ -296,5 +265,3 @@

def abort(
code: t.Union[int, "BaseResponse"], *args: t.Any, **kwargs: t.Any
) -> "te.NoReturn":
def abort(code: int | BaseResponse, *args: t.Any, **kwargs: t.Any) -> t.NoReturn:
"""Raise an :exc:`~werkzeug.exceptions.HTTPException` for the given

@@ -369,4 +336,6 @@ status code.

session["_flashes"] = flashes
app = current_app._get_current_object() # type: ignore
message_flashed.send(
current_app._get_current_object(), # type: ignore
app,
_async_wrapper=app.ensure_sync,
message=message,

@@ -379,3 +348,3 @@ category=category,

with_categories: bool = False, category_filter: t.Iterable[str] = ()
) -> t.Union[t.List[str], t.List[t.Tuple[str, str]]]:
) -> list[str] | list[tuple[str, str]]:
"""Pulls all flashed messages from the session and returns them.

@@ -420,3 +389,3 @@ Further calls in the same request to the function will return

def _prepare_send_file_kwargs(**kwargs: t.Any) -> t.Dict[str, t.Any]:
def _prepare_send_file_kwargs(**kwargs: t.Any) -> dict[str, t.Any]:
if kwargs.get("max_age") is None:

@@ -435,13 +404,11 @@ kwargs["max_age"] = current_app.get_send_file_max_age

def send_file(
path_or_file: t.Union[os.PathLike, str, t.BinaryIO],
mimetype: t.Optional[str] = None,
path_or_file: os.PathLike | str | t.BinaryIO,
mimetype: str | None = None,
as_attachment: bool = False,
download_name: t.Optional[str] = None,
download_name: str | None = None,
conditional: bool = True,
etag: t.Union[bool, str] = True,
last_modified: t.Optional[t.Union[datetime, int, float]] = None,
max_age: t.Optional[
t.Union[int, t.Callable[[t.Optional[str]], t.Optional[int]]]
] = None,
) -> "Response":
etag: bool | str = True,
last_modified: datetime | int | float | None = None,
max_age: None | (int | t.Callable[[str | None], int | None]) = None,
) -> Response:
"""Send the contents of a file to the client.

@@ -564,6 +531,6 @@

def send_from_directory(
directory: t.Union[os.PathLike, str],
path: t.Union[os.PathLike, str],
directory: os.PathLike | str,
path: os.PathLike | str,
**kwargs: t.Any,
) -> "Response":
) -> Response:
"""Send a file from within a directory using :func:`send_file`.

@@ -623,8 +590,16 @@

# Next attempt: check the loader.
loader = pkgutil.get_loader(import_name)
try:
spec = importlib.util.find_spec(import_name)
if spec is None:
raise ValueError
except (ImportError, ValueError):
loader = None
else:
loader = spec.loader
# Loader does not exist or we're referring to an unloaded main
# module or a main module without path (interactive sessions), go
# with the current working directory.
if loader is None or import_name == "__main__":
if loader is None:
return os.getcwd()

@@ -662,2 +637,6 @@

.. deprecated:: 2.3
Will be removed in Flask 2.4. Use a lock inside the decorated function if
locking is needed.
.. versionchanged:: 2.0

@@ -670,5 +649,13 @@ Inherits from Werkzeug's ``cached_property`` (and ``property``).

fget: t.Callable[[t.Any], t.Any],
name: t.Optional[str] = None,
doc: t.Optional[str] = None,
name: str | None = None,
doc: str | None = 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)

@@ -701,3 +688,12 @@ self.lock = RLock()

:rtype: bool
.. deprecated:: 2.3
Will be removed in Flask 2.4.
"""
warnings.warn(
"The 'is_ip' function is deprecated and will be removed in Flask 2.4.",
DeprecationWarning,
stacklevel=2,
)
for family in (socket.AF_INET, socket.AF_INET6):

@@ -715,4 +711,4 @@ try:

@lru_cache(maxsize=None)
def _split_blueprint_path(name: str) -> t.List[str]:
out: t.List[str] = [name]
def _split_blueprint_path(name: str) -> list[str]:
out: list[str] = [name]

@@ -719,0 +715,0 @@ if "." in name:

@@ -6,4 +6,2 @@ from __future__ import annotations

from jinja2.utils import htmlsafe_json_dumps as _jinja_htmlsafe_dumps
from ..globals import current_app

@@ -13,75 +11,6 @@ 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.

@@ -96,2 +25,5 @@

.. versionchanged:: 2.3
The ``app`` parameter was removed.
.. versionchanged:: 2.2

@@ -101,5 +33,2 @@ Calls ``current_app.json.dumps``, allowing an app to override

.. versionchanged:: 2.2
The ``app`` parameter will be removed in Flask 2.3.
.. versionchanged:: 2.0.2

@@ -115,17 +44,5 @@ :class:`decimal.Decimal` is supported by converting to a string.

"""
if app is not None:
import warnings
if current_app:
return current_app.json.dumps(obj, **kwargs)
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)
kwargs.setdefault("default", _default)

@@ -135,5 +52,3 @@ 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.

@@ -150,2 +65,5 @@

.. versionchanged:: 2.3
The ``app`` parameter was removed.
.. versionchanged:: 2.2

@@ -155,5 +73,2 @@ Calls ``current_app.json.dump``, allowing an app to override

.. versionchanged:: 2.2
The ``app`` parameter will be removed in Flask 2.3.
.. versionchanged:: 2.0

@@ -163,17 +78,5 @@ Writing to a binary file, and the ``encoding`` argument, will be

"""
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,
)
if current_app:
current_app.json.dump(obj, fp, **kwargs)
else:
app = current_app
if app:
app.json.dump(obj, fp, **kwargs)
else:
kwargs.setdefault("default", _default)

@@ -183,3 +86,3 @@ _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.

@@ -194,2 +97,5 @@

.. versionchanged:: 2.3
The ``app`` parameter was removed.
.. versionchanged:: 2.2

@@ -199,5 +105,2 @@ Calls ``current_app.json.loads``, allowing an app to override

.. versionchanged:: 2.2
The ``app`` parameter will be removed in Flask 2.3.
.. versionchanged:: 2.0

@@ -211,21 +114,9 @@ ``encoding`` will be removed in Flask 2.1. The data must be a

"""
if app is not None:
import warnings
if current_app:
return current_app.json.loads(s, **kwargs)
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)
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.

@@ -240,2 +131,5 @@

.. versionchanged:: 2.3
The ``app`` parameter was removed.
.. versionchanged:: 2.2

@@ -252,74 +146,8 @@ Calls ``current_app.json.load``, allowing an app to override

"""
if app is not None:
import warnings
if current_app:
return current_app.json.load(fp, **kwargs)
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)
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:

@@ -326,0 +154,0 @@ """Serialize the given arguments as JSON, and return a

@@ -13,4 +13,2 @@ from __future__ import annotations

from ..globals import request
if t.TYPE_CHECKING: # pragma: no cover

@@ -78,3 +76,3 @@ from ..app import Flask

def _prepare_response_obj(
self, args: t.Tuple[t.Any, ...], kwargs: t.Dict[str, t.Any]
self, args: tuple[t.Any, ...], kwargs: dict[str, t.Any]
) -> t.Any:

@@ -181,53 +179,5 @@ if args and kwargs:

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

@@ -241,19 +191,2 @@

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

@@ -278,20 +211,5 @@

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"]
dump_args: dict[str, t.Any] = {}
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)

@@ -301,16 +219,4 @@ else:

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
)

@@ -43,2 +43,4 @@ """

"""
from __future__ import annotations
import typing as t

@@ -65,5 +67,5 @@ from base64 import b64decode

#: only used as an intermediate step during tagging.
key: t.Optional[str] = None
key: str | None = None
def __init__(self, serializer: "TaggedJSONSerializer") -> None:
def __init__(self, serializer: TaggedJSONSerializer) -> None:
"""Create a tagger for the given serializer."""

@@ -249,4 +251,4 @@ self.serializer = serializer

def __init__(self) -> None:
self.tags: t.Dict[str, JSONTag] = {}
self.order: t.List[JSONTag] = []
self.tags: dict[str, JSONTag] = {}
self.order: list[JSONTag] = []

@@ -258,5 +260,5 @@ for cls in self.default_tags:

self,
tag_class: t.Type[JSONTag],
tag_class: type[JSONTag],
force: bool = False,
index: t.Optional[int] = None,
index: int | None = None,
) -> None:

@@ -290,3 +292,3 @@ """Register a new tag with this serializer.

def tag(self, value: t.Any) -> t.Dict[str, t.Any]:
def tag(self, value: t.Any) -> dict[str, t.Any]:
"""Convert a value to a tagged representation if necessary."""

@@ -299,3 +301,3 @@ for tag in self.order:

def untag(self, value: t.Dict[str, t.Any]) -> t.Any:
def untag(self, value: dict[str, t.Any]) -> t.Any:
"""Convert a tagged representation back to the original type."""

@@ -302,0 +304,0 @@ if len(value) != 1:

@@ -0,1 +1,3 @@

from __future__ import annotations
import logging

@@ -53,3 +55,3 @@ import sys

def create_logger(app: "Flask") -> logging.Logger:
def create_logger(app: Flask) -> logging.Logger:
"""Get the Flask app's logger and configure it if needed.

@@ -56,0 +58,0 @@

@@ -0,6 +1,6 @@

from __future__ import annotations
import importlib.util
import json
import os
import pathlib
import pkgutil
import sys

@@ -15,2 +15,3 @@ import typing as t

from werkzeug.exceptions import HTTPException
from werkzeug.utils import cached_property

@@ -21,3 +22,2 @@ from . import typing as ft

from .helpers import get_root_path
from .helpers import locked_cached_property
from .helpers import send_from_directory

@@ -76,26 +76,12 @@ from .templating import _default_template_ctx_processor

name: str
_static_folder: t.Optional[str] = None
_static_url_path: t.Optional[str] = None
_static_folder: str | None = None
_static_url_path: str | None = 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,
static_folder: t.Optional[t.Union[str, os.PathLike]] = None,
static_url_path: t.Optional[str] = None,
template_folder: t.Optional[t.Union[str, os.PathLike]] = None,
root_path: t.Optional[str] = None,
static_folder: str | os.PathLike | None = None,
static_url_path: str | None = None,
template_folder: str | os.PathLike | None = None,
root_path: str | None = None,
):

@@ -133,3 +119,3 @@ #: The name of the package or module that this object belongs

#: directly and its format may change at any time.
self.view_functions: t.Dict[str, t.Callable] = {}
self.view_functions: dict[str, t.Callable] = {}

@@ -149,5 +135,5 @@ #: A data structure of registered error handlers, in the format

#: directly and its format may change at any time.
self.error_handler_spec: t.Dict[
self.error_handler_spec: dict[
ft.AppOrBlueprintKey,
t.Dict[t.Optional[int], t.Dict[t.Type[Exception], ft.ErrorHandlerCallable]],
dict[int | None, dict[type[Exception], ft.ErrorHandlerCallable]],
] = defaultdict(lambda: defaultdict(dict))

@@ -165,4 +151,4 @@

#: directly and its format may change at any time.
self.before_request_funcs: t.Dict[
ft.AppOrBlueprintKey, t.List[ft.BeforeRequestCallable]
self.before_request_funcs: dict[
ft.AppOrBlueprintKey, list[ft.BeforeRequestCallable]
] = defaultdict(list)

@@ -180,4 +166,4 @@

#: directly and its format may change at any time.
self.after_request_funcs: t.Dict[
ft.AppOrBlueprintKey, t.List[ft.AfterRequestCallable]
self.after_request_funcs: dict[
ft.AppOrBlueprintKey, list[ft.AfterRequestCallable]
] = defaultdict(list)

@@ -196,4 +182,4 @@

#: directly and its format may change at any time.
self.teardown_request_funcs: t.Dict[
ft.AppOrBlueprintKey, t.List[ft.TeardownCallable]
self.teardown_request_funcs: dict[
ft.AppOrBlueprintKey, list[ft.TeardownCallable]
] = defaultdict(list)

@@ -212,4 +198,4 @@

#: directly and its format may change at any time.
self.template_context_processors: t.Dict[
ft.AppOrBlueprintKey, t.List[ft.TemplateContextProcessorCallable]
self.template_context_processors: dict[
ft.AppOrBlueprintKey, list[ft.TemplateContextProcessorCallable]
] = defaultdict(list, {None: [_default_template_ctx_processor]})

@@ -228,5 +214,5 @@

#: directly and its format may change at any time.
self.url_value_preprocessors: t.Dict[
self.url_value_preprocessors: dict[
ft.AppOrBlueprintKey,
t.List[ft.URLValuePreprocessorCallable],
list[ft.URLValuePreprocessorCallable],
] = defaultdict(list)

@@ -245,4 +231,4 @@

#: directly and its format may change at any time.
self.url_default_functions: t.Dict[
ft.AppOrBlueprintKey, t.List[ft.URLDefaultCallable]
self.url_default_functions: dict[
ft.AppOrBlueprintKey, list[ft.URLDefaultCallable]
] = defaultdict(list)

@@ -257,3 +243,3 @@

@property
def static_folder(self) -> t.Optional[str]:
def static_folder(self) -> str | None:
"""The absolute path to the configured static folder. ``None``

@@ -268,3 +254,3 @@ if no static folder is set.

@static_folder.setter
def static_folder(self, value: t.Optional[t.Union[str, os.PathLike]]) -> None:
def static_folder(self, value: str | os.PathLike | None) -> None:
if value is not None:

@@ -284,3 +270,3 @@ value = os.fspath(value).rstrip(r"\/")

@property
def static_url_path(self) -> t.Optional[str]:
def static_url_path(self) -> str | None:
"""The URL prefix that the static route will be accessible from.

@@ -301,3 +287,3 @@

@static_url_path.setter
def static_url_path(self, value: t.Optional[str]) -> None:
def static_url_path(self, value: str | None) -> None:
if value is not None:

@@ -308,3 +294,3 @@ value = value.rstrip("/")

def get_send_file_max_age(self, filename: t.Optional[str]) -> t.Optional[int]:
def get_send_file_max_age(self, filename: str | None) -> int | None:
"""Used by :func:`send_file` to determine the ``max_age`` cache

@@ -333,3 +319,3 @@ value for a given file path if it wasn't passed.

def send_static_file(self, filename: str) -> "Response":
def send_static_file(self, filename: str) -> Response:
"""The view function used to serve files from

@@ -352,4 +338,4 @@ :attr:`static_folder`. A route is automatically registered for

@locked_cached_property
def jinja_loader(self) -> t.Optional[FileSystemLoader]:
@cached_property
def jinja_loader(self) -> FileSystemLoader | None:
"""The Jinja loader for this object's templates. By default this

@@ -476,5 +462,5 @@ is a class :class:`jinja2.loaders.FileSystemLoader` to

rule: str,
endpoint: t.Optional[str] = None,
view_func: t.Optional[ft.RouteCallable] = None,
provide_automatic_options: t.Optional[bool] = None,
endpoint: str | None = None,
view_func: ft.RouteCallable | None = None,
provide_automatic_options: bool | None = None,
**options: t.Any,

@@ -705,3 +691,3 @@ ) -> None:

def errorhandler(
self, code_or_exception: t.Union[t.Type[Exception], int]
self, code_or_exception: type[Exception] | int
) -> t.Callable[[T_error_handler], T_error_handler]:

@@ -751,3 +737,3 @@ """Register a function to handle errors by code or exception class.

self,
code_or_exception: t.Union[t.Type[Exception], int],
code_or_exception: type[Exception] | int,
f: ft.ErrorHandlerCallable,

@@ -766,4 +752,4 @@ ) -> None:

def _get_exc_class_and_code(
exc_class_or_code: t.Union[t.Type[Exception], int]
) -> t.Tuple[t.Type[Exception], t.Optional[int]]:
exc_class_or_code: type[Exception] | int,
) -> tuple[type[Exception], int | None]:
"""Get the exception class being handled. For HTTP status codes

@@ -776,3 +762,3 @@ or ``HTTPException`` subclasses, return both the exception and

"""
exc_class: t.Type[Exception]
exc_class: type[Exception]

@@ -819,27 +805,2 @@ if isinstance(exc_class_or_code, int):

def _matching_loader_thinks_module_is_package(loader, mod_name):
"""Attempt to figure out if the given name is a package or a module.
:param: loader: The loader that handled the name.
:param mod_name: The name of the package or module.
"""
# Use loader.is_package if it's available.
if hasattr(loader, "is_package"):
return loader.is_package(mod_name)
cls = type(loader)
# NamespaceLoader doesn't implement is_package, but all names it
# loads must be packages.
if cls.__module__ == "_frozen_importlib" and cls.__name__ == "NamespaceLoader":
return True
# Otherwise we need to fail with an error that explains what went
# wrong.
raise AttributeError(
f"'{cls.__name__}.is_package()' must be implemented for PEP 302"
f" import hooks."
)
def _path_is_relative_to(path: pathlib.PurePath, base: str) -> bool:

@@ -863,61 +824,37 @@ # Path.is_relative_to doesn't exist until Python 3.9

raise ValueError("not found")
# ImportError: the machinery told us it does not exist
# ValueError:
# - the module name was invalid
# - the module name is __main__
# - *we* raised `ValueError` due to `root_spec` being `None`
except (ImportError, ValueError):
pass # handled below
else:
# ImportError: the machinery told us it does not exist
# ValueError:
# - the module name was invalid
# - the module name is __main__
# - we raised `ValueError` due to `root_spec` being `None`
return os.getcwd()
if root_spec.origin in {"namespace", None}:
# namespace package
if root_spec.origin in {"namespace", None}:
package_spec = importlib.util.find_spec(import_name)
if package_spec is not None and package_spec.submodule_search_locations:
# Pick the path in the namespace that contains the submodule.
package_path = pathlib.Path(
os.path.commonpath(package_spec.submodule_search_locations)
)
search_locations = (
location
for location in root_spec.submodule_search_locations
if _path_is_relative_to(package_path, location)
)
else:
# Pick the first path.
search_locations = iter(root_spec.submodule_search_locations)
return os.path.dirname(next(search_locations))
# a package (with __init__.py)
elif root_spec.submodule_search_locations:
return os.path.dirname(os.path.dirname(root_spec.origin))
# just a normal module
package_spec = importlib.util.find_spec(import_name)
if package_spec is not None and package_spec.submodule_search_locations:
# Pick the path in the namespace that contains the submodule.
package_path = pathlib.Path(
os.path.commonpath(package_spec.submodule_search_locations)
)
search_location = next(
location
for location in root_spec.submodule_search_locations
if _path_is_relative_to(package_path, location)
)
else:
return os.path.dirname(root_spec.origin)
# Pick the first path.
search_location = root_spec.submodule_search_locations[0]
# we were unable to find the `package_path` using PEP 451 loaders
loader = pkgutil.get_loader(root_mod_name)
if loader is None or root_mod_name == "__main__":
# import name is not found, or interactive/main module
return os.getcwd()
if hasattr(loader, "get_filename"):
filename = loader.get_filename(root_mod_name)
elif hasattr(loader, "archive"):
# zipimporter's loader.archive points to the .egg or .zip file.
filename = loader.archive
return os.path.dirname(search_location)
elif root_spec.submodule_search_locations:
# package with __init__.py
return os.path.dirname(os.path.dirname(root_spec.origin))
else:
# At least one loader is missing both get_filename and archive:
# Google App Engine's HardenedModulesHook, use __file__.
filename = importlib.import_module(root_mod_name).__file__
# module
return os.path.dirname(root_spec.origin)
package_path = os.path.abspath(os.path.dirname(filename))
# If the imported name is a package, filename is currently pointing
# to the root of the package, need to get the current directory.
if _matching_loader_thinks_module_is_package(loader, root_mod_name):
package_path = os.path.dirname(package_path)
return package_path
def find_package(import_name: str):

@@ -924,0 +861,0 @@ """Find the prefix that a package is installed under, and the path

@@ -0,4 +1,5 @@

from __future__ import annotations
import hashlib
import typing as t
import warnings
from collections.abc import MutableMapping

@@ -12,7 +13,5 @@ from datetime import datetime

from .helpers import is_ip
from .json.tag import TaggedJSONSerializer
if t.TYPE_CHECKING: # pragma: no cover
import typing_extensions as te
from .app import Flask

@@ -98,3 +97,3 @@ from .wrappers import Request, Response

def _fail(self, *args: t.Any, **kwargs: t.Any) -> "te.NoReturn":
def _fail(self, *args: t.Any, **kwargs: t.Any) -> t.NoReturn:
raise RuntimeError(

@@ -160,3 +159,3 @@ "The session is unavailable because no secret "

def make_null_session(self, app: "Flask") -> NullSession:
def make_null_session(self, app: Flask) -> NullSession:
"""Creates a null session which acts as a replacement object if the

@@ -182,65 +181,20 @@ real session support could not be loaded due to a configuration

def get_cookie_name(self, app: "Flask") -> str:
def get_cookie_name(self, app: Flask) -> str:
"""The name of the session cookie. Uses``app.config["SESSION_COOKIE_NAME"]``."""
return app.config["SESSION_COOKIE_NAME"]
def get_cookie_domain(self, app: "Flask") -> t.Optional[str]:
"""Returns the domain that should be set for the session cookie.
def get_cookie_domain(self, app: Flask) -> str | None:
"""The value of the ``Domain`` parameter on the session cookie. If not set,
browsers will only send the cookie to the exact domain it was set from.
Otherwise, they will send it to any subdomain of the given value as well.
Uses ``SESSION_COOKIE_DOMAIN`` if it is configured, otherwise
falls back to detecting the domain based on ``SERVER_NAME``.
Uses the :data:`SESSION_COOKIE_DOMAIN` config.
Once detected (or if not set at all), ``SESSION_COOKIE_DOMAIN`` is
updated to avoid re-running the logic.
.. versionchanged:: 2.3
Not set by default, does not fall back to ``SERVER_NAME``.
"""
rv = app.config["SESSION_COOKIE_DOMAIN"]
return rv if rv else None
# set explicitly, or cached from SERVER_NAME detection
# if False, return None
if rv is not None:
return rv if rv else None
rv = app.config["SERVER_NAME"]
# server name not set, cache False to return none next time
if not rv:
app.config["SESSION_COOKIE_DOMAIN"] = False
return None
# chop off the port which is usually not supported by browsers
# remove any leading '.' since we'll add that later
rv = rv.rsplit(":", 1)[0].lstrip(".")
if "." not in rv:
# Chrome doesn't allow names without a '.'. This should only
# come up with localhost. Hack around this by not setting
# the name, and show a warning.
warnings.warn(
f"{rv!r} is not a valid cookie domain, it must contain"
" a '.'. Add an entry to your hosts file, for example"
f" '{rv}.localdomain', and use that instead."
)
app.config["SESSION_COOKIE_DOMAIN"] = False
return None
ip = is_ip(rv)
if ip:
warnings.warn(
"The session cookie domain is an IP address. This may not work"
" as intended in some browsers. Add an entry to your hosts"
' file, for example "localhost.localdomain", and use that'
" instead."
)
# if this is not an ip and app is mounted at the root, allow subdomain
# matching by adding a '.' prefix
if self.get_cookie_path(app) == "/" and not ip:
rv = f".{rv}"
app.config["SESSION_COOKIE_DOMAIN"] = rv
return rv
def get_cookie_path(self, app: "Flask") -> str:
def get_cookie_path(self, app: Flask) -> str:
"""Returns the path for which the cookie should be valid. The

@@ -253,3 +207,3 @@ default implementation uses the value from the ``SESSION_COOKIE_PATH``

def get_cookie_httponly(self, app: "Flask") -> bool:
def get_cookie_httponly(self, app: Flask) -> bool:
"""Returns True if the session cookie should be httponly. This

@@ -261,3 +215,3 @@ currently just returns the value of the ``SESSION_COOKIE_HTTPONLY``

def get_cookie_secure(self, app: "Flask") -> bool:
def get_cookie_secure(self, app: Flask) -> bool:
"""Returns True if the cookie should be secure. This currently

@@ -268,3 +222,3 @@ just returns the value of the ``SESSION_COOKIE_SECURE`` setting.

def get_cookie_samesite(self, app: "Flask") -> str:
def get_cookie_samesite(self, app: Flask) -> str:
"""Return ``'Strict'`` or ``'Lax'`` if the cookie should use the

@@ -276,5 +230,3 @@ ``SameSite`` attribute. This currently just returns the value of

def get_expiration_time(
self, app: "Flask", session: SessionMixin
) -> t.Optional[datetime]:
def get_expiration_time(self, app: Flask, session: SessionMixin) -> datetime | None:
"""A helper method that returns an expiration date for the session

@@ -289,3 +241,3 @@ or ``None`` if the session is linked to the browser session. The

def should_set_cookie(self, app: "Flask", session: SessionMixin) -> bool:
def should_set_cookie(self, app: Flask, session: SessionMixin) -> bool:
"""Used by session backends to determine if a ``Set-Cookie`` header

@@ -306,5 +258,3 @@ should be set for this session cookie for this response. If the session

def open_session(
self, app: "Flask", request: "Request"
) -> t.Optional[SessionMixin]:
def open_session(self, app: Flask, request: Request) -> SessionMixin | None:
"""This is called at the beginning of each request, after

@@ -324,3 +274,3 @@ pushing the request context, before matching the URL.

def save_session(
self, app: "Flask", session: SessionMixin, response: "Response"
self, app: Flask, session: SessionMixin, response: Response
) -> None:

@@ -356,5 +306,3 @@ """This is called at the end of each request, after generating

def get_signing_serializer(
self, app: "Flask"
) -> t.Optional[URLSafeTimedSerializer]:
def get_signing_serializer(self, app: Flask) -> URLSafeTimedSerializer | None:
if not app.secret_key:

@@ -372,5 +320,3 @@ return None

def open_session(
self, app: "Flask", request: "Request"
) -> t.Optional[SecureCookieSession]:
def open_session(self, app: Flask, request: Request) -> SecureCookieSession | None:
s = self.get_signing_serializer(app)

@@ -390,3 +336,3 @@ if s is None:

def save_session(
self, app: "Flask", session: SessionMixin, response: "Response"
self, app: Flask, session: SessionMixin, response: Response
) -> None:

@@ -393,0 +339,0 @@ name = self.get_cookie_name(app)

@@ -0,47 +1,11 @@

from __future__ import annotations
import typing as t
import warnings
try:
from blinker import Namespace
from blinker import Namespace
signals_available = True
except ImportError:
signals_available = False
class Namespace: # type: ignore
def signal(self, name: str, doc: t.Optional[str] = None) -> "_FakeSignal":
return _FakeSignal(name, doc)
class _FakeSignal:
"""If blinker is unavailable, create a fake class with the same
interface that allows sending of signals but will fail with an
error on anything else. Instead of doing anything on send, it
will just ignore the arguments and do nothing instead.
"""
def __init__(self, name: str, doc: t.Optional[str] = None) -> None:
self.name = name
self.__doc__ = doc
def send(self, *args: t.Any, **kwargs: t.Any) -> t.Any:
pass
def _fail(self, *args: t.Any, **kwargs: t.Any) -> t.Any:
raise RuntimeError(
"Signalling support is unavailable because the blinker"
" library is not installed."
) from None
connect = connect_via = connected_to = temporarily_connected_to = _fail
disconnect = _fail
has_receivers_for = receivers_for = _fail
del _fail
# The namespace for code signals. If you are not Flask code, do
# not put signals in here. Create your own namespace instead.
# This namespace is only for signals provided by Flask itself.
_signals = Namespace()
# Core signals. For usage examples grep the source code or consult
# the API documentation in docs/api.rst as well as docs/signals.rst
template_rendered = _signals.signal("template-rendered")

@@ -57,1 +21,14 @@ before_render_template = _signals.signal("before-render-template")

message_flashed = _signals.signal("message-flashed")
def __getattr__(name: str) -> t.Any:
if name == "signals_available":
warnings.warn(
"The 'signals_available' attribute is deprecated and will be removed in"
" Flask 2.4. Signals are always available.",
DeprecationWarning,
stacklevel=2,
)
return True
raise AttributeError(name)

@@ -0,1 +1,3 @@

from __future__ import annotations
import typing as t

@@ -21,3 +23,3 @@

def _default_template_ctx_processor() -> t.Dict[str, t.Any]:
def _default_template_ctx_processor() -> dict[str, t.Any]:
"""Default template context processor. Injects `request`,

@@ -28,3 +30,3 @@ `session` and `g`.

reqctx = _cv_request.get(None)
rv: t.Dict[str, t.Any] = {}
rv: dict[str, t.Any] = {}
if appctx is not None:

@@ -44,3 +46,3 @@ rv["g"] = appctx.g

def __init__(self, app: "Flask", **options: t.Any) -> None:
def __init__(self, app: Flask, **options: t.Any) -> None:
if "loader" not in options:

@@ -57,3 +59,3 @@ options["loader"] = app.create_global_jinja_loader()

def __init__(self, app: "Flask") -> None:
def __init__(self, app: Flask) -> None:
self.app = app

@@ -63,3 +65,3 @@

self, environment: Environment, template: str
) -> t.Tuple[str, t.Optional[str], t.Optional[t.Callable]]:
) -> tuple[str, str | None, t.Callable | None]:
if self.app.config["EXPLAIN_TEMPLATE_LOADING"]:

@@ -71,8 +73,6 @@ return self._get_source_explained(environment, template)

self, environment: Environment, template: str
) -> t.Tuple[str, t.Optional[str], t.Optional[t.Callable]]:
) -> tuple[str, str | None, t.Callable | None]:
attempts = []
rv: t.Optional[t.Tuple[str, t.Optional[str], t.Optional[t.Callable[[], bool]]]]
trv: t.Optional[
t.Tuple[str, t.Optional[str], t.Optional[t.Callable[[], bool]]]
] = None
rv: tuple[str, str | None, t.Callable[[], bool] | None] | None
trv: None | (tuple[str, str | None, t.Callable[[], bool] | None]) = None

@@ -98,3 +98,3 @@ for srcobj, loader in self._iter_loaders(template):

self, environment: Environment, template: str
) -> t.Tuple[str, t.Optional[str], t.Optional[t.Callable]]:
) -> tuple[str, str | None, t.Callable | None]:
for _srcobj, loader in self._iter_loaders(template):

@@ -109,3 +109,3 @@ try:

self, template: str
) -> t.Generator[t.Tuple["Scaffold", BaseLoader], None, None]:
) -> t.Generator[tuple[Scaffold, BaseLoader], None, None]:
loader = self.app.jinja_loader

@@ -120,3 +120,3 @@ if loader is not None:

def list_templates(self) -> t.List[str]:
def list_templates(self) -> list[str]:
result = set()

@@ -136,7 +136,11 @@ loader = self.app.jinja_loader

def _render(app: "Flask", template: Template, context: t.Dict[str, t.Any]) -> str:
def _render(app: Flask, template: Template, context: dict[str, t.Any]) -> str:
app.update_template_context(context)
before_render_template.send(app, template=template, context=context)
before_render_template.send(
app, _async_wrapper=app.ensure_sync, template=template, context=context
)
rv = template.render(context)
template_rendered.send(app, template=template, context=context)
template_rendered.send(
app, _async_wrapper=app.ensure_sync, template=template, context=context
)
return rv

@@ -146,4 +150,4 @@

def render_template(
template_name_or_list: t.Union[str, Template, t.List[t.Union[str, Template]]],
**context: t.Any
template_name_or_list: str | Template | list[str | Template],
**context: t.Any,
) -> str:

@@ -174,10 +178,14 @@ """Render a template by name with the given context.

def _stream(
app: "Flask", template: Template, context: t.Dict[str, t.Any]
app: Flask, template: Template, context: dict[str, t.Any]
) -> t.Iterator[str]:
app.update_template_context(context)
before_render_template.send(app, template=template, context=context)
before_render_template.send(
app, _async_wrapper=app.ensure_sync, template=template, context=context
)
def generate() -> t.Iterator[str]:
yield from template.generate(context)
template_rendered.send(app, template=template, context=context)
template_rendered.send(
app, _async_wrapper=app.ensure_sync, template=template, context=context
)

@@ -194,4 +202,4 @@ rv = generate()

def stream_template(
template_name_or_list: t.Union[str, Template, t.List[t.Union[str, Template]]],
**context: t.Any
template_name_or_list: str | Template | list[str | Template],
**context: t.Any,
) -> t.Iterator[str]:

@@ -198,0 +206,0 @@ """Render a template by name with the given context as a stream.

@@ -0,1 +1,4 @@

from __future__ import annotations
import importlib.metadata
import typing as t

@@ -46,7 +49,7 @@ from contextlib import contextmanager

self,
app: "Flask",
app: Flask,
path: str = "/",
base_url: t.Optional[str] = None,
subdomain: t.Optional[str] = None,
url_scheme: t.Optional[str] = None,
base_url: str | None = None,
subdomain: str | None = None,
url_scheme: str | None = None,
*args: t.Any,

@@ -94,2 +97,14 @@ **kwargs: t.Any,

_werkzeug_version = ""
def _get_werkzeug_version() -> str:
global _werkzeug_version
if not _werkzeug_version:
_werkzeug_version = importlib.metadata.version("werkzeug")
return _werkzeug_version
class FlaskClient(Client):

@@ -109,3 +124,3 @@ """Works like a regular Werkzeug test client but has knowledge about

application: "Flask"
application: Flask

@@ -115,7 +130,7 @@ def __init__(self, *args: t.Any, **kwargs: t.Any) -> None:

self.preserve_context = False
self._new_contexts: t.List[t.ContextManager[t.Any]] = []
self._new_contexts: list[t.ContextManager[t.Any]] = []
self._context_stack = ExitStack()
self.environ_base = {
"REMOTE_ADDR": "127.0.0.1",
"HTTP_USER_AGENT": f"werkzeug/{werkzeug.__version__}",
"HTTP_USER_AGENT": f"Werkzeug/{_get_werkzeug_version()}",
}

@@ -143,6 +158,3 @@

"""
# new cookie interface for Werkzeug >= 2.3
cookie_storage = self._cookies if hasattr(self, "_cookies") else self.cookie_jar
if cookie_storage is None:
if self._cookies is None:
raise TypeError(

@@ -154,8 +166,4 @@ "Cookies are disabled. Create a client with 'use_cookies=True'."

ctx = app.test_request_context(*args, **kwargs)
self._add_cookies_to_wsgi(ctx.request.environ)
if hasattr(self, "_add_cookies_to_wsgi"):
self._add_cookies_to_wsgi(ctx.request.environ)
else:
self.cookie_jar.inject_wsgi(ctx.request.environ) # type: ignore[union-attr]
with ctx:

@@ -176,21 +184,7 @@ sess = app.session_interface.open_session(app, ctx.request)

if hasattr(self, "_update_cookies_from_response"):
try:
# Werkzeug>=2.3.3
self._update_cookies_from_response(
ctx.request.host.partition(":")[0],
ctx.request.path,
resp.headers.getlist("Set-Cookie"),
)
except TypeError:
# Werkzeug>=2.3.0,<2.3.3
self._update_cookies_from_response( # type: ignore[call-arg]
ctx.request.host.partition(":")[0],
resp.headers.getlist("Set-Cookie"), # type: ignore[arg-type]
)
else:
# Werkzeug<2.3.0
self.cookie_jar.extract_wsgi( # type: ignore[union-attr]
ctx.request.environ, resp.headers
)
self._update_cookies_from_response(
ctx.request.host.partition(":")[0],
ctx.request.path,
resp.headers.getlist("Set-Cookie"),
)

@@ -220,3 +214,3 @@ def _copy_environ(self, other):

**kwargs: t.Any,
) -> "TestResponse":
) -> TestResponse:
if args and isinstance(

@@ -260,3 +254,3 @@ args[0], (werkzeug.test.EnvironBuilder, dict, BaseRequest)

def __enter__(self) -> "FlaskClient":
def __enter__(self) -> FlaskClient:
if self.preserve_context:

@@ -269,5 +263,5 @@ raise RuntimeError("Cannot nest client invocations")

self,
exc_type: t.Optional[type],
exc_value: t.Optional[BaseException],
tb: t.Optional[TracebackType],
exc_type: type | None,
exc_value: BaseException | None,
tb: TracebackType | None,
) -> None:

@@ -284,3 +278,3 @@ self.preserve_context = False

def __init__(self, app: "Flask", **kwargs: t.Any) -> None:
def __init__(self, app: Flask, **kwargs: t.Any) -> None:
self.app = app

@@ -287,0 +281,0 @@ super().__init__(**kwargs)

@@ -0,1 +1,3 @@

from __future__ import annotations
import typing as t

@@ -2,0 +4,0 @@

@@ -0,1 +1,3 @@

from __future__ import annotations
import typing as t

@@ -48,3 +50,3 @@

#: ``add_url_rule`` by default.
methods: t.ClassVar[t.Optional[t.Collection[str]]] = None
methods: t.ClassVar[t.Collection[str] | None] = None

@@ -54,3 +56,3 @@ #: Control whether the ``OPTIONS`` method is handled automatically.

#: ``add_url_rule`` by default.
provide_automatic_options: t.ClassVar[t.Optional[bool]] = None
provide_automatic_options: t.ClassVar[bool | None] = None

@@ -63,3 +65,3 @@ #: A list of decorators to apply, in order, to the generated view

#: .. versionadded:: 0.8
decorators: t.ClassVar[t.List[t.Callable]] = []
decorators: t.ClassVar[list[t.Callable]] = []

@@ -66,0 +68,0 @@ #: Create a new instance of this view class for every request by

@@ -0,1 +1,3 @@

from __future__ import annotations
import typing as t

@@ -40,7 +42,7 @@

#: .. versionadded:: 0.6
url_rule: t.Optional["Rule"] = None
url_rule: Rule | None = None
#: A dict of view arguments that matched the request. If an exception
#: happened when matching, this will be ``None``.
view_args: t.Optional[t.Dict[str, t.Any]] = None
view_args: dict[str, t.Any] | None = None

@@ -51,6 +53,6 @@ #: If matching the URL failed, this is the exception that will be

#: something similar.
routing_exception: t.Optional[Exception] = None
routing_exception: Exception | None = None
@property
def max_content_length(self) -> t.Optional[int]: # type: ignore
def max_content_length(self) -> int | None: # type: ignore
"""Read-only view of the ``MAX_CONTENT_LENGTH`` config key."""

@@ -63,3 +65,3 @@ if current_app:

@property
def endpoint(self) -> t.Optional[str]:
def endpoint(self) -> str | None:
"""The endpoint that matched the request URL.

@@ -79,3 +81,3 @@

@property
def blueprint(self) -> t.Optional[str]:
def blueprint(self) -> str | None:
"""The registered name of the current blueprint.

@@ -99,3 +101,3 @@

@property
def blueprints(self) -> t.List[str]:
def blueprints(self) -> list[str]:
"""The registered names of the current blueprint upwards through

@@ -131,3 +133,3 @@ parent blueprints.

def on_json_loading_failed(self, e: t.Optional[ValueError]) -> t.Any:
def on_json_loading_failed(self, e: ValueError | None) -> t.Any:
try:

@@ -160,3 +162,3 @@ return super().on_json_loading_failed(e)

default_mimetype = "text/html"
default_mimetype: str | None = "text/html"

@@ -163,0 +165,0 @@ json_module = json

import os
import pkgutil
import sys
import textwrap

@@ -23,3 +22,2 @@ import pytest

(os.environ, "FLASK_APP", monkeypatch.notset),
(os.environ, "FLASK_ENV", monkeypatch.notset),
(os.environ, "FLASK_DEBUG", monkeypatch.notset),

@@ -134,6 +132,7 @@ (os.environ, "FLASK_RUN_FROM_CLI", monkeypatch.notset),

@pytest.fixture
def modules_tmpdir(tmpdir, monkeypatch):
"""A tmpdir added to sys.path."""
rv = tmpdir.mkdir("modules_tmpdir")
monkeypatch.syspath_prepend(str(rv))
def modules_tmp_path(tmp_path, monkeypatch):
"""A temporary directory added to sys.path."""
rv = tmp_path / "modules_tmp"
rv.mkdir()
monkeypatch.syspath_prepend(os.fspath(rv))
return rv

@@ -143,16 +142,14 @@

@pytest.fixture
def modules_tmpdir_prefix(modules_tmpdir, monkeypatch):
monkeypatch.setattr(sys, "prefix", str(modules_tmpdir))
return modules_tmpdir
def modules_tmp_path_prefix(modules_tmp_path, monkeypatch):
monkeypatch.setattr(sys, "prefix", os.fspath(modules_tmp_path))
return modules_tmp_path
@pytest.fixture
def site_packages(modules_tmpdir, monkeypatch):
def site_packages(modules_tmp_path, monkeypatch):
"""Create a fake site-packages."""
rv = (
modules_tmpdir.mkdir("lib")
.mkdir(f"python{sys.version_info.major}.{sys.version_info.minor}")
.mkdir("site-packages")
)
monkeypatch.syspath_prepend(str(rv))
py_dir = f"python{sys.version_info.major}.{sys.version_info.minor}"
rv = modules_tmp_path / "lib" / py_dir / "site-packages"
rv.mkdir(parents=True)
monkeypatch.syspath_prepend(os.fspath(rv))
return rv

@@ -162,38 +159,2 @@

@pytest.fixture
def install_egg(modules_tmpdir, monkeypatch):
"""Generate egg from package name inside base and put the egg into
sys.path."""
def inner(name, base=modules_tmpdir):
base.join(name).ensure_dir()
base.join(name).join("__init__.py").ensure()
egg_setup = base.join("setup.py")
egg_setup.write(
textwrap.dedent(
f"""
from setuptools import setup
setup(
name="{name}",
version="1.0",
packages=["site_egg"],
zip_safe=True,
)
"""
)
)
import subprocess
subprocess.check_call(
[sys.executable, "setup.py", "bdist_egg"], cwd=str(modules_tmpdir)
)
(egg_path,) = modules_tmpdir.join("dist/").listdir()
monkeypatch.syspath_prepend(str(egg_path))
return egg_path
return inner
@pytest.fixture
def purge_module(request):

@@ -200,0 +161,0 @@ def inner(name):

@@ -98,3 +98,2 @@ import asyncio

def test_async_before_after_request():
app_first_called = False
app_before_called = False

@@ -111,9 +110,2 @@ app_after_called = False

with pytest.deprecated_call():
@app.before_first_request
async def before_first():
nonlocal app_first_called
app_first_called = True
@app.before_request

@@ -151,3 +143,2 @@ async def before():

test_client.get("/")
assert app_first_called
assert app_before_called

@@ -154,0 +145,0 @@ assert app_after_called

import gc
import re
import time
import uuid

@@ -10,6 +9,6 @@ import warnings

from platform import python_implementation
from threading import Thread
import pytest
import werkzeug.serving
from markupsafe import Markup
from werkzeug.exceptions import BadRequest

@@ -256,4 +255,4 @@ from werkzeug.exceptions import Forbidden

def test_session_using_server_name(app, client):
app.config.update(SERVER_NAME="example.com")
def test_session_path(app, client):
app.config.update(APPLICATION_ROOT="/foo")

@@ -265,34 +264,4 @@ @app.route("/")

rv = client.get("/", "http://example.com/")
cookie = rv.headers["set-cookie"].lower()
# or condition for Werkzeug < 2.3
assert "domain=example.com" in cookie or "domain=.example.com" in cookie
def test_session_using_server_name_and_port(app, client):
app.config.update(SERVER_NAME="example.com:8080")
@app.route("/")
def index():
flask.session["testing"] = 42
return "Hello World"
rv = client.get("/", "http://example.com:8080/")
cookie = rv.headers["set-cookie"].lower()
# or condition for Werkzeug < 2.3
assert "domain=example.com" in cookie or "domain=.example.com" in cookie
def test_session_using_server_name_port_and_path(app, client):
app.config.update(SERVER_NAME="example.com:8080", APPLICATION_ROOT="/foo")
@app.route("/")
def index():
flask.session["testing"] = 42
return "Hello World"
rv = client.get("/", "http://example.com:8080/foo")
assert "domain=example.com" in rv.headers["set-cookie"].lower()
assert "path=/foo" in rv.headers["set-cookie"].lower()
assert "httponly" in rv.headers["set-cookie"].lower()

@@ -389,30 +358,2 @@

def test_session_localhost_warning(recwarn, app, client):
app.config.update(SERVER_NAME="localhost:5000")
@app.route("/")
def index():
flask.session["testing"] = 42
return "testing"
rv = client.get("/", "http://localhost:5000/")
assert "domain" not in rv.headers["set-cookie"].lower()
w = recwarn.pop(UserWarning)
assert "'localhost' is not a valid cookie domain" in str(w.message)
def test_session_ip_warning(recwarn, app, client):
app.config.update(SERVER_NAME="127.0.0.1:5000")
@app.route("/")
def index():
flask.session["testing"] = 42
return "testing"
rv = client.get("/", "http://127.0.0.1:5000/")
assert "domain=127.0.0.1" in rv.headers["set-cookie"].lower()
w = recwarn.pop(UserWarning)
assert "cookie domain is an IP" in str(w.message)
def test_missing_session(app):

@@ -485,3 +426,3 @@ app.secret_key = None

flask.session["b"] = b"\xff"
flask.session["m"] = flask.Markup("<html>")
flask.session["m"] = Markup("<html>")
flask.session["u"] = the_uuid

@@ -498,6 +439,6 @@ flask.session["d"] = now

assert s["t"] == (1, 2, 3)
assert type(s["b"]) == bytes
assert type(s["b"]) is bytes
assert s["b"] == b"\xff"
assert type(s["m"]) == flask.Markup
assert s["m"] == flask.Markup("<html>")
assert type(s["m"]) is Markup
assert s["m"] == Markup("<html>")
assert s["u"] == the_uuid

@@ -649,3 +590,3 @@ assert s["d"] == now

flask.flash("Hello World", "error")
flask.flash(flask.Markup("<em>Testing</em>"), "warning")
flask.flash(Markup("<em>Testing</em>"), "warning")
return ""

@@ -659,3 +600,3 @@

"Hello World",
flask.Markup("<em>Testing</em>"),
Markup("<em>Testing</em>"),
]

@@ -671,3 +612,3 @@ return ""

("error", "Hello World"),
("warning", flask.Markup("<em>Testing</em>")),
("warning", Markup("<em>Testing</em>")),
]

@@ -691,3 +632,3 @@ return ""

("message", "Hello World"),
("warning", flask.Markup("<em>Testing</em>")),
("warning", Markup("<em>Testing</em>")),
]

@@ -701,3 +642,3 @@ return ""

assert messages[0] == "Hello World"
assert messages[1] == flask.Markup("<em>Testing</em>")
assert messages[1] == Markup("<em>Testing</em>")
return ""

@@ -833,3 +774,3 @@

def teardown_request1(exc):
assert type(exc) == ZeroDivisionError
assert type(exc) is ZeroDivisionError
called.append(True)

@@ -846,3 +787,3 @@ # This raises a new error and blows away sys.exc_info(), so we can

def teardown_request2(exc):
assert type(exc) == ZeroDivisionError
assert type(exc) is ZeroDivisionError
called.append(True)

@@ -1701,3 +1642,2 @@ # This raises a new error and blows away sys.exc_info(), so we can

assert not app.got_first_request
assert client.get("/").data == b"Awesome"

@@ -1711,39 +1651,2 @@

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

@@ -1750,0 +1653,0 @@ app.config["DEBUG"] = True

@@ -259,2 +259,7 @@ import pytest

def test_empty_name_not_allowed(app, client):
with pytest.raises(ValueError):
flask.Blueprint("", __name__)
def test_dotted_names_from_app(app, client):

@@ -726,8 +731,2 @@ test = flask.Blueprint("test", __name__)

with pytest.deprecated_call():
@bp.before_app_first_request
def before_first_request():
evts.append("first")
@bp.before_app_request

@@ -760,3 +759,3 @@ def before_app():

assert resp == b"request|after"
assert evts == ["first", "before", "after", "teardown"]
assert evts == ["before", "after", "teardown"]

@@ -766,3 +765,3 @@ # second request

assert resp == b"request|after"
assert evts == ["first"] + ["before", "after", "teardown"] * 2
assert evts == ["before", "after", "teardown"] * 2

@@ -957,2 +956,51 @@

def test_nesting_subdomains(app, client) -> None:
subdomain = "api"
parent = flask.Blueprint("parent", __name__)
child = flask.Blueprint("child", __name__)
@child.route("/child/")
def index():
return "child"
parent.register_blueprint(child)
app.register_blueprint(parent, subdomain=subdomain)
client.allow_subdomain_redirects = True
domain_name = "domain.tld"
app.config["SERVER_NAME"] = domain_name
response = client.get("/child/", base_url="http://api." + domain_name)
assert response.status_code == 200
def test_child_and_parent_subdomain(app, client) -> None:
child_subdomain = "api"
parent_subdomain = "parent"
parent = flask.Blueprint("parent", __name__)
child = flask.Blueprint("child", __name__, subdomain=child_subdomain)
@child.route("/")
def index():
return "child"
parent.register_blueprint(child)
app.register_blueprint(parent, subdomain=parent_subdomain)
client.allow_subdomain_redirects = True
domain_name = "domain.tld"
app.config["SERVER_NAME"] = domain_name
response = client.get(
"/", base_url=f"http://{child_subdomain}.{parent_subdomain}.{domain_name}"
)
assert response.status_code == 200
response = client.get("/", base_url=f"http://{parent_subdomain}.{domain_name}")
assert response.status_code == 404
def test_unique_blueprint_names(app, client) -> None:

@@ -959,0 +1007,0 @@ bp = flask.Blueprint("bp", __name__)

# This file was part of Flask-CLI and was modified under the terms of
# its Revised BSD License. Copyright © 2015 CERN.
import importlib.metadata
import os
import platform
import ssl

@@ -230,6 +232,2 @@ import sys

def test_get_version(test_apps, capsys):
from flask import __version__ as flask_version
from werkzeug import __version__ as werkzeug_version
from platform import python_version
class MockCtx:

@@ -245,5 +243,5 @@ resilient_parsing = False

out, err = capsys.readouterr()
assert f"Python {python_version()}" in out
assert f"Flask {flask_version}" in out
assert f"Werkzeug {werkzeug_version}" in out
assert f"Python {platform.python_version()}" in out
assert f"Flask {importlib.metadata.version('flask')}" in out
assert f"Werkzeug {importlib.metadata.version('werkzeug')}" in out

@@ -438,12 +436,8 @@

app = Flask(__name__)
app.testing = True
@app.route("/get_post/<int:x>/<int:y>", methods=["GET", "POST"])
def yyy_get_post(x, y):
pass
@app.route("/zzz_post", methods=["POST"])
def aaa_post():
pass
app.add_url_rule(
"/get_post/<int:x>/<int:y>",
methods=["GET", "POST"],
endpoint="yyy_get_post",
)
app.add_url_rule("/zzz_post", methods=["POST"], endpoint="aaa_post")
return app

@@ -456,13 +450,2 @@

@pytest.fixture
def invoke_no_routes(self, runner):
def create_app():
app = Flask(__name__, static_folder=None)
app.testing = True
return app
cli = FlaskGroup(create_app=create_app)
return partial(runner.invoke, cli)
def expect_order(self, order, output):

@@ -500,8 +483,28 @@ # skip the header and match the start of each row

def test_no_routes(self, invoke_no_routes):
result = invoke_no_routes(["routes"])
def test_no_routes(self, runner):
app = Flask(__name__, static_folder=None)
cli = FlaskGroup(create_app=lambda: app)
result = runner.invoke(cli, ["routes"])
assert result.exit_code == 0
assert "No routes were registered." in result.output
def test_subdomain(self, runner):
app = Flask(__name__, static_folder=None)
app.add_url_rule("/a", subdomain="a", endpoint="a")
app.add_url_rule("/b", subdomain="b", endpoint="b")
cli = FlaskGroup(create_app=lambda: app)
result = runner.invoke(cli, ["routes"])
assert result.exit_code == 0
assert "Subdomain" in result.output
def test_host(self, runner):
app = Flask(__name__, static_folder=None, host_matching=True)
app.add_url_rule("/a", host="a", endpoint="a")
app.add_url_rule("/b", host="b", endpoint="b")
cli = FlaskGroup(create_app=lambda: app)
result = runner.invoke(cli, ["routes"])
assert result.exit_code == 0
assert "Host" in result.output
def dotenv_not_available():

@@ -508,0 +511,0 @@ try:

import json
import os
import textwrap

@@ -9,3 +8,2 @@ import pytest

# config keys used for the TestConfig

@@ -34,3 +32,3 @@ TEST_KEY = "foo"

def test_config_from_file():
def test_config_from_file_json():
app = flask.Flask(__name__)

@@ -42,2 +40,12 @@ current_dir = os.path.dirname(os.path.abspath(__file__))

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

@@ -239,15 +247,8 @@ monkeypatch.setenv("FLASK_STRING", "value")

@pytest.mark.parametrize("encoding", ["utf-8", "iso-8859-15", "latin-1"])
def test_from_pyfile_weird_encoding(tmpdir, encoding):
f = tmpdir.join("my_config.py")
f.write_binary(
textwrap.dedent(
f"""
# -*- coding: {encoding} -*-
TEST_VALUE = "föö"
"""
).encode(encoding)
)
def test_from_pyfile_weird_encoding(tmp_path, encoding):
f = tmp_path / "my_config.py"
f.write_text(f'# -*- coding: {encoding} -*-\nTEST_VALUE = "föö"\n', encoding)
app = flask.Flask(__name__)
app.config.from_pyfile(str(f))
app.config.from_pyfile(os.fspath(f))
value = app.config["TEST_VALUE"]
assert value == "föö"

@@ -221,4 +221,4 @@ import io

def test_name_with_import_error(self, modules_tmpdir):
modules_tmpdir.join("importerror.py").write("raise NotImplementedError()")
def test_name_with_import_error(self, modules_tmp_path):
(modules_tmp_path / "importerror.py").write_text("raise NotImplementedError()")
try:

@@ -306,20 +306,14 @@ flask.Flask("importerror")

@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

@@ -326,0 +320,0 @@ def test_make_response(self):

@@ -1,2 +0,2 @@

import sys
import os

@@ -8,13 +8,12 @@ import pytest

def test_explicit_instance_paths(modules_tmpdir):
with pytest.raises(ValueError) as excinfo:
def test_explicit_instance_paths(modules_tmp_path):
with pytest.raises(ValueError, match=".*must be absolute"):
flask.Flask(__name__, instance_path="instance")
assert "must be absolute" in str(excinfo.value)
app = flask.Flask(__name__, instance_path=str(modules_tmpdir))
assert app.instance_path == str(modules_tmpdir)
app = flask.Flask(__name__, instance_path=os.fspath(modules_tmp_path))
assert app.instance_path == os.fspath(modules_tmp_path)
def test_uninstalled_module_paths(modules_tmpdir, purge_module):
app = modules_tmpdir.join("config_module_app.py").write(
def test_uninstalled_module_paths(modules_tmp_path, purge_module):
(modules_tmp_path / "config_module_app.py").write_text(
"import os\n"

@@ -29,9 +28,9 @@ "import flask\n"

assert app.instance_path == str(modules_tmpdir.join("instance"))
assert app.instance_path == os.fspath(modules_tmp_path / "instance")
def test_uninstalled_package_paths(modules_tmpdir, purge_module):
app = modules_tmpdir.mkdir("config_package_app")
init = app.join("__init__.py")
init.write(
def test_uninstalled_package_paths(modules_tmp_path, purge_module):
app = modules_tmp_path / "config_package_app"
app.mkdir()
(app / "__init__.py").write_text(
"import os\n"

@@ -46,12 +45,12 @@ "import flask\n"

assert app.instance_path == str(modules_tmpdir.join("instance"))
assert app.instance_path == os.fspath(modules_tmp_path / "instance")
def test_uninstalled_namespace_paths(tmpdir, monkeypatch, purge_module):
def test_uninstalled_namespace_paths(tmp_path, monkeypatch, purge_module):
def create_namespace(package):
project = tmpdir.join(f"project-{package}")
monkeypatch.syspath_prepend(str(project))
project.join("namespace").join(package).join("__init__.py").write(
"import flask\napp = flask.Flask(__name__)\n", ensure=True
)
project = tmp_path / f"project-{package}"
monkeypatch.syspath_prepend(os.fspath(project))
ns = project / "namespace" / package
ns.mkdir(parents=True)
(ns / "__init__.py").write_text("import flask\napp = flask.Flask(__name__)\n")
return project

@@ -66,9 +65,9 @@

assert app.instance_path == str(project2.join("instance"))
assert app.instance_path == os.fspath(project2 / "instance")
def test_installed_module_paths(
modules_tmpdir, modules_tmpdir_prefix, purge_module, site_packages, limit_loader
modules_tmp_path, modules_tmp_path_prefix, purge_module, site_packages, limit_loader
):
site_packages.join("site_app.py").write(
(site_packages / "site_app.py").write_text(
"import flask\napp = flask.Flask(__name__)\n"

@@ -80,14 +79,17 @@ )

assert app.instance_path == modules_tmpdir.join("var").join("site_app-instance")
assert app.instance_path == os.fspath(
modules_tmp_path / "var" / "site_app-instance"
)
def test_installed_package_paths(
limit_loader, modules_tmpdir, modules_tmpdir_prefix, purge_module, monkeypatch
limit_loader, modules_tmp_path, modules_tmp_path_prefix, purge_module, monkeypatch
):
installed_path = modules_tmpdir.mkdir("path")
installed_path = modules_tmp_path / "path"
installed_path.mkdir()
monkeypatch.syspath_prepend(installed_path)
app = installed_path.mkdir("installed_package")
init = app.join("__init__.py")
init.write("import flask\napp = flask.Flask(__name__)")
app = installed_path / "installed_package"
app.mkdir()
(app / "__init__.py").write_text("import flask\napp = flask.Flask(__name__)\n")
purge_module("installed_package")

@@ -97,4 +99,4 @@

assert app.instance_path == modules_tmpdir.join("var").join(
"installed_package-instance"
assert app.instance_path == os.fspath(
modules_tmp_path / "var" / "installed_package-instance"
)

@@ -104,7 +106,7 @@

def test_prefix_package_paths(
limit_loader, modules_tmpdir, modules_tmpdir_prefix, purge_module, site_packages
limit_loader, modules_tmp_path, modules_tmp_path_prefix, purge_module, site_packages
):
app = site_packages.mkdir("site_package")
init = app.join("__init__.py")
init.write("import flask\napp = flask.Flask(__name__)")
app = site_packages / "site_package"
app.mkdir()
(app / "__init__.py").write_text("import flask\napp = flask.Flask(__name__)\n")
purge_module("site_package")

@@ -114,20 +116,4 @@

assert site_package.app.instance_path == modules_tmpdir.join("var").join(
"site_package-instance"
assert site_package.app.instance_path == os.fspath(
modules_tmp_path / "var" / "site_package-instance"
)
def test_egg_installed_paths(install_egg, modules_tmpdir, modules_tmpdir_prefix):
modules_tmpdir.mkdir("site_egg").join("__init__.py").write(
"import flask\n\napp = flask.Flask(__name__)"
)
install_egg("site_egg")
try:
import site_egg
assert site_egg.app.instance_path == str(
modules_tmpdir.join("var/").join("site_egg-instance")
)
finally:
if "site_egg" in sys.modules:
del sys.modules["site_egg"]

@@ -6,4 +6,4 @@ from datetime import datetime

import pytest
from markupsafe import Markup
from flask import Markup
from flask.json.tag import JSONTag

@@ -10,0 +10,0 @@ from flask.json.tag import TaggedJSONSerializer

@@ -1,15 +0,4 @@

import pytest
try:
import blinker
except ImportError:
blinker = None
import flask
pytestmark = pytest.mark.skipif(
blinker is None, reason="Signals require the blinker library."
)
def test_template_rendered(app, client):

@@ -16,0 +5,0 @@ @app.route("/")

@@ -6,2 +6,3 @@ import logging

from jinja2 import TemplateNotFound
from markupsafe import Markup

@@ -77,3 +78,3 @@ import flask

return flask.render_template(
"escaping_template.html", text=text, html=flask.Markup(text)
"escaping_template.html", text=text, html=Markup(text)
)

@@ -98,3 +99,3 @@

return flask.render_template(
"non_escaping_template.txt", text=text, html=flask.Markup(text)
"non_escaping_template.txt", text=text, html=Markup(text)
)

@@ -101,0 +102,0 @@

@@ -0,4 +1,5 @@

import importlib.metadata
import click
import pytest
import werkzeug

@@ -13,8 +14,3 @@ import flask

try:
import blinker
except ImportError:
blinker = None
def test_environ_defaults_from_config(app, client):

@@ -47,30 +43,31 @@ app.config["SERVER_NAME"] = "example.com:1234"

def test_environ_base_default(app, client, app_ctx):
def test_environ_base_default(app, client):
@app.route("/")
def index():
flask.g.user_agent = flask.request.headers["User-Agent"]
return flask.request.remote_addr
flask.g.remote_addr = flask.request.remote_addr
flask.g.user_agent = flask.request.user_agent.string
return ""
rv = client.get("/")
assert rv.data == b"127.0.0.1"
assert flask.g.user_agent == f"werkzeug/{werkzeug.__version__}"
with client:
client.get("/")
assert flask.g.remote_addr == "127.0.0.1"
assert flask.g.user_agent == (
f"Werkzeug/{importlib.metadata.version('werkzeug')}"
)
def test_environ_base_modified(app, client, app_ctx):
def test_environ_base_modified(app, client):
@app.route("/")
def index():
flask.g.user_agent = flask.request.headers["User-Agent"]
return flask.request.remote_addr
flask.g.remote_addr = flask.request.remote_addr
flask.g.user_agent = flask.request.user_agent.string
return ""
client.environ_base["REMOTE_ADDR"] = "0.0.0.0"
client.environ_base["REMOTE_ADDR"] = "192.168.0.22"
client.environ_base["HTTP_USER_AGENT"] = "Foo"
rv = client.get("/")
assert rv.data == b"0.0.0.0"
assert flask.g.user_agent == "Foo"
client.environ_base["REMOTE_ADDR"] = "0.0.0.1"
client.environ_base["HTTP_USER_AGENT"] = "Bar"
rv = client.get("/")
assert rv.data == b"0.0.0.1"
assert flask.g.user_agent == "Bar"
with client:
client.get("/")
assert flask.g.remote_addr == "192.168.0.22"
assert flask.g.user_agent == "Foo"

@@ -290,3 +287,2 @@

@pytest.mark.skipif(blinker is None, reason="blinker is not installed")
def test_client_json_no_app_context(app, client):

@@ -293,0 +289,0 @@ @app.route("/hello", methods=["POST"])

@@ -6,4 +6,2 @@ from __future__ import annotations

import typing_extensions as te
from flask import Flask

@@ -44,3 +42,3 @@ from flask import jsonify

class StatusJSON(te.TypedDict):
class StatusJSON(t.TypedDict):
status: str

@@ -47,0 +45,0 @@

+2
-10
[tox]
envlist =
py3{12,11,10,9,8,7}
pypy3{9,8,7}
py3{12,11,10,9,8}
pypy310
py311-min

@@ -37,6 +37,2 @@ py38-dev

[testenv:typing]
package = wheel
wheel_build_env = .pkg
constrain_package_deps = true
use_frozen_constraints = true
deps = -r requirements/typing.txt

@@ -46,7 +42,3 @@ commands = mypy

[testenv:docs]
package = wheel
wheel_build_env = .pkg
constrain_package_deps = true
use_frozen_constraints = true
deps = -r requirements/docs.txt
commands = sphinx-build -W -b html -d {envtmpdir}/doctrees docs {envtmpdir}/html
Copyright 2010 Pallets
This logo or a modified version may be used by anyone to refer to the
Flask project, but does not indicate endorsement by the project.
Redistribution and use in source (SVG) and binary (renders in PNG, etc.)
forms, with or without modification, are permitted provided that the
following conditions are met:
1. Redistributions of source code must retain the above copyright
notice and this list of conditions.
2. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
We would appreciate that you make the image a link to
https://palletsprojects.com/p/flask/ if you use it in a medium that
supports links.

Sorry, the diff of this file is too big to display

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="211.15901"
height="190.52811"
id="svg2"
version="1.1"
inkscape:version="0.48.5 r10040"
sodipodi:docname="logo-lineart.svg">
<defs
id="defs4">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 526.18109 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="744.09448 : 526.18109 : 1"
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
id="perspective10" />
<inkscape:perspective
id="perspective2824"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective2840"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective2878"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective2894"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective2910"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective2926"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective2976"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3020"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3036"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3052"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3866"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.1723341"
inkscape:cx="242.05817"
inkscape:cy="92.686293"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1676"
inkscape:window-height="1005"
inkscape:window-x="4"
inkscape:window-y="0"
inkscape:window-maximized="1"
fit-margin-top="10"
fit-margin-left="10"
fit-margin-right="10"
fit-margin-bottom="10" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-29.820801,-20.186869)">
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="M 9.7525867,55.788422 C 40.421293,45.982204 33.821969,46.567748 69.327984,40.346648 c 8.493721,2.411576 22.910914,5.687215 22.240236,12.296506 -6.241933,2.320572 -15.351869,-6.455434 -20.254712,-1.539882 0.01014,18.421641 5.965221,38.200493 13.480678,55.747588 7.515457,17.5471 18.880714,32.86245 34.290034,42.35708 20.42595,12.66826 41.92048,14.9356 63.64846,15.65546 6.66858,0.23786 17.30912,-1.47838 20.01846,0 -4.9124,8.703 -19.28006,12.8118 -34.21844,14.71154 -14.93837,1.89974 -30.44747,1.59043 -37.64272,1.45723 -15.88921,-0.50065 -29.5942,-2.65111 -42.06658,-7.29048 C 56.640409,160.78176 38.428746,134.71246 24.668078,106.25832 16.765019,89.693325 11.290118,72.259923 9.7525867,55.788422 z"
id="path3826"
inkscape:connector-curvature="0"
transform="translate(29.820801,20.186869)"
sodipodi:nodetypes="ccccscccscccc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 54.066806,32.351647 c -0.427165,0.87404 -0.384822,1.998232 -0.02834,2.90275 0.781834,1.983761 2.799883,3.252081 4.397491,4.681241 0.728446,0.651642 2.26934,0.803097 2.364296,1.769134 0.215279,2.190161 -2.700769,3.566537 -4.456242,4.921486 -1.316317,1.015991 -3.845581,0.776849 -4.451985,2.314219 -0.417515,1.058499 0.837317,2.10047 1.1679,3.188615 0.465799,1.533243 1.642442,3.150334 1.145997,4.674061 -0.597449,1.833868 -2.700081,2.84663 -4.420626,3.754433 -1.893115,0.998854 -4.450538,0.497797 -6.207667,1.715064 -1.674125,1.159765 -3.485979,2.907099 -3.554321,4.925579 -0.03097,0.915115 -0.384582,2.676814 -0.233936,3.114037 12.863193,-4.155671 20.195138,-6.507915 28.694286,-8.598094 8.499136,-2.090222 16.108852,-3.399531 29.579722,-5.689662 -0.06867,-0.457321 -1.197061,-1.855664 -1.647827,-2.652661 -0.994254,-1.75795 -3.408869,-2.469029 -5.429591,-2.722885 -2.120906,-0.26644 -4.15652,1.360749 -6.296964,1.350851 -1.945327,-0.009 -4.277958,0.06569 -5.655921,-1.283841 -1.144955,-1.121286 -0.849755,-3.099246 -1.145997,-4.674061 -0.210243,-1.117649 0.420309,-2.621884 -0.439473,-3.367212 -1.248754,-1.082519 -3.380554,0.299434 -5.017542,0.0075 -2.183125,-0.389274 -5.405114,-0.260713 -6.227327,-2.302063 -0.362663,-0.900401 0.93342,-1.747432 1.277831,-2.662119 0.75535,-2.006065 1.957858,-4.064009 1.733419,-6.184432 -0.102333,-0.966833 -0.5848,-1.983113 -1.367813,-2.56044 -1.68203,-1.191313 -4.366912,-1.323763 -7.531636,-0.525881 -3.164723,0.797885 -5.342193,2.137743 -6.247739,3.90434 z"
id="path3832"
inkscape:connector-curvature="0"
sodipodi:nodetypes="csssssscssscccssscssssssccc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 71.836704,50.617573 c 0,0 -24.55635,5.277975 -35.918352,8.551988 C 27.8621,61.491007 12.143824,67.37947 12.143824,67.37947"
id="path3847"
inkscape:connector-curvature="0"
transform="translate(29.820801,20.186869)"
sodipodi:nodetypes="csc" />
</g>
</svg>

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

include LICENSE.rst
graft js_example/templates
graft tests
global-exclude *.pyc
[metadata]
name = js_example
version = 1.1.0
url = https://flask.palletsprojects.com/patterns/jquery/
license = BSD-3-Clause
maintainer = Pallets
maintainer_email = contact@palletsprojects.com
description = Demonstrates making AJAX requests to Flask.
long_description = file: README.rst
long_description_content_type = text/x-rst
[options]
packages = find:
include_package_data = true
install_requires =
Flask
[options.extras_require]
test =
pytest
blinker
[tool:pytest]
testpaths = tests
[coverage:run]
branch = True
source =
js_example
from setuptools import setup
setup()
include LICENSE.rst
include flaskr/schema.sql
graft flaskr/static
graft flaskr/templates
graft tests
global-exclude *.pyc
[metadata]
name = flaskr
version = 1.0.0
url = https://flask.palletsprojects.com/tutorial/
license = BSD-3-Clause
maintainer = Pallets
maintainer_email = contact@palletsprojects.com
description = The basic blog app built in the Flask tutorial.
long_description = file: README.rst
long_description_content_type = text/x-rst
[options]
packages = find:
include_package_data = true
install_requires =
Flask
[options.extras_require]
test =
pytest
[tool:pytest]
testpaths = tests
[coverage:run]
branch = True
source =
flaskr
from setuptools import setup
setup()
include CHANGES.rst
include CONTRIBUTING.rst
include tox.ini
include requirements/*.txt
graft artwork
graft docs
prune docs/_build
graft examples
graft tests
include src/flask/py.typed
global-exclude *.pyc
[metadata]
name = Flask
version = attr: flask.__version__
url = https://palletsprojects.com/p/flask
project_urls =
Donate = https://palletsprojects.com/donate
Documentation = https://flask.palletsprojects.com/
Changes = https://flask.palletsprojects.com/changes/
Source Code = https://github.com/pallets/flask/
Issue Tracker = https://github.com/pallets/flask/issues/
Twitter = https://twitter.com/PalletsTeam
Chat = https://discord.gg/pallets
license = BSD-3-Clause
author = Armin Ronacher
author_email = armin.ronacher@active-4.com
maintainer = Pallets
maintainer_email = contact@palletsprojects.com
description = A simple framework for building complex web applications.
long_description = file: README.rst
long_description_content_type = text/x-rst
classifiers =
Development Status :: 5 - Production/Stable
Environment :: Web Environment
Framework :: Flask
Intended Audience :: Developers
License :: OSI Approved :: BSD License
Operating System :: OS Independent
Programming Language :: Python
Topic :: Internet :: WWW/HTTP :: Dynamic Content
Topic :: Internet :: WWW/HTTP :: WSGI
Topic :: Internet :: WWW/HTTP :: WSGI :: Application
Topic :: Software Development :: Libraries :: Application Frameworks
[options]
packages = find:
package_dir = = src
include_package_data = True
python_requires = >= 3.7
[options.packages.find]
where = src
[options.entry_points]
console_scripts =
flask = flask.cli:main
[tool:pytest]
testpaths = tests
filterwarnings =
error
[coverage:run]
branch = True
source =
flask
tests
[coverage:paths]
source =
src
*/site-packages
[mypy]
files = src/flask, tests/typing
python_version = 3.7
show_error_codes = True
allow_redefinition = True
disallow_subclassing_any = True
no_implicit_optional = True
local_partial_types = True
strict_equality = True
warn_redundant_casts = True
warn_unused_configs = True
warn_unused_ignores = True
[mypy-asgiref.*]
ignore_missing_imports = True
[mypy-blinker.*]
ignore_missing_imports = True
[mypy-dotenv.*]
ignore_missing_imports = True
[mypy-cryptography.*]
ignore_missing_imports = True
[mypy-importlib_metadata]
ignore_missing_imports = True
[egg_info]
tag_build =
tag_date = 0
from setuptools import setup
# Metadata goes in setup.cfg. These are here for GitHub's dependency graph.
setup(
name="Flask",
install_requires=[
"Werkzeug >= 2.2.2",
"Jinja2 >= 3.0",
"itsdangerous >= 2.0",
"click >= 8.0",
"importlib-metadata >= 3.6.0; python_version < '3.10'",
],
extras_require={
"async": ["asgiref >= 3.2"],
"dotenv": ["python-dotenv"],
},
)
[console_scripts]
flask = flask.cli:main
Metadata-Version: 2.1
Name: Flask
Version: 2.2.5
Summary: A simple framework for building complex web applications.
Home-page: https://palletsprojects.com/p/flask
Author: Armin Ronacher
Author-email: armin.ronacher@active-4.com
Maintainer: Pallets
Maintainer-email: contact@palletsprojects.com
License: BSD-3-Clause
Project-URL: Donate, https://palletsprojects.com/donate
Project-URL: Documentation, https://flask.palletsprojects.com/
Project-URL: Changes, https://flask.palletsprojects.com/changes/
Project-URL: Source Code, https://github.com/pallets/flask/
Project-URL: Issue Tracker, https://github.com/pallets/flask/issues/
Project-URL: Twitter, https://twitter.com/PalletsTeam
Project-URL: Chat, https://discord.gg/pallets
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: Framework :: Flask
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
Classifier: Topic :: Internet :: WWW/HTTP :: WSGI
Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Requires-Python: >=3.7
Description-Content-Type: text/x-rst
Provides-Extra: async
Provides-Extra: dotenv
License-File: LICENSE.rst
Flask
=====
Flask is a lightweight `WSGI`_ web application framework. It is designed
to make getting started quick and easy, with the ability to scale up to
complex applications. It began as a simple wrapper around `Werkzeug`_
and `Jinja`_ and has become one of the most popular Python web
application frameworks.
Flask offers suggestions, but doesn't enforce any dependencies or
project layout. It is up to the developer to choose the tools and
libraries they want to use. There are many extensions provided by the
community that make adding new functionality easy.
.. _WSGI: https://wsgi.readthedocs.io/
.. _Werkzeug: https://werkzeug.palletsprojects.com/
.. _Jinja: https://jinja.palletsprojects.com/
Installing
----------
Install and update using `pip`_:
.. code-block:: text
$ pip install -U Flask
.. _pip: https://pip.pypa.io/en/stable/getting-started/
A Simple Example
----------------
.. code-block:: python
# save this as app.py
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello, World!"
.. code-block:: text
$ flask run
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
Contributing
------------
For guidance on setting up a development environment and how to make a
contribution to Flask, see the `contributing guidelines`_.
.. _contributing guidelines: https://github.com/pallets/flask/blob/main/CONTRIBUTING.rst
Donate
------
The Pallets organization develops and supports Flask and the libraries
it uses. In order to grow the community of contributors and users, and
allow the maintainers to devote more time to the projects, `please
donate today`_.
.. _please donate today: https://palletsprojects.com/donate
Links
-----
- Documentation: https://flask.palletsprojects.com/
- Changes: https://flask.palletsprojects.com/changes/
- PyPI Releases: https://pypi.org/project/Flask/
- Source Code: https://github.com/pallets/flask/
- Issue Tracker: https://github.com/pallets/flask/issues/
- Website: https://palletsprojects.com/p/flask/
- Twitter: https://twitter.com/PalletsTeam
- Chat: https://discord.gg/pallets
Werkzeug>=2.2.2
Jinja2>=3.0
itsdangerous>=2.0
click>=8.0
[:python_version < "3.10"]
importlib-metadata>=3.6.0
[async]
asgiref>=3.2
[dotenv]
python-dotenv
CHANGES.rst
CONTRIBUTING.rst
LICENSE.rst
MANIFEST.in
README.rst
setup.cfg
setup.py
tox.ini
artwork/LICENSE.rst
artwork/logo-full.svg
artwork/logo-lineart.svg
docs/Makefile
docs/api.rst
docs/appcontext.rst
docs/async-await.rst
docs/blueprints.rst
docs/changes.rst
docs/cli.rst
docs/conf.py
docs/config.rst
docs/contributing.rst
docs/debugging.rst
docs/design.rst
docs/errorhandling.rst
docs/extensiondev.rst
docs/extensions.rst
docs/index.rst
docs/installation.rst
docs/license.rst
docs/lifecycle.rst
docs/logging.rst
docs/make.bat
docs/quickstart.rst
docs/reqcontext.rst
docs/security.rst
docs/server.rst
docs/shell.rst
docs/signals.rst
docs/templating.rst
docs/testing.rst
docs/views.rst
docs/_static/debugger.png
docs/_static/flask-icon.png
docs/_static/flask-logo.png
docs/_static/no.png
docs/_static/pycharm-run-config.png
docs/_static/yes.png
docs/deploying/apache-httpd.rst
docs/deploying/asgi.rst
docs/deploying/eventlet.rst
docs/deploying/gevent.rst
docs/deploying/gunicorn.rst
docs/deploying/index.rst
docs/deploying/mod_wsgi.rst
docs/deploying/nginx.rst
docs/deploying/proxy_fix.rst
docs/deploying/uwsgi.rst
docs/deploying/waitress.rst
docs/patterns/appdispatch.rst
docs/patterns/appfactories.rst
docs/patterns/caching.rst
docs/patterns/celery.rst
docs/patterns/deferredcallbacks.rst
docs/patterns/favicon.rst
docs/patterns/fileuploads.rst
docs/patterns/flashing.rst
docs/patterns/index.rst
docs/patterns/javascript.rst
docs/patterns/jquery.rst
docs/patterns/lazyloading.rst
docs/patterns/methodoverrides.rst
docs/patterns/mongoengine.rst
docs/patterns/packages.rst
docs/patterns/requestchecksum.rst
docs/patterns/singlepageapplications.rst
docs/patterns/sqlalchemy.rst
docs/patterns/sqlite3.rst
docs/patterns/streaming.rst
docs/patterns/subclassing.rst
docs/patterns/templateinheritance.rst
docs/patterns/urlprocessors.rst
docs/patterns/viewdecorators.rst
docs/patterns/wtforms.rst
docs/tutorial/blog.rst
docs/tutorial/database.rst
docs/tutorial/deploy.rst
docs/tutorial/factory.rst
docs/tutorial/flaskr_edit.png
docs/tutorial/flaskr_index.png
docs/tutorial/flaskr_login.png
docs/tutorial/index.rst
docs/tutorial/install.rst
docs/tutorial/layout.rst
docs/tutorial/next.rst
docs/tutorial/static.rst
docs/tutorial/templates.rst
docs/tutorial/tests.rst
docs/tutorial/views.rst
examples/celery/README.md
examples/celery/make_celery.py
examples/celery/pyproject.toml
examples/celery/requirements.txt
examples/celery/src/task_app/__init__.py
examples/celery/src/task_app/tasks.py
examples/celery/src/task_app/views.py
examples/celery/src/task_app/templates/index.html
examples/javascript/.gitignore
examples/javascript/LICENSE.rst
examples/javascript/MANIFEST.in
examples/javascript/README.rst
examples/javascript/setup.cfg
examples/javascript/setup.py
examples/javascript/js_example/__init__.py
examples/javascript/js_example/views.py
examples/javascript/js_example/templates/base.html
examples/javascript/js_example/templates/fetch.html
examples/javascript/js_example/templates/jquery.html
examples/javascript/js_example/templates/xhr.html
examples/javascript/tests/conftest.py
examples/javascript/tests/test_js_example.py
examples/tutorial/.gitignore
examples/tutorial/LICENSE.rst
examples/tutorial/MANIFEST.in
examples/tutorial/README.rst
examples/tutorial/setup.cfg
examples/tutorial/setup.py
examples/tutorial/flaskr/__init__.py
examples/tutorial/flaskr/auth.py
examples/tutorial/flaskr/blog.py
examples/tutorial/flaskr/db.py
examples/tutorial/flaskr/schema.sql
examples/tutorial/flaskr/static/style.css
examples/tutorial/flaskr/templates/base.html
examples/tutorial/flaskr/templates/auth/login.html
examples/tutorial/flaskr/templates/auth/register.html
examples/tutorial/flaskr/templates/blog/create.html
examples/tutorial/flaskr/templates/blog/index.html
examples/tutorial/flaskr/templates/blog/update.html
examples/tutorial/tests/conftest.py
examples/tutorial/tests/data.sql
examples/tutorial/tests/test_auth.py
examples/tutorial/tests/test_blog.py
examples/tutorial/tests/test_db.py
examples/tutorial/tests/test_factory.py
requirements/build.txt
requirements/dev.txt
requirements/docs.txt
requirements/tests-pallets-min.txt
requirements/tests.txt
requirements/typing.txt
src/Flask.egg-info/PKG-INFO
src/Flask.egg-info/SOURCES.txt
src/Flask.egg-info/dependency_links.txt
src/Flask.egg-info/entry_points.txt
src/Flask.egg-info/requires.txt
src/Flask.egg-info/top_level.txt
src/flask/__init__.py
src/flask/__main__.py
src/flask/app.py
src/flask/blueprints.py
src/flask/cli.py
src/flask/config.py
src/flask/ctx.py
src/flask/debughelpers.py
src/flask/globals.py
src/flask/helpers.py
src/flask/logging.py
src/flask/py.typed
src/flask/scaffold.py
src/flask/sessions.py
src/flask/signals.py
src/flask/templating.py
src/flask/testing.py
src/flask/typing.py
src/flask/views.py
src/flask/wrappers.py
src/flask/json/__init__.py
src/flask/json/provider.py
src/flask/json/tag.py
tests/conftest.py
tests/test_appctx.py
tests/test_async.py
tests/test_basic.py
tests/test_blueprints.py
tests/test_cli.py
tests/test_config.py
tests/test_converters.py
tests/test_helpers.py
tests/test_instance_config.py
tests/test_json.py
tests/test_json_tag.py
tests/test_logging.py
tests/test_regression.py
tests/test_reqctx.py
tests/test_session_interface.py
tests/test_signals.py
tests/test_subclassing.py
tests/test_templating.py
tests/test_testing.py
tests/test_user_error_handler.py
tests/test_views.py
tests/static/config.json
tests/static/index.html
tests/templates/_macro.html
tests/templates/context_template.html
tests/templates/escaping_template.html
tests/templates/mail.txt
tests/templates/non_escaping_template.txt
tests/templates/simple_template.html
tests/templates/template_filter.html
tests/templates/template_test.html
tests/templates/nested/nested.txt
tests/test_apps/.env
tests/test_apps/.flaskenv
tests/test_apps/blueprintapp/__init__.py
tests/test_apps/blueprintapp/apps/__init__.py
tests/test_apps/blueprintapp/apps/admin/__init__.py
tests/test_apps/blueprintapp/apps/admin/static/test.txt
tests/test_apps/blueprintapp/apps/admin/static/css/test.css
tests/test_apps/blueprintapp/apps/admin/templates/admin/index.html
tests/test_apps/blueprintapp/apps/frontend/__init__.py
tests/test_apps/blueprintapp/apps/frontend/templates/frontend/index.html
tests/test_apps/cliapp/__init__.py
tests/test_apps/cliapp/app.py
tests/test_apps/cliapp/factory.py
tests/test_apps/cliapp/importerrorapp.py
tests/test_apps/cliapp/message.txt
tests/test_apps/cliapp/multiapp.py
tests/test_apps/cliapp/inner1/__init__.py
tests/test_apps/cliapp/inner1/inner2/__init__.py
tests/test_apps/cliapp/inner1/inner2/flask.py
tests/test_apps/helloworld/hello.py
tests/test_apps/helloworld/wsgi.py
tests/test_apps/subdomaintestmodule/__init__.py
tests/test_apps/subdomaintestmodule/static/hello.txt
tests/typing/typing_app_decorators.py
tests/typing/typing_error_handler.py
tests/typing/typing_route.py

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display