flask
Advanced tools
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
| [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"] |
+108
| [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 |
| build |
| -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 @@ |
+5
-33
@@ -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`. |
+13
-0
@@ -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 |
+13
-28
@@ -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 |
+2
-4
@@ -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 |
+18
-77
@@ -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 |
+7
-10
@@ -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 |
+2
-17
@@ -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`_. |
+9
-14
@@ -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. |
+20
-41
| 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 |
+10
-12
@@ -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 @@ |
+1
-1
@@ -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 | ||
+0
-2
@@ -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 |
+10
-10
@@ -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) |
+56
-142
@@ -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 |
+46
-32
| 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", |
+20
-11
@@ -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:: |
+25
-23
@@ -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 @@ |
+18
-29
@@ -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, |
+66
-70
@@ -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: |
+24
-196
@@ -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 @@ |
+68
-131
@@ -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 |
+24
-78
@@ -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) |
+18
-41
@@ -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) |
+31
-23
@@ -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. |
+35
-41
@@ -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 |
+13
-52
| 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 |
+14
-111
| 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__) |
+33
-30
| # 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: |
+15
-14
| 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öö" |
+10
-16
@@ -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 @@ |
+21
-25
@@ -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() |
-11
| 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 |
-94
| [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 | ||
-17
| 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
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
1494855
-6.68%230
-3.77%13421
-5.4%