Compare commits

..

No commits in common. "main" and "3.0.1" have entirely different histories.
main ... 3.0.1

136 changed files with 3144 additions and 5415 deletions

View file

@ -1,7 +1,9 @@
#!/bin/bash
set -e
python3 -m venv --upgrade-deps .venv
python3 -m venv .venv
. .venv/bin/activate
pip install -U pip
pip install -r requirements/dev.txt
pip install -e .
pre-commit install --install-hooks

View file

@ -9,5 +9,5 @@ end_of_line = lf
charset = utf-8
max_line_length = 88
[*.{css,html,js,json,jsx,scss,ts,tsx,yaml,yml}]
[*.{yml,yaml,json,js,css,html}]
indent_size = 2

View file

@ -5,7 +5,7 @@ about: Report a bug in Flask (not other projects which depend on Flask)
<!--
This issue tracker is a tool to address bugs in Flask itself. Please use
GitHub Discussions or the Pallets Discord for questions about your own code.
Pallets Discord or Stack Overflow for questions about your own code.
Replace this comment with a clear outline of what the bug is.
-->

View file

@ -1,11 +1,11 @@
blank_issues_enabled: false
contact_links:
- name: Security issue
url: https://github.com/pallets/flask/security/advisories/new
about: Do not report security issues publicly. Create a private advisory.
- name: Questions on GitHub Discussions
url: https://github.com/pallets/flask/discussions/
about: Ask questions about your own code on the Discussions tab.
- name: Questions on Discord
url: security@palletsprojects.com
about: Do not report security issues publicly. Email our security contact.
- name: Questions
url: https://stackoverflow.com/questions/tagged/flask?tab=Frequent
about: Search for and ask questions about your code on Stack Overflow.
- name: Questions and discussions
url: https://discord.gg/pallets
about: Ask questions about your own code on our Discord chat.
about: Discuss questions about your code on our Discord chat.

19
.github/SECURITY.md vendored Normal file
View file

@ -0,0 +1,19 @@
# Security Policy
If you believe you have identified a security issue with a Pallets
project, **do not open a public issue**. To responsibly report a
security issue, please email security@palletsprojects.com. A security
team member will contact you acknowledging the report and how to
continue.
Be sure to include as much detail as necessary in your report. As with
reporting normal issues, a minimal reproducible example will help the
maintainers address the issue faster. If you are able, you may also
include a fix for the issue generated with `git format-patch`.
The current and previous release will receive security patches, with
older versions evaluated based on usage information and severity.
After fixing an issue, we will make a security release along with an
announcement on our blog. We may obtain a CVE id as well. You may
include a name and link if you would like to be credited for the report.

9
.github/dependabot.yml vendored Normal file
View file

@ -0,0 +1,9 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"
day: "monday"
time: "16:00"
timezone: "UTC"

View file

@ -1,7 +1,6 @@
<!--
Before opening a PR, open a ticket describing the issue or feature the
PR will address. An issue is not required for fixing typos in
documentation, or other simple non-code changes.
PR will address. Follow the steps in CONTRIBUTING.rst.
Replace this comment with a description of the change. Describe how it
addresses the linked ticket.
@ -10,16 +9,22 @@ addresses the linked ticket.
<!--
Link to relevant issues or previous PRs, one per line. Use "fixes" to
automatically close an issue.
fixes #<issue number>
-->
- fixes #<issue number>
<!--
Ensure each step in CONTRIBUTING.rst is complete, especially the following:
Ensure each step in CONTRIBUTING.rst is complete by adding an "x" to
each box below.
- Add tests that demonstrate the correct behavior of the change. Tests
should fail without the change.
- Add or update relevant docs, in the docs folder and in code.
- Add an entry in CHANGES.rst summarizing the change and linking to the issue.
- Add `.. versionchanged::` entries in any relevant code docs.
If only docs were changed, these aren't relevant and can be removed.
-->
Checklist:
- [ ] Add tests that demonstrate the correct behavior of the change. Tests should fail without the change.
- [ ] Add or update relevant docs, in the docs folder and in code.
- [ ] Add an entry in `CHANGES.rst` summarizing the change and linking to the issue.
- [ ] Add `.. versionchanged::` entries in any relevant code docs.
- [ ] Run `pre-commit` hooks and fix any issues.
- [ ] Run `pytest` and `tox`, no tests failed.

View file

@ -1,26 +1,21 @@
name: Lock inactive closed issues
# Lock closed issues that have not received any further activity for two weeks.
# This does not close open issues, only humans may do that. It is easier to
# respond to new issues with fresh examples rather than continuing discussions
# on old issues.
name: 'Lock threads'
# Lock closed issues that have not received any further activity for
# two weeks. This does not close open issues, only humans may do that.
# We find that it is easier to respond to new issues with fresh examples
# rather than continuing discussions on old issues.
on:
schedule:
- cron: '0 0 * * *'
permissions: {}
permissions:
issues: write
pull-requests: write
concurrency:
group: lock
cancel-in-progress: true
jobs:
lock:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
discussions: write
steps:
- uses: dessant/lock-threads@7266a7ce5c1df01b1c6db85bf8cd86c737dadbe7 # v6.0.0
- uses: dessant/lock-threads@7de207be1d3ce97a9abe6ff1306222982d1ca9f9
with:
issue-inactive-days: 14
pr-inactive-days: 14
discussion-inactive-days: 14

View file

@ -1,29 +0,0 @@
name: pre-commit
on:
pull_request:
push:
branches: [main, stable]
permissions: {}
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
main:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
enable-cache: true
prune-cache: false
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
id: setup-python
with:
python-version-file: pyproject.toml
- uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
with:
path: ~/.cache/pre-commit
key: pre-commit|${{ hashFiles('pyproject.toml', '.pre-commit-config.yaml') }}
- run: uv run --locked --no-default-groups --group pre-commit pre-commit run --show-diff-on-failure --color=always --all-files

View file

@ -1,62 +1,69 @@
name: Publish
on:
push:
tags: ['*']
permissions: {}
concurrency:
group: publish-${{ github.event.push.ref }}
cancel-in-progress: true
tags:
- '*'
jobs:
build:
runs-on: ubuntu-latest
outputs:
artifact-id: ${{ steps.upload-artifact.outputs.artifact-id }}
hash: ${{ steps.hash.outputs.hash }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
- uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c
with:
persist-credentials: false
- uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
enable-cache: false
prune-cache: false
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version-file: pyproject.toml
python-version: '3.x'
cache: pip
cache-dependency-path: requirements*/*.txt
- run: pip install -r requirements/build.txt
# Use the commit date instead of the current date during the build.
- run: echo "SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)" >> $GITHUB_ENV
- run: uv build
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
id: upload-artifact
- run: python -m build
# Generate hashes used for provenance.
- name: generate hash
id: hash
run: cd dist && echo "hash=$(sha256sum * | base64 -w0)" >> $GITHUB_OUTPUT
- uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32
with:
name: dist
path: dist/
if-no-files-found: error
create-release:
path: ./dist
provenance:
needs: [build]
permissions:
actions: read
id-token: write
contents: write
# Can't pin with hash due to how this workflow works.
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v1.9.0
with:
base64-subjects: ${{ needs.build.outputs.hash }}
create-release:
# Upload the sdist, wheels, and provenance to a GitHub release. They remain
# available as build artifacts for a while as well.
needs: [provenance]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
artifact-ids: ${{ needs.build.outputs.artifact-id }}
path: dist/
- uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a
- name: create release
run: gh release create --draft --repo ${GITHUB_REPOSITORY} ${GITHUB_REF_NAME} dist/*
run: >
gh release create --draft --repo ${{ github.repository }}
${{ github.ref_name }}
*.intoto.jsonl/* dist/*
env:
GH_TOKEN: ${{ github.token }}
publish-pypi:
needs: [build]
environment:
name: publish
url: https://pypi.org/project/Flask/${{ github.ref_name }}
needs: [provenance]
# Wait for approval before attempting to upload to PyPI. This allows reviewing the
# files in the draft release.
environment: publish
runs-on: ubuntu-latest
permissions:
id-token: write
steps:
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
- uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a
- uses: pypa/gh-action-pypi-publish@f946db0f765b9ae754e44bfd5ae5b8b91cfb37ef
with:
artifact-ids: ${{ needs.build.outputs.artifact-id }}
path: dist/
- uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
with:
packages-dir: "dist/"
repository-url: https://test.pypi.org/legacy/
- uses: pypa/gh-action-pypi-publish@f946db0f765b9ae754e44bfd5ae5b8b91cfb37ef

View file

@ -1,63 +1,49 @@
name: Tests
on:
pull_request:
paths-ignore: ['docs/**', 'README.md']
push:
branches: [main, stable]
paths-ignore: ['docs/**', 'README.md']
permissions: {}
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
branches:
- main
- '*.x'
paths-ignore:
- 'docs/**'
- '*.md'
- '*.rst'
pull_request:
paths-ignore:
- 'docs/**'
- '*.md'
- '*.rst'
jobs:
tests:
name: ${{ matrix.name || matrix.python }}
runs-on: ${{ matrix.os || 'ubuntu-latest' }}
name: ${{ matrix.name }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- {python: '3.14'}
- {python: '3.14t'}
- {name: Windows, python: '3.14', os: windows-latest}
- {name: Mac, python: '3.14', os: macos-latest}
- {python: '3.13'}
- {python: '3.12'}
- {python: '3.11'}
- {python: '3.10'}
- {name: PyPy, python: 'pypy-3.11', tox: pypy3.11}
- {name: Minimum Versions, python: '3.14', tox: tests-min}
- {name: Development Versions, python: '3.10', tox: tests-dev}
- {name: Linux, python: '3.12', os: ubuntu-latest, tox: py312}
- {name: Windows, python: '3.12', os: windows-latest, tox: py312}
- {name: Mac, python: '3.12', os: macos-latest, tox: py312}
- {name: '3.11', python: '3.11', os: ubuntu-latest, tox: py311}
- {name: '3.10', python: '3.10', os: ubuntu-latest, tox: py310}
- {name: '3.9', python: '3.9', os: ubuntu-latest, tox: py39}
- {name: '3.8', python: '3.8', os: ubuntu-latest, tox: py38}
- {name: 'PyPy', python: 'pypy-3.10', os: ubuntu-latest, tox: pypy310}
- {name: 'Minimum Versions', python: '3.12', os: ubuntu-latest, tox: py312-min}
- {name: 'Development Versions', python: '3.8', os: ubuntu-latest, tox: py38-dev}
- {name: Typing, python: '3.12', os: ubuntu-latest, tox: typing}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
enable-cache: true
prune-cache: false
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
- uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c
with:
python-version: ${{ matrix.python }}
- run: uv run --locked --no-default-groups --group dev tox run
env:
TOX_ENV: ${{ matrix.tox || format('py{0}', matrix.python) }}
typing:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
enable-cache: true
prune-cache: false
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version-file: pyproject.toml
cache: 'pip'
cache-dependency-path: requirements*/*.txt
- name: cache mypy
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2
with:
path: ./.mypy_cache
key: mypy|${{ hashFiles('pyproject.toml') }}
- run: uv run --locked --no-default-groups --group dev tox run -e typing
key: mypy|${{ matrix.python }}|${{ hashFiles('pyproject.toml') }}
if: matrix.tox == 'typing'
- run: pip install tox
- run: tox run -e ${{ matrix.tox }}

View file

@ -1,22 +0,0 @@
name: GitHub Actions security analysis with zizmor
on:
pull_request:
paths: ["**/*.yaml?"]
push:
branches: [main, stable]
paths: ["**/*.yaml?"]
permissions: {}
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
zizmor:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: zizmorcore/zizmor-action@71321a20a9ded102f6e9ce5718a2fcec2c4f70d8 # v0.5.2
with:
advanced-security: false
annotations: true

8
.gitignore vendored
View file

@ -1,8 +1,10 @@
.idea/
.vscode/
__pycache__/
dist/
.coverage*
htmlcov/
.tox/
.coverage
.coverage.*
htmlcov/
docs/_build/
dist/
venv/

View file

@ -1,20 +1,13 @@
ci:
autoupdate_schedule: monthly
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: 5e2fb545eba1ea9dc051f6f962d52fe8f76a9794 # frozen: v0.15.13
rev: v0.1.13
hooks:
- id: ruff-check
- id: ruff
- id: ruff-format
- repo: https://github.com/astral-sh/uv-pre-commit
rev: fa60a193803535a9e2accdb3ca4b1b584b1150cb # frozen: 0.11.15
hooks:
- id: uv-lock
- repo: https://github.com/codespell-project/codespell
rev: 2ccb47ff45ad361a21071a7eedda4c37e6ae8c5a # frozen: v2.4.2
hooks:
- id: codespell
args: ['--write-changes']
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: 3e8a8703264a2f4a69428a0aa4dcb512790b2c8c # frozen: v6.0.0
rev: v4.5.0
hooks:
- id: check-merge-conflict
- id: debug-statements

View file

@ -1,10 +1,13 @@
version: 2
build:
os: ubuntu-24.04
os: ubuntu-22.04
tools:
python: '3.13'
commands:
- asdf plugin add uv
- asdf install uv latest
- asdf global uv latest
- uv run --group docs sphinx-build -W -b dirhtml docs $READTHEDOCS_OUTPUT/html
python: "3.12"
python:
install:
- requirements: requirements/docs.txt
- method: pip
path: .
sphinx:
builder: dirhtml
fail_on_warning: true

View file

@ -1,130 +1,9 @@
Version 3.2.0
-------------
Unreleased
- Drop support for Python 3.9. :pr:`5730`
- Remove previously deprecated code: ``__version__``. :pr:`5648`
- ``RequestContext`` has merged with ``AppContext``. ``RequestContext`` is now
a deprecated alias. If an app context is already pushed, it is not reused
when dispatching a request. This greatly simplifies the internal code for tracking
the active context. :issue:`5639`
- Many ``Flask`` methods involved in request dispatch now take the current
``AppContext`` as the first parameter, instead of using the proxy objects.
If subclasses were overriding these methods, the old signature is detected,
shows a deprecation warning, and will continue to work during the
deprecation period. :issue:`5815`
- All teardown callbacks are called, even if any raise an error. :pr:`5928`
- The ``should_ignore_error`` is deprecated. Handle errors as needed in
teardown handlers instead. :issue:`5816`
- ``template_filter``, ``template_test``, and ``template_global`` decorators
can be used without parentheses. :issue:`5729`
- ``redirect`` returns a ``303`` status code by default instead of ``302``.
This tells the client to always switch to ``GET``, rather than only
switching ``POST`` to ``GET``. This preserves the current behavior of
``GET`` and ``POST`` redirects, and is also correct for frontend libraries
such as HTMX. :issue:`5895`
- ``provide_automatic_options=True`` can be used to enable it for a view when
it's disabled in config. Previously, only disabling worked. :issue:`5916`
- ``Flask.select_jinja_autoescape`` uses case-insensitive comparison instead
of only lower case file extensions. :pr:`6012`
Version 3.1.3
-------------
Released 2026-02-18
- The session is marked as accessed for operations that only access the keys
but not the values, such as ``in`` and ``len``. :ghsa:`68rp-wp8r-4726`
Version 3.1.2
-------------
Released 2025-08-19
- ``stream_with_context`` does not fail inside async views. :issue:`5774`
- When using ``follow_redirects`` in the test client, the final state
of ``session`` is correct. :issue:`5786`
- Relax type hint for passing bytes IO to ``send_file``. :issue:`5776`
Version 3.1.1
-------------
Released 2025-05-13
- Fix signing key selection order when key rotation is enabled via
``SECRET_KEY_FALLBACKS``. :ghsa:`4grg-w6v8-c28g`
- Fix type hint for ``cli_runner.invoke``. :issue:`5645`
- ``flask --help`` loads the app and plugins first to make sure all commands
are shown. :issue:`5673`
- Mark sans-io base class as being able to handle views that return
``AsyncIterable``. This is not accurate for Flask, but makes typing easier
for Quart. :pr:`5659`
Version 3.1.0
-------------
Released 2024-11-13
- Drop support for Python 3.8. :pr:`5623`
- Update minimum dependency versions to latest feature releases.
Werkzeug >= 3.1, ItsDangerous >= 2.2, Blinker >= 1.9. :pr:`5624,5633`
- Provide a configuration option to control automatic option
responses. :pr:`5496`
- ``Flask.open_resource``/``open_instance_resource`` and
``Blueprint.open_resource`` take an ``encoding`` parameter to use when
opening in text mode. It defaults to ``utf-8``. :issue:`5504`
- ``Request.max_content_length`` can be customized per-request instead of only
through the ``MAX_CONTENT_LENGTH`` config. Added
``MAX_FORM_MEMORY_SIZE`` and ``MAX_FORM_PARTS`` config. Added documentation
about resource limits to the security page. :issue:`5625`
- Add support for the ``Partitioned`` cookie attribute (CHIPS), with the
``SESSION_COOKIE_PARTITIONED`` config. :issue:`5472`
- ``-e path`` takes precedence over default ``.env`` and ``.flaskenv`` files.
``load_dotenv`` loads default files in addition to a path unless
``load_defaults=False`` is passed. :issue:`5628`
- Support key rotation with the ``SECRET_KEY_FALLBACKS`` config, a list of old
secret keys that can still be used for unsigning. Extensions will need to
add support. :issue:`5621`
- Fix how setting ``host_matching=True`` or ``subdomain_matching=False``
interacts with ``SERVER_NAME``. Setting ``SERVER_NAME`` no longer restricts
requests to only that domain. :issue:`5553`
- ``Request.trusted_hosts`` is checked during routing, and can be set through
the ``TRUSTED_HOSTS`` config. :issue:`5636`
Version 3.0.3
-------------
Released 2024-04-07
- The default ``hashlib.sha1`` may not be available in FIPS builds. Don't
access it at import time so the developer has time to change the default.
:issue:`5448`
- Don't initialize the ``cli`` attribute in the sansio scaffold, but rather in
the ``Flask`` concrete class. :pr:`5270`
Version 3.0.2
-------------
Released 2024-02-03
- Correct type for ``jinja_loader`` property. :issue:`5388`
- Fix error with ``--extra-files`` and ``--exclude-patterns`` CLI options.
:issue:`5391`
Version 3.0.1
-------------
Released 2024-01-18
- Correct type for ``path`` argument to ``send_file``. :issue:`5336`
- Correct type for ``path`` argument to ``send_file``. :issue:`5230`
- Fix a typo in an error message for the ``flask run --key`` option. :pr:`5344`
- Session data is untagged without relying on the built-in ``json.loads``
``object_hook``. This allows other JSON providers that don't implement that.
@ -164,7 +43,6 @@ Released 2023-05-01
- Set ``Vary: Cookie`` header when the session is accessed, modified, or refreshed.
- Update Werkzeug requirement to >=2.3.3 to apply recent bug fixes.
:ghsa:`m2qf-hxjv-5gpq`
Version 2.3.1
@ -935,7 +813,7 @@ Released 2018-04-26
explicitly for each exception if you want to avoid traversing the
MRO. :pr:`2362`
- Fix incorrect JSON encoding of aware, non-UTC datetimes. :pr:`2374`
- Template auto reloading will honor debug mode even if
- Template auto reloading will honor debug mode even even if
``Flask.jinja_env`` was already accessed. :pr:`2373`
- The following old deprecated code was removed. :issue:`2385`
@ -1417,7 +1295,7 @@ Released 2011-09-29, codename Rakija
of Flask itself and no longer of the test client. This cleaned up
some internal logic and lowers the odds of runaway request contexts
in unittests.
- Fixed the Jinja environment's ``list_templates`` method not
- Fixed the Jinja2 environment's ``list_templates`` method not
returning the correct names when blueprints or modules were
involved.
@ -1503,7 +1381,7 @@ Released 2010-12-31
- Fixed an issue where the default ``OPTIONS`` response was not
exposing all valid methods in the ``Allow`` header.
- Jinja template loading syntax now allows "./" in front of a
- Jinja2 template loading syntax now allows "./" in front of a
template load path. Previously this caused issues with module
setups.
- Fixed an issue where the subdomain setting for modules was ignored

76
CODE_OF_CONDUCT.md Normal file
View file

@ -0,0 +1,76 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at report@palletsprojects.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq

238
CONTRIBUTING.rst Normal file
View file

@ -0,0 +1,238 @@
How to contribute to Flask
==========================
Thank you for considering contributing to Flask!
Support questions
-----------------
Please don't use the issue tracker for this. The issue tracker is a tool
to address bugs and feature requests in Flask itself. Use one of the
following resources for questions about using Flask or issues with your
own code:
- The ``#questions`` channel on our Discord chat:
https://discord.gg/pallets
- Ask on `Stack Overflow`_. Search with Google first using:
``site:stackoverflow.com flask {search term, exception message, etc.}``
- Ask on our `GitHub Discussions`_ for long term discussion or larger
questions.
.. _Stack Overflow: https://stackoverflow.com/questions/tagged/flask?tab=Frequent
.. _GitHub Discussions: https://github.com/pallets/flask/discussions
Reporting issues
----------------
Include the following information in your post:
- Describe what you expected to happen.
- If possible, include a `minimal reproducible example`_ to help us
identify the issue. This also helps check that the issue is not with
your own code.
- Describe what actually happened. Include the full traceback if there
was an exception.
- List your Python and Flask versions. If possible, check if this
issue is already fixed in the latest releases or the latest code in
the repository.
.. _minimal reproducible example: https://stackoverflow.com/help/minimal-reproducible-example
Submitting patches
------------------
If there is not an open issue for what you want to submit, prefer
opening one for discussion before working on a PR. You can work on any
issue that doesn't have an open PR linked to it or a maintainer assigned
to it. These show up in the sidebar. No need to ask if you can work on
an issue that interests you.
Include the following in your patch:
- Use `Black`_ to format your code. This and other tools will run
automatically if you install `pre-commit`_ using the instructions
below.
- Include tests if your patch adds or changes code. Make sure the test
fails without your patch.
- Update any relevant docs pages and docstrings. Docs pages and
docstrings should be wrapped at 72 characters.
- Add an entry in ``CHANGES.rst``. Use the same style as other
entries. Also include ``.. versionchanged::`` inline changelogs in
relevant docstrings.
.. _Black: https://black.readthedocs.io
.. _pre-commit: https://pre-commit.com
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`_.
- Configure git with your `username`_ and `email`_.
.. code-block:: text
$ git config --global user.name 'your name'
$ git config --global user.email 'your email'
- Fork Flask to your GitHub account by clicking the `Fork`_ button.
- `Clone`_ your fork locally, replacing ``your-username`` in the command below with
your actual username.
.. code-block:: text
$ git clone https://github.com/your-username/flask
$ cd flask
- Create a virtualenv. Use the latest version of Python.
- Linux/macOS
.. code-block:: text
$ python3 -m venv .venv
$ . .venv/bin/activate
- Windows
.. code-block:: text
> py -3 -m venv .venv
> .venv\Scripts\activate
- 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 .
- Install the pre-commit hooks.
.. code-block:: text
$ 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
.. _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:
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.
.. code-block:: text
$ git fetch origin
$ git checkout -b your-branch-name origin/2.0.x
If you're submitting a feature addition or change, branch off of the "main" branch.
.. code-block:: text
$ git fetch origin
$ git checkout -b your-branch-name origin/main
- 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 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:
Running the tests
~~~~~~~~~~~~~~~~~
Run the basic test suite with pytest.
.. code-block:: text
$ pytest
This runs the tests for the current environment, which is usually
sufficient. CI will run the full suite when you submit your pull
request. You can run the full test suite with tox if you don't want to
wait.
.. code-block:: text
$ tox
Running test coverage
~~~~~~~~~~~~~~~~~~~~~
Generating a report of lines that do not have test coverage can indicate
where to start contributing. Run ``pytest`` using ``coverage`` and
generate a report.
If you are using GitHub Codespaces, ``coverage`` is already installed
so you can skip the installation command.
.. code-block:: text
$ pip install coverage
$ coverage run -m pytest
$ coverage html
Open ``htmlcov/index.html`` in your browser to explore the report.
Read more about `coverage <https://coverage.readthedocs.io>`__.
Building the docs
~~~~~~~~~~~~~~~~~
Build the docs in the ``docs`` directory using Sphinx.
.. code-block:: text
$ cd docs
$ make html
Open ``_build/html/index.html`` in your browser to view the docs.
Read more about `Sphinx <https://www.sphinx-doc.org/en/stable/>`__.

View file

@ -1,53 +0,0 @@
<div align="center"><img src="https://raw.githubusercontent.com/pallets/flask/refs/heads/stable/docs/_static/flask-name.svg" alt="" height="150"></div>
# 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/
## A Simple Example
```python
# save this as app.py
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello, World!"
```
```
$ flask run
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
```
## 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
## Contributing
See our [detailed contributing documentation][contrib] for many ways to
contribute, including reporting issues, requesting features, asking or answering
questions, and making PRs.
[contrib]: https://palletsprojects.com/contributing/

80
README.rst Normal file
View file

@ -0,0 +1,80 @@
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/
- Chat: https://discord.gg/pallets

BIN
docs/_static/flask-horizontal.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View file

@ -1,15 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 500 500" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<rect id="Icon" x="0" y="0" width="500" height="500" style="fill:none;"/>
<clipPath id="_clip1">
<rect x="0" y="0" width="500" height="500"/>
</clipPath>
<g clip-path="url(#_clip1)">
<g>
<path d="M224.446,59.975c-0.056,-4.151 -0.483,-5.543 -2.7,-6.823c-2.104,-1.393 -5.288,-1.421 -8.329,-0.085l-204.674,87.64c-3.042,1.336 -5.913,4.008 -7.448,6.908c-1.535,2.899 -1.705,5.97 -0.511,8.158l17.084,31.384l0.228,0.369c1.847,2.928 6.026,3.696 10.29,1.82l1.251,-0.54c5.344,22.4 14.1,50.429 25.783,70.413l178.294,-79.794c-2.559,-23.14 -9.552,-89.602 -9.268,-119.479l0,0.029Z" style="fill:#3babc3;fill-rule:nonzero;"/>
<path d="M238.603,205.776l-171.698,76.838c10.091,19.132 22.542,39.428 37.722,58.986c50.429,-25.698 100.887,-51.396 151.316,-77.094c-3.269,-8.471 -6.452,-17.653 -17.34,-58.73Z" style="fill:#3babc3;fill-rule:nonzero;"/>
<path d="M497.601,388.846l-12.139,-18.535c-1.819,-2.018 -4.633,-2.786 -7.106,-1.791l-15.578,5.999c-1.848,-2.047 -4.52,-2.815 -7.135,-1.791c-5.089,1.99 -10.206,4.008 -15.294,5.998c-1.649,0.625 -2.104,1.847 -1.791,3.439l0.995,4.861c-28.711,3.099 -77.236,1.564 -120.701,-32.577c-19.216,-15.066 -37.239,-36.386 -52.277,-66.206l-144.75,73.768c26.466,29.08 59.697,54.864 100.973,70.385c57.422,21.633 130.593,23.679 222.838,-13.475l0.512,2.616c0.455,2.928 3.98,6.026 8.755,4.15l15.323,-5.97c5.258,-1.99 5.287,-6.026 4.519,-8.641l19.729,-7.704c2.217,-0.853 9.096,-6.169 3.183,-14.526l-0.056,-0Z" style="fill:#3babc3;fill-rule:nonzero;"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2 KiB

View file

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 500 500" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<rect id="Logo" x="0" y="0" width="500" height="500" style="fill:none;"/>
<g>
<path id="Box" d="M500,50l0,400c0,27.596 -22.404,50 -50,50l-400,0c-27.596,0 -50,-22.404 -50,-50l0,-400c0,-27.596 22.404,-50 50,-50l400,0c27.596,0 50,22.404 50,50Z" style="fill:url(#_Linear1);"/>
<path id="Shadow" d="M500,398.646l0,51.354c0,27.596 -22.404,50 -50,50l-94.111,0l-170.452,-170.451c13.541,12.279 29.511,22.845 48.242,29.888c34.453,12.98 78.356,14.208 133.703,-8.084l0.307,1.569c0.273,1.757 2.388,3.616 5.253,2.49l9.193,-3.582c3.156,-1.194 3.173,-3.616 2.712,-5.185l11.837,-4.622c1.331,-0.512 5.458,-3.701 1.91,-8.716l-0.034,0l-7.283,-11.12c-1.091,-1.211 -2.78,-1.672 -4.264,-1.075l-9.346,3.599c-1.109,-1.228 -2.712,-1.688 -4.281,-1.074c-3.054,1.194 -6.124,2.404 -9.177,3.598c-0.989,0.376 -1.262,1.109 -1.074,2.064l0.597,2.917c-17.227,1.859 -46.342,0.938 -72.421,-19.547c-11.53,-9.039 -22.343,-21.831 -31.366,-39.723l-83.923,42.769l-13.246,-10.755c30.258,-15.419 60.532,-30.837 90.79,-46.256c-1.961,-5.083 -3.872,-10.592 -10.404,-35.238l-98.082,43.893l-11.828,-11.828l106.976,-47.876c-1.534,-13.88 -5.728,-53.736 -5.56,-71.67l-0,-0.017c-0.027,-1.894 -0.184,-2.827 -0.85,-3.504l266.182,266.182Zm-388.562,-185.436c1.272,1.164 3.414,1.356 5.594,0.397l0.75,-0.324c0.645,2.703 1.373,5.543 2.181,8.452l-8.525,-8.525Z" style="fill:#3b808b;"/>
<g id="Icon">
<path d="M234.668,135.985c-0.034,-2.49 -0.29,-3.326 -1.62,-4.094c-1.263,-0.835 -3.173,-0.852 -4.998,-0.051l-122.804,52.584c-1.825,0.802 -3.548,2.405 -4.469,4.145c-0.921,1.74 -1.023,3.582 -0.307,4.895l10.251,18.83l0.136,0.222c1.109,1.757 3.616,2.217 6.175,1.091l0.75,-0.324c3.207,13.441 8.46,30.258 15.47,42.248l106.976,-47.876c-1.535,-13.884 -5.731,-53.761 -5.56,-71.687l-0,0.017Z" style="fill:#fff;fill-rule:nonzero;"/>
<path d="M243.162,223.466l-103.019,46.103c6.055,11.478 13.525,23.656 22.633,35.391c30.258,-15.419 60.532,-30.837 90.79,-46.256c-1.961,-5.083 -3.872,-10.592 -10.404,-35.238Z" style="fill:#fff;fill-rule:nonzero;"/>
<path d="M398.56,333.307l-7.283,-11.12c-1.091,-1.211 -2.78,-1.672 -4.264,-1.075l-9.346,3.599c-1.109,-1.228 -2.712,-1.688 -4.281,-1.074c-3.054,1.194 -6.124,2.404 -9.177,3.598c-0.989,0.376 -1.262,1.109 -1.074,2.064l0.597,2.917c-17.227,1.859 -46.342,0.938 -72.421,-19.547c-11.53,-9.039 -22.343,-21.831 -31.366,-39.723l-86.85,44.26c15.879,17.449 35.818,32.919 60.584,42.231c34.453,12.98 78.356,14.208 133.703,-8.084l0.307,1.569c0.273,1.757 2.388,3.616 5.253,2.49l9.193,-3.582c3.156,-1.194 3.173,-3.616 2.712,-5.185l11.837,-4.622c1.331,-0.512 5.458,-3.701 1.91,-8.716l-0.034,0Z" style="fill:#fff;fill-rule:nonzero;"/>
</g>
</g>
<defs>
<linearGradient id="_Linear1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(3.06162e-14,500,-500,3.06162e-14,267.59,0)"><stop offset="0" style="stop-color:#bdddeb;stop-opacity:1"/><stop offset="1" style="stop-color:#53a9d1;stop-opacity:1"/></linearGradient>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 3.4 KiB

View file

@ -1,23 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 706 300" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g>
<path id="Name" d="M420.35,117.6l-37.65,-0c-4.4,-0 -7.475,0.85 -9.225,2.55c-1.75,1.7 -2.625,4.65 -2.625,8.85l-0,14.85l39.15,-0l-1.5,18.6l-37.65,-0l-0,43.35l-20.85,-0l-0,-85.05c-0,-7.9 1.725,-13.5 5.175,-16.8c3.45,-3.3 9.375,-4.95 17.775,-4.95l47.4,-0l0,18.6Z" style="fill-rule:nonzero;"/>
<path d="M455.75,205.8l-19.5,0.15l-0,-112.95l19.5,-0l-0,112.8Z" style="fill-rule:nonzero;"/>
<path d="M535.7,205.8l-13.05,-0l-6,-10.35l-20.85,11.25c-11.6,-0 -19.4,-4.2 -23.4,-12.6c-2,-4.1 -3.375,-8.525 -4.125,-13.275c-0.75,-4.75 -1.125,-9.7 -1.125,-14.85c-0,-5.15 0.05,-8.95 0.15,-11.4c0.1,-2.45 0.35,-5.3 0.75,-8.55c0.4,-3.25 0.975,-5.975 1.725,-8.175c0.75,-2.2 1.825,-4.475 3.225,-6.825c1.4,-2.35 3.1,-4.225 5.1,-5.625c4.5,-3.1 10.35,-4.65 17.55,-4.65l20.55,-0l19.5,-1.2l-0,86.25Zm-19.5,-27.3l-0,-40.2l-14.85,-0c-5.5,-0 -9.325,2.1 -11.475,6.3c-2.15,4.2 -3.225,10.475 -3.225,18.825c-0,8.35 1.025,14.225 3.075,17.625c2.05,3.4 5.925,5.1 11.625,5.1l14.85,-7.65Z" style="fill-rule:nonzero;"/>
<path d="M615.65,182.1l0,2.25c-0.6,7.5 -3.775,13.15 -9.525,16.95c-5.75,3.8 -12.925,5.7 -21.525,5.7c-12.7,-0 -21.6,-2.3 -26.7,-6.9c-4.7,-4.2 -7.05,-10.4 -7.05,-18.6l0,-1.8l17.7,0c0,4.6 1.2,7.75 3.6,9.45c2.4,1.7 6.55,2.55 12.45,2.55c8,0 12,-2.9 12,-8.7c0,-4.8 -1.4,-8 -4.2,-9.6c-1.3,-0.8 -2.95,-1.4 -4.95,-1.8l-15.15,-2.55c-13.2,-2.1 -19.8,-10.35 -19.8,-24.75c0,-8 2.925,-14.225 8.775,-18.675c5.85,-4.45 13.275,-6.675 22.275,-6.675c20.5,0 30.75,8.85 30.75,26.55l0,1.95l-16.95,0c-0.2,-4.7 -1.45,-7.9 -3.75,-9.6c-2.3,-1.7 -5.525,-2.55 -9.675,-2.55c-4.15,0 -7.275,0.825 -9.375,2.475c-2.1,1.65 -3.15,3.475 -3.15,5.475c0,5.7 2.3,8.95 6.9,9.75l18.15,3.3c12.8,2.4 19.2,11 19.2,25.8Z" style="fill-rule:nonzero;"/>
<path d="M705.65,205.8l-23.4,-0l-22.5,-30.3l-8.55,12.15l0,18.15l-19.5,-0l0,-112.8l19.5,-0l0,71.25l27.3,-40.65l22.05,-0l-28.05,38.4l33.15,43.8Z" style="fill-rule:nonzero;"/>
<g id="Logo">
<path id="Box" d="M300,30l0,240c0,16.557 -13.443,30 -30,30l-240,-0c-16.557,-0 -30,-13.443 -30,-30l0,-240c0,-16.557 13.443,-30 30,-30l240,0c16.557,0 30,13.443 30,30Z" style="fill:url(#_Linear1);"/>
<path id="Shadow" d="M300,239.188l0,30.812c0,16.557 -13.443,30 -30,30l-56.467,-0l-102.271,-102.271c8.125,7.368 17.707,13.707 28.945,17.933c20.672,7.788 47.014,8.525 80.222,-4.85l0.184,0.941c0.164,1.054 1.433,2.17 3.152,1.494l5.516,-2.149c1.893,-0.716 1.904,-2.169 1.627,-3.111l7.103,-2.773c0.798,-0.307 3.274,-2.221 1.146,-5.23l-0.021,0l-4.37,-6.672c-0.655,-0.727 -1.668,-1.003 -2.558,-0.645l-5.608,2.16c-0.665,-0.737 -1.627,-1.013 -2.569,-0.645c-1.832,0.716 -3.674,1.443 -5.505,2.159c-0.594,0.225 -0.758,0.665 -0.645,1.239l0.358,1.749c-10.336,1.116 -27.805,0.563 -43.452,-11.727c-6.918,-5.424 -13.406,-13.099 -18.82,-23.835l-50.354,25.662l-7.947,-6.453c18.154,-9.251 36.319,-18.502 54.474,-27.754c-1.177,-3.049 -2.323,-6.355 -6.243,-21.143l-58.849,26.336l-7.097,-7.096l64.186,-28.726c-0.921,-8.328 -3.437,-32.242 -3.336,-43.002l-0,-0.01c-0.016,-1.137 -0.111,-1.697 -0.51,-2.103l159.709,159.71Zm-233.137,-111.262c0.763,0.699 2.048,0.814 3.356,0.238l0.45,-0.194c0.387,1.622 0.824,3.325 1.309,5.071l-5.115,-5.115Z" style="fill:#3b808b;"/>
<g id="Icon">
<path d="M140.801,81.591c-0.021,-1.494 -0.174,-1.996 -0.972,-2.456c-0.758,-0.502 -1.904,-0.512 -2.999,-0.031l-73.683,31.551c-1.095,0.481 -2.128,1.443 -2.681,2.486c-0.552,1.044 -0.614,2.149 -0.184,2.937l6.151,11.298l0.081,0.133c0.666,1.055 2.17,1.331 3.705,0.655l0.45,-0.194c1.924,8.064 5.076,18.155 9.282,25.349l64.186,-28.726c-0.921,-8.33 -3.439,-32.257 -3.336,-43.012l-0,0.01Z" style="fill:#fff;fill-rule:nonzero;"/>
<path d="M145.897,134.079l-61.811,27.662c3.633,6.887 8.115,14.194 13.58,21.235c18.154,-9.251 36.319,-18.502 54.474,-27.754c-1.177,-3.049 -2.323,-6.355 -6.243,-21.143Z" style="fill:#fff;fill-rule:nonzero;"/>
<path d="M239.136,199.984l-4.37,-6.672c-0.655,-0.727 -1.668,-1.003 -2.558,-0.645l-5.608,2.16c-0.665,-0.737 -1.627,-1.013 -2.569,-0.645c-1.832,0.716 -3.674,1.443 -5.505,2.159c-0.594,0.225 -0.758,0.665 -0.645,1.239l0.358,1.749c-10.336,1.116 -27.805,0.563 -43.452,-11.727c-6.918,-5.424 -13.406,-13.099 -18.82,-23.835l-52.11,26.557c9.528,10.469 21.491,19.751 36.35,25.338c20.672,7.788 47.014,8.525 80.222,-4.85l0.184,0.941c0.164,1.054 1.433,2.17 3.152,1.494l5.516,-2.149c1.893,-0.716 1.904,-2.169 1.627,-3.111l7.103,-2.773c0.798,-0.307 3.274,-2.221 1.146,-5.23l-0.021,0Z" style="fill:#fff;fill-rule:nonzero;"/>
</g>
</g>
</g>
<defs>
<linearGradient id="_Linear1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1.83697e-14,300,-300,1.83697e-14,160.554,0)"><stop offset="0" style="stop-color:#bdddeb;stop-opacity:1"/><stop offset="1" style="stop-color:#53a9d1;stop-opacity:1"/></linearGradient>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 5.2 KiB

BIN
docs/_static/flask-vertical.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
docs/_static/shortcut-icon.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View file

@ -31,15 +31,17 @@ Incoming Request Data
:inherited-members:
:exclude-members: json_module
.. data:: request
.. attribute:: request
A proxy to the request data for the current request, an instance of
:class:`.Request`.
To access incoming request data, you can use the global `request`
object. Flask parses incoming request data for you and gives you
access to it through that global object. Internally Flask makes
sure that you always get the correct data for the active thread if you
are in a multithreaded environment.
This is only available when a :doc:`request context </appcontext>` is
active.
This is a proxy. See :ref:`notes-on-proxies` for more information.
This is a proxy. See :ref:`context-visibility` for more information.
The request object is an instance of a :class:`~flask.Request`.
Response Objects
@ -60,33 +62,40 @@ does this is by using a signed cookie. The user can look at the session
contents, but can't modify it unless they know the secret key, so make sure to
set that to something complex and unguessable.
To access the current session you can use the :data:`.session` proxy.
To access the current session you can use the :class:`session` object:
.. data:: session
.. class:: session
A proxy to the session data for the current request, an instance of
:class:`.SessionMixin`.
The session object works pretty much like an ordinary dict, with the
difference that it keeps track of modifications.
This is only available when a :doc:`request context </appcontext>` is
active.
This is a proxy. See :ref:`notes-on-proxies` for more information.
This is a proxy. See :ref:`context-visibility` for more information.
The following attributes are interesting:
The session object works like a dict but tracks assignment and access to its
keys. It cannot track modifications to mutable values, you need to set
:attr:`~.SessionMixin.modified` manually when modifying a list, dict, etc.
.. attribute:: new
.. code-block:: python
``True`` if the session is new, ``False`` otherwise.
# appending to a list is not detected
session["numbers"].append(42)
.. attribute:: modified
``True`` if the session object detected a modification. Be advised
that modifications on mutable structures are not picked up
automatically, in that situation you have to explicitly set the
attribute to ``True`` yourself. Here an example::
# this change is not picked up because a mutable object (here
# a list) is changed.
session['objects'].append(42)
# so mark it as modified yourself
session.modified = True
The session is persisted across requests using a cookie. By default the
users's browser will clear the cookie when it is closed. Set
:attr:`~.SessionMixin.permanent` to ``True`` to persist the cookie for
:data:`PERMANENT_SESSION_LIFETIME`.
.. attribute:: permanent
If set to ``True`` the session lives for
:attr:`~flask.Flask.permanent_session_lifetime` seconds. The
default is 31 days. If set to ``False`` (which is the default) the
session will be deleted when the user closes the browser.
Session Interface
@ -149,21 +158,20 @@ another, a global variable is not good enough because it would break in
threaded environments. Flask provides you with a special object that
ensures it is only valid for the active request and that will return
different values for each request. In a nutshell: it does the right
thing, like it does for :data:`.request` and :data:`.session`.
thing, like it does for :class:`request` and :class:`session`.
.. data:: g
A proxy to a namespace object used to store data during a single request or
app context. An instance of :attr:`.Flask.app_ctx_globals_class`, which
defaults to :class:`._AppCtxGlobals`.
A namespace object that can store data during an
:doc:`application context </appcontext>`. This is an instance of
:attr:`Flask.app_ctx_globals_class`, which defaults to
:class:`ctx._AppCtxGlobals`.
This is a good place to store resources during a request. For example, a
:meth:`~.Flask.before_request` function could load a user object from a
session id, then set ``g.user`` to be used in the view function.
This is a good place to store resources during a request. For
example, a ``before_request`` function could load a user object from
a session id, then set ``g.user`` to be used in the view function.
This is only available when an :doc:`app context </appcontext>` is active.
This is a proxy. See :ref:`context-visibility` for more information.
This is a proxy. See :ref:`notes-on-proxies` for more information.
.. versionchanged:: 0.10
Bound to the application context instead of the request context.
@ -177,16 +185,17 @@ Useful Functions and Classes
.. data:: current_app
A proxy to the :class:`.Flask` application handling the current request or
other activity.
A proxy to the application handling the current request. This is
useful to access the application without needing to import it, or if
it can't be imported, such as when using the application factory
pattern or in blueprints and extensions.
This is useful to access the application without needing to import it, or if
it can't be imported, such as when using the application factory pattern or
in blueprints and extensions.
This is only available when an
:doc:`application context </appcontext>` is pushed. This happens
automatically during requests and CLI commands. It can be controlled
manually with :meth:`~flask.Flask.app_context`.
This is only available when an :doc:`app context </appcontext>` is active.
This is a proxy. See :ref:`context-visibility` for more information.
This is a proxy. See :ref:`notes-on-proxies` for more information.
.. autofunction:: has_request_context
@ -290,31 +299,31 @@ Stream Helpers
Useful Internals
----------------
.. autoclass:: flask.ctx.RequestContext
:members:
.. data:: flask.globals.request_ctx
The current :class:`~flask.ctx.RequestContext`. If a request context
is not active, accessing attributes on this proxy will raise a
``RuntimeError``.
This is an internal object that is essential to how Flask handles
requests. Accessing this should not be needed in most cases. Most
likely you want :data:`request` and :data:`session` instead.
.. autoclass:: flask.ctx.AppContext
:members:
.. data:: flask.globals.app_ctx
A proxy to the active :class:`.AppContext`.
The current :class:`~flask.ctx.AppContext`. If an app context is not
active, accessing attributes on this proxy will raise a
``RuntimeError``.
This is an internal object that is essential to how Flask handles requests.
Accessing this should not be needed in most cases. Most likely you want
:data:`.current_app`, :data:`.g`, :data:`.request`, and :data:`.session` instead.
This is only available when a :doc:`request context </appcontext>` is
active.
This is a proxy. See :ref:`context-visibility` for more information.
.. class:: flask.ctx.RequestContext
.. deprecated:: 3.2
Merged with :class:`AppContext`. This alias will be removed in Flask 4.0.
.. data:: flask.globals.request_ctx
.. deprecated:: 3.2
Merged with :data:`.app_ctx`. This alias will be removed in Flask 4.0.
This is an internal object that is essential to how Flask handles
requests. Accessing this should not be needed in most cases. Most
likely you want :data:`current_app` and :data:`g` instead.
.. autoclass:: flask.blueprints.BlueprintSetupState
:members:
@ -596,7 +605,7 @@ This specifies that ``/users/`` will be the URL for page one and
``/users/page/N`` will be the URL for page ``N``.
If a URL contains a default value, it will be redirected to its simpler
form with a 308 redirect. In the above example, ``/users/page/1`` will
form with a 301 redirect. In the above example, ``/users/page/1`` will
be redirected to ``/users/``. If your route handles ``GET`` and ``POST``
requests, make sure the default route only handles ``GET``, as redirects
can't preserve form data. ::

View file

@ -1,63 +1,74 @@
The App and Request Context
===========================
.. currentmodule:: flask
The context keeps track of data and objects during a request, CLI command, or
other activity. Rather than passing this data around to every function, the
:data:`.current_app`, :data:`.g`, :data:`.request`, and :data:`.session` proxies
are accessed instead.
The Application Context
=======================
When handling a request, the context is referred to as the "request context"
because it contains request data in addition to application data. Otherwise,
such as during a CLI command, it is referred to as the "app context". During an
app context, :data:`.current_app` and :data:`.g` are available, while during a
request context :data:`.request` and :data:`.session` are also available.
The application context keeps track of the application-level data during
a request, CLI command, or other activity. Rather than passing the
application around to each function, the :data:`current_app` and
:data:`g` proxies are accessed instead.
This is similar to :doc:`/reqcontext`, which keeps track of
request-level data during a request. A corresponding application context
is pushed when a request context is pushed.
Purpose of the Context
----------------------
The context and proxies help solve two development issues: circular imports, and
passing around global data during a request.
The :class:`Flask` application object has attributes, such as
:attr:`~Flask.config`, that are useful to access within views and
:doc:`CLI commands </cli>`. However, importing the ``app`` instance
within the modules in your project is prone to circular import issues.
When using the :doc:`app factory pattern </patterns/appfactories>` or
writing reusable :doc:`blueprints </blueprints>` or
:doc:`extensions </extensions>` there won't be an ``app`` instance to
import at all.
The :class:`.Flask` application object has attributes, such as
:attr:`~.Flask.config`, that are useful to access within views and other
functions. However, importing the ``app`` instance within the modules in your
project is prone to circular import issues. When using the
:doc:`app factory pattern </patterns/appfactories>` or writing reusable
:doc:`blueprints </blueprints>` or :doc:`extensions </extensions>` there won't
be an ``app`` instance to import at all.
Flask solves this issue with the *application context*. Rather than
referring to an ``app`` directly, you use the :data:`current_app`
proxy, which points to the application handling the current activity.
When the application handles a request, it creates a :class:`.Request` object.
Because a *worker* handles only one request at a time, the request data can be
considered global to that worker during that request. Passing it as an argument
through every function during the request becomes verbose and redundant.
Flask automatically *pushes* an application context when handling a
request. View functions, error handlers, and other functions that run
during a request will have access to :data:`current_app`.
Flask solves these issues with the *active context* pattern. Rather than
importing an ``app`` directly, or having to pass it and the request through to
every single function, you import and access the proxies, which point to the
currently active application and request data. This is sometimes referred to
as "context local" data.
Flask will also automatically push an app context when running CLI
commands registered with :attr:`Flask.cli` using ``@app.cli.command()``.
Context During Setup
--------------------
Lifetime of the Context
-----------------------
If you try to access :data:`.current_app`, :data:`.g`, or anything that uses it,
outside an app context, you'll get this error message:
The application context is created and destroyed as necessary. When a
Flask application begins handling a request, it pushes an application
context and a :doc:`request context </reqcontext>`. When the request
ends it pops the request context then the application context.
Typically, an application context will have the same lifetime as a
request.
See :doc:`/reqcontext` for more information about how the contexts work
and the full life cycle of a request.
Manually Push a Context
-----------------------
If you try to access :data:`current_app`, or anything that uses it,
outside an application context, you'll get this error message:
.. code-block:: pytb
RuntimeError: Working outside of application context.
Attempted to use functionality that expected a current application to be
set. To solve this, set up an app context using 'with app.app_context()'.
See the documentation on app context for more information.
This typically means that you attempted to use functionality that
needed to interface with the current application object in some way.
To solve this, set up an application context with app.app_context().
If you see that error while configuring your application, such as when
initializing an extension, you can push a context manually since you have direct
access to the ``app``. Use :meth:`.Flask.app_context` in a ``with`` block.
.. code-block:: python
initializing an extension, you can push a context manually since you
have direct access to the ``app``. Use :meth:`~Flask.app_context` in a
``with`` block, and everything that runs in the block will have access
to :data:`current_app`. ::
def create_app():
app = Flask(__name__)
@ -67,121 +78,70 @@ access to the ``app``. Use :meth:`.Flask.app_context` in a ``with`` block.
return app
If you see that error somewhere else in your code not related to setting up the
application, it most likely indicates that you should move that code into a view
function or CLI command.
If you see that error somewhere else in your code not related to
configuring the application, it most likely indicates that you should
move that code into a view function or CLI command.
Context During Testing
----------------------
Storing Data
------------
See :doc:`/testing` for detailed information about managing the context during
tests.
The application context is a good place to store common data during a
request or CLI command. Flask provides the :data:`g object <g>` for this
purpose. It is a simple namespace object that has the same lifetime as
an application context.
If you try to access :data:`.request`, :data:`.session`, or anything that uses
it, outside a request context, you'll get this error message:
.. note::
The ``g`` name stands for "global", but that is referring to the
data being global *within a context*. The data on ``g`` is lost
after the context ends, and it is not an appropriate place to store
data between requests. Use the :data:`session` or a database to
store data across requests.
.. code-block:: pytb
A common use for :data:`g` is to manage resources during a request.
RuntimeError: Working outside of request context.
1. ``get_X()`` creates resource ``X`` if it does not exist, caching it
as ``g.X``.
2. ``teardown_X()`` closes or otherwise deallocates the resource if it
exists. It is registered as a :meth:`~Flask.teardown_appcontext`
handler.
Attempted to use functionality that expected an active HTTP request. See the
documentation on request context for more information.
For example, you can manage a database connection using this pattern::
This will probably only happen during tests. If you see that error somewhere
else in your code not related to testing, it most likely indicates that you
should move that code into a view function.
from flask import g
The primary way to solve this is to use :meth:`.Flask.test_client` to simulate
a full request.
def get_db():
if 'db' not in g:
g.db = connect_to_database()
If you only want to unit test one function, rather than a full request, use
:meth:`.Flask.test_request_context` in a ``with`` block.
return g.db
.. code-block:: python
@app.teardown_appcontext
def teardown_db(exception):
db = g.pop('db', None)
def generate_report(year):
format = request.args.get("format")
...
if db is not None:
db.close()
with app.test_request_context(
"/make_report/2017", query_string={"format": "short"}
):
generate_report()
During a request, every call to ``get_db()`` will return the same
connection, and it will be closed automatically at the end of the
request.
You can use :class:`~werkzeug.local.LocalProxy` to make a new context
local from ``get_db()``::
from werkzeug.local import LocalProxy
db = LocalProxy(get_db)
Accessing ``db`` will call ``get_db`` internally, in the same way that
:data:`current_app` works.
.. _context-visibility:
Events and Signals
------------------
Visibility of the Context
-------------------------
The application will call functions registered with :meth:`~Flask.teardown_appcontext`
when the application context is popped.
The context will have the same lifetime as an activity, such as a request, CLI
command, or ``with`` block. Various callbacks and signals registered with the
app will be run during the context.
When a Flask application handles a request, it pushes a request context
to set the active application and request data. When it handles a CLI command,
it pushes an app context to set the active application. When the activity ends,
it pops that context. Proxy objects like :data:`.request`, :data:`.session`,
:data:`.g`, and :data:`.current_app`, are accessible while the context is pushed
and active, and are not accessible after the context is popped.
The context is unique to each thread (or other worker type). The proxies cannot
be passed to another worker, which has a different context space and will not
know about the active context in the parent's space.
Besides being scoped to each worker, the proxy object has a separate type and
identity than the proxied real object. In some cases you'll need access to the
real object, rather than the proxy. Use the
:meth:`~.LocalProxy._get_current_object` method in those cases.
.. code-block:: python
app = current_app._get_current_object()
my_signal.send(app)
Lifecycle of the Context
------------------------
Flask dispatches a request in multiple stages which can affect the request,
response, and how errors are handled. See :doc:`/lifecycle` for a list of all
the steps, callbacks, and signals during each request. The following are the
steps directly related to the context.
- The app context is pushed, the proxies are available.
- The :data:`.appcontext_pushed` signal is sent.
- The request is dispatched.
- Any :meth:`.Flask.teardown_request` decorated functions are called.
- The :data:`.request_tearing_down` signal is sent.
- Any :meth:`.Flask.teardown_appcontext` decorated functions are called.
- The :data:`.appcontext_tearing_down` signal is sent.
- The app context is popped, the proxies are no longer available.
- The :data:`.appcontext_popped` signal is sent.
The teardown callbacks are called by the context when it is popped. They are
called even if there is an unhandled exception during dispatch. They may be
called multiple times in some test scenarios. This means there is no guarantee
that any other parts of the request dispatch have run. Be sure to write these
functions in a way that does not depend on other callbacks. All callbacks are
called even if any raise an error.
How the Context Works
---------------------
Context locals are implemented using Python's :mod:`contextvars` and Werkzeug's
:class:`~werkzeug.local.LocalProxy`. Python's contextvars are a low level
structure to manage data local to a thread or coroutine. ``LocalProxy`` wraps
the contextvar so that access to any attributes and methods is forwarded to the
object stored in the contextvar.
The context is tracked like a stack, with the active context at the top of the
stack. Flask manages pushing and popping contexts during requests, CLI commands,
testing, ``with`` blocks, etc. The proxies access attributes on the active
context.
Because it is a stack, other contexts may be pushed to change the proxies during
an already active context. This is not a common pattern, but can be used in
advanced use cases. For example, a Flask application can be used as WSGI
middleware, calling another wrapped Flask app from a view.
The following signals are sent: :data:`appcontext_pushed`,
:data:`appcontext_tearing_down`, and :data:`appcontext_popped`.

View file

@ -23,6 +23,18 @@ method in views that inherit from the :class:`flask.views.View` class, as
well as all the HTTP method handlers in views that inherit from the
:class:`flask.views.MethodView` class.
.. admonition:: Using ``async`` on Windows on Python 3.8
Python 3.8 has a bug related to asyncio on Windows. If you encounter
something like ``ValueError: set_wakeup_fd only works in main thread``,
please upgrade to Python 3.9.
.. admonition:: Using ``async`` with greenlet
When using gevent or eventlet to serve an application or patch the
runtime, greenlet>=1.0 is required. When using PyPy, PyPy>=7.3.7 is
required.
Performance
-----------
@ -72,15 +84,15 @@ Flask based on the `ASGI`_ standard instead of WSGI. This allows it to
handle many concurrent requests, long running requests, and websockets
without requiring multiple worker processes or threads.
It has also already been possible to :doc:`run Flask with Gevent </gevent>` to
get many of the benefits of async request handling. Gevent patches low-level
Python functions to accomplish this, whereas ``async``/``await`` and ASGI use
standard, modern Python capabilities. Deciding whether you should use gevent
with Flask, or Quart, or something else is ultimately up to understanding the
specific needs of your project.
It has also already been possible to run Flask with Gevent or Eventlet
to get many of the benefits of async request handling. These libraries
patch low-level Python functions to accomplish this, whereas ``async``/
``await`` and ASGI use standard, modern Python capabilities. Deciding
whether you should use Flask, Quart, or something else is ultimately up
to understanding the specific needs of your project.
.. _Quart: https://quart.palletsprojects.com
.. _ASGI: https://asgi.readthedocs.io
.. _Quart: https://github.com/pallets/quart
.. _ASGI: https://asgi.readthedocs.io/en/latest/
Extensions
@ -114,6 +126,6 @@ implemented async support, or make a feature request or PR to them.
Other event loops
-----------------
At the moment Flask only supports :mod:`asyncio`. It's possible to override
:meth:`flask.Flask.ensure_sync` to change how async functions are wrapped to use
a different library. See :ref:`gevent-asyncio` for an example.
At the moment Flask only supports :mod:`asyncio`. It's possible to
override :meth:`flask.Flask.ensure_sync` to change how async functions
are wrapped to use a different library.

View file

@ -11,23 +11,16 @@ release, version = get_version("Flask")
# General --------------------------------------------------------------
default_role = "code"
master_doc = "index"
extensions = [
"sphinx.ext.autodoc",
"sphinx.ext.extlinks",
"sphinx.ext.intersphinx",
"sphinxcontrib.log_cabinet",
"sphinx_tabs.tabs",
"pallets_sphinx_themes",
"sphinx_issues",
"sphinx_tabs.tabs",
]
autodoc_member_order = "bysource"
autodoc_typehints = "description"
autodoc_preserve_defaults = True
extlinks = {
"issue": ("https://github.com/pallets/flask/issues/%s", "#%s"),
"pr": ("https://github.com/pallets/flask/pull/%s", "#%s"),
"ghsa": ("https://github.com/pallets/flask/security/advisories/GHSA-%s", "GHSA-%s"),
}
intersphinx_mapping = {
"python": ("https://docs.python.org/3/", None),
"werkzeug": ("https://werkzeug.palletsprojects.com/", None),
@ -38,6 +31,7 @@ intersphinx_mapping = {
"wtforms": ("https://wtforms.readthedocs.io/", None),
"blinker": ("https://blinker.readthedocs.io/", None),
}
issues_github_path = "pallets/flask"
# HTML -----------------------------------------------------------------
@ -58,13 +52,14 @@ html_sidebars = {
}
singlehtml_sidebars = {"index": ["project.html", "localtoc.html", "ethicalads.html"]}
html_static_path = ["_static"]
html_favicon = "_static/flask-icon.svg"
html_logo = "_static/flask-logo.svg"
html_favicon = "_static/shortcut-icon.png"
html_logo = "_static/flask-vertical.png"
html_title = f"Flask Documentation ({version})"
html_show_sourcelink = False
gettext_uuid = True
gettext_compact = False
# LaTeX ----------------------------------------------------------------
latex_documents = [(master_doc, f"Flask-{version}.tex", html_title, author, "manual")]
# Local Extensions -----------------------------------------------------

View file

@ -79,7 +79,7 @@ The following configuration values are used internally by Flask:
.. py:data:: TESTING
Enable testing mode. Exceptions are propagated rather than handled by
Enable testing mode. Exceptions are propagated rather than handled by the
the app's error handlers. Extensions may also change their behavior to
facilitate easier testing. You should enable this in your own tests.
@ -125,25 +125,6 @@ The following configuration values are used internally by Flask:
Default: ``None``
.. py:data:: SECRET_KEY_FALLBACKS
A list of old secret keys that can still be used for unsigning. This allows
a project to implement key rotation without invalidating active sessions or
other recently-signed secrets.
Keys should be removed after an appropriate period of time, as checking each
additional key adds some overhead.
Order should not matter, but the default implementation will test the last
key in the list first, so it might make sense to order oldest to newest.
Flask's built-in secure cookie session supports this. Extensions that use
:data:`SECRET_KEY` may not support this yet.
Default: ``None``
.. versionadded:: 3.1
.. py:data:: SESSION_COOKIE_NAME
The name of the session cookie. Can be changed in case you already have a
@ -161,12 +142,6 @@ The following configuration values are used internally by Flask:
Default: ``None``
.. warning::
If this is changed after the browser created a cookie is created with
one setting, it may result in another being created. Browsers may send
send both in an undefined order. In that case, you may want to change
:data:`SESSION_COOKIE_NAME` as well or otherwise invalidate old sessions.
.. versionchanged:: 2.3
Not set by default, does not fall back to ``SERVER_NAME``.
@ -192,23 +167,6 @@ The following configuration values are used internally by Flask:
Default: ``False``
.. py:data:: SESSION_COOKIE_PARTITIONED
Browsers will send cookies based on the top-level document's domain, rather
than only the domain of the document setting the cookie. This prevents third
party cookies set in iframes from "leaking" between separate sites.
Browsers are beginning to disallow non-partitioned third party cookies, so
you need to mark your cookies partitioned if you expect them to work in such
embedded situations.
Enabling this implicitly enables :data:`SESSION_COOKIE_SECURE` as well, as
it is only valid when served over HTTPS.
Default: ``False``
.. versionadded:: 3.1
.. py:data:: SESSION_COOKIE_SAMESITE
Restrict how cookies are sent with requests from external sites. Can
@ -261,40 +219,16 @@ The following configuration values are used internally by Flask:
Default: ``None``
.. py:data:: TRUSTED_HOSTS
Validate :attr:`.Request.host` and other attributes that use it against
these trusted values. Raise a :exc:`~werkzeug.exceptions.SecurityError` if
the host is invalid, which results in a 400 error. If it is ``None``, all
hosts are valid. Each value is either an exact match, or can start with
a dot ``.`` to match any subdomain.
Validation is done during routing against this value. ``before_request`` and
``after_request`` callbacks will still be called.
Default: ``None``
.. versionadded:: 3.1
.. py:data:: SERVER_NAME
Inform the application what host and port it is bound to.
Inform the application what host and port it is bound to. Required
for subdomain route matching support.
Must be set if ``subdomain_matching`` is enabled, to be able to extract the
subdomain from the request.
Must be set for ``url_for`` to generate external URLs outside of a
request context.
If set, ``url_for`` can generate external URLs with only an application
context instead of a request context.
Default: ``None``
.. versionchanged:: 3.1
Does not restrict requests to only this domain, for both
``subdomain_matching`` and ``host_matching``.
.. versionchanged:: 1.0
Does not implicitly enable ``subdomain_matching``.
.. versionchanged:: 2.3
Does not affect ``SESSION_COOKIE_DOMAIN``.
@ -319,54 +253,12 @@ The following configuration values are used internally by Flask:
.. py:data:: MAX_CONTENT_LENGTH
The maximum number of bytes that will be read during this request. If
this limit is exceeded, a 413 :exc:`~werkzeug.exceptions.RequestEntityTooLarge`
error is raised. If it is set to ``None``, no limit is enforced at the
Flask application level. However, if it is ``None`` and the request has no
``Content-Length`` header and the WSGI server does not indicate that it
terminates the stream, then no data is read to avoid an infinite stream.
Each request defaults to this config. It can be set on a specific
:attr:`.Request.max_content_length` to apply the limit to that specific
view. This should be set appropriately based on an application's or view's
specific needs.
Don't read more than this many bytes from the incoming request data. If not
set and the request does not specify a ``CONTENT_LENGTH``, no data will be
read for security.
Default: ``None``
.. versionadded:: 0.6
.. py:data:: MAX_FORM_MEMORY_SIZE
The maximum size in bytes any non-file form field may be in a
``multipart/form-data`` body. If this limit is exceeded, a 413
:exc:`~werkzeug.exceptions.RequestEntityTooLarge` error is raised. If it is
set to ``None``, no limit is enforced at the Flask application level.
Each request defaults to this config. It can be set on a specific
:attr:`.Request.max_form_memory_parts` to apply the limit to that specific
view. This should be set appropriately based on an application's or view's
specific needs.
Default: ``500_000``
.. versionadded:: 3.1
.. py:data:: MAX_FORM_PARTS
The maximum number of fields that may be present in a
``multipart/form-data`` body. If this limit is exceeded, a 413
:exc:`~werkzeug.exceptions.RequestEntityTooLarge` error is raised. If it
is set to ``None``, no limit is enforced at the Flask application level.
Each request defaults to this config. It can be set on a specific
:attr:`.Request.max_form_parts` to apply the limit to that specific view.
This should be set appropriately based on an application's or view's
specific needs.
Default: ``1_000``
.. versionadded:: 3.1
.. py:data:: TEMPLATES_AUTO_RELOAD
Reload templates when they are changed. If not set, it will be enabled in
@ -388,12 +280,6 @@ The following configuration values are used internally by Flask:
``4093``. Larger cookies may be silently ignored by browsers. Set to
``0`` to disable the warning.
.. py:data:: PROVIDE_AUTOMATIC_OPTIONS
Set to ``False`` to disable the automatic addition of OPTIONS
responses. This can be overridden per route by altering the
``provide_automatic_options`` attribute.
.. versionadded:: 0.4
``LOGGER_NAME``
@ -445,10 +331,6 @@ The following configuration values are used internally by Flask:
.. versionchanged:: 2.3
``ENV`` was removed.
.. versionadded:: 3.1
Added :data:`PROVIDE_AUTOMATIC_OPTIONS` to control the default
addition of autogenerated OPTIONS responses.
Configuring from Python Files
-----------------------------

View file

@ -1,8 +1 @@
Contributing
============
See the Pallets `detailed contributing documentation <contrib_>`_ for many ways
to contribute, including reporting issues, requesting features, asking or
answering questions, and making PRs.
.. _contrib: https://palletsprojects.com/contributing/
.. include:: ../CONTRIBUTING.rst

View file

@ -20,7 +20,7 @@ wrapping the Flask app,
asgi_app = WsgiToAsgi(app)
and then serving the ``asgi_app`` with the ASGI server, e.g. using
`Hypercorn <https://github.com/pgjones/hypercorn>`_,
`Hypercorn <https://gitlab.com/pgjones/hypercorn>`_,
.. sourcecode:: text

View file

@ -1,8 +1,80 @@
:orphan:
eventlet
========
`Eventlet is no longer maintained.`__ Use :doc:`/deploying/gevent` instead.
Prefer using :doc:`gunicorn` with eventlet workers rather than using
`eventlet`_ directly. Gunicorn provides a much more configurable and
production-tested server.
__ https://eventlet.readthedocs.io
`eventlet`_ allows writing asynchronous, coroutine-based code that looks
like standard synchronous Python. It uses `greenlet`_ to enable task
switching without writing ``async/await`` or using ``asyncio``.
:doc:`gevent` is another library that does the same thing. Certain
dependencies you have, or other considerations, may affect which of the
two you choose to use.
eventlet provides a WSGI server that can handle many connections at once
instead of one per worker process. You must actually use eventlet in
your own code to see any benefit to using the server.
.. _eventlet: https://eventlet.net/
.. _greenlet: https://greenlet.readthedocs.io/en/latest/
Installing
----------
When using eventlet, greenlet>=1.0 is required, otherwise context locals
such as ``request`` will not work as expected. When using PyPy,
PyPy>=7.3.7 is required.
Create a virtualenv, install your application, then install
``eventlet``.
.. code-block:: text
$ cd hello-app
$ python -m venv .venv
$ . .venv/bin/activate
$ pip install . # install your application
$ pip install eventlet
Running
-------
To use eventlet to serve your application, write a script that imports
its ``wsgi.server``, as well as your app or app factory.
.. code-block:: python
:caption: ``wsgi.py``
import eventlet
from eventlet import wsgi
from hello import create_app
app = create_app()
wsgi.server(eventlet.listen(("127.0.0.1", 8000)), app)
.. code-block:: text
$ python wsgi.py
(x) wsgi starting up on http://127.0.0.1:8000
Binding Externally
------------------
eventlet should not be run as root because it would cause your
application code to run as root, which is not secure. However, this
means it will not be possible to bind to port 80 or 443. Instead, a
reverse proxy such as :doc:`nginx` or :doc:`apache-httpd` should be used
in front of eventlet.
You can bind to all external IPs on a non-privileged port by using
``0.0.0.0`` in the server arguments shown in the previous section.
Don't do this when using a reverse proxy setup, otherwise it will be
possible to bypass the proxy.
``0.0.0.0`` is not a valid address to navigate to, you'd use a specific
IP address in your browser.

View file

@ -7,12 +7,15 @@ configurable and production-tested servers.
`gevent`_ allows writing asynchronous, coroutine-based code that looks
like standard synchronous Python. It uses `greenlet`_ to enable task
switching without writing ``async/await`` or using ``asyncio``. This is
not the same as Python's ``async/await``, or the ASGI server spec.
switching without writing ``async/await`` or using ``asyncio``.
:doc:`eventlet` is another library that does the same thing. Certain
dependencies you have, or other considerations, may affect which of the
two you choose to use.
gevent provides a WSGI server that can handle many connections at once
instead of one per worker process. See :doc:`/gevent` for more
information about enabling it in your application.
instead of one per worker process. You must actually use gevent in your
own code to see any benefit to using the server.
.. _gevent: https://www.gevent.org/
.. _greenlet: https://greenlet.readthedocs.io/en/latest/
@ -21,7 +24,8 @@ information about enabling it in your application.
Installing
----------
When using gevent, greenlet>=1.0 is required. When using PyPy,
When using gevent, greenlet>=1.0 is required, otherwise context locals
such as ``request`` will not work as expected. When using PyPy,
PyPy>=7.3.7 is required.
Create a virtualenv, install your application, then install ``gevent``.

View file

@ -8,7 +8,7 @@ multiple worker implementations for performance tuning.
* It does not support Windows (but does run on WSL).
* It is easy to install as it does not require additional dependencies
or compilation.
* It has built-in async worker support using gevent.
* It has built-in async worker support using gevent or eventlet.
This page outlines the basics of running Gunicorn. Be sure to read its
`documentation`_ and use ``gunicorn --help`` to understand what features
@ -93,19 +93,20 @@ otherwise it will be possible to bypass the proxy.
IP address in your browser.
Async with gevent
-----------------
Async with gevent or eventlet
-----------------------------
The default sync worker is appropriate for most use cases. If you need numerous,
long running, concurrent connections, Gunicorn provides an asynchronous worker
using `gevent`_. This is not the same as Python's ``async/await``, or the ASGI
server spec. See :doc:`/gevent` for more information about enabling it in your
application.
The default sync worker is appropriate for many use cases. If you need
asynchronous support, Gunicorn provides workers using either `gevent`_
or `eventlet`_. This is not the same as Python's ``async/await``, or the
ASGI server spec. You must actually use gevent/eventlet in your own code
to see any benefit to using the workers.
.. _gevent: https://www.gevent.org/
When using either gevent or eventlet, greenlet>=1.0 is required,
otherwise context locals such as ``request`` will not work as expected.
When using PyPy, PyPy>=7.3.7 is required.
When using gevent, greenlet>=1.0 is required. When using PyPy, PyPy>=7.3.7 is
required.
To use gevent:
.. code-block:: text
@ -114,3 +115,16 @@ required.
Listening at: http://127.0.0.1:8000 (x)
Using worker: gevent
Booting worker with pid: x
To use eventlet:
.. code-block:: text
$ gunicorn -k eventlet 'hello:create_app()'
Starting gunicorn 20.1.0
Listening at: http://127.0.0.1:8000 (x)
Using worker: eventlet
Booting worker with pid: x
.. _gevent: https://www.gevent.org/
.. _eventlet: https://eventlet.net/

View file

@ -36,6 +36,7 @@ discusses platforms that can manage this for you.
mod_wsgi
uwsgi
gevent
eventlet
asgi
WSGI servers have HTTP servers built-in. However, a dedicated HTTP

View file

@ -119,16 +119,15 @@ IP address in your browser.
Async with gevent
-----------------
The default sync worker is appropriate for most use cases. If you need numerous,
long running, concurrent connections, uWSGI provides an asynchronous worker
using `gevent`_. This is not the same as Python's ``async/await``, or the ASGI
server spec. See :doc:`/gevent` for more information about enabling it in your
application.
The default sync worker is appropriate for many use cases. If you need
asynchronous support, uWSGI provides a `gevent`_ worker. This is not the
same as Python's ``async/await``, or the ASGI server spec. You must
actually use gevent in your own code to see any benefit to using the
worker.
.. _gevent: https://www.gevent.org/
When using gevent, greenlet>=1.0 is required. When using PyPy, PyPy>=7.3.7 is
required.
When using gevent, greenlet>=1.0 is required, otherwise context locals
such as ``request`` will not work as expected. When using PyPy,
PyPy>=7.3.7 is required.
.. code-block:: text
@ -141,3 +140,6 @@ required.
spawned uWSGI worker 1 (pid: x, cores: 100)
spawned uWSGI http 1 (pid: x)
*** running gevent loop engine [addr:x] ***
.. _gevent: https://www.gevent.org/

View file

@ -68,7 +68,7 @@ reverse proxy such as :doc:`nginx` or :doc:`apache-httpd` should be used
in front of Waitress.
You can bind to all external IPs on a non-privileged port by not
specifying the ``--host`` option. Don't do this when using a reverse
specifying the ``--host`` option. Don't do this when using a revers
proxy setup, otherwise it will be possible to bypass the proxy.
``0.0.0.0`` is not a valid address to navigate to, you'd use a specific

View file

@ -96,10 +96,10 @@ is ambiguous.
One Template Engine
-------------------
Flask decides on one template engine: Jinja. Why doesn't Flask have a
Flask decides on one template engine: Jinja2. Why doesn't Flask have a
pluggable template engine interface? You can obviously use a different
template engine, but Flask will still configure Jinja for you. While
that limitation that Jinja is *always* configured will probably go away,
template engine, but Flask will still configure Jinja2 for you. While
that limitation that Jinja2 is *always* configured will probably go away,
the decision to bundle one template engine and use that will not.
Template engines are like programming languages and each of those engines
@ -107,7 +107,7 @@ has a certain understanding about how things work. On the surface they
all work the same: you tell the engine to evaluate a template with a set
of variables and take the return value as string.
But that's about where similarities end. Jinja for example has an
But that's about where similarities end. Jinja2 for example has an
extensive filter system, a certain way to do template inheritance,
support for reusable blocks (macros) that can be used from inside
templates and also from Python code, supports iterative template
@ -118,8 +118,8 @@ other hand treats templates similar to Python modules.
When it comes to connecting a template engine with an application or
framework there is more than just rendering templates. For instance,
Flask uses Jinja's extensive autoescaping support. Also it provides
ways to access macros from Jinja templates.
Flask uses Jinja2's extensive autoescaping support. Also it provides
ways to access macros from Jinja2 templates.
A template abstraction layer that would not take the unique features of
the template engines away is a science on its own and a too large
@ -150,7 +150,7 @@ authentication technologies, and more. Flask may be "micro", but it's ready for
production use on a variety of needs.
Why does Flask call itself a microframework and yet it depends on two
libraries (namely Werkzeug and Jinja). Why shouldn't it? If we look
libraries (namely Werkzeug and Jinja2). Why shouldn't it? If we look
over to the Ruby side of web development there we have a protocol very
similar to WSGI. Just that it's called Rack there, but besides that it
looks very much like a WSGI rendition for Ruby. But nearly all
@ -169,20 +169,19 @@ infrastructure, packages with dependencies are no longer an issue and
there are very few reasons against having libraries that depend on others.
Context Locals
--------------
Thread Locals
-------------
Flask uses special context locals and proxies to provide access to the
current app and request data to any code running during a request, CLI command,
etc. Context locals are specific to the worker handling the activity, such as a
thread, process, coroutine, or greenlet.
Flask uses thread local objects (context local objects in fact, they
support greenlet contexts as well) for request, session and an extra
object you can put your own things on (:data:`~flask.g`). Why is that and
isn't that a bad idea?
The context and proxies help solve two development issues: circular imports, and
passing around global data. :data:`.current_app` can be used to access the
application object without needing to import the app object directly, avoiding
circular import issues. :data:`.request`, :data:`.session`, and :data:`.g` can
be imported to access the current data for the request, rather than needing to
pass them as arguments through every single function in your project.
Yes it is usually not such a bright idea to use thread locals. They cause
troubles for servers that are not based on the concept of threads and make
large applications harder to maintain. However Flask is just not designed
for large applications or asynchronous servers. Flask wants to make it
quick and easy to write a traditional web application.
Async/await and ASGI support
@ -209,7 +208,7 @@ What Flask is, What Flask is Not
Flask will never have a database layer. It will not have a form library
or anything else in that direction. Flask itself just bridges to Werkzeug
to implement a proper WSGI application and to Jinja to handle templating.
to implement a proper WSGI application and to Jinja2 to handle templating.
It also binds to a few common standard library packages such as logging.
Everything else is up for extensions.

View file

@ -67,7 +67,7 @@ application instance.
It is important that the app is not stored on the extension, don't do
``self.app = app``. The only time the extension should have direct
access to an app is during ``init_app``, otherwise it should use
:data:`.current_app`.
:data:`current_app`.
This allows the extension to support the application factory pattern,
avoids circular import issues when importing the extension instance
@ -105,7 +105,7 @@ during an extension's ``init_app`` method.
A common pattern is to use :meth:`~Flask.before_request` to initialize
some data or a connection at the beginning of each request, then
:meth:`~Flask.teardown_request` to clean it up at the end. This can be
stored on :data:`.g`, discussed more below.
stored on :data:`g`, discussed more below.
A more lazy approach is to provide a method that initializes and caches
the data or connection. For example, a ``ext.get_db`` method could
@ -179,12 +179,13 @@ name as a prefix, or as a namespace.
g._hello = SimpleNamespace()
g._hello.user_id = 2
The data in ``g`` lasts for an application context. An application context is
active during a request, CLI command, or ``with app.app_context()`` block. If
you're storing something that should be closed, use
:meth:`~flask.Flask.teardown_appcontext` to ensure that it gets closed when the
app context ends. If it should only be valid during a request, or would not be
used in the CLI outside a request, use :meth:`~flask.Flask.teardown_request`.
The data in ``g`` lasts for an application context. An application
context is active when a request context is, or when a CLI command is
run. If you're storing something that should be closed, use
:meth:`~flask.Flask.teardown_appcontext` to ensure that it gets closed
when the application context ends. If it should only be valid during a
request, or would not be used in the CLI outside a request, use
:meth:`~flask.Flask.teardown_request`.
Views and Models
@ -293,13 +294,10 @@ ecosystem remain consistent and compatible.
indicate minimum compatibility support. For example,
``sqlalchemy>=1.4``.
9. Indicate the versions of Python supported using ``python_requires=">=version"``.
Flask and Pallets policy is to support all Python versions that are not
within six months of end of life (EOL). See Python's `EOL calendar`_ for
timing.
Flask itself supports Python >=3.8 as of April 2023, but this will update over time.
.. _PyPI: https://pypi.org/search/?c=Framework+%3A%3A+Flask
.. _Discord Chat: https://discord.gg/pallets
.. _GitHub Discussions: https://github.com/pallets/flask/discussions
.. _Official Pallets Themes: https://pypi.org/project/Pallets-Sphinx-Themes/
.. _Pallets-Eco: https://github.com/pallets-eco
.. _EOL calendar: https://devguide.python.org/versions/

View file

@ -1,125 +0,0 @@
Async with Gevent
=================
`Gevent`_ patches Python's standard library to run within special async workers
called `greenlets`_. Gevent has existed since long before Python's native
asyncio was available, and Flask has always worked with it.
.. _gevent: https://www.gevent.org
.. _greenlets: https://greenlet.readthedocs.io
Gevent is a reliable way to handle numerous, long lived, concurrent connections,
and to achieve similar capabilities to ASGI and asyncio. This works without
needing to write ``async def`` or ``await`` anywhere, but relies on gevent and
greenlet's low level manipulation of the Python interpreter.
Deciding whether you should use gevent with Flask, or `Quart`_, or something
else, is ultimately up to understanding the specific needs of your project.
.. _quart: https://quart.palletsprojects.com
Enabling gevent
---------------
You need to apply gevent's patching as early as possible in your code. This
enables gevent's underlying event loop and converts many Python internals to run
inside it. Add the following at the top of your project's module or top
``__init__.py``:
.. code-block:: python
import gevent.monkey
gevent.monkey.patch_all()
When deploying in production, use :doc:`/deploying/gunicorn` or
:doc:`/deploying/uwsgi` with a gevent worker, as described on those pages.
To run concurrent tasks within your own code, such as views, use
|gevent.spawn|_:
.. |gevent.spawn| replace:: ``gevent.spawn()``
.. _gevent.spawn: https://www.gevent.org/api/gevent.html#gevent.spawn
.. code-block:: python
@app.post("/send")
def send_email():
gevent.spawn(email.send, to="example@example.example", text="example")
return "Email is being sent."
If you need to access :data:`request` or other Flask context globals within the
spawned function, decorate the function with :func:`.stream_with_context` or
:func:`.copy_current_request_context`. Prefer passing the exact data you need
when spawning the function, rather than using the decorators.
.. note::
When using gevent, greenlet>=1.0 is required. When using PyPy, PyPy>=7.3.7
is required.
.. _gevent-asyncio:
Combining with ``async``/``await``
----------------------------------
Gevent's patching does not interact well with Flask's built-in asyncio support.
If you want to use Gevent and asyncio in the same app, you'll need to override
:meth:`flask.Flask.async_to_sync` to run async functions inside gevent.
.. code-block:: python
import gevent.monkey
gevent.monkey.patch_all()
import asyncio
from flask import Flask, request
loop = asyncio.EventLoop()
gevent.spawn(loop.run_forever)
class GeventFlask(Flask):
def async_to_sync(self, func):
def run(*args, **kwargs):
coro = func(*args, **kwargs)
future = asyncio.run_coroutine_threadsafe(coro, loop)
return future.result()
return run
app = GeventFlask(__name__)
@app.get("/")
async def greet():
await asyncio.sleep(1)
return f"Hello, {request.args.get("name", "World")}!"
This starts an asyncio event loop in a gevent worker. Async functions are
scheduled on that event loop. This may still have limitations, and may need to
be modified further when using other asyncio implementations.
libuv
~~~~~
`libuv`_ is another event loop implementation that `gevent supports`_. There's
also a project called `uvloop`_ that enables libuv in asyncio. If you want to
use libuv, use gevent's support, not uvloop. It may be possible to further
modify the ``async_to_sync`` code from the previous section to work with uvloop,
but that's not currently known.
.. _libuv: https://libuv.org/
.. _gevent supports: https://www.gevent.org/loop_impls.html
.. _uvloop: https://uvloop.readthedocs.io/
To enable gevent's libuv support, add the following at the *very* top of your
code, before ``gevent.monkey.patch_all()``:
.. code-block:: python
import gevent
gevent.config.loop = "libuv"
import gevent.monkey
gevent.monkey.patch_all()

View file

@ -3,15 +3,10 @@
Welcome to Flask
================
.. image:: _static/flask-name.svg
.. image:: _static/flask-horizontal.png
:align: center
:height: 200px
Welcome to Flask's documentation. 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.
Get started with :doc:`installation`
Welcome to Flask's documentation. Get started with :doc:`installation`
and then get an overview with the :doc:`quickstart`. There is also a
more detailed :doc:`tutorial/index` that shows how to create a small but
complete application with Flask. Common patterns are described in the
@ -52,15 +47,15 @@ community-maintained extensions to add even more functionality.
views
lifecycle
appcontext
reqcontext
blueprints
extensions
cli
server
shell
patterns/index
web-security
security
deploying/index
gevent
async-await

View file

@ -5,7 +5,7 @@ Installation
Python Version
--------------
We recommend using the latest version of Python. Flask supports Python 3.10 and newer.
We recommend using the latest version of Python. Flask supports Python 3.8 and newer.
Dependencies
@ -51,8 +51,9 @@ use them if you install them.
greenlet
~~~~~~~~
You may choose to use :doc:`/gevent` with your application. In this case,
greenlet>=1.0 is required. When using PyPy, PyPy>=7.3.7 is required.
You may choose to use gevent or eventlet with your application. In this
case, greenlet>=1.0 is required. When using PyPy, PyPy>=7.3.7 is
required.
These are not minimum supported versions, they only indicate the first
versions that added necessary features. You should use the latest

View file

@ -1,5 +1,4 @@
BSD-3-Clause License
====================
.. literalinclude:: ../LICENSE.txt
:language: text
.. include:: ../LICENSE.rst

View file

@ -117,12 +117,15 @@ the view function, and pass the return value back to the server. But there are m
parts that you can use to customize its behavior.
#. WSGI server calls the Flask object, which calls :meth:`.Flask.wsgi_app`.
#. An :class:`.AppContext` object is created. This converts the WSGI ``environ``
dict into a :class:`.Request` object.
#. The :doc:`app context <appcontext>` is pushed, which makes
:data:`.current_app`, :data:`.g`, :data:`.request`, and :data:`.session`
available.
#. A :class:`.RequestContext` object is created. This converts the WSGI ``environ``
dict into a :class:`.Request` object. It also creates an :class:`AppContext` object.
#. The :doc:`app context <appcontext>` is pushed, which makes :data:`.current_app` and
:data:`.g` available.
#. The :data:`.appcontext_pushed` signal is sent.
#. The :doc:`request context <reqcontext>` is pushed, which makes :attr:`.request` and
:class:`.session` available.
#. The session is opened, loading any existing session data using the app's
:attr:`~.Flask.session_interface`, an instance of :class:`.SessionInterface`.
#. The URL is matched against the URL rules registered with the :meth:`~.Flask.route`
decorator during application setup. If there is no match, the error - usually a 404,
405, or redirect - is stored to be handled later.
@ -138,8 +141,7 @@ parts that you can use to customize its behavior.
called to handle the error and return a response.
#. Whatever returned a response value - a before request function, the view, or an
error handler, that value is converted to a :class:`.Response` object.
#. Any :func:`~.after_this_request` decorated functions are called, which can modify
the response object. They are then cleared.
#. Any :func:`~.after_this_request` decorated functions are called, then cleared.
#. Any :meth:`~.Flask.after_request` decorated functions are called, which can modify
the response object.
#. The session is saved, persisting any modified session data using the app's
@ -152,19 +154,14 @@ parts that you can use to customize its behavior.
#. The response object's status, headers, and body are returned to the WSGI server.
#. Any :meth:`~.Flask.teardown_request` decorated functions are called.
#. The :data:`.request_tearing_down` signal is sent.
#. The request context is popped, :attr:`.request` and :class:`.session` are no longer
available.
#. Any :meth:`~.Flask.teardown_appcontext` decorated functions are called.
#. The :data:`.appcontext_tearing_down` signal is sent.
#. The app context is popped, :data:`.current_app`, :data:`.g`, :data:`.request`,
and :data:`.session` are no longer available.
#. The app context is popped, :data:`.current_app` and :data:`.g` are no longer
available.
#. The :data:`.appcontext_popped` signal is sent.
When executing a CLI command or plain app context without request data, the same
order of steps is followed, omitting the steps that refer to the request.
A :class:`Blueprint` can add handlers for these events that are specific to the
blueprint. The handlers for a blueprint will run if the blueprint
owns the route that matches the request.
There are even more decorators and customization points than this, but that aren't part
of every request lifecycle. They're more specific to certain things you might use during
a request, such as templates, building URLs, or handling JSON data. See the rest of this

View file

@ -159,7 +159,7 @@ Depending on your project, it may be more useful to configure each logger you
care about separately, instead of configuring only the root logger. ::
for logger in (
logging.getLogger(app.name),
app.logger,
logging.getLogger('sqlalchemy'),
logging.getLogger('other_package'),
):

View file

@ -99,9 +99,9 @@ to the factory like this:
.. code-block:: text
$ flask --app 'hello:create_app(local_auth=True)' run
$ flask --app hello:create_app(local_auth=True) run
Then the ``create_app`` factory in ``hello`` is called with the keyword
Then the ``create_app`` factory in ``myapp`` is called with the keyword
argument ``local_auth=True``. See :doc:`/cli` for more detail.
Factory Improvements

View file

@ -24,11 +24,8 @@ the root path of the domain you either need to configure the web server to
serve the icon at the root or if you can't do that you're out of luck. If
however your application is the root you can simply route a redirect::
app.add_url_rule(
"/favicon.ico",
endpoint="favicon",
redirect_to=url_for("static", filename="favicon.ico"),
)
app.add_url_rule('/favicon.ico',
redirect_to=url_for('static', filename='favicon.ico'))
If you want to save the extra redirect request you can also write a view
using :func:`~flask.send_from_directory`::

View file

@ -136,8 +136,7 @@ In general, prefer sending request data as form data, as would be used
when submitting an HTML form. JSON can represent more complex data, but
unless you need that it's better to stick with the simpler format. When
sending JSON data, the ``Content-Type: application/json`` header must be
sent as well, otherwise Flask will return a 415 Unsupported Media Type
error.
sent as well, otherwise Flask will return a 400 error.
.. code-block:: javascript
@ -245,9 +244,8 @@ Receiving JSON in Views
Use the :attr:`~flask.Request.json` property of the
:data:`~flask.request` object to decode the request's body as JSON. If
the body is not valid JSON, a 400 Bad Request error will be raised. If
the ``Content-Type`` header is not set to ``application/json``, a 415
Unsupported Media Type error will be raised.
the body is not valid JSON, or the ``Content-Type`` header is not set to
``application/json``, a 400 Bad Request error will be raised.
.. code-block:: python

View file

@ -10,7 +10,8 @@ A running MongoDB server and `Flask-MongoEngine`_ are required. ::
pip install flask-mongoengine
.. _MongoEngine: http://mongoengine.org
.. _Flask-MongoEngine: https://docs.mongoengine.org/projects/flask-mongoengine/en/latest/
.. _Flask-MongoEngine: https://flask-mongoengine.readthedocs.io
Configuration
-------------
@ -79,7 +80,7 @@ Queries
Use the class ``objects`` attribute to make queries. A keyword argument
looks for an equal value on the field. ::
bttf = Movie.objects(title="Back To The Future").get_or_404()
bttf = Movies.objects(title="Back To The Future").get_or_404()
Query operators may be used by concatenating them with the field name
using a double-underscore. ``objects``, and queries returned by

View file

@ -131,8 +131,9 @@ Here is an example :file:`database.py` module for your application::
def init_db():
metadata.create_all(bind=engine)
As in the declarative approach, you need to close the session after each app
context. Put this into your application module::
As in the declarative approach, you need to close the session after
each request or application context shutdown. Put this into your
application module::
from yourapplication.database import db_session

View file

@ -1,9 +1,9 @@
Using SQLite 3 with Flask
=========================
You can implement a few functions to work with a SQLite connection during a
request context. The connection is created the first time it's accessed,
reused on subsequent access, until it is closed when the request context ends.
In Flask you can easily implement the opening of database connections on
demand and closing them when the context dies (usually at the end of the
request).
Here is a simple example of how you can use SQLite 3 with Flask::

View file

@ -8,21 +8,6 @@ roundtrip to the filesystem?
The answer is by using generators and direct responses.
HTTP Response Behavior
----------------------
**Headers cannot be changed after the streaming response starts.**
When using streaming, it's important to be aware of the order than an HTTP
response is sent. All headers must be sent first, then the body. More headers
cannot be sent after the body has begun. Therefore, you must make sure all
headers are set before starting the response, outside the generator.
In particular, if the generator will access ``session``, be sure to do so in the
view as well so that the ``Vary: cookie`` header will be set. Do not modify the
session in the generator, as the ``Set-Cookie`` header will already be sent.
Basic Usage
-----------
@ -44,7 +29,7 @@ debug environments with profilers and other things you might have enabled.
Streaming from Templates
------------------------
The Jinja template engine supports rendering a template piece by
The Jinja2 template engine supports rendering a template piece by
piece, returning an iterator of strings. Flask provides the
:func:`~flask.stream_template` and :func:`~flask.stream_template_string`
functions to make this easier to use.
@ -64,13 +49,13 @@ the template.
Streaming with Context
----------------------
The :data:`.request` proxy will not be active while the generator is
running, because the app has already returned control to the WSGI server at that
point. If you try to access ``request``, you'll get a ``RuntimeError``.
The :data:`~flask.request` will not be active while the generator is
running, because the view has already returned at that point. If you try
to access ``request``, you'll get a ``RuntimeError``.
If your generator function relies on data in ``request``, use the
:func:`.stream_with_context` wrapper. This will keep the request context active
during the generator.
:func:`~flask.stream_with_context` wrapper. This will keep the request
context active during the generator.
.. code-block:: python

View file

@ -99,7 +99,7 @@ WTForm's field function, which renders the field for us. The keyword
arguments will be inserted as HTML attributes. So, for example, you can
call ``render_field(form.username, class='username')`` to add a class to
the input element. Note that WTForms returns standard Python strings,
so we have to tell Jinja that this data is already HTML-escaped with
so we have to tell Jinja2 that this data is already HTML-escaped with
the ``|safe`` filter.
Here is the :file:`register.html` template for the function we used above, which

View file

@ -139,16 +139,18 @@ how you're using untrusted data.
.. code-block:: python
from flask import request
from markupsafe import escape
@app.route("/hello")
def hello():
name = request.args.get("name", "Flask")
@app.route("/<name>")
def hello(name):
return f"Hello, {escape(name)}!"
If a user submits ``/hello?name=<script>alert("bad")</script>``, escaping causes
it to be rendered as text, rather than running the script in the user's browser.
If a user managed to submit the name ``<script>alert("bad")</script>``,
escaping causes it to be rendered as text, rather than running the
script in the user's browser.
``<name>`` in the route captures a value from the URL and passes it to
the view function. These variable rules are explained below.
Routing
@ -258,7 +260,7 @@ Why would you want to build URLs using the URL reversing function
For example, here we use the :meth:`~flask.Flask.test_request_context` method
to try out :func:`~flask.url_for`. :meth:`~flask.Flask.test_request_context`
tells Flask to behave as though it's handling a request even while we use a
Python shell. See :doc:`/appcontext`.
Python shell. See :ref:`context-locals`.
.. code-block:: python
@ -352,7 +354,7 @@ Rendering Templates
Generating HTML from within Python is not fun, and actually pretty
cumbersome because you have to do the HTML escaping on your own to keep
the application secure. Because of that Flask configures the `Jinja
the application secure. Because of that Flask configures the `Jinja2
<https://palletsprojects.com/p/jinja/>`_ template engine for you automatically.
Templates can be used to generate any type of text file. For web applications, you'll
@ -373,7 +375,7 @@ Here's a simple example of how to render a template::
@app.route('/hello/')
@app.route('/hello/<name>')
def hello(name=None):
return render_template('hello.html', person=name)
return render_template('hello.html', name=name)
Flask will look for templates in the :file:`templates` folder. So if your
application is a module, this folder is next to that module, if it's a
@ -392,8 +394,8 @@ package it's actually inside your package:
/templates
/hello.html
For templates you can use the full power of Jinja templates. Head over
to the official `Jinja Template Documentation
For templates you can use the full power of Jinja2 templates. Head over
to the official `Jinja2 Template Documentation
<https://jinja.palletsprojects.com/templates/>`_ for more information.
Here is an example template:
@ -402,8 +404,8 @@ Here is an example template:
<!doctype html>
<title>Hello from Flask</title>
{% if person %}
<h1>Hello {{ person }}!</h1>
{% if name %}
<h1>Hello {{ name }}!</h1>
{% else %}
<h1>Hello, World!</h1>
{% endif %}
@ -417,7 +419,7 @@ know how that works, see :doc:`patterns/templateinheritance`. Basically
template inheritance makes it possible to keep certain elements on each
page (like header, navigation and footer).
Automatic escaping is enabled, so if ``person`` contains HTML it will be escaped
Automatic escaping is enabled, so if ``name`` contains HTML it will be escaped
automatically. If you can trust a variable and you know that it will be
safe HTML (for example because it came from a module that converts wiki
markup to HTML) you can mark it as safe by using the
@ -449,58 +451,105 @@ Here is a basic introduction to how the :class:`~markupsafe.Markup` class works:
Accessing Request Data
----------------------
For web applications it's crucial to react to the data a client sends to the
server. In Flask this information is provided by the global :data:`.request`
object, which is an instance of :class:`.Request`. This object has many
attributes and methods to work with the incoming request data, but here is a
broad overview. First it needs to be imported.
For web applications it's crucial to react to the data a client sends to
the server. In Flask this information is provided by the global
:class:`~flask.request` object. If you have some experience with Python
you might be wondering how that object can be global and how Flask
manages to still be threadsafe. The answer is context locals:
.. code-block:: python
.. _context-locals:
Context Locals
``````````````
.. admonition:: Insider Information
If you want to understand how that works and how you can implement
tests with context locals, read this section, otherwise just skip it.
Certain objects in Flask are global objects, but not of the usual kind.
These objects are actually proxies to objects that are local to a specific
context. What a mouthful. But that is actually quite easy to understand.
Imagine the context being the handling thread. A request comes in and the
web server decides to spawn a new thread (or something else, the
underlying object is capable of dealing with concurrency systems other
than threads). When Flask starts its internal request handling it
figures out that the current thread is the active context and binds the
current application and the WSGI environments to that context (thread).
It does that in an intelligent way so that one application can invoke another
application without breaking.
So what does this mean to you? Basically you can completely ignore that
this is the case unless you are doing something like unit testing. You
will notice that code which depends on a request object will suddenly break
because there is no request object. The solution is creating a request
object yourself and binding it to the context. The easiest solution for
unit testing is to use the :meth:`~flask.Flask.test_request_context`
context manager. In combination with the ``with`` statement it will bind a
test request so that you can interact with it. Here is an example::
from flask import request
If you have some experience with Python you might be wondering how that object
can be global when Flask handles multiple requests at a time. The answer is
that :data:`.request` is actually a proxy, pointing at whatever request is
currently being handled by a given worker, which is managed internally by Flask
and Python. See :doc:`/appcontext` for much more information.
with app.test_request_context('/hello', method='POST'):
# now you can do something with the request until the
# end of the with block, such as basic assertions:
assert request.path == '/hello'
assert request.method == 'POST'
The current request method is available in the :attr:`~.Request.method`
attribute. To access form data (data transmitted in a ``POST`` or ``PUT``
request), use the :attr:`~flask.Request.form` attribute, which behaves like a
dict.
The other possibility is passing a whole WSGI environment to the
:meth:`~flask.Flask.request_context` method::
.. code-block:: python
with app.request_context(environ):
assert request.method == 'POST'
@app.route("/login", methods=["GET", "POST"])
The Request Object
``````````````````
The request object is documented in the API section and we will not cover
it here in detail (see :class:`~flask.Request`). Here is a broad overview of
some of the most common operations. First of all you have to import it from
the ``flask`` module::
from flask import request
The current request method is available by using the
:attr:`~flask.Request.method` attribute. To access form data (data
transmitted in a ``POST`` or ``PUT`` request) you can use the
:attr:`~flask.Request.form` attribute. Here is a full example of the two
attributes mentioned above::
@app.route('/login', methods=['POST', 'GET'])
def login():
error = None
if request.method == "POST":
if valid_login(request.form["username"], request.form["password"]):
return store_login(request.form["username"])
if request.method == 'POST':
if valid_login(request.form['username'],
request.form['password']):
return log_the_user_in(request.form['username'])
else:
error = "Invalid username or password"
error = 'Invalid username/password'
# the code below is executed if the request method
# was GET or the credentials were invalid
return render_template('login.html', error=error)
# Executed if the request method was GET or the credentials were invalid.
return render_template("login.html", error=error)
What happens if the key does not exist in the ``form`` attribute? In that
case a special :exc:`KeyError` is raised. You can catch it like a
standard :exc:`KeyError` but if you don't do that, a HTTP 400 Bad Request
error page is shown instead. So for many situations you don't have to
deal with that problem.
If the key does not exist in ``form``, a special :exc:`KeyError` is raised. You
can catch it like a normal ``KeyError``, otherwise it will return a HTTP 400
Bad Request error page. You can also use the
:meth:`~werkzeug.datastructures.MultiDict.get` method to get a default
instead of an error.
To access parameters submitted in the URL (``?key=value``), use the
:attr:`~.Request.args` attribute. Key errors behave the same as ``form``,
returning a 400 response if not caught.
.. code-block:: python
To access parameters submitted in the URL (``?key=value``) you can use the
:attr:`~flask.Request.args` attribute::
searchword = request.args.get('key', '')
For a full list of methods and attributes of the request object, see the
:class:`~.Request` documentation.
We recommend accessing URL parameters with `get` or by catching the
:exc:`KeyError` because users might change the URL and presenting them a 400
bad request page in that case is not user friendly.
For a full list of methods and attributes of the request object, head over
to the :class:`~flask.Request` documentation.
File Uploads

View file

@ -1,6 +1,243 @@
:orphan:
.. currentmodule:: flask
The Request Context
===================
Obsolete, see :doc:`/appcontext` instead.
The request context keeps track of the request-level data during a
request. Rather than passing the request object to each function that
runs during a request, the :data:`request` and :data:`session` proxies
are accessed instead.
This is similar to :doc:`/appcontext`, which keeps track of the
application-level data independent of a request. A corresponding
application context is pushed when a request context is pushed.
Purpose of the Context
----------------------
When the :class:`Flask` application handles a request, it creates a
:class:`Request` object based on the environment it received from the
WSGI server. Because a *worker* (thread, process, or coroutine depending
on the server) handles only one request at a time, the request data can
be considered global to that worker during that request. Flask uses the
term *context local* for this.
Flask automatically *pushes* a request context when handling a request.
View functions, error handlers, and other functions that run during a
request will have access to the :data:`request` proxy, which points to
the request object for the current request.
Lifetime of the Context
-----------------------
When a Flask application begins handling a request, it pushes a request
context, which also pushes an :doc:`app context </appcontext>`. When the
request ends it pops the request context then the application context.
The context is unique to each thread (or other worker type).
:data:`request` cannot be passed to another thread, the other thread has
a different context space and will not know about the request the parent
thread was pointing to.
Context locals are implemented using Python's :mod:`contextvars` and
Werkzeug's :class:`~werkzeug.local.LocalProxy`. Python manages the
lifetime of context vars automatically, and local proxy wraps that
low-level interface to make the data easier to work with.
Manually Push a Context
-----------------------
If you try to access :data:`request`, or anything that uses it, outside
a request context, you'll get this error message:
.. code-block:: pytb
RuntimeError: Working outside of request context.
This typically means that you attempted to use functionality that
needed an active HTTP request. Consult the documentation on testing
for information about how to avoid this problem.
This should typically only happen when testing code that expects an
active request. One option is to use the
:meth:`test client <Flask.test_client>` to simulate a full request. Or
you can use :meth:`~Flask.test_request_context` in a ``with`` block, and
everything that runs in the block will have access to :data:`request`,
populated with your test data. ::
def generate_report(year):
format = request.args.get("format")
...
with app.test_request_context(
"/make_report/2017", query_string={"format": "short"}
):
generate_report()
If you see that error somewhere else in your code not related to
testing, it most likely indicates that you should move that code into a
view function.
For information on how to use the request context from the interactive
Python shell, see :doc:`/shell`.
How the Context Works
---------------------
The :meth:`Flask.wsgi_app` method is called to handle each request. It
manages the contexts during the request. Internally, the request and
application contexts work like stacks. When contexts are pushed, the
proxies that depend on them are available and point at information from
the top item.
When the request starts, a :class:`~ctx.RequestContext` is created and
pushed, which creates and pushes an :class:`~ctx.AppContext` first if
a context for that application is not already the top context. While
these contexts are pushed, the :data:`current_app`, :data:`g`,
:data:`request`, and :data:`session` proxies are available to the
original thread handling the request.
Other contexts may be pushed to change the proxies during a request.
While this is not a common pattern, it can be used in advanced
applications to, for example, do internal redirects or chain different
applications together.
After the request is dispatched and a response is generated and sent,
the request context is popped, which then pops the application context.
Immediately before they are popped, the :meth:`~Flask.teardown_request`
and :meth:`~Flask.teardown_appcontext` functions are executed. These
execute even if an unhandled exception occurred during dispatch.
.. _callbacks-and-errors:
Callbacks and Errors
--------------------
Flask dispatches a request in multiple stages which can affect the
request, response, and how errors are handled. The contexts are active
during all of these stages.
A :class:`Blueprint` can add handlers for these events that are specific
to the blueprint. The handlers for a blueprint will run if the blueprint
owns the route that matches the request.
#. Before each request, :meth:`~Flask.before_request` functions are
called. If one of these functions return a value, the other
functions are skipped. The return value is treated as the response
and the view function is not called.
#. If the :meth:`~Flask.before_request` functions did not return a
response, the view function for the matched route is called and
returns a response.
#. The return value of the view is converted into an actual response
object and passed to the :meth:`~Flask.after_request`
functions. Each function returns a modified or new response object.
#. After the response is returned, the contexts are popped, which calls
the :meth:`~Flask.teardown_request` and
:meth:`~Flask.teardown_appcontext` functions. These functions are
called even if an unhandled exception was raised at any point above.
If an exception is raised before the teardown functions, Flask tries to
match it with an :meth:`~Flask.errorhandler` function to handle the
exception and return a response. If no error handler is found, or the
handler itself raises an exception, Flask returns a generic
``500 Internal Server Error`` response. The teardown functions are still
called, and are passed the exception object.
If debug mode is enabled, unhandled exceptions are not converted to a
``500`` response and instead are propagated to the WSGI server. This
allows the development server to present the interactive debugger with
the traceback.
Teardown Callbacks
~~~~~~~~~~~~~~~~~~
The teardown callbacks are independent of the request dispatch, and are
instead called by the contexts when they are popped. The functions are
called even if there is an unhandled exception during dispatch, and for
manually pushed contexts. This means there is no guarantee that any
other parts of the request dispatch have run first. Be sure to write
these functions in a way that does not depend on other callbacks and
will not fail.
During testing, it can be useful to defer popping the contexts after the
request ends, so that their data can be accessed in the test function.
Use the :meth:`~Flask.test_client` as a ``with`` block to preserve the
contexts until the ``with`` block exits.
.. code-block:: python
from flask import Flask, request
app = Flask(__name__)
@app.route('/')
def hello():
print('during view')
return 'Hello, World!'
@app.teardown_request
def show_teardown(exception):
print('after with block')
with app.test_request_context():
print('during with block')
# teardown functions are called after the context with block exits
with app.test_client() as client:
client.get('/')
# the contexts are not popped even though the request ended
print(request.path)
# the contexts are popped and teardown functions are called after
# the client with block exits
Signals
~~~~~~~
The following signals are sent:
#. :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.
.. _notes-on-proxies:
Notes On Proxies
----------------
Some of the objects provided by Flask are proxies to other objects. The
proxies are accessed in the same way for each worker thread, but
point to the unique object bound to each worker behind the scenes as
described on this page.
Most of the time you don't have to care about that, but there are some
exceptions where it is good to know that this object is actually a proxy:
- The proxy objects cannot fake their type as the actual object types.
If you want to perform instance checks, you have to do that on the
object being proxied.
- The reference to the proxied object is needed in some situations,
such as sending :doc:`signals` or passing data to a background
thread.
If you need to access the underlying object that is proxied, use the
:meth:`~werkzeug.local.LocalProxy._get_current_object` method::
app = current_app._get_current_object()
my_signal.send(app)

View file

@ -1,43 +1,9 @@
Security Considerations
=======================
Web applications face many types of potential security problems, and it can be
hard to get everything right, or even to know what "right" is in general. Flask
tries to solve a few of these things by default, but there are other parts you
may have to take care of yourself. Many of these solutions are tradeoffs, and
will depend on each application's specific needs and threat model. Many hosting
platforms may take care of certain types of problems without the need for the
Flask application to handle them.
Resource Use
------------
A common category of attacks is "Denial of Service" (DoS or DDoS). This is a
very broad category, and different variants target different layers in a
deployed application. In general, something is done to increase how much
processing time or memory is used to handle each request, to the point where
there are not enough resources to handle legitimate requests.
Flask provides a few configuration options to handle resource use. They can
also be set on individual requests to customize only that request. The
documentation for each goes into more detail.
- :data:`MAX_CONTENT_LENGTH` or :attr:`.Request.max_content_length` controls
how much data will be read from a request. It is not set by default,
although it will still block truly unlimited streams unless the WSGI server
indicates support.
- :data:`MAX_FORM_MEMORY_SIZE` or :attr:`.Request.max_form_memory_size`
controls how large any non-file ``multipart/form-data`` field can be. It is
set to 500kB by default.
- :data:`MAX_FORM_PARTS` or :attr:`.Request.max_form_parts` controls how many
``multipart/form-data`` fields can be parsed. It is set to 1000 by default.
Combined with the default `max_form_memory_size`, this means that a form
will occupy at most 500MB of memory.
Regardless of these settings, you should also review what settings are available
from your operating system, container deployment (Docker etc), WSGI server, HTTP
server, and hosting platform. They typically have ways to set process resource
limits, timeouts, and other checks regardless of how Flask is configured.
Web applications usually face all kinds of security problems and it's very
hard to get everything right. Flask tries to solve a few of these things
for you, but there are a couple more you have to take care of yourself.
.. _security-xss:
@ -51,12 +17,12 @@ tags. For more information on that have a look at the Wikipedia article
on `Cross-Site Scripting
<https://en.wikipedia.org/wiki/Cross-site_scripting>`_.
Flask configures Jinja to automatically escape all values unless
Flask configures Jinja2 to automatically escape all values unless
explicitly told otherwise. This should rule out all XSS problems caused
in templates, but there are still other places where you have to be
careful:
- generating HTML without the help of Jinja
- generating HTML without the help of Jinja2
- calling :class:`~markupsafe.Markup` on data submitted by users
- sending out HTML from uploaded files, never do that, use the
``Content-Disposition: attachment`` header to prevent that problem.
@ -65,7 +31,7 @@ careful:
trick a browser to execute HTML.
Another thing that is very important are unquoted attributes. While
Jinja can protect you from XSS issues by escaping HTML, there is one
Jinja2 can protect you from XSS issues by escaping HTML, there is one
thing it cannot protect you from: XSS by attribute injection. To counter
this possible attack vector, be sure to always quote your attributes with
either double or single quotes when using Jinja expressions in them:
@ -158,7 +124,7 @@ recommend reviewing each of the headers below for use in your application.
The `Flask-Talisman`_ extension can be used to manage HTTPS and the security
headers for you.
.. _Flask-Talisman: https://github.com/wntrblm/flask-talisman
.. _Flask-Talisman: https://github.com/GoogleCloudPlatform/flask-talisman
HTTP Strict Transport Security (HSTS)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -269,25 +235,17 @@ values (or any values that need secure signatures).
.. _samesite_support: https://caniuse.com/#feat=same-site-cookie-attribute
Host Header Validation
----------------------
HTTP Public Key Pinning (HPKP)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The ``Host`` header is used by the client to indicate what host name the request
was made to. This is used, for example, by ``url_for(..., _external=True)`` to
generate full URLs, for use in email or other messages outside the browser
window.
This tells the browser to authenticate with the server using only the specific
certificate key to prevent MITM attacks.
By default the app doesn't know what host(s) it is allowed to be accessed
through, and assumes any host is valid. Although browsers do not allow setting
the ``Host`` header, requests made by attackers in other scenarios could set
the ``Host`` header to a value they want.
.. warning::
Be careful when enabling this, as it is very difficult to undo if you set up
or upgrade your key incorrectly.
When deploying your application, set :data:`TRUSTED_HOSTS` to restrict what
values the ``Host`` header may be.
The ``Host`` header may be modified by proxies in between the client and your
application. See :doc:`deploying/proxy_fix` to tell your app which proxy values
to trust.
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Public_Key_Pinning
Copy/Paste to Terminal

View file

@ -77,7 +77,7 @@ following example shows that process id 6847 is using port 5000.
macOS Monterey and later automatically starts a service that uses port
5000. You can choose to disable this service instead of using a different port by
searching for "AirPlay Receiver" in System Settings and toggling it off.
searching for "AirPlay Receiver" in System Preferences and toggling it off.
Deferred Errors on Reload

View file

@ -1,37 +1,56 @@
Working with the Shell
======================
One of the reasons everybody loves Python is the interactive shell. It allows
you to play around with code in real time and immediately get results back.
Flask provides the ``flask shell`` CLI command to start an interactive Python
shell with some setup done to make working with the Flask app easier.
.. versionadded:: 0.3
.. code-block:: text
One of the reasons everybody loves Python is the interactive shell. It
basically allows you to execute Python commands in real time and
immediately get results back. Flask itself does not come with an
interactive shell, because it does not require any specific setup upfront,
just import your application and start playing around.
$ flask shell
There are however some handy helpers to make playing around in the shell a
more pleasant experience. The main issue with interactive console
sessions is that you're not triggering a request like a browser does which
means that :data:`~flask.g`, :data:`~flask.request` and others are not
available. But the code you want to test might depend on them, so what
can you do?
This is where some helper functions come in handy. Keep in mind however
that these functions are not only there for interactive shell usage, but
also for unit testing and other situations that require a faked request
context.
Generally it's recommended that you read :doc:`reqcontext` first.
Command Line Interface
----------------------
Starting with Flask 0.11 the recommended way to work with the shell is the
``flask shell`` command which does a lot of this automatically for you.
For instance the shell is automatically initialized with a loaded
application context.
For more information see :doc:`/cli`.
Creating a Request Context
--------------------------
``flask shell`` pushes an app context automatically, so :data:`.current_app` and
:data:`.g` are already available. However, there is no HTTP request being
handled in the shell, so :data:`.request` and :data:`.session` are not yet
available.
The easiest way to create a proper request context from the shell is by
using the :attr:`~flask.Flask.test_request_context` method which creates
us a :class:`~flask.ctx.RequestContext`:
>>> ctx = app.test_request_context()
Normally you would use the ``with`` statement to make this context active, but
in the shell it's easier to call :meth:`~.RequestContext.push` and
:meth:`~.RequestContext.pop` manually:
Normally you would use the ``with`` statement to make this request object
active, but in the shell it's easier to use the
:meth:`~flask.ctx.RequestContext.push` and
:meth:`~flask.ctx.RequestContext.pop` methods by hand:
>>> ctx.push()
From that point onwards you can work with the request object until you call
``pop``:
From that point onwards you can work with the request object until you
call `pop`:
>>> ctx.pop()

View file

@ -144,10 +144,11 @@ function, you can pass ``current_app._get_current_object()`` as sender.
Signals and Flask's Request Context
-----------------------------------
Context-local proxies are available between :data:`~flask.request_started` and
:data:`~flask.request_finished`, so you can rely on :class:`flask.g` and others
as needed. Note the limitations described in :ref:`signals-sending` and the
:data:`~flask.request_tearing_down` signal.
Signals fully support :doc:`reqcontext` when receiving signals.
Context-local variables are consistently available between
:data:`~flask.request_started` and :data:`~flask.request_finished`, so you can
rely on :class:`flask.g` and others as needed. Note the limitations described
in :ref:`signals-sending` and the :data:`~flask.request_tearing_down` signal.
Decorator Based Signal Subscriptions

View file

@ -1,21 +1,21 @@
Templates
=========
Flask leverages Jinja as its template engine. You are obviously free to use
a different template engine, but you still have to install Jinja to run
Flask leverages Jinja2 as its template engine. You are obviously free to use
a different template engine, but you still have to install Jinja2 to run
Flask itself. This requirement is necessary to enable rich extensions.
An extension can depend on Jinja being present.
An extension can depend on Jinja2 being present.
This section only gives a very quick introduction into how Jinja
This section only gives a very quick introduction into how Jinja2
is integrated into Flask. If you want information on the template
engine's syntax itself, head over to the official `Jinja Template
engine's syntax itself, head over to the official `Jinja2 Template
Documentation <https://jinja.palletsprojects.com/templates/>`_ for
more information.
Jinja Setup
-----------
Unless customized, Jinja is configured by Flask as follows:
Unless customized, Jinja2 is configured by Flask as follows:
- autoescaping is enabled for all templates ending in ``.html``,
``.htm``, ``.xml``, ``.xhtml``, as well as ``.svg`` when using
@ -25,13 +25,13 @@ Unless customized, Jinja is configured by Flask as follows:
- a template has the ability to opt in/out autoescaping with the
``{% autoescape %}`` tag.
- Flask inserts a couple of global functions and helpers into the
Jinja context, additionally to the values that are present by
Jinja2 context, additionally to the values that are present by
default.
Standard Context
----------------
The following global variables are available within Jinja templates
The following global variables are available within Jinja2 templates
by default:
.. data:: config
@ -137,58 +137,32 @@ using in this block.
.. _registering-filters:
Registering Filters, Tests, and Globals
---------------------------------------
Registering Filters
-------------------
The Flask app and blueprints provide decorators and methods to register your own
filters, tests, and global functions for use in Jinja templates. They all follow
the same pattern, so the following examples only discuss filters.
If you want to register your own filters in Jinja2 you have two ways to do
that. You can either put them by hand into the
:attr:`~flask.Flask.jinja_env` of the application or use the
:meth:`~flask.Flask.template_filter` decorator.
Decorate a function with :meth:`~.Flask.template_filter` to register it as a
template filter.
The two following examples work the same and both reverse an object::
.. code-block:: python
@app.template_filter('reverse')
def reverse_filter(s):
return s[::-1]
@app.template_filter
def reverse(s):
return reversed(s)
def reverse_filter(s):
return s[::-1]
app.jinja_env.filters['reverse'] = reverse_filter
.. code-block:: jinja
In case of the decorator the argument is optional if you want to use the
function name as name of the filter. Once registered, you can use the filter
in your templates in the same way as Jinja2's builtin filters, for example if
you have a Python list in context called `mylist`::
{% for item in data | reverse %}
{% for x in mylist | reverse %}
{% endfor %}
By default it will use the name of the function as the name of the filter, but
that can be changed by passing a name to the decorator.
.. code-block:: python
@app.template_filter("reverse")
def reverse_filter(s):
return reversed(s)
A filter can be registered separately using :meth:`~.Flask.add_template_filter`.
The name is optional and will use the function name if not given.
.. code-block:: python
def reverse_filter(s):
return reversed(s)
app.add_template_filter(reverse_filter, "reverse")
For template tests, use the :meth:`~.Flask.template_test` decorator or
:meth:`~.Flask.add_template_test` method. For template global functions, use the
:meth:`~.Flask.template_global` decorator or :meth:`~.Flask.add_template_global`
method.
The same methods also exist on :class:`.Blueprint`, prefixed with ``app_`` to
indicate that the registered functions will be available to all templates, not
only when rendering from within the blueprint.
The Jinja environment is also available as :attr:`~.Flask.jinja_env`. It may be
modified directly, as you would when using Jinja outside Flask.
Context Processors
------------------
@ -237,7 +211,7 @@ strings. This can be used for streaming HTML in chunks to speed up
initial page load, or to save memory when rendering a very large
template.
The Jinja template engine supports rendering a template piece
The Jinja2 template engine supports rendering a template piece
by piece, returning an iterator of strings. Flask provides the
:func:`~flask.stream_template` and :func:`~flask.stream_template_string`
functions to make this easier to use.
@ -251,11 +225,5 @@ functions to make this easier to use.
return stream_template("timeline.html")
These functions automatically apply the
:func:`~flask.stream_with_context` wrapper if a request is active, so that
:data:`.request`, :data:`.session`, and :data:`.g` remain available in the
template.
More headers cannot be sent after the body has begun. Therefore, you must
make sure all headers are set before starting the response. In particular,
if the template will access ``session``, be sure to do so in the view as
well so that the ``Vary: cookie`` header will be set.
:func:`~flask.stream_with_context` wrapper if a request is active, so
that it remains available in the template.

View file

@ -192,7 +192,7 @@ which records the request that produced that response.
.. code-block:: python
def test_logout_redirect(client):
response = client.get("/logout", follow_redirects=True)
response = client.get("/logout")
# Check that there was one redirect response.
assert len(response.history) == 1
# Check that the second request was to the index page.
@ -275,10 +275,11 @@ command from the command line.
Tests that depend on an Active Context
--------------------------------------
You may have functions that are called from views or commands, that expect an
active :doc:`app context </appcontext>` because they access :data:`.request`,
:data:`.session`, :data:`.g`, or :data:`.current_app`. Rather than testing them by
making a request or invoking the command, you can create and activate a context
You may have functions that are called from views or commands, that
expect an active :doc:`application context </appcontext>` or
:doc:`request context </reqcontext>` because they access ``request``,
``session``, or ``current_app``. Rather than testing them by making a
request or invoking the command, you can create and activate a context
directly.
Use ``with app.app_context()`` to push an application context. For

View file

@ -305,7 +305,7 @@ The pattern ``{{ request.form['title'] or post['title'] }}`` is used to
choose what data appears in the form. When the form hasn't been
submitted, the original ``post`` data appears, but if invalid form data
was posted you want to display that so the user can fix the error, so
``request.form`` is used instead. :data:`.request` is another variable
``request.form`` is used instead. :data:`request` is another variable
that's automatically available in templates.

View file

@ -37,7 +37,6 @@ response is sent.
:caption: ``flaskr/db.py``
import sqlite3
from datetime import datetime
import click
from flask import current_app, g
@ -60,17 +59,17 @@ response is sent.
if db is not None:
db.close()
:data:`.g` is a special object that is unique for each request. It is
:data:`g` is a special object that is unique for each request. It is
used to store data that might be accessed by multiple functions during
the request. The connection is stored and reused instead of creating a
new connection if ``get_db`` is called a second time in the same
request.
:data:`.current_app` is another special object that points to the Flask
:data:`current_app` is another special object that points to the Flask
application handling the request. Since you used an application factory,
there is no application object when writing the rest of your code.
``get_db`` will be called when the application has been created and is
handling a request, so :data:`.current_app` can be used.
handling a request, so :data:`current_app` can be used.
:func:`sqlite3.connect` establishes a connection to the file pointed at
by the ``DATABASE`` configuration key. This file doesn't have to exist
@ -133,11 +132,6 @@ Add the Python functions that will run these SQL commands to the
init_db()
click.echo('Initialized the database.')
sqlite3.register_converter(
"timestamp", lambda v: datetime.fromisoformat(v.decode())
)
:meth:`open_resource() <Flask.open_resource>` opens a file relative to
the ``flaskr`` package, which is useful since you won't necessarily know
where that location is when deploying the application later. ``get_db``
@ -148,10 +142,6 @@ read from the file.
that calls the ``init_db`` function and shows a success message to the
user. You can read :doc:`/cli` to learn more about writing commands.
The call to :func:`sqlite3.register_converter` tells Python how to
interpret timestamp values in the database. We convert the value to a
:class:`datetime.datetime`.
Register with the Application
-----------------------------

View file

@ -56,7 +56,10 @@ directory should be treated as a package.
app.config.from_mapping(test_config)
# ensure the instance folder exists
os.makedirs(app.instance_path, exist_ok=True)
try:
os.makedirs(app.instance_path)
except OSError:
pass
# a simple page that says hello
@app.route('/hello')

View file

@ -81,7 +81,8 @@ By the end, your project layout will look like this:
│ ├── test_auth.py
│ └── test_blog.py
├── .venv/
└── pyproject.toml
├── pyproject.toml
└── MANIFEST.in
If you're using version control, the following files that are generated
while running your project should be ignored. There may be other files
@ -102,4 +103,8 @@ write. For example, with git:
.coverage
htmlcov/
dist/
build/
*.egg-info/
Continue to :doc:`factory`.

View file

@ -71,7 +71,7 @@ specific sections.
{% block content %}{% endblock %}
</section>
:data:`.g` is automatically available in templates. Based on if
:data:`g` is automatically available in templates. Based on if
``g.user`` is set (from ``load_logged_in_user``), either the username
and a log out link are displayed, or links to register and log in
are displayed. :func:`url_for` is also automatically available, and is

View file

@ -311,7 +311,7 @@ input and error messages without writing the same code three times.
The tests for the ``login`` view are very similar to those for
``register``. Rather than testing the data in the database,
:data:`.session` should have ``user_id`` set after logging in.
:data:`session` should have ``user_id`` set after logging in.
.. code-block:: python
:caption: ``tests/test_auth.py``
@ -336,10 +336,10 @@ The tests for the ``login`` view are very similar to those for
assert message in response.data
Using ``client`` in a ``with`` block allows accessing context variables
such as :data:`.session` after the response is returned. Normally,
such as :data:`session` after the response is returned. Normally,
accessing ``session`` outside of a request would raise an error.
Testing ``logout`` is the opposite of ``login``. :data:`.session` should
Testing ``logout`` is the opposite of ``login``. :data:`session` should
not contain ``user_id`` after logging out.
.. code-block:: python

View file

@ -208,13 +208,13 @@ There are a few differences from the ``register`` view:
password in the same way as the stored hash and securely compares
them. If they match, the password is valid.
#. :data:`.session` is a :class:`dict` that stores data across requests.
#. :data:`session` is a :class:`dict` that stores data across requests.
When validation succeeds, the user's ``id`` is stored in a new
session. The data is stored in a *cookie* that is sent to the
browser, and the browser then sends it back with subsequent requests.
Flask securely *signs* the data so that it can't be tampered with.
Now that the user's ``id`` is stored in the :data:`.session`, it will be
Now that the user's ``id`` is stored in the :data:`session`, it will be
available on subsequent requests. At the beginning of each request, if
a user is logged in their information should be loaded and made
available to other views.
@ -236,7 +236,7 @@ available to other views.
:meth:`bp.before_app_request() <Blueprint.before_app_request>` registers
a function that runs before the view function, no matter what URL is
requested. ``load_logged_in_user`` checks if a user id is stored in the
:data:`.session` and gets that user's data from the database, storing it
:data:`session` and gets that user's data from the database, storing it
on :data:`g.user <g>`, which lasts for the length of the request. If
there is no user id, or if the id doesn't exist, ``g.user`` will be
``None``.
@ -245,7 +245,7 @@ there is no user id, or if the id doesn't exist, ``g.user`` will be
Logout
------
To log out, you need to remove the user id from the :data:`.session`.
To log out, you need to remove the user id from the :data:`session`.
Then ``load_logged_in_user`` won't load a user on subsequent requests.
.. code-block:: python

View file

@ -3,8 +3,8 @@ name = "flask-example-celery"
version = "1.0.0"
description = "Example Flask application with Celery background tasks."
readme = "README.md"
classifiers = ["Private :: Do Not Upload"]
dependencies = ["flask", "celery[redis]"]
requires-python = ">=3.8"
dependencies = ["flask>=2.2.2", "celery[redis]>=5.2.7"]
[build-system]
requires = ["flit_core<4"]

View file

@ -15,7 +15,7 @@ page. Demonstrates using |fetch|_, |XMLHttpRequest|_, and
.. |jQuery.ajax| replace:: ``jQuery.ajax``
.. _jQuery.ajax: https://api.jquery.com/jQuery.ajax/
.. _Flask docs: https://flask.palletsprojects.com/patterns/javascript/
.. _Flask docs: https://flask.palletsprojects.com/patterns/jquery/
Install

View file

@ -3,13 +3,12 @@ name = "js_example"
version = "1.1.0"
description = "Demonstrates making AJAX requests to Flask."
readme = "README.rst"
license = {file = "LICENSE.txt"}
license = {file = "LICENSE.rst"}
maintainers = [{name = "Pallets", email = "contact@palletsprojects.com"}]
classifiers = ["Private :: Do Not Upload"]
dependencies = ["flask"]
[project.urls]
Documentation = "https://flask.palletsprojects.com/patterns/javascript/"
Documentation = "https://flask.palletsprojects.com/patterns/jquery/"
[project.optional-dependencies]
test = ["pytest"]

View file

@ -5,7 +5,7 @@ from flask import template_rendered
@pytest.mark.parametrize(
("path", "template_name"),
(
("/", "fetch.html"),
("/", "xhr.html"),
("/plain", "xhr.html"),
("/fetch", "fetch.html"),
("/jquery", "jquery.html"),

View file

@ -21,7 +21,10 @@ def create_app(test_config=None):
app.config.update(test_config)
# ensure the instance folder exists
os.makedirs(app.instance_path, exist_ok=True)
try:
os.makedirs(app.instance_path)
except OSError:
pass
@app.route("/hello")
def hello():

View file

@ -1,5 +1,4 @@
import sqlite3
from datetime import datetime
import click
from flask import current_app
@ -45,9 +44,6 @@ def init_db_command():
click.echo("Initialized the database.")
sqlite3.register_converter("timestamp", lambda v: datetime.fromisoformat(v.decode()))
def init_app(app):
"""Register database functions with the Flask app. This is called by
the application factory.

View file

@ -3,9 +3,8 @@ name = "flaskr"
version = "1.0.0"
description = "The basic blog app built in the Flask tutorial."
readme = "README.rst"
license = {file = "LICENSE.txt"}
license = {text = "BSD-3-Clause"}
maintainers = [{name = "Pallets", email = "contact@palletsprojects.com"}]
classifiers = ["Private :: Do Not Upload"]
dependencies = [
"flask",
]

View file

@ -1,88 +1,50 @@
[project]
name = "Flask"
version = "3.2.0.dev"
version = "3.0.1"
description = "A simple framework for building complex web applications."
readme = "README.md"
license = "BSD-3-Clause"
license-files = ["LICENSE.txt"]
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",
"Typing :: Typed",
]
requires-python = ">=3.10"
requires-python = ">=3.8"
dependencies = [
"blinker>=1.9.0",
"Werkzeug>=3.0.0",
"Jinja2>=3.1.2",
"itsdangerous>=2.1.2",
"click>=8.1.3",
"itsdangerous>=2.2.0",
"jinja2>=3.1.2",
"markupsafe>=2.1.1",
"werkzeug>=3.1.0",
]
[project.optional-dependencies]
async = ["asgiref>=3.2"]
dotenv = ["python-dotenv"]
[dependency-groups]
dev = [
"ruff",
"tox",
"tox-uv",
]
docs = [
"pallets-sphinx-themes",
"sphinx<9",
"sphinx-tabs",
"sphinxcontrib-log-cabinet",
]
docs-auto = [
"sphinx-autobuild",
]
gha-update = [
"gha-update ; python_full_version >= '3.12'",
]
pre-commit = [
"pre-commit",
"pre-commit-uv",
]
tests = [
"asgiref",
"pytest",
"python-dotenv",
]
typing = [
"asgiref",
"cryptography",
"mypy",
"pyright",
"pytest",
"python-dotenv",
"types-contextvars",
"types-dataclasses",
"blinker>=1.6.2",
"importlib-metadata>=3.6.0; python_version < '3.10'",
]
[project.urls]
Donate = "https://palletsprojects.com/donate"
Documentation = "https://flask.palletsprojects.com/"
Changes = "https://flask.palletsprojects.com/page/changes/"
Source = "https://github.com/pallets/flask/"
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>=3.11,<4"]
requires = ["flit_core<4"]
build-backend = "flit_core.buildapi"
[tool.flit.module]
@ -92,17 +54,16 @@ name = "flask"
include = [
"docs/",
"examples/",
"requirements/",
"tests/",
"CHANGES.rst",
"uv.lock"
"CONTRIBUTING.rst",
"tox.ini",
]
exclude = [
"docs/_build/",
]
[tool.uv]
default-groups = ["dev", "pre-commit", "tests", "typing"]
[tool.pytest.ini_options]
testpaths = ["tests"]
filterwarnings = [
@ -116,16 +77,9 @@ source = ["flask", "tests"]
[tool.coverage.paths]
source = ["src", "*/site-packages"]
[tool.coverage.report]
exclude_also = [
"if t.TYPE_CHECKING",
"raise NotImplementedError",
": \\.{3}",
]
[tool.mypy]
python_version = "3.10"
files = ["src", "tests/type_check"]
python_version = "3.8"
files = ["src/flask", "tests/typing"]
show_error_codes = true
pretty = true
strict = true
@ -139,16 +93,11 @@ module = [
]
ignore_missing_imports = true
[tool.pyright]
pythonVersion = "3.10"
include = ["src", "tests/type_check"]
typeCheckingMode = "basic"
[tool.ruff]
src = ["src"]
fix = true
show-fixes = true
output-format = "full"
show-source = true
[tool.ruff.lint]
select = [
@ -159,119 +108,8 @@ select = [
"UP", # pyupgrade
"W", # pycodestyle warning
]
ignore-init-module-imports = true
[tool.ruff.lint.isort]
force-single-line = true
order-by-type = false
[tool.codespell]
ignore-words-list = "te"
[tool.tox]
env_list = [
"py3.14", "py3.14t",
"py3.13", "py3.12", "py3.11", "py3.10",
"pypy3.11",
"tests-min", "tests-dev",
"style",
"typing",
"docs",
]
[tool.tox.env_run_base]
description = "pytest on latest dependency versions"
runner = "uv-venv-lock-runner"
package = "wheel"
wheel_build_env = ".pkg"
constrain_package_deps = true
use_frozen_constraints = true
dependency_groups = ["tests"]
env_tmp_dir = "{toxworkdir}/tmp/{envname}"
commands = [[
"pytest", "-v", "--tb=short", "--basetemp={env_tmp_dir}",
{replace = "posargs", default = [], extend = true},
]]
[tool.tox.env.tests-min]
description = "pytest on minimum dependency versions"
base_python = ["3.14"]
commands = [
[
"uv", "pip", "install",
"blinker==1.9.0",
"click==8.1.3",
"itsdangerous==2.2.0",
"jinja2==3.1.2",
"markupsafe==2.1.1",
"werkzeug==3.1.0",
],
[
"pytest", "-v", "--tb=short", "--basetemp={env_tmp_dir}",
{replace = "posargs", default = [], extend = true},
],
]
[tool.tox.env.tests-dev]
description = "pytest on development dependency versions (git main branch)"
base_python = ["3.10"]
commands = [
[
"uv", "pip", "install",
"https://github.com/pallets-eco/blinker/archive/refs/heads/main.tar.gz",
"https://github.com/pallets/click/archive/refs/heads/main.tar.gz",
"https://github.com/pallets/itsdangerous/archive/refs/heads/main.tar.gz",
"https://github.com/pallets/jinja/archive/refs/heads/main.tar.gz",
"https://github.com/pallets/markupsafe/archive/refs/heads/main.tar.gz",
"https://github.com/pallets/werkzeug/archive/refs/heads/main.tar.gz",
],
[
"pytest", "-v", "--tb=short", "--basetemp={env_tmp_dir}",
{replace = "posargs", default = [], extend = true},
],
]
[tool.tox.env.style]
description = "run all pre-commit hooks on all files"
dependency_groups = ["pre-commit"]
skip_install = true
commands = [["pre-commit", "run", "--all-files"]]
[tool.tox.env.typing]
description = "run static type checkers"
dependency_groups = ["typing"]
commands = [
["mypy"],
["pyright"],
]
[tool.tox.env.docs]
description = "build docs"
dependency_groups = ["docs"]
commands = [["sphinx-build", "-E", "-W", "-b", "dirhtml", "docs", "docs/_build/dirhtml"]]
[tool.tox.env.docs-auto]
description = "continuously rebuild docs and start a local server"
dependency_groups = ["docs", "docs-auto"]
commands = [["sphinx-autobuild", "-W", "-b", "dirhtml", "--watch", "src", "docs", "docs/_build/dirhtml"]]
[tool.tox.env.update-actions]
description = "update GitHub Actions pins"
labels = ["update"]
dependency_groups = ["gha-update"]
skip_install = true
commands = [["gha-update"]]
[tool.tox.env.update-pre_commit]
description = "update pre-commit pins"
labels = ["update"]
dependency_groups = ["pre-commit"]
skip_install = true
commands = [["pre-commit", "autoupdate", "--freeze", "-j4"]]
[tool.tox.env.update-requirements]
description = "update uv lock"
labels = ["update"]
dependency_groups = []
no_default_groups = true
skip_install = true
commands = [["uv", "lock", {replace = "posargs", default = ["-U"], extend = true}]]

View file

@ -0,0 +1,2 @@
Dependabot will only update files in the `requirements` directory. This directory is
separate because the pins in here should not be updated automatically.

View file

@ -0,0 +1,6 @@
https://github.com/pallets/werkzeug/archive/refs/heads/main.tar.gz
https://github.com/pallets/jinja/archive/refs/heads/main.tar.gz
https://github.com/pallets/markupsafe/archive/refs/heads/main.tar.gz
https://github.com/pallets/itsdangerous/archive/refs/heads/main.tar.gz
https://github.com/pallets/click/archive/refs/heads/main.tar.gz
https://github.com/pallets-eco/blinker/archive/refs/heads/main.tar.gz

View file

@ -0,0 +1,6 @@
werkzeug==3.0.0
jinja2==3.1.2
markupsafe==2.1.1
itsdangerous==2.1.2
click==8.1.3
blinker==1.6.2

View file

@ -0,0 +1,21 @@
#
# This file is autogenerated by pip-compile with Python 3.11
# by the following command:
#
# pip-compile tests-min.in
#
blinker==1.6.2
# via -r tests-min.in
click==8.1.3
# via -r tests-min.in
itsdangerous==2.1.2
# via -r tests-min.in
jinja2==3.1.2
# via -r tests-min.in
markupsafe==2.1.1
# via
# -r tests-min.in
# jinja2
# werkzeug
werkzeug==3.0.0
# via -r tests-min.in

1
requirements/build.in Normal file
View file

@ -0,0 +1 @@
build

12
requirements/build.txt Normal file
View file

@ -0,0 +1,12 @@
#
# This file is autogenerated by pip-compile with Python 3.11
# by the following command:
#
# pip-compile build.in
#
build==1.0.3
# via -r build.in
packaging==23.2
# via build
pyproject-hooks==1.0.0
# via build

6
requirements/dev.in Normal file
View file

@ -0,0 +1,6 @@
-r docs.in
-r tests.in
-r typing.in
pip-tools
pre-commit
tox

151
requirements/dev.txt Normal file
View file

@ -0,0 +1,151 @@
#
# This file is autogenerated by pip-compile with Python 3.11
# by the following command:
#
# pip-compile dev.in
#
alabaster==0.7.16
# via sphinx
asgiref==3.7.2
# via
# -r tests.in
# -r typing.in
babel==2.14.0
# via sphinx
build==1.0.3
# via pip-tools
cachetools==5.3.2
# via tox
certifi==2023.11.17
# via requests
cffi==1.16.0
# via cryptography
cfgv==3.4.0
# via pre-commit
chardet==5.2.0
# via tox
charset-normalizer==3.3.2
# via requests
click==8.1.7
# via pip-tools
colorama==0.4.6
# via tox
cryptography==41.0.7
# via -r typing.in
distlib==0.3.8
# via virtualenv
docutils==0.18.1
# via
# sphinx
# sphinx-tabs
filelock==3.13.1
# via
# tox
# virtualenv
identify==2.5.33
# via pre-commit
idna==3.6
# via requests
imagesize==1.4.1
# via sphinx
iniconfig==2.0.0
# via pytest
jinja2==3.1.3
# via sphinx
markupsafe==2.1.3
# via jinja2
mypy==1.8.0
# via -r typing.in
mypy-extensions==1.0.0
# via mypy
nodeenv==1.8.0
# via pre-commit
packaging==23.2
# via
# build
# pallets-sphinx-themes
# pyproject-api
# pytest
# sphinx
# tox
pallets-sphinx-themes==2.1.1
# via -r docs.in
pip-tools==7.3.0
# via -r dev.in
platformdirs==4.1.0
# via
# tox
# virtualenv
pluggy==1.3.0
# via
# pytest
# tox
pre-commit==3.6.0
# via -r dev.in
pycparser==2.21
# via cffi
pygments==2.17.2
# via
# sphinx
# sphinx-tabs
pyproject-api==1.6.1
# via tox
pyproject-hooks==1.0.0
# via build
pytest==7.4.4
# via -r tests.in
python-dotenv==1.0.0
# via
# -r tests.in
# -r typing.in
pyyaml==6.0.1
# via pre-commit
requests==2.31.0
# via sphinx
snowballstemmer==2.2.0
# via sphinx
sphinx==7.2.6
# via
# -r docs.in
# pallets-sphinx-themes
# sphinx-issues
# sphinx-tabs
# sphinxcontrib-log-cabinet
sphinx-issues==3.0.1
# via -r docs.in
sphinx-tabs==3.4.4
# via -r docs.in
sphinxcontrib-applehelp==1.0.8
# via sphinx
sphinxcontrib-devhelp==1.0.6
# via sphinx
sphinxcontrib-htmlhelp==2.0.5
# via sphinx
sphinxcontrib-jsmath==1.0.1
# via sphinx
sphinxcontrib-log-cabinet==1.0.1
# via -r docs.in
sphinxcontrib-qthelp==1.0.7
# via sphinx
sphinxcontrib-serializinghtml==1.1.10
# via sphinx
tox==4.12.0
# via -r dev.in
types-contextvars==2.4.7.3
# via -r typing.in
types-dataclasses==0.6.6
# via -r typing.in
typing-extensions==4.9.0
# via mypy
urllib3==2.1.0
# via requests
virtualenv==20.25.0
# via
# pre-commit
# tox
wheel==0.42.0
# via pip-tools
# The following packages are considered to be unsafe in a requirements file:
# pip
# setuptools

5
requirements/docs.in Normal file
View file

@ -0,0 +1,5 @@
pallets-sphinx-themes
sphinx
sphinx-issues
sphinxcontrib-log-cabinet
sphinx-tabs

67
requirements/docs.txt Normal file
View file

@ -0,0 +1,67 @@
#
# This file is autogenerated by pip-compile with Python 3.11
# by the following command:
#
# pip-compile docs.in
#
alabaster==0.7.16
# via sphinx
babel==2.14.0
# via sphinx
certifi==2023.11.17
# via requests
charset-normalizer==3.3.2
# via requests
docutils==0.18.1
# via
# sphinx
# sphinx-tabs
idna==3.6
# via requests
imagesize==1.4.1
# via sphinx
jinja2==3.1.3
# via sphinx
markupsafe==2.1.3
# via jinja2
packaging==23.2
# via
# pallets-sphinx-themes
# sphinx
pallets-sphinx-themes==2.1.1
# via -r docs.in
pygments==2.17.2
# via
# sphinx
# sphinx-tabs
requests==2.31.0
# via sphinx
snowballstemmer==2.2.0
# via sphinx
sphinx==7.2.6
# via
# -r docs.in
# pallets-sphinx-themes
# sphinx-issues
# sphinx-tabs
# sphinxcontrib-log-cabinet
sphinx-issues==3.0.1
# via -r docs.in
sphinx-tabs==3.4.4
# via -r docs.in
sphinxcontrib-applehelp==1.0.8
# via sphinx
sphinxcontrib-devhelp==1.0.6
# via sphinx
sphinxcontrib-htmlhelp==2.0.5
# via sphinx
sphinxcontrib-jsmath==1.0.1
# via sphinx
sphinxcontrib-log-cabinet==1.0.1
# via -r docs.in
sphinxcontrib-qthelp==1.0.7
# via sphinx
sphinxcontrib-serializinghtml==1.1.10
# via sphinx
urllib3==2.1.0
# via requests

4
requirements/tests.in Normal file
View file

@ -0,0 +1,4 @@
pytest
asgiref
greenlet ; python_version < "3.11"
python-dotenv

18
requirements/tests.txt Normal file
View file

@ -0,0 +1,18 @@
#
# This file is autogenerated by pip-compile with Python 3.11
# by the following command:
#
# pip-compile tests.in
#
asgiref==3.7.2
# via -r tests.in
iniconfig==2.0.0
# via pytest
packaging==23.2
# via pytest
pluggy==1.3.0
# via pytest
pytest==7.4.4
# via -r tests.in
python-dotenv==1.0.0
# via -r tests.in

6
requirements/typing.in Normal file
View file

@ -0,0 +1,6 @@
mypy
types-contextvars
types-dataclasses
asgiref
cryptography
python-dotenv

26
requirements/typing.txt Normal file
View file

@ -0,0 +1,26 @@
#
# This file is autogenerated by pip-compile with Python 3.11
# by the following command:
#
# pip-compile typing.in
#
asgiref==3.7.2
# via -r typing.in
cffi==1.16.0
# via cryptography
cryptography==41.0.7
# via -r typing.in
mypy==1.8.0
# via -r typing.in
mypy-extensions==1.0.0
# via mypy
pycparser==2.21
# via cffi
python-dotenv==1.0.0
# via -r typing.in
types-contextvars==2.4.7.3
# via -r typing.in
types-dataclasses==0.6.6
# via -r typing.in
typing-extensions==4.9.0
# via mypy

View file

@ -1,3 +1,7 @@
from __future__ import annotations
import typing as t
from . import json as json
from .app import Flask as Flask
from .blueprints import Blueprint as Blueprint
@ -37,3 +41,20 @@ from .templating import stream_template as stream_template
from .templating import stream_template_string as stream_template_string
from .wrappers import Request as Request
from .wrappers import Response as Response
def __getattr__(name: str) -> t.Any:
if name == "__version__":
import importlib.metadata
import warnings
warnings.warn(
"The '__version__' attribute is deprecated and will be removed in"
" Flask 3.1. Use feature detection or"
" 'importlib.metadata.version(\"flask\")' instead.",
DeprecationWarning,
stacklevel=2,
)
return importlib.metadata.version("flask")
raise AttributeError(name)

View file

@ -1,13 +1,11 @@
from __future__ import annotations
import collections.abc as cabc
import inspect
import os
import sys
import typing as t
import weakref
from datetime import timedelta
from functools import update_wrapper
from inspect import iscoroutinefunction
from itertools import chain
from types import TracebackType
@ -26,22 +24,24 @@ from werkzeug.routing import RoutingException
from werkzeug.routing import Rule
from werkzeug.serving import is_running_from_reloader
from werkzeug.wrappers import Response as BaseResponse
from werkzeug.wsgi import get_host
from . import cli
from . import typing as ft
from .ctx import AppContext
from .ctx import RequestContext
from .globals import _cv_app
from .globals import app_ctx
from .globals import _cv_request
from .globals import current_app
from .globals import g
from .globals import request
from .globals import request_ctx
from .globals import session
from .helpers import _CollectErrors
from .helpers import get_debug_flag
from .helpers import get_flashed_messages
from .helpers import get_load_dotenv
from .helpers import send_from_directory
from .sansio.app import App
from .sansio.scaffold import _sentinel
from .sessions import SecureCookieSessionInterface
from .sessions import SessionInterface
from .signals import appcontext_tearing_down
@ -59,7 +59,6 @@ if t.TYPE_CHECKING: # pragma: no cover
from .testing import FlaskClient
from .testing import FlaskCliRunner
from .typing import HeadersValue
T_shell_context_processor = t.TypeVar(
"T_shell_context_processor", bound=ft.ShellContextProcessorCallable
@ -77,35 +76,6 @@ def _make_timedelta(value: timedelta | int | None) -> timedelta | None:
return timedelta(seconds=value)
F = t.TypeVar("F", bound=t.Callable[..., t.Any])
# Other methods may call the overridden method with the new ctx arg. Remove it
# and call the method with the remaining args.
def remove_ctx(f: F) -> F:
def wrapper(self: Flask, *args: t.Any, **kwargs: t.Any) -> t.Any:
if args and isinstance(args[0], AppContext):
args = args[1:]
return f(self, *args, **kwargs)
return update_wrapper(wrapper, f) # type: ignore[return-value]
# The overridden method may call super().base_method without the new ctx arg.
# Add it to the args for the call.
def add_ctx(f: F) -> F:
def wrapper(self: Flask, *args: t.Any, **kwargs: t.Any) -> t.Any:
if not args:
args = (app_ctx._get_current_object(),)
elif not isinstance(args[0], AppContext):
args = (app_ctx._get_current_object(), *args)
return f(self, *args, **kwargs)
return update_wrapper(wrapper, f) # type: ignore[return-value]
class Flask(App):
"""The flask object implements a WSGI application and acts as the central
object. It is passed the name of the module or package of the
@ -209,10 +179,8 @@ class Flask(App):
"TESTING": False,
"PROPAGATE_EXCEPTIONS": None,
"SECRET_KEY": None,
"SECRET_KEY_FALLBACKS": None,
"PERMANENT_SESSION_LIFETIME": timedelta(days=31),
"USE_X_SENDFILE": False,
"TRUSTED_HOSTS": None,
"SERVER_NAME": None,
"APPLICATION_ROOT": "/",
"SESSION_COOKIE_NAME": "session",
@ -220,12 +188,9 @@ class Flask(App):
"SESSION_COOKIE_PATH": None,
"SESSION_COOKIE_HTTPONLY": True,
"SESSION_COOKIE_SECURE": False,
"SESSION_COOKIE_PARTITIONED": False,
"SESSION_COOKIE_SAMESITE": None,
"SESSION_REFRESH_EACH_REQUEST": True,
"MAX_CONTENT_LENGTH": None,
"MAX_FORM_MEMORY_SIZE": 500_000,
"MAX_FORM_PARTS": 1_000,
"SEND_FILE_MAX_AGE_DEFAULT": None,
"TRAP_BAD_REQUEST_ERRORS": None,
"TRAP_HTTP_EXCEPTIONS": False,
@ -233,7 +198,6 @@ class Flask(App):
"PREFERRED_URL_SCHEME": "http",
"TEMPLATES_AUTO_RELOAD": None,
"MAX_COOKIE_SIZE": 4093,
"PROVIDE_AUTOMATIC_OPTIONS": True,
}
)
@ -251,62 +215,6 @@ class Flask(App):
#: .. versionadded:: 0.8
session_interface: SessionInterface = SecureCookieSessionInterface()
def __init_subclass__(cls, **kwargs: t.Any) -> None:
import warnings
# These method signatures were updated to take a ctx param. Detect
# overridden methods in subclasses that still have the old signature.
# Show a deprecation warning and wrap to call with correct args.
for method in (
cls.handle_http_exception,
cls.handle_user_exception,
cls.handle_exception,
cls.log_exception,
cls.dispatch_request,
cls.full_dispatch_request,
cls.finalize_request,
cls.make_default_options_response,
cls.preprocess_request,
cls.process_response,
cls.do_teardown_request,
cls.do_teardown_appcontext,
):
base_method = getattr(Flask, method.__name__)
if method is base_method:
# not overridden
continue
# get the second parameter (first is self)
iter_params = iter(inspect.signature(method).parameters.values())
next(iter_params)
param = next(iter_params, None)
# must have second parameter named ctx or annotated AppContext
if param is None or not (
# no annotation, match name
(param.annotation is inspect.Parameter.empty and param.name == "ctx")
or (
# string annotation, access path ends with AppContext
isinstance(param.annotation, str)
and param.annotation.rpartition(".")[2] == "AppContext"
)
or (
# class annotation
inspect.isclass(param.annotation)
and issubclass(param.annotation, AppContext)
)
):
warnings.warn(
f"The '{method.__name__}' method now takes 'ctx: AppContext'"
" as the first parameter. The old signature is deprecated"
" and will not be supported in Flask 4.0.",
DeprecationWarning,
stacklevel=2,
)
setattr(cls, method.__name__, remove_ctx(method))
setattr(Flask, method.__name__, add_ctx(base_method))
def __init__(
self,
import_name: str,
@ -333,25 +241,15 @@ class Flask(App):
root_path=root_path,
)
#: The Click command group for registering CLI commands for this
#: object. The commands are available from the ``flask`` command
#: once the application has been discovered and blueprints have
#: been registered.
self.cli = cli.AppGroup()
# Set the name of the Click group in case someone wants to add
# the app's commands to another CLI tool.
self.cli.name = self.name
# Add a static route using the provided static_url_path, static_host,
# and static_folder if there is a configured static_folder.
# Note we do this without checking if static_folder exists.
# For one, it might be created while the server is running (e.g. during
# development). Also, Google App Engine stores static files somewhere
if self.has_static_folder:
assert bool(static_host) == host_matching, (
"Invalid static_host/host_matching combination"
)
assert (
bool(static_host) == host_matching
), "Invalid static_host/host_matching combination"
# Use a weakref to avoid creating a reference cycle between the app
# and the view function (see #3761).
self_ref = weakref.ref(self)
@ -359,7 +257,7 @@ class Flask(App):
f"{self.static_url_path}/<path:filename>",
endpoint="static",
host=static_host,
view_func=lambda **kw: self_ref().send_static_file(**kw), # type: ignore
view_func=lambda **kw: self_ref().send_static_file(**kw), # type: ignore # noqa: B950
)
def get_send_file_max_age(self, filename: str | None) -> int | None:
@ -379,7 +277,7 @@ class Flask(App):
.. versionadded:: 0.9
"""
value = self.config["SEND_FILE_MAX_AGE_DEFAULT"]
value = current_app.config["SEND_FILE_MAX_AGE_DEFAULT"]
if value is None:
return None
@ -411,10 +309,9 @@ class Flask(App):
t.cast(str, self.static_folder), filename, max_age=max_age
)
def open_resource(
self, resource: str, mode: str = "rb", encoding: str | None = None
) -> t.IO[t.AnyStr]:
"""Open a resource file relative to :attr:`root_path` for reading.
def open_resource(self, resource: str, mode: str = "rb") -> t.IO[t.AnyStr]:
"""Open a resource file relative to :attr:`root_path` for
reading.
For example, if the file ``schema.sql`` is next to the file
``app.py`` where the ``Flask`` app is defined, it can be opened
@ -425,46 +322,31 @@ class Flask(App):
with app.open_resource("schema.sql") as f:
conn.executescript(f.read())
:param resource: Path to the resource relative to :attr:`root_path`.
:param mode: Open the file in this mode. Only reading is supported,
valid values are ``"r"`` (or ``"rt"``) and ``"rb"``.
:param encoding: Open the file with this encoding when opening in text
mode. This is ignored when opening in binary mode.
:param resource: Path to the resource relative to
:attr:`root_path`.
:param mode: Open the file in this mode. Only reading is
supported, valid values are "r" (or "rt") and "rb".
Note this is a duplicate of the same method in the Flask
class.
.. versionchanged:: 3.1
Added the ``encoding`` parameter.
"""
if mode not in {"r", "rt", "rb"}:
raise ValueError("Resources can only be opened for reading.")
path = os.path.join(self.root_path, resource)
return open(os.path.join(self.root_path, resource), mode)
if mode == "rb":
return open(path, mode) # pyright: ignore
def open_instance_resource(self, resource: str, mode: str = "rb") -> t.IO[t.AnyStr]:
"""Opens a resource from the application's instance folder
(:attr:`instance_path`). Otherwise works like
:meth:`open_resource`. Instance resources can also be opened for
writing.
return open(path, mode, encoding=encoding)
def open_instance_resource(
self, resource: str, mode: str = "rb", encoding: str | None = "utf-8"
) -> t.IO[t.AnyStr]:
"""Open a resource file relative to the application's instance folder
:attr:`instance_path`. Unlike :meth:`open_resource`, files in the
instance folder can be opened for writing.
:param resource: Path to the resource relative to :attr:`instance_path`.
:param mode: Open the file in this mode.
:param encoding: Open the file with this encoding when opening in text
mode. This is ignored when opening in binary mode.
.. versionchanged:: 3.1
Added the ``encoding`` parameter.
:param resource: the name of the resource. To access resources within
subfolders use forward slashes as separator.
:param mode: resource file opening mode, default is 'rb'.
"""
path = os.path.join(self.instance_path, resource)
if "b" in mode:
return open(path, mode)
return open(path, mode, encoding=encoding)
return open(os.path.join(self.instance_path, resource), mode)
def create_jinja_environment(self) -> Environment:
"""Create the Jinja environment based on :attr:`jinja_options`
@ -511,45 +393,32 @@ class Flask(App):
is created at a point where the request context is not yet set
up so the request is passed explicitly.
.. versionchanged:: 3.1
If :data:`SERVER_NAME` is set, it does not restrict requests to
only that domain, for both ``subdomain_matching`` and
``host_matching``.
.. versionadded:: 0.6
.. versionchanged:: 0.9
This can now also be called without a request object when the
URL adapter is created for the application context.
.. versionchanged:: 1.0
:data:`SERVER_NAME` no longer implicitly enables subdomain
matching. Use :attr:`subdomain_matching` instead.
.. versionchanged:: 0.9
This can be called outside a request when the URL adapter is created
for an application context.
.. versionadded:: 0.6
"""
if request is not None:
if (trusted_hosts := self.config["TRUSTED_HOSTS"]) is not None:
request.trusted_hosts = trusted_hosts
# Check trusted_hosts here until bind_to_environ does.
request.host = get_host(request.environ, request.trusted_hosts) # pyright: ignore
subdomain = None
server_name = self.config["SERVER_NAME"]
if self.url_map.host_matching:
# Don't pass SERVER_NAME, otherwise it's used and the actual
# host is ignored, which breaks host matching.
server_name = None
elif not self.subdomain_matching:
# Werkzeug doesn't implement subdomain matching yet. Until then,
# disable it by forcing the current subdomain to the default, or
# the empty string.
subdomain = self.url_map.default_subdomain or ""
# If subdomain matching is disabled (the default), use the
# default subdomain in all cases. This should be the default
# in Werkzeug but it currently does not have that feature.
if not self.subdomain_matching:
subdomain = self.url_map.default_subdomain or None
else:
subdomain = None
return self.url_map.bind_to_environ(
request.environ, server_name=server_name, subdomain=subdomain
request.environ,
server_name=self.config["SERVER_NAME"],
subdomain=subdomain,
)
# Need at least SERVER_NAME to match/build outside a request.
# We need at the very least the server name to be set for this
# to work.
if self.config["SERVER_NAME"] is not None:
return self.url_map.bind(
self.config["SERVER_NAME"],
@ -587,9 +456,7 @@ class Flask(App):
raise FormDataRoutingRedirect(request)
def update_template_context(
self, ctx: AppContext, context: dict[str, t.Any]
) -> None:
def update_template_context(self, context: dict[str, t.Any]) -> None:
"""Update the template context with some commonly used variables.
This injects request, session, config and g into the template
context as well as everything template context processors want
@ -603,8 +470,8 @@ class Flask(App):
names: t.Iterable[str | None] = (None,)
# A template may be rendered outside a request context.
if ctx.has_request:
names = chain(names, reversed(ctx.request.blueprints))
if request:
names = chain(names, reversed(request.blueprints))
# The values passed to render_template take precedence. Keep a
# copy to re-apply after all context functions.
@ -828,7 +695,7 @@ class Flask(App):
return cls(self, **kwargs) # type: ignore
def handle_http_exception(
self, ctx: AppContext, e: HTTPException
self, e: HTTPException
) -> HTTPException | ft.ResponseReturnValue:
"""Handles an HTTP exception. By default this will invoke the
registered error handlers and fall back to returning the
@ -857,13 +724,13 @@ class Flask(App):
if isinstance(e, RoutingException):
return e
handler = self._find_error_handler(e, ctx.request.blueprints)
handler = self._find_error_handler(e, request.blueprints)
if handler is None:
return e
return self.ensure_sync(handler)(e) # type: ignore[no-any-return]
def handle_user_exception(
self, ctx: AppContext, e: Exception
self, e: Exception
) -> HTTPException | ft.ResponseReturnValue:
"""This method is called whenever an exception occurs that
should be handled. A special case is :class:`~werkzeug
@ -885,16 +752,16 @@ class Flask(App):
e.show_exception = True
if isinstance(e, HTTPException) and not self.trap_http_exception(e):
return self.handle_http_exception(ctx, e)
return self.handle_http_exception(e)
handler = self._find_error_handler(e, ctx.request.blueprints)
handler = self._find_error_handler(e, request.blueprints)
if handler is None:
raise
return self.ensure_sync(handler)(e) # type: ignore[no-any-return]
def handle_exception(self, ctx: AppContext, e: Exception) -> Response:
def handle_exception(self, e: Exception) -> Response:
"""Handle an exception that did not have an error handler
associated with it, or that was raised from an error handler.
This always causes a 500 ``InternalServerError``.
@ -937,20 +804,19 @@ class Flask(App):
raise e
self.log_exception(ctx, exc_info)
self.log_exception(exc_info)
server_error: InternalServerError | ft.ResponseReturnValue
server_error = InternalServerError(original_exception=e)
handler = self._find_error_handler(server_error, ctx.request.blueprints)
handler = self._find_error_handler(server_error, request.blueprints)
if handler is not None:
server_error = self.ensure_sync(handler)(server_error)
return self.finalize_request(ctx, server_error, from_error_handler=True)
return self.finalize_request(server_error, from_error_handler=True)
def log_exception(
self,
ctx: AppContext,
exc_info: tuple[type, BaseException, TracebackType] | tuple[None, None, None],
exc_info: (tuple[type, BaseException, TracebackType] | tuple[None, None, None]),
) -> None:
"""Logs an exception. This is called by :meth:`handle_exception`
if debugging is disabled and right before the handler is called.
@ -960,10 +826,10 @@ class Flask(App):
.. versionadded:: 0.8
"""
self.logger.error(
f"Exception on {ctx.request.path} [{ctx.request.method}]", exc_info=exc_info
f"Exception on {request.path} [{request.method}]", exc_info=exc_info
)
def dispatch_request(self, ctx: AppContext) -> ft.ResponseReturnValue:
def dispatch_request(self) -> ft.ResponseReturnValue:
"""Does the request dispatching. Matches the URL and returns the
return value of the view or error handler. This does not have to
be a response object. In order to convert the return value to a
@ -973,8 +839,7 @@ class Flask(App):
This no longer does the exception handling, this code was
moved to the new :meth:`full_dispatch_request`.
"""
req = ctx.request
req = request_ctx.request
if req.routing_exception is not None:
self.raise_routing_exception(req)
rule: Rule = req.url_rule # type: ignore[assignment]
@ -984,43 +849,31 @@ class Flask(App):
getattr(rule, "provide_automatic_options", False)
and req.method == "OPTIONS"
):
return self.make_default_options_response(ctx)
return self.make_default_options_response()
# otherwise dispatch to the handler for that endpoint
view_args: dict[str, t.Any] = req.view_args # type: ignore[assignment]
return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return]
def full_dispatch_request(self, ctx: AppContext) -> Response:
def full_dispatch_request(self) -> Response:
"""Dispatches the request and on top of that performs request
pre and postprocessing as well as HTTP exception catching and
error handling.
.. versionadded:: 0.7
"""
if not self._got_first_request and self.should_ignore_error is not None:
import warnings
warnings.warn(
"The 'should_ignore_error' method is deprecated and will"
" be removed in Flask 3.3. Handle errors as needed in"
" teardown handlers instead.",
DeprecationWarning,
stacklevel=1,
)
self._got_first_request = True
try:
request_started.send(self, _async_wrapper=self.ensure_sync)
rv = self.preprocess_request(ctx)
rv = self.preprocess_request()
if rv is None:
rv = self.dispatch_request(ctx)
rv = self.dispatch_request()
except Exception as e:
rv = self.handle_user_exception(ctx, e)
return self.finalize_request(ctx, rv)
rv = self.handle_user_exception(e)
return self.finalize_request(rv)
def finalize_request(
self,
ctx: AppContext,
rv: ft.ResponseReturnValue | HTTPException,
from_error_handler: bool = False,
) -> Response:
@ -1038,7 +891,7 @@ class Flask(App):
"""
response = self.make_response(rv)
try:
response = self.process_response(ctx, response)
response = self.process_response(response)
request_finished.send(
self, _async_wrapper=self.ensure_sync, response=response
)
@ -1050,14 +903,15 @@ class Flask(App):
)
return response
def make_default_options_response(self, ctx: AppContext) -> Response:
def make_default_options_response(self) -> Response:
"""This method is called to create the default ``OPTIONS`` response.
This can be changed through subclassing to change the default
behavior of ``OPTIONS`` responses.
.. versionadded:: 0.7
"""
methods = ctx.url_adapter.allowed_methods() # type: ignore[union-attr]
adapter = request_ctx.url_adapter
methods = adapter.allowed_methods() # type: ignore[union-attr]
rv = self.response_class()
rv.allow.update(methods)
return rv
@ -1156,9 +1010,11 @@ class Flask(App):
.. versionadded:: 2.2
Moved from ``flask.url_for``, which calls this method.
"""
if (ctx := _cv_app.get(None)) is not None and ctx.has_request:
url_adapter = ctx.url_adapter
blueprint_name = ctx.request.blueprint
req_ctx = _cv_request.get(None)
if req_ctx is not None:
url_adapter = req_ctx.url_adapter
blueprint_name = req_ctx.request.blueprint
# If the endpoint starts with "." and the request matches a
# blueprint, the endpoint is relative to the blueprint.
@ -1173,11 +1029,13 @@ class Flask(App):
if _external is None:
_external = _scheme is not None
else:
app_ctx = _cv_app.get(None)
# If called by helpers.url_for, an app context is active,
# use its url_adapter. Otherwise, app.url_for was called
# directly, build an adapter.
if ctx is not None:
url_adapter = ctx.url_adapter
if app_ctx is not None:
url_adapter = app_ctx.url_adapter
else:
url_adapter = self.create_url_adapter(None)
@ -1278,8 +1136,7 @@ class Flask(App):
response object.
"""
status: int | None = None
headers: HeadersValue | None = None
status = headers = None
# unpack tuple returns
if isinstance(rv, tuple):
@ -1291,7 +1148,7 @@ class Flask(App):
# decide if a 2-tuple has status or headers
elif len_rv == 2:
if isinstance(rv[1], (Headers, dict, tuple, list)):
rv, headers = rv # pyright: ignore
rv, headers = rv
else:
rv, status = rv # type: ignore[assignment,misc]
# other sized tuples are not allowed
@ -1317,7 +1174,7 @@ class Flask(App):
# waiting to do it manually, so that the class can handle any
# special logic
rv = self.response_class(
rv, # pyright: ignore
rv,
status=status,
headers=headers, # type: ignore[arg-type]
)
@ -1359,11 +1216,11 @@ class Flask(App):
# extend existing headers with provided headers
if headers:
rv.headers.update(headers)
rv.headers.update(headers) # type: ignore[arg-type]
return rv
def preprocess_request(self, ctx: AppContext) -> ft.ResponseReturnValue | None:
def preprocess_request(self) -> ft.ResponseReturnValue | None:
"""Called before the request is dispatched. Calls
:attr:`url_value_preprocessors` registered with the app and the
current blueprint (if any). Then calls :attr:`before_request_funcs`
@ -1373,13 +1230,12 @@ class Flask(App):
value is handled as if it was the return value from the view, and
further request handling is stopped.
"""
req = ctx.request
names = (None, *reversed(req.blueprints))
names = (None, *reversed(request.blueprints))
for name in names:
if name in self.url_value_preprocessors:
for url_func in self.url_value_preprocessors[name]:
url_func(req.endpoint, req.view_args)
url_func(request.endpoint, request.view_args)
for name in names:
if name in self.before_request_funcs:
@ -1391,7 +1247,7 @@ class Flask(App):
return None
def process_response(self, ctx: AppContext, response: Response) -> Response:
def process_response(self, response: Response) -> Response:
"""Can be overridden in order to modify the response object
before it's sent to the WSGI server. By default this will
call all the :meth:`after_request` decorated functions.
@ -1404,90 +1260,92 @@ class Flask(App):
:return: a new response object or the same, has to be an
instance of :attr:`response_class`.
"""
ctx = request_ctx._get_current_object() # type: ignore[attr-defined]
for func in ctx._after_request_functions:
response = self.ensure_sync(func)(response)
for name in chain(ctx.request.blueprints, (None,)):
for name in chain(request.blueprints, (None,)):
if name in self.after_request_funcs:
for func in reversed(self.after_request_funcs[name]):
response = self.ensure_sync(func)(response)
if not self.session_interface.is_null_session(ctx._get_session()):
self.session_interface.save_session(self, ctx._get_session(), response)
if not self.session_interface.is_null_session(ctx.session):
self.session_interface.save_session(self, ctx.session, response)
return response
def do_teardown_request(
self, ctx: AppContext, exc: BaseException | None = None
self,
exc: BaseException | None = _sentinel, # type: ignore[assignment]
) -> None:
"""Called after the request is dispatched and the response is finalized,
right before the request context is popped. Called by
:meth:`.AppContext.pop`.
"""Called after the request is dispatched and the response is
returned, right before the request context is popped.
This calls all functions decorated with :meth:`teardown_request`, and
:meth:`Blueprint.teardown_request` if a blueprint handled the request.
Finally, the :data:`request_tearing_down` signal is sent.
This calls all functions decorated with
:meth:`teardown_request`, and :meth:`Blueprint.teardown_request`
if a blueprint handled the request. Finally, the
:data:`request_tearing_down` signal is sent.
:param exc: An unhandled exception raised while dispatching the request.
Passed to each teardown function.
This is called by
:meth:`RequestContext.pop() <flask.ctx.RequestContext.pop>`,
which may be delayed during testing to maintain access to
resources.
.. versionchanged:: 3.2
All callbacks are called rather than stopping on the first error.
:param exc: An unhandled exception raised while dispatching the
request. Detected from the current exception information if
not passed. Passed to each teardown function.
.. versionchanged:: 0.9
Added the ``exc`` argument.
"""
collect_errors = _CollectErrors()
if exc is _sentinel:
exc = sys.exc_info()[1]
for name in chain(ctx.request.blueprints, (None,)):
for name in chain(request.blueprints, (None,)):
if name in self.teardown_request_funcs:
for func in reversed(self.teardown_request_funcs[name]):
with collect_errors:
self.ensure_sync(func)(exc)
self.ensure_sync(func)(exc)
with collect_errors:
request_tearing_down.send(self, _async_wrapper=self.ensure_sync, exc=exc)
collect_errors.raise_any("Errors during request teardown")
request_tearing_down.send(self, _async_wrapper=self.ensure_sync, exc=exc)
def do_teardown_appcontext(
self, ctx: AppContext, exc: BaseException | None = None
self,
exc: BaseException | None = _sentinel, # type: ignore[assignment]
) -> None:
"""Called right before the application context is popped. Called by
:meth:`.AppContext.pop`.
"""Called right before the application context is popped.
This calls all functions decorated with :meth:`teardown_appcontext`.
Then the :data:`appcontext_tearing_down` signal is sent.
When handling a request, the application context is popped
after the request context. See :meth:`do_teardown_request`.
:param exc: An unhandled exception raised while the context was active.
Passed to each teardown function.
This calls all functions decorated with
:meth:`teardown_appcontext`. Then the
:data:`appcontext_tearing_down` signal is sent.
.. versionchanged:: 3.2
All callbacks are called rather than stopping on the first error.
This is called by
:meth:`AppContext.pop() <flask.ctx.AppContext.pop>`.
.. versionadded:: 0.9
"""
collect_errors = _CollectErrors()
if exc is _sentinel:
exc = sys.exc_info()[1]
for func in reversed(self.teardown_appcontext_funcs):
with collect_errors:
self.ensure_sync(func)(exc)
self.ensure_sync(func)(exc)
with collect_errors:
appcontext_tearing_down.send(self, _async_wrapper=self.ensure_sync, exc=exc)
collect_errors.raise_any("Errors during app teardown")
appcontext_tearing_down.send(self, _async_wrapper=self.ensure_sync, exc=exc)
def app_context(self) -> AppContext:
"""Create an :class:`.AppContext`. When the context is pushed,
:data:`.current_app` and :data:`.g` become available.
"""Create an :class:`~flask.ctx.AppContext`. Use as a ``with``
block to push the context, which will make :data:`current_app`
point at this application.
A context is automatically pushed when handling each request, and when
running any ``flask`` CLI command. Use this as a ``with`` block to
manually push a context outside of those situations, such as during
setup or testing.
An application context is automatically pushed by
:meth:`RequestContext.push() <flask.ctx.RequestContext.push>`
when handling a request, and when running a CLI command. Use
this to manually create a context outside of these situations.
.. code-block:: python
::
with app.app_context():
init_db()
@ -1498,37 +1356,44 @@ class Flask(App):
"""
return AppContext(self)
def request_context(self, environ: WSGIEnvironment) -> AppContext:
"""Create an :class:`.AppContext` with request information representing
the given WSGI environment. A context is automatically pushed when
handling each request. When the context is pushed, :data:`.request`,
:data:`.session`, :data:`g:, and :data:`.current_app` become available.
def request_context(self, environ: WSGIEnvironment) -> RequestContext:
"""Create a :class:`~flask.ctx.RequestContext` representing a
WSGI environment. Use a ``with`` block to push the context,
which will make :data:`request` point at this request.
This method should not be used in your own code. Creating a valid WSGI
environ is not trivial. Use :meth:`test_request_context` to correctly
create a WSGI environ and request context instead.
See :doc:`/reqcontext`.
See :doc:`/appcontext`.
Typically you should not call this from your own code. A request
context is automatically pushed by the :meth:`wsgi_app` when
handling a request. Use :meth:`test_request_context` to create
an environment and context instead of this method.
:param environ: A WSGI environment.
:param environ: a WSGI environment
"""
return AppContext.from_environ(self, environ)
return RequestContext(self, environ)
def test_request_context(self, *args: t.Any, **kwargs: t.Any) -> AppContext:
"""Create an :class:`.AppContext` with request information created from
the given arguments. When the context is pushed, :data:`.request`,
:data:`.session`, :data:`g:, and :data:`.current_app` become available.
def test_request_context(self, *args: t.Any, **kwargs: t.Any) -> RequestContext:
"""Create a :class:`~flask.ctx.RequestContext` for a WSGI
environment created from the given values. This is mostly useful
during testing, where you may want to run a function that uses
request data without dispatching a full request.
This is useful during testing to run a function that uses request data
without dispatching a full request. Use this as a ``with`` block to push
a context.
See :doc:`/reqcontext`.
.. code-block:: python
Use a ``with`` block to push the context, which will make
:data:`request` point at the request for the created
environment. ::
with app.test_request_context(...):
generate_report()
See :doc:`/appcontext`.
When using the shell, it may be easier to push and pop the
context manually to avoid indentation. ::
ctx = app.test_request_context(...)
ctx.push()
...
ctx.pop()
Takes the same arguments as Werkzeug's
:class:`~werkzeug.test.EnvironBuilder`, with some defaults from
@ -1538,18 +1403,20 @@ class Flask(App):
:param path: URL path being requested.
:param base_url: Base URL where the app is being served, which
``path`` is relative to. If not given, built from
:data:`PREFERRED_URL_SCHEME`, ``subdomain``, :data:`SERVER_NAME`,
and :data:`APPLICATION_ROOT`.
:param subdomain: Subdomain name to prepend to :data:`SERVER_NAME`.
:data:`PREFERRED_URL_SCHEME`, ``subdomain``,
:data:`SERVER_NAME`, and :data:`APPLICATION_ROOT`.
:param subdomain: Subdomain name to append to
:data:`SERVER_NAME`.
:param url_scheme: Scheme to use instead of
:data:`PREFERRED_URL_SCHEME`.
:param data: The request body text or bytes,or a dict of form data.
:param data: The request body, either as a string or a dict of
form keys and values.
:param json: If given, this is serialized as JSON and passed as
``data``. Also defaults ``content_type`` to
``application/json``.
:param args: Other positional arguments passed to
:param args: other positional arguments passed to
:class:`~werkzeug.test.EnvironBuilder`.
:param kwargs: Other keyword arguments passed to
:param kwargs: other keyword arguments passed to
:class:`~werkzeug.test.EnvironBuilder`.
"""
from .testing import EnvironBuilder
@ -1557,12 +1424,10 @@ class Flask(App):
builder = EnvironBuilder(self, *args, **kwargs)
try:
environ = builder.get_environ()
return self.request_context(builder.get_environ())
finally:
builder.close()
return self.request_context(environ)
def wsgi_app(
self, environ: WSGIEnvironment, start_response: StartResponse
) -> cabc.Iterable[bytes]:
@ -1583,6 +1448,7 @@ class Flask(App):
Teardown events for the request and app contexts are called
even if an unhandled error occurs. Other events may not be
called depending on when an error occurs during dispatch.
See :ref:`callbacks-and-errors`.
:param environ: A WSGI environment.
:param start_response: A callable accepting a status code,
@ -1594,23 +1460,20 @@ class Flask(App):
try:
try:
ctx.push()
response = self.full_dispatch_request(ctx)
response = self.full_dispatch_request()
except Exception as e:
error = e
response = self.handle_exception(ctx, e)
except:
response = self.handle_exception(e)
except: # noqa: B001
error = sys.exc_info()[1]
raise
return response(environ, start_response)
finally:
if "werkzeug.debug.preserve_context" in environ:
environ["werkzeug.debug.preserve_context"](ctx)
environ["werkzeug.debug.preserve_context"](_cv_app.get())
environ["werkzeug.debug.preserve_context"](_cv_request.get())
if (
error is not None
and self.should_ignore_error is not None
and self.should_ignore_error(error)
):
if error is not None and self.should_ignore_error(error):
error = None
ctx.pop(error)

View file

@ -4,54 +4,16 @@ import os
import typing as t
from datetime import timedelta
from .cli import AppGroup
from .globals import current_app
from .helpers import send_from_directory
from .sansio.blueprints import Blueprint as SansioBlueprint
from .sansio.blueprints import BlueprintSetupState as BlueprintSetupState # noqa
from .sansio.scaffold import _sentinel
if t.TYPE_CHECKING: # pragma: no cover
from .wrappers import Response
class Blueprint(SansioBlueprint):
def __init__(
self,
name: str,
import_name: str,
static_folder: str | os.PathLike[str] | None = None,
static_url_path: str | None = None,
template_folder: str | os.PathLike[str] | None = None,
url_prefix: str | None = None,
subdomain: str | None = None,
url_defaults: dict[str, t.Any] | None = None,
root_path: str | None = None,
cli_group: str | None = _sentinel, # type: ignore
) -> None:
super().__init__(
name,
import_name,
static_folder,
static_url_path,
template_folder,
url_prefix,
subdomain,
url_defaults,
root_path,
cli_group,
)
#: The Click command group for registering CLI commands for this
#: object. The commands are available from the ``flask`` command
#: once the application has been discovered and blueprints have
#: been registered.
self.cli = AppGroup()
# Set the name of the Click group in case someone wants to add
# the app's commands to another CLI tool.
self.cli.name = self.name
def get_send_file_max_age(self, filename: str | None) -> int | None:
"""Used by :func:`send_file` to determine the ``max_age`` cache
value for a given file path if it wasn't passed.
@ -101,28 +63,29 @@ class Blueprint(SansioBlueprint):
t.cast(str, self.static_folder), filename, max_age=max_age
)
def open_resource(
self, resource: str, mode: str = "rb", encoding: str | None = "utf-8"
) -> t.IO[t.AnyStr]:
"""Open a resource file relative to :attr:`root_path` for reading. The
blueprint-relative equivalent of the app's :meth:`~.Flask.open_resource`
method.
def open_resource(self, resource: str, mode: str = "rb") -> t.IO[t.AnyStr]:
"""Open a resource file relative to :attr:`root_path` for
reading.
:param resource: Path to the resource relative to :attr:`root_path`.
:param mode: Open the file in this mode. Only reading is supported,
valid values are ``"r"`` (or ``"rt"``) and ``"rb"``.
:param encoding: Open the file with this encoding when opening in text
mode. This is ignored when opening in binary mode.
For example, if the file ``schema.sql`` is next to the file
``app.py`` where the ``Flask`` app is defined, it can be opened
with:
.. code-block:: python
with app.open_resource("schema.sql") as f:
conn.executescript(f.read())
:param resource: Path to the resource relative to
:attr:`root_path`.
:param mode: Open the file in this mode. Only reading is
supported, valid values are "r" (or "rt") and "rb".
Note this is a duplicate of the same method in the Flask
class.
.. versionchanged:: 3.1
Added the ``encoding`` parameter.
"""
if mode not in {"r", "rt", "rb"}:
raise ValueError("Resources can only be opened for reading.")
path = os.path.join(self.root_path, resource)
if mode == "rb":
return open(path, mode) # pyright: ignore
return open(path, mode, encoding=encoding)
return open(os.path.join(self.root_path, resource), mode)

View file

@ -229,13 +229,15 @@ def prepare_import(path: str) -> str:
@t.overload
def locate_app(
module_name: str, app_name: str | None, raise_if_not_found: t.Literal[True] = True
) -> Flask: ...
) -> Flask:
...
@t.overload
def locate_app(
module_name: str, app_name: str | None, raise_if_not_found: t.Literal[False] = ...
) -> Flask | None: ...
) -> Flask | None:
...
def locate_app(
@ -297,9 +299,6 @@ class ScriptInfo:
a bigger role. Typically it's created automatically by the
:class:`FlaskGroup` but you can also manually create it and pass it
onwards as click object.
.. versionchanged:: 3.1
Added the ``load_dotenv_defaults`` parameter and attribute.
"""
def __init__(
@ -307,7 +306,6 @@ class ScriptInfo:
app_import_path: str | None = None,
create_app: t.Callable[..., Flask] | None = None,
set_debug_flag: bool = True,
load_dotenv_defaults: bool = True,
) -> None:
#: Optionally the import path for the Flask application.
self.app_import_path = app_import_path
@ -318,16 +316,6 @@ class ScriptInfo:
#: this script info.
self.data: dict[t.Any, t.Any] = {}
self.set_debug_flag = set_debug_flag
self.load_dotenv_defaults = get_load_dotenv(load_dotenv_defaults)
"""Whether default ``.flaskenv`` and ``.env`` files should be loaded.
``ScriptInfo`` doesn't load anything, this is for reference when doing
the load elsewhere during processing.
.. versionadded:: 3.1
"""
self._loaded_app: Flask | None = None
def load_app(self) -> Flask:
@ -337,9 +325,9 @@ class ScriptInfo:
"""
if self._loaded_app is not None:
return self._loaded_app
app: Flask | None = None
if self.create_app is not None:
app = self.create_app()
app: Flask | None = self.create_app()
else:
if self.app_import_path:
path, name = (
@ -468,7 +456,7 @@ _app_option = click.Option(
def _set_debug(ctx: click.Context, param: click.Option, value: bool) -> bool | None:
# If the flag isn't provided, it will default to False. Don't use
# that, let debug be set by env in that case.
source = ctx.get_parameter_source(param.name)
source = ctx.get_parameter_source(param.name) # type: ignore[arg-type]
if source is not None and source in (
ParameterSource.DEFAULT,
@ -493,22 +481,23 @@ _debug_option = click.Option(
def _env_file_callback(
ctx: click.Context, param: click.Option, value: str | None
) -> str | None:
if value is None:
return None
import importlib
try:
import dotenv # noqa: F401
importlib.import_module("dotenv")
except ImportError:
# Only show an error if a value was passed, otherwise we still want to
# call load_dotenv and show a message without exiting.
if value is not None:
raise click.BadParameter(
"python-dotenv must be installed to load an env file.",
ctx=ctx,
param=param,
) from None
# Load if a value was passed, or we want to load default files, or both.
if value is not None or ctx.obj.load_dotenv_defaults:
load_dotenv(value, load_defaults=ctx.obj.load_dotenv_defaults)
raise click.BadParameter(
"python-dotenv must be installed to load an env file.",
ctx=ctx,
param=param,
) from None
# Don't check FLASK_SKIP_DOTENV, that only disables automatically
# loading .env and .flaskenv files.
load_dotenv(value)
return value
@ -517,11 +506,7 @@ def _env_file_callback(
_env_file_option = click.Option(
["-e", "--env-file"],
type=click.Path(exists=True, dir_okay=False),
help=(
"Load environment variables from this file, taking precedence over"
" those set by '.env' and '.flaskenv'. Variables set directly in the"
" environment take highest precedence. python-dotenv must be installed."
),
help="Load environment variables from this file. python-dotenv must be installed.",
is_eager=True,
expose_value=False,
callback=_env_file_callback,
@ -545,9 +530,6 @@ class FlaskGroup(AppGroup):
directory to the directory containing the first file found.
:param set_debug_flag: Set the app's debug flag.
.. versionchanged:: 3.1
``-e path`` takes precedence over default ``.env`` and ``.flaskenv`` files.
.. versionchanged:: 2.2
Added the ``-A/--app``, ``--debug/--no-debug``, ``-e/--env-file`` options.
@ -569,7 +551,7 @@ class FlaskGroup(AppGroup):
set_debug_flag: bool = True,
**extra: t.Any,
) -> None:
params: list[click.Parameter] = list(extra.pop("params", None) or ())
params = list(extra.pop("params", None) or ())
# Processing is done with option callbacks instead of a group
# callback. This allows users to make a custom group callback
# without losing the behavior. --env-file must come first so
@ -601,7 +583,15 @@ class FlaskGroup(AppGroup):
if self._loaded_plugin_commands:
return
for ep in importlib.metadata.entry_points(group="flask.commands"):
if sys.version_info >= (3, 10):
from importlib import metadata
else:
# Use a backport on Python < 3.10. We technically have
# importlib.metadata on 3.8+, but the API changed in 3.10,
# so use the backport for consistency.
import importlib_metadata as metadata
for ep in metadata.entry_points(group="flask.commands"):
self.add_command(ep.load(), ep.name)
self._loaded_plugin_commands = True
@ -628,7 +618,7 @@ class FlaskGroup(AppGroup):
# Push an app context for the loaded app unless it is already
# active somehow. This makes the context available to parameter
# and command callbacks without needing @with_appcontext.
if not current_app or current_app._get_current_object() is not app:
if not current_app or current_app._get_current_object() is not app: # type: ignore[attr-defined]
ctx.with_resource(app.app_context())
return app.cli.get_command(ctx, name)
@ -666,19 +656,20 @@ class FlaskGroup(AppGroup):
# when importing, blocking whatever command is being called.
os.environ["FLASK_RUN_FROM_CLI"] = "true"
# Attempt to load .env and .flask env files. The --env-file
# option can cause another file to be loaded.
if get_load_dotenv(self.load_dotenv):
load_dotenv()
if "obj" not in extra and "obj" not in self.context_settings:
extra["obj"] = ScriptInfo(
create_app=self.create_app,
set_debug_flag=self.set_debug_flag,
load_dotenv_defaults=self.load_dotenv,
create_app=self.create_app, set_debug_flag=self.set_debug_flag
)
return super().make_context(info_name, args, parent=parent, **extra)
def parse_args(self, ctx: click.Context, args: list[str]) -> list[str]:
if (not args and self.no_args_is_help) or (
len(args) == 1 and args[0] in self.get_help_option_names(ctx)
):
if not args and self.no_args_is_help:
# Attempt to load --env-file and --app early in case they
# were given as env vars. Otherwise no_args_is_help will not
# see commands from app.cli.
@ -695,26 +686,18 @@ def _path_is_ancestor(path: str, other: str) -> bool:
return os.path.join(path, other[len(path) :].lstrip(os.sep)) == other
def load_dotenv(
path: str | os.PathLike[str] | None = None, load_defaults: bool = True
) -> bool:
"""Load "dotenv" files to set environment variables. A given path takes
precedence over ``.env``, which takes precedence over ``.flaskenv``. After
loading and combining these files, values are only set if the key is not
already set in ``os.environ``.
def load_dotenv(path: str | os.PathLike[str] | None = None) -> bool:
"""Load "dotenv" files in order of precedence to set environment variables.
If an env var is already set it is not overwritten, so earlier files in the
list are preferred over later files.
This is a no-op if `python-dotenv`_ is not installed.
.. _python-dotenv: https://github.com/theskumar/python-dotenv#readme
:param path: Load the file at this location.
:param load_defaults: Search for and load the default ``.flaskenv`` and
``.env`` files.
:return: ``True`` if at least one env var was loaded.
.. versionchanged:: 3.1
Added the ``load_defaults`` parameter. A given path takes precedence
over default files.
:param path: Load the file at this location instead of searching.
:return: ``True`` if a file was loaded.
.. versionchanged:: 2.0
The current directory is not changed to the location of the
@ -734,33 +717,34 @@ def load_dotenv(
except ImportError:
if path or os.path.isfile(".env") or os.path.isfile(".flaskenv"):
click.secho(
" * Tip: There are .env files present. Install python-dotenv"
" to use them.",
" * Tip: There are .env or .flaskenv files present."
' Do "pip install python-dotenv" to use them.',
fg="yellow",
err=True,
)
return False
data: dict[str, str | None] = {}
# Always return after attempting to load a given path, don't load
# the default files.
if path is not None:
if os.path.isfile(path):
return dotenv.load_dotenv(path, encoding="utf-8")
if load_defaults:
for default_name in (".flaskenv", ".env"):
if not (default_path := dotenv.find_dotenv(default_name, usecwd=True)):
continue
return False
data |= dotenv.dotenv_values(default_path, encoding="utf-8")
loaded = False
if path is not None and os.path.isfile(path):
data |= dotenv.dotenv_values(path, encoding="utf-8")
for name in (".env", ".flaskenv"):
path = dotenv.find_dotenv(name, usecwd=True)
for key, value in data.items():
if key in os.environ or value is None:
if not path:
continue
os.environ[key] = value
dotenv.load_dotenv(path, encoding="utf-8")
loaded = True
return bool(data) # True if at least one env var was loaded.
return loaded # True if at least one file was located and loaded.
def show_server_banner(debug: bool, app_import_path: str | None) -> None:
@ -777,7 +761,7 @@ def show_server_banner(debug: bool, app_import_path: str | None) -> None:
click.echo(f" * Debug mode: {'on' if debug else 'off'}")
class CertParamType(click.ParamType[t.Any]):
class CertParamType(click.ParamType):
"""Click option type for the ``--cert`` option. Allows either an
existing file, the string ``'adhoc'``, or an import for a
:class:`~ssl.SSLContext` object.
@ -803,7 +787,7 @@ class CertParamType(click.ParamType[t.Any]):
try:
return self.path_type(value, param, ctx)
except click.BadParameter:
value = click.STRING(value, param, ctx).lower() # type: ignore[union-attr]
value = click.STRING(value, param, ctx).lower()
if value == "adhoc":
try:
@ -874,9 +858,7 @@ class SeparatedPathType(click.Path):
self, value: t.Any, param: click.Parameter | None, ctx: click.Context | None
) -> t.Any:
items = self.split_envvar_value(value)
# can't call no-arg super() inside list comprehension until Python 3.12
super_convert = super().convert
return [super_convert(item, param, ctx) for item in items]
return [super().convert(item, param, ctx) for item in items]
@click.command("run", short_help="Run a development server.")
@ -952,7 +934,7 @@ def run_command(
option.
"""
try:
app: WSGIApplication = info.load_app() # pyright: ignore
app: WSGIApplication = info.load_app()
except Exception as e:
if is_running_from_reloader():
# When reloading, print out the error immediately, but raise

View file

@ -27,10 +27,12 @@ class ConfigAttribute(t.Generic[T]):
self.get_converter = get_converter
@t.overload
def __get__(self, obj: None, owner: None) -> te.Self: ...
def __get__(self, obj: None, owner: None) -> te.Self:
...
@t.overload
def __get__(self, obj: App, owner: type[App]) -> T: ...
def __get__(self, obj: App, owner: type[App]) -> T:
...
def __get__(self, obj: App | None, owner: type[App] | None = None) -> T | te.Self:
if obj is None:
@ -150,13 +152,13 @@ class Config(dict): # type: ignore[type-arg]
.. versionadded:: 2.1
"""
prefix = f"{prefix}_"
len_prefix = len(prefix)
for key in sorted(os.environ):
if not key.startswith(prefix):
continue
value = os.environ[key]
key = key.removeprefix(prefix)
try:
value = loads(value)
@ -164,6 +166,9 @@ class Config(dict): # type: ignore[type-arg]
# Keep the value as a string if loading failed.
pass
# Change to key.removeprefix(prefix) on Python >= 3.9.
key = key[len_prefix:]
if "__" not in key:
# A non-nested key, set directly.
self[key] = value

Some files were not shown because too many files have changed in this diff Show more