Compare commits
No commits in common. "main" and "2.3.x" have entirely different histories.
143 changed files with 4858 additions and 7404 deletions
|
|
@ -1,7 +1,9 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e
|
set -e
|
||||||
python3 -m venv --upgrade-deps .venv
|
|
||||||
|
python3 -m venv .venv
|
||||||
. .venv/bin/activate
|
. .venv/bin/activate
|
||||||
|
pip install -U pip
|
||||||
pip install -r requirements/dev.txt
|
pip install -r requirements/dev.txt
|
||||||
pip install -e .
|
pip install -e .
|
||||||
pre-commit install --install-hooks
|
pre-commit install --install-hooks
|
||||||
|
|
|
||||||
|
|
@ -9,5 +9,5 @@ end_of_line = lf
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
max_line_length = 88
|
max_line_length = 88
|
||||||
|
|
||||||
[*.{css,html,js,json,jsx,scss,ts,tsx,yaml,yml}]
|
[*.{yml,yaml,json,js,css,html}]
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
|
|
||||||
25
.flake8
Normal file
25
.flake8
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
[flake8]
|
||||||
|
extend-select =
|
||||||
|
# bugbear
|
||||||
|
B
|
||||||
|
# bugbear opinions
|
||||||
|
B9
|
||||||
|
# implicit str concat
|
||||||
|
ISC
|
||||||
|
extend-ignore =
|
||||||
|
# slice notation whitespace, invalid
|
||||||
|
E203
|
||||||
|
# line length, handled by bugbear B950
|
||||||
|
E501
|
||||||
|
# bare except, handled by bugbear B001
|
||||||
|
E722
|
||||||
|
# zip with strict=, requires python >= 3.10
|
||||||
|
B905
|
||||||
|
# string formatting opinion, B028 renamed to B907
|
||||||
|
B028
|
||||||
|
B907
|
||||||
|
# up to 88 allowed by bugbear B950
|
||||||
|
max-line-length = 80
|
||||||
|
per-file-ignores =
|
||||||
|
# __init__ exports names
|
||||||
|
src/flask/__init__.py: F401
|
||||||
2
.github/ISSUE_TEMPLATE/bug-report.md
vendored
2
.github/ISSUE_TEMPLATE/bug-report.md
vendored
|
|
@ -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
|
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.
|
Replace this comment with a clear outline of what the bug is.
|
||||||
-->
|
-->
|
||||||
|
|
|
||||||
14
.github/ISSUE_TEMPLATE/config.yml
vendored
14
.github/ISSUE_TEMPLATE/config.yml
vendored
|
|
@ -1,11 +1,11 @@
|
||||||
blank_issues_enabled: false
|
blank_issues_enabled: false
|
||||||
contact_links:
|
contact_links:
|
||||||
- name: Security issue
|
- name: Security issue
|
||||||
url: https://github.com/pallets/flask/security/advisories/new
|
url: security@palletsprojects.com
|
||||||
about: Do not report security issues publicly. Create a private advisory.
|
about: Do not report security issues publicly. Email our security contact.
|
||||||
- name: Questions on GitHub Discussions
|
- name: Questions
|
||||||
url: https://github.com/pallets/flask/discussions/
|
url: https://stackoverflow.com/questions/tagged/flask?tab=Frequent
|
||||||
about: Ask questions about your own code on the Discussions tab.
|
about: Search for and ask questions about your code on Stack Overflow.
|
||||||
- name: Questions on Discord
|
- name: Questions and discussions
|
||||||
url: https://discord.gg/pallets
|
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
19
.github/SECURITY.md
vendored
Normal 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
9
.github/dependabot.yml
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "monthly"
|
||||||
|
day: "monday"
|
||||||
|
time: "16:00"
|
||||||
|
timezone: "UTC"
|
||||||
25
.github/pull_request_template.md
vendored
25
.github/pull_request_template.md
vendored
|
|
@ -1,7 +1,6 @@
|
||||||
<!--
|
<!--
|
||||||
Before opening a PR, open a ticket describing the issue or feature the
|
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
|
PR will address. Follow the steps in CONTRIBUTING.rst.
|
||||||
documentation, or other simple non-code changes.
|
|
||||||
|
|
||||||
Replace this comment with a description of the change. Describe how it
|
Replace this comment with a description of the change. Describe how it
|
||||||
addresses the linked ticket.
|
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
|
Link to relevant issues or previous PRs, one per line. Use "fixes" to
|
||||||
automatically close an issue.
|
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
|
If only docs were changed, these aren't relevant and can be removed.
|
||||||
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.
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
|
||||||
25
.github/workflows/lock.yaml
vendored
25
.github/workflows/lock.yaml
vendored
|
|
@ -1,26 +1,25 @@
|
||||||
name: Lock inactive closed issues
|
name: 'Lock threads'
|
||||||
# Lock closed issues that have not received any further activity for two weeks.
|
# Lock closed issues that have not received any further activity for
|
||||||
# This does not close open issues, only humans may do that. It is easier to
|
# two weeks. This does not close open issues, only humans may do that.
|
||||||
# respond to new issues with fresh examples rather than continuing discussions
|
# We find that it is easier to respond to new issues with fresh examples
|
||||||
# on old issues.
|
# rather than continuing discussions on old issues.
|
||||||
|
|
||||||
on:
|
on:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 0 * * *'
|
- cron: '0 0 * * *'
|
||||||
permissions: {}
|
|
||||||
|
permissions:
|
||||||
|
issues: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: lock
|
group: lock
|
||||||
cancel-in-progress: true
|
|
||||||
jobs:
|
jobs:
|
||||||
lock:
|
lock:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
|
||||||
issues: write
|
|
||||||
pull-requests: write
|
|
||||||
discussions: write
|
|
||||||
steps:
|
steps:
|
||||||
- uses: dessant/lock-threads@7266a7ce5c1df01b1c6db85bf8cd86c737dadbe7 # v6.0.0
|
- uses: dessant/lock-threads@c1b35aecc5cdb1a34539d14196df55838bb2f836
|
||||||
with:
|
with:
|
||||||
issue-inactive-days: 14
|
issue-inactive-days: 14
|
||||||
pr-inactive-days: 14
|
pr-inactive-days: 14
|
||||||
discussion-inactive-days: 14
|
|
||||||
|
|
|
||||||
29
.github/workflows/pre-commit.yaml
vendored
29
.github/workflows/pre-commit.yaml
vendored
|
|
@ -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
|
|
||||||
82
.github/workflows/publish.yaml
vendored
82
.github/workflows/publish.yaml
vendored
|
|
@ -1,62 +1,72 @@
|
||||||
name: Publish
|
name: Publish
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags: ['*']
|
tags:
|
||||||
permissions: {}
|
- '*'
|
||||||
concurrency:
|
|
||||||
group: publish-${{ github.event.push.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
outputs:
|
outputs:
|
||||||
artifact-id: ${{ steps.upload-artifact.outputs.artifact-id }}
|
hash: ${{ steps.hash.outputs.hash }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
- uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3
|
||||||
|
- uses: actions/setup-python@d27e3f3d7c64b4bbf8e4abfb9b63b83e846e0435
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
python-version: '3.x'
|
||||||
- uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
|
cache: 'pip'
|
||||||
with:
|
cache-dependency-path: 'requirements/*.txt'
|
||||||
enable-cache: false
|
- run: pip install -r requirements/build.txt
|
||||||
prune-cache: false
|
# Use the commit date instead of the current date during the build.
|
||||||
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
|
||||||
with:
|
|
||||||
python-version-file: pyproject.toml
|
|
||||||
- run: echo "SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)" >> $GITHUB_ENV
|
- run: echo "SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)" >> $GITHUB_ENV
|
||||||
- run: uv build
|
- run: python -m build
|
||||||
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
# Generate hashes used for provenance.
|
||||||
id: upload-artifact
|
- name: generate hash
|
||||||
|
id: hash
|
||||||
|
run: cd dist && echo "hash=$(sha256sum * | base64 -w0)" >> $GITHUB_OUTPUT
|
||||||
|
- uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce
|
||||||
with:
|
with:
|
||||||
name: dist
|
path: ./dist
|
||||||
path: dist/
|
provenance:
|
||||||
if-no-files-found: error
|
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.5.0
|
||||||
|
with:
|
||||||
|
base64-subjects: ${{ needs.build.outputs.hash }}
|
||||||
create-release:
|
create-release:
|
||||||
needs: [build]
|
# 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
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
- uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a
|
||||||
with:
|
|
||||||
artifact-ids: ${{ needs.build.outputs.artifact-id }}
|
|
||||||
path: dist/
|
|
||||||
- name: create release
|
- 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/* artifact/*
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ github.token }}
|
GH_TOKEN: ${{ github.token }}
|
||||||
publish-pypi:
|
publish-pypi:
|
||||||
needs: [build]
|
needs: ['provenance']
|
||||||
environment:
|
# Wait for approval before attempting to upload to PyPI. This allows reviewing the
|
||||||
name: publish
|
# files in the draft release.
|
||||||
url: https://pypi.org/project/Flask/${{ github.ref_name }}
|
environment: 'publish'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
id-token: write
|
id-token: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
- uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a
|
||||||
|
# Try uploading to Test PyPI first, in case something fails.
|
||||||
|
- uses: pypa/gh-action-pypi-publish@29930c9cf57955dc1b98162d0d8bc3ec80d9e75c
|
||||||
with:
|
with:
|
||||||
artifact-ids: ${{ needs.build.outputs.artifact-id }}
|
repository-url: https://test.pypi.org/legacy/
|
||||||
path: dist/
|
packages-dir: artifact/
|
||||||
- uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
|
- uses: pypa/gh-action-pypi-publish@29930c9cf57955dc1b98162d0d8bc3ec80d9e75c
|
||||||
with:
|
with:
|
||||||
packages-dir: "dist/"
|
packages-dir: artifact/
|
||||||
|
|
|
||||||
85
.github/workflows/tests.yaml
vendored
85
.github/workflows/tests.yaml
vendored
|
|
@ -1,63 +1,52 @@
|
||||||
name: Tests
|
name: Tests
|
||||||
on:
|
on:
|
||||||
pull_request:
|
|
||||||
paths-ignore: ['docs/**', 'README.md']
|
|
||||||
push:
|
push:
|
||||||
branches: [main, stable]
|
branches:
|
||||||
paths-ignore: ['docs/**', 'README.md']
|
- main
|
||||||
permissions: {}
|
- '*.x'
|
||||||
concurrency:
|
paths-ignore:
|
||||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
- 'docs/**'
|
||||||
cancel-in-progress: true
|
- '*.md'
|
||||||
|
- '*.rst'
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- '*.x'
|
||||||
|
paths-ignore:
|
||||||
|
- 'docs/**'
|
||||||
|
- '*.md'
|
||||||
|
- '*.rst'
|
||||||
jobs:
|
jobs:
|
||||||
tests:
|
tests:
|
||||||
name: ${{ matrix.name || matrix.python }}
|
name: ${{ matrix.name }}
|
||||||
runs-on: ${{ matrix.os || 'ubuntu-latest' }}
|
runs-on: ${{ matrix.os }}
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- {python: '3.14'}
|
- {name: Linux, python: '3.11', os: ubuntu-latest, tox: py311}
|
||||||
- {python: '3.14t'}
|
- {name: Windows, python: '3.11', os: windows-latest, tox: py311}
|
||||||
- {name: Windows, python: '3.14', os: windows-latest}
|
- {name: Mac, python: '3.11', os: macos-latest, tox: py311}
|
||||||
- {name: Mac, python: '3.14', os: macos-latest}
|
- {name: '3.12-dev', python: '3.12-dev', os: ubuntu-latest, tox: py312}
|
||||||
- {python: '3.13'}
|
- {name: '3.10', python: '3.10', os: ubuntu-latest, tox: py310}
|
||||||
- {python: '3.12'}
|
- {name: '3.9', python: '3.9', os: ubuntu-latest, tox: py39}
|
||||||
- {python: '3.11'}
|
- {name: '3.8', python: '3.8', os: ubuntu-latest, tox: py38}
|
||||||
- {python: '3.10'}
|
- {name: 'PyPy', python: 'pypy-3.10', os: ubuntu-latest, tox: pypy310}
|
||||||
- {name: PyPy, python: 'pypy-3.11', tox: pypy3.11}
|
- {name: 'Minimum Versions', python: '3.11', os: ubuntu-latest, tox: py311-min}
|
||||||
- {name: Minimum Versions, python: '3.14', tox: tests-min}
|
- {name: 'Development Versions', python: '3.8', os: ubuntu-latest, tox: py38-dev}
|
||||||
- {name: Development Versions, python: '3.10', tox: tests-dev}
|
- {name: Typing, python: '3.11', os: ubuntu-latest, tox: typing}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
- uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3
|
||||||
with:
|
- uses: actions/setup-python@d27e3f3d7c64b4bbf8e4abfb9b63b83e846e0435
|
||||||
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:
|
with:
|
||||||
python-version: ${{ matrix.python }}
|
python-version: ${{ matrix.python }}
|
||||||
- run: uv run --locked --no-default-groups --group dev tox run
|
cache: 'pip'
|
||||||
env:
|
cache-dependency-path: 'requirements/*.txt'
|
||||||
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
|
|
||||||
- name: cache mypy
|
- name: cache mypy
|
||||||
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
|
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8
|
||||||
with:
|
with:
|
||||||
path: ./.mypy_cache
|
path: ./.mypy_cache
|
||||||
key: mypy|${{ hashFiles('pyproject.toml') }}
|
key: mypy|${{ matrix.python }}|${{ hashFiles('pyproject.toml') }}
|
||||||
- run: uv run --locked --no-default-groups --group dev tox run -e typing
|
if: matrix.tox == 'typing'
|
||||||
|
- run: pip install tox
|
||||||
|
- run: tox run -e ${{ matrix.tox }}
|
||||||
|
|
|
||||||
22
.github/workflows/zizmor.yaml
vendored
22
.github/workflows/zizmor.yaml
vendored
|
|
@ -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
|
|
||||||
27
.gitignore
vendored
27
.gitignore
vendored
|
|
@ -1,8 +1,25 @@
|
||||||
.idea/
|
.DS_Store
|
||||||
.vscode/
|
.env
|
||||||
__pycache__/
|
.flaskenv
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
.venv/
|
||||||
|
env*
|
||||||
dist/
|
dist/
|
||||||
.coverage*
|
build/
|
||||||
htmlcov/
|
*.egg
|
||||||
|
*.egg-info/
|
||||||
.tox/
|
.tox/
|
||||||
|
.cache/
|
||||||
|
.pytest_cache/
|
||||||
|
.idea/
|
||||||
docs/_build/
|
docs/_build/
|
||||||
|
.vscode
|
||||||
|
|
||||||
|
# Coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
*,cover
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,37 @@
|
||||||
|
ci:
|
||||||
|
autoupdate_branch: "2.3.x"
|
||||||
|
autoupdate_schedule: monthly
|
||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
- repo: https://github.com/asottile/pyupgrade
|
||||||
rev: 5e2fb545eba1ea9dc051f6f962d52fe8f76a9794 # frozen: v0.15.13
|
rev: v3.10.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: ruff-check
|
- id: pyupgrade
|
||||||
- id: ruff-format
|
args: ["--py38-plus"]
|
||||||
- repo: https://github.com/astral-sh/uv-pre-commit
|
- repo: https://github.com/asottile/reorder-python-imports
|
||||||
rev: fa60a193803535a9e2accdb3ca4b1b584b1150cb # frozen: 0.11.15
|
rev: v3.10.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: uv-lock
|
- id: reorder-python-imports
|
||||||
- repo: https://github.com/codespell-project/codespell
|
name: Reorder Python imports (src, tests)
|
||||||
rev: 2ccb47ff45ad361a21071a7eedda4c37e6ae8c5a # frozen: v2.4.2
|
files: "^(?!examples/)"
|
||||||
|
args: ["--application-directories", "src"]
|
||||||
|
- repo: https://github.com/psf/black
|
||||||
|
rev: 23.7.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: codespell
|
- id: black
|
||||||
args: ['--write-changes']
|
- repo: https://github.com/PyCQA/flake8
|
||||||
|
rev: 6.1.0
|
||||||
|
hooks:
|
||||||
|
- id: flake8
|
||||||
|
additional_dependencies:
|
||||||
|
- flake8-bugbear
|
||||||
|
- flake8-implicit-str-concat
|
||||||
|
- repo: https://github.com/peterdemin/pip-compile-multi
|
||||||
|
rev: v2.6.3
|
||||||
|
hooks:
|
||||||
|
- id: pip-compile-multi-verify
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
rev: 3e8a8703264a2f4a69428a0aa4dcb512790b2c8c # frozen: v6.0.0
|
rev: v4.4.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: check-merge-conflict
|
|
||||||
- id: debug-statements
|
|
||||||
- id: fix-byte-order-marker
|
- id: fix-byte-order-marker
|
||||||
- id: trailing-whitespace
|
- id: trailing-whitespace
|
||||||
- id: end-of-file-fixer
|
- id: end-of-file-fixer
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,13 @@
|
||||||
version: 2
|
version: 2
|
||||||
build:
|
build:
|
||||||
os: ubuntu-24.04
|
os: ubuntu-20.04
|
||||||
tools:
|
tools:
|
||||||
python: '3.13'
|
python: "3.10"
|
||||||
commands:
|
python:
|
||||||
- asdf plugin add uv
|
install:
|
||||||
- asdf install uv latest
|
- requirements: requirements/docs.txt
|
||||||
- asdf global uv latest
|
- method: pip
|
||||||
- uv run --group docs sphinx-build -W -b dirhtml docs $READTHEDOCS_OUTPUT/html
|
path: .
|
||||||
|
sphinx:
|
||||||
|
builder: dirhtml
|
||||||
|
fail_on_warning: true
|
||||||
|
|
|
||||||
155
CHANGES.rst
155
CHANGES.rst
|
|
@ -1,151 +1,3 @@
|
||||||
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`
|
|
||||||
- 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.
|
|
||||||
:issue:`5381`
|
|
||||||
- Address more type findings when using mypy strict mode. :pr:`5383`
|
|
||||||
|
|
||||||
|
|
||||||
Version 3.0.0
|
|
||||||
-------------
|
|
||||||
|
|
||||||
Released 2023-09-30
|
|
||||||
|
|
||||||
- Remove previously deprecated code. :pr:`5223`
|
|
||||||
- Deprecate the ``__version__`` attribute. Use feature detection, or
|
|
||||||
``importlib.metadata.version("flask")``, instead. :issue:`5230`
|
|
||||||
- Restructure the code such that the Flask (app) and Blueprint
|
|
||||||
classes have Sans-IO bases. :pr:`5127`
|
|
||||||
- Allow self as an argument to url_for. :pr:`5264`
|
|
||||||
- Require Werkzeug >= 3.0.0.
|
|
||||||
|
|
||||||
|
|
||||||
Version 2.3.3
|
Version 2.3.3
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
|
|
@ -164,7 +16,6 @@ Released 2023-05-01
|
||||||
|
|
||||||
- Set ``Vary: Cookie`` header when the session is accessed, modified, or refreshed.
|
- Set ``Vary: Cookie`` header when the session is accessed, modified, or refreshed.
|
||||||
- Update Werkzeug requirement to >=2.3.3 to apply recent bug fixes.
|
- Update Werkzeug requirement to >=2.3.3 to apply recent bug fixes.
|
||||||
:ghsa:`m2qf-hxjv-5gpq`
|
|
||||||
|
|
||||||
|
|
||||||
Version 2.3.1
|
Version 2.3.1
|
||||||
|
|
@ -935,7 +786,7 @@ Released 2018-04-26
|
||||||
explicitly for each exception if you want to avoid traversing the
|
explicitly for each exception if you want to avoid traversing the
|
||||||
MRO. :pr:`2362`
|
MRO. :pr:`2362`
|
||||||
- Fix incorrect JSON encoding of aware, non-UTC datetimes. :pr:`2374`
|
- 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`
|
``Flask.jinja_env`` was already accessed. :pr:`2373`
|
||||||
- The following old deprecated code was removed. :issue:`2385`
|
- The following old deprecated code was removed. :issue:`2385`
|
||||||
|
|
||||||
|
|
@ -1417,7 +1268,7 @@ Released 2011-09-29, codename Rakija
|
||||||
of Flask itself and no longer of the test client. This cleaned up
|
of Flask itself and no longer of the test client. This cleaned up
|
||||||
some internal logic and lowers the odds of runaway request contexts
|
some internal logic and lowers the odds of runaway request contexts
|
||||||
in unittests.
|
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
|
returning the correct names when blueprints or modules were
|
||||||
involved.
|
involved.
|
||||||
|
|
||||||
|
|
@ -1503,7 +1354,7 @@ Released 2010-12-31
|
||||||
|
|
||||||
- Fixed an issue where the default ``OPTIONS`` response was not
|
- Fixed an issue where the default ``OPTIONS`` response was not
|
||||||
exposing all valid methods in the ``Allow`` header.
|
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
|
template load path. Previously this caused issues with module
|
||||||
setups.
|
setups.
|
||||||
- Fixed an issue where the subdomain setting for modules was ignored
|
- Fixed an issue where the subdomain setting for modules was ignored
|
||||||
|
|
|
||||||
76
CODE_OF_CONDUCT.md
Normal file
76
CODE_OF_CONDUCT.md
Normal 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
238
CONTRIBUTING.rst
Normal 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/>`__.
|
||||||
53
README.md
53
README.md
|
|
@ -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
80
README.rst
Normal 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
BIN
docs/_static/flask-horizontal.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
15
docs/_static/flask-icon.svg
vendored
15
docs/_static/flask-icon.svg
vendored
|
|
@ -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 |
17
docs/_static/flask-logo.svg
vendored
17
docs/_static/flask-logo.svg
vendored
|
|
@ -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 |
23
docs/_static/flask-name.svg
vendored
23
docs/_static/flask-name.svg
vendored
|
|
@ -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
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
BIN
docs/_static/shortcut-icon.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.9 KiB |
136
docs/api.rst
136
docs/api.rst
|
|
@ -31,15 +31,17 @@ Incoming Request Data
|
||||||
:inherited-members:
|
:inherited-members:
|
||||||
:exclude-members: json_module
|
:exclude-members: json_module
|
||||||
|
|
||||||
.. data:: request
|
.. attribute:: request
|
||||||
|
|
||||||
A proxy to the request data for the current request, an instance of
|
To access incoming request data, you can use the global `request`
|
||||||
:class:`.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
|
This is a proxy. See :ref:`notes-on-proxies` for more information.
|
||||||
active.
|
|
||||||
|
|
||||||
This is a proxy. See :ref:`context-visibility` for more information.
|
The request object is an instance of a :class:`~flask.Request`.
|
||||||
|
|
||||||
|
|
||||||
Response Objects
|
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
|
contents, but can't modify it unless they know the secret key, so make sure to
|
||||||
set that to something complex and unguessable.
|
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
|
The session object works pretty much like an ordinary dict, with the
|
||||||
:class:`.SessionMixin`.
|
difference that it keeps track of modifications.
|
||||||
|
|
||||||
This is only available when a :doc:`request context </appcontext>` is
|
This is a proxy. See :ref:`notes-on-proxies` for more information.
|
||||||
active.
|
|
||||||
|
|
||||||
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
|
.. attribute:: new
|
||||||
keys. It cannot track modifications to mutable values, you need to set
|
|
||||||
:attr:`~.SessionMixin.modified` manually when modifying a list, dict, etc.
|
|
||||||
|
|
||||||
.. code-block:: python
|
``True`` if the session is new, ``False`` otherwise.
|
||||||
|
|
||||||
# appending to a list is not detected
|
.. attribute:: modified
|
||||||
session["numbers"].append(42)
|
|
||||||
|
``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
|
# so mark it as modified yourself
|
||||||
session.modified = True
|
session.modified = True
|
||||||
|
|
||||||
The session is persisted across requests using a cookie. By default the
|
.. attribute:: permanent
|
||||||
users's browser will clear the cookie when it is closed. Set
|
|
||||||
:attr:`~.SessionMixin.permanent` to ``True`` to persist the cookie for
|
If set to ``True`` the session lives for
|
||||||
:data:`PERMANENT_SESSION_LIFETIME`.
|
: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
|
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
|
threaded environments. Flask provides you with a special object that
|
||||||
ensures it is only valid for the active request and that will return
|
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
|
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
|
.. data:: g
|
||||||
|
|
||||||
A proxy to a namespace object used to store data during a single request or
|
A namespace object that can store data during an
|
||||||
app context. An instance of :attr:`.Flask.app_ctx_globals_class`, which
|
:doc:`application context </appcontext>`. This is an instance of
|
||||||
defaults to :class:`._AppCtxGlobals`.
|
: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
|
This is a good place to store resources during a request. For
|
||||||
:meth:`~.Flask.before_request` function could load a user object from a
|
example, a ``before_request`` function could load a user object from
|
||||||
session id, then set ``g.user`` to be used in the view function.
|
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:`notes-on-proxies` for more information.
|
||||||
|
|
||||||
This is a proxy. See :ref:`context-visibility` for more information.
|
|
||||||
|
|
||||||
.. versionchanged:: 0.10
|
.. versionchanged:: 0.10
|
||||||
Bound to the application context instead of the request context.
|
Bound to the application context instead of the request context.
|
||||||
|
|
@ -177,16 +185,17 @@ Useful Functions and Classes
|
||||||
|
|
||||||
.. data:: current_app
|
.. data:: current_app
|
||||||
|
|
||||||
A proxy to the :class:`.Flask` application handling the current request or
|
A proxy to the application handling the current request. This is
|
||||||
other activity.
|
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
|
This is only available when an
|
||||||
it can't be imported, such as when using the application factory pattern or
|
:doc:`application context </appcontext>` is pushed. This happens
|
||||||
in blueprints and extensions.
|
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:`notes-on-proxies` for more information.
|
||||||
|
|
||||||
This is a proxy. See :ref:`context-visibility` for more information.
|
|
||||||
|
|
||||||
.. autofunction:: has_request_context
|
.. autofunction:: has_request_context
|
||||||
|
|
||||||
|
|
@ -290,31 +299,31 @@ Stream Helpers
|
||||||
Useful Internals
|
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
|
.. autoclass:: flask.ctx.AppContext
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
.. data:: flask.globals.app_ctx
|
.. 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.
|
This is an internal object that is essential to how Flask handles
|
||||||
Accessing this should not be needed in most cases. Most likely you want
|
requests. Accessing this should not be needed in most cases. Most
|
||||||
:data:`.current_app`, :data:`.g`, :data:`.request`, and :data:`.session` instead.
|
likely you want :data:`current_app` and :data:`g` 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.
|
|
||||||
|
|
||||||
.. autoclass:: flask.blueprints.BlueprintSetupState
|
.. autoclass:: flask.blueprints.BlueprintSetupState
|
||||||
:members:
|
:members:
|
||||||
|
|
@ -510,6 +519,11 @@ Signals are provided by the `Blinker`_ library. See :doc:`signals` for an introd
|
||||||
|
|
||||||
.. versionadded:: 0.10
|
.. versionadded:: 0.10
|
||||||
|
|
||||||
|
.. data:: signals.signals_available
|
||||||
|
|
||||||
|
.. deprecated:: 2.3
|
||||||
|
Will be removed in Flask 2.4. Signals are always available
|
||||||
|
|
||||||
|
|
||||||
Class-Based Views
|
Class-Based Views
|
||||||
-----------------
|
-----------------
|
||||||
|
|
@ -596,7 +610,7 @@ This specifies that ``/users/`` will be the URL for page one and
|
||||||
``/users/page/N`` will be the URL for page ``N``.
|
``/users/page/N`` will be the URL for page ``N``.
|
||||||
|
|
||||||
If a URL contains a default value, it will be redirected to its simpler
|
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``
|
be redirected to ``/users/``. If your route handles ``GET`` and ``POST``
|
||||||
requests, make sure the default route only handles ``GET``, as redirects
|
requests, make sure the default route only handles ``GET``, as redirects
|
||||||
can't preserve form data. ::
|
can't preserve form data. ::
|
||||||
|
|
|
||||||
|
|
@ -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
|
The Application Context
|
||||||
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.
|
|
||||||
|
|
||||||
When handling a request, the context is referred to as the "request context"
|
The application context keeps track of the application-level data during
|
||||||
because it contains request data in addition to application data. Otherwise,
|
a request, CLI command, or other activity. Rather than passing the
|
||||||
such as during a CLI command, it is referred to as the "app context". During an
|
application around to each function, the :data:`current_app` and
|
||||||
app context, :data:`.current_app` and :data:`.g` are available, while during a
|
:data:`g` proxies are accessed instead.
|
||||||
request context :data:`.request` and :data:`.session` are also available.
|
|
||||||
|
|
||||||
|
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
|
Purpose of the Context
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
The context and proxies help solve two development issues: circular imports, and
|
The :class:`Flask` application object has attributes, such as
|
||||||
passing around global data during a request.
|
: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
|
Flask solves this issue with the *application context*. Rather than
|
||||||
:attr:`~.Flask.config`, that are useful to access within views and other
|
referring to an ``app`` directly, you use the :data:`current_app`
|
||||||
functions. However, importing the ``app`` instance within the modules in your
|
proxy, which points to the application handling the current activity.
|
||||||
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.
|
|
||||||
|
|
||||||
When the application handles a request, it creates a :class:`.Request` object.
|
Flask automatically *pushes* an application context when handling a
|
||||||
Because a *worker* handles only one request at a time, the request data can be
|
request. View functions, error handlers, and other functions that run
|
||||||
considered global to that worker during that request. Passing it as an argument
|
during a request will have access to :data:`current_app`.
|
||||||
through every function during the request becomes verbose and redundant.
|
|
||||||
|
|
||||||
Flask solves these issues with the *active context* pattern. Rather than
|
Flask will also automatically push an app context when running CLI
|
||||||
importing an ``app`` directly, or having to pass it and the request through to
|
commands registered with :attr:`Flask.cli` using ``@app.cli.command()``.
|
||||||
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.
|
|
||||||
|
|
||||||
|
|
||||||
Context During Setup
|
Lifetime of the Context
|
||||||
--------------------
|
-----------------------
|
||||||
|
|
||||||
If you try to access :data:`.current_app`, :data:`.g`, or anything that uses it,
|
The application context is created and destroyed as necessary. When a
|
||||||
outside an app context, you'll get this error message:
|
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
|
.. code-block:: pytb
|
||||||
|
|
||||||
RuntimeError: Working outside of application context.
|
RuntimeError: Working outside of application context.
|
||||||
|
|
||||||
Attempted to use functionality that expected a current application to be
|
This typically means that you attempted to use functionality that
|
||||||
set. To solve this, set up an app context using 'with app.app_context()'.
|
needed to interface with the current application object in some way.
|
||||||
See the documentation on app context for more information.
|
To solve this, set up an application context with app.app_context().
|
||||||
|
|
||||||
If you see that error while configuring your application, such as when
|
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
|
initializing an extension, you can push a context manually since you
|
||||||
access to the ``app``. Use :meth:`.Flask.app_context` in a ``with`` block.
|
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
|
||||||
.. code-block:: python
|
to :data:`current_app`. ::
|
||||||
|
|
||||||
def create_app():
|
def create_app():
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
@ -67,121 +78,70 @@ access to the ``app``. Use :meth:`.Flask.app_context` in a ``with`` block.
|
||||||
|
|
||||||
return app
|
return app
|
||||||
|
|
||||||
If you see that error somewhere else in your code not related to setting up the
|
If you see that error somewhere else in your code not related to
|
||||||
application, it most likely indicates that you should move that code into a view
|
configuring the application, it most likely indicates that you should
|
||||||
function or CLI command.
|
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
|
The application context is a good place to store common data during a
|
||||||
tests.
|
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
|
.. note::
|
||||||
it, outside a request context, you'll get this error message:
|
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
|
For example, you can manage a database connection using this pattern::
|
||||||
documentation on request context for more information.
|
|
||||||
|
|
||||||
This will probably only happen during tests. If you see that error somewhere
|
from flask import g
|
||||||
else in your code not related to testing, it most likely indicates that you
|
|
||||||
should move that code into a view function.
|
|
||||||
|
|
||||||
The primary way to solve this is to use :meth:`.Flask.test_client` to simulate
|
def get_db():
|
||||||
a full request.
|
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
|
return g.db
|
||||||
:meth:`.Flask.test_request_context` in a ``with`` block.
|
|
||||||
|
|
||||||
.. code-block:: python
|
@app.teardown_appcontext
|
||||||
|
def teardown_db(exception):
|
||||||
|
db = g.pop('db', None)
|
||||||
|
|
||||||
def generate_report(year):
|
if db is not None:
|
||||||
format = request.args.get("format")
|
db.close()
|
||||||
...
|
|
||||||
|
|
||||||
with app.test_request_context(
|
During a request, every call to ``get_db()`` will return the same
|
||||||
"/make_report/2017", query_string={"format": "short"}
|
connection, and it will be closed automatically at the end of the
|
||||||
):
|
request.
|
||||||
generate_report()
|
|
||||||
|
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
|
The following signals are sent: :data:`appcontext_pushed`,
|
||||||
command, or ``with`` block. Various callbacks and signals registered with the
|
:data:`appcontext_tearing_down`, and :data:`appcontext_popped`.
|
||||||
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.
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
well as all the HTTP method handlers in views that inherit from the
|
||||||
:class:`flask.views.MethodView` class.
|
: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
|
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
|
handle many concurrent requests, long running requests, and websockets
|
||||||
without requiring multiple worker processes or threads.
|
without requiring multiple worker processes or threads.
|
||||||
|
|
||||||
It has also already been possible to :doc:`run Flask with Gevent </gevent>` to
|
It has also already been possible to run Flask with Gevent or Eventlet
|
||||||
get many of the benefits of async request handling. Gevent patches low-level
|
to get many of the benefits of async request handling. These libraries
|
||||||
Python functions to accomplish this, whereas ``async``/``await`` and ASGI use
|
patch low-level Python functions to accomplish this, whereas ``async``/
|
||||||
standard, modern Python capabilities. Deciding whether you should use gevent
|
``await`` and ASGI use standard, modern Python capabilities. Deciding
|
||||||
with Flask, or Quart, or something else is ultimately up to understanding the
|
whether you should use Flask, Quart, or something else is ultimately up
|
||||||
specific needs of your project.
|
to understanding the specific needs of your project.
|
||||||
|
|
||||||
.. _Quart: https://quart.palletsprojects.com
|
.. _Quart: https://github.com/pallets/quart
|
||||||
.. _ASGI: https://asgi.readthedocs.io
|
.. _ASGI: https://asgi.readthedocs.io/en/latest/
|
||||||
|
|
||||||
|
|
||||||
Extensions
|
Extensions
|
||||||
|
|
@ -114,6 +126,6 @@ implemented async support, or make a feature request or PR to them.
|
||||||
Other event loops
|
Other event loops
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
At the moment Flask only supports :mod:`asyncio`. It's possible to override
|
At the moment Flask only supports :mod:`asyncio`. It's possible to
|
||||||
:meth:`flask.Flask.ensure_sync` to change how async functions are wrapped to use
|
override :meth:`flask.Flask.ensure_sync` to change how async functions
|
||||||
a different library. See :ref:`gevent-asyncio` for an example.
|
are wrapped to use a different library.
|
||||||
|
|
|
||||||
23
docs/conf.py
23
docs/conf.py
|
|
@ -11,23 +11,16 @@ release, version = get_version("Flask")
|
||||||
|
|
||||||
# General --------------------------------------------------------------
|
# General --------------------------------------------------------------
|
||||||
|
|
||||||
default_role = "code"
|
master_doc = "index"
|
||||||
extensions = [
|
extensions = [
|
||||||
"sphinx.ext.autodoc",
|
"sphinx.ext.autodoc",
|
||||||
"sphinx.ext.extlinks",
|
|
||||||
"sphinx.ext.intersphinx",
|
"sphinx.ext.intersphinx",
|
||||||
"sphinxcontrib.log_cabinet",
|
"sphinxcontrib.log_cabinet",
|
||||||
"sphinx_tabs.tabs",
|
|
||||||
"pallets_sphinx_themes",
|
"pallets_sphinx_themes",
|
||||||
|
"sphinx_issues",
|
||||||
|
"sphinx_tabs.tabs",
|
||||||
]
|
]
|
||||||
autodoc_member_order = "bysource"
|
|
||||||
autodoc_typehints = "description"
|
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 = {
|
intersphinx_mapping = {
|
||||||
"python": ("https://docs.python.org/3/", None),
|
"python": ("https://docs.python.org/3/", None),
|
||||||
"werkzeug": ("https://werkzeug.palletsprojects.com/", None),
|
"werkzeug": ("https://werkzeug.palletsprojects.com/", None),
|
||||||
|
|
@ -38,6 +31,7 @@ intersphinx_mapping = {
|
||||||
"wtforms": ("https://wtforms.readthedocs.io/", None),
|
"wtforms": ("https://wtforms.readthedocs.io/", None),
|
||||||
"blinker": ("https://blinker.readthedocs.io/", None),
|
"blinker": ("https://blinker.readthedocs.io/", None),
|
||||||
}
|
}
|
||||||
|
issues_github_path = "pallets/flask"
|
||||||
|
|
||||||
# HTML -----------------------------------------------------------------
|
# HTML -----------------------------------------------------------------
|
||||||
|
|
||||||
|
|
@ -58,13 +52,14 @@ html_sidebars = {
|
||||||
}
|
}
|
||||||
singlehtml_sidebars = {"index": ["project.html", "localtoc.html", "ethicalads.html"]}
|
singlehtml_sidebars = {"index": ["project.html", "localtoc.html", "ethicalads.html"]}
|
||||||
html_static_path = ["_static"]
|
html_static_path = ["_static"]
|
||||||
html_favicon = "_static/flask-icon.svg"
|
html_favicon = "_static/shortcut-icon.png"
|
||||||
html_logo = "_static/flask-logo.svg"
|
html_logo = "_static/flask-vertical.png"
|
||||||
html_title = f"Flask Documentation ({version})"
|
html_title = f"Flask Documentation ({version})"
|
||||||
html_show_sourcelink = False
|
html_show_sourcelink = False
|
||||||
|
|
||||||
gettext_uuid = True
|
# LaTeX ----------------------------------------------------------------
|
||||||
gettext_compact = False
|
|
||||||
|
latex_documents = [(master_doc, f"Flask-{version}.tex", html_title, author, "manual")]
|
||||||
|
|
||||||
# Local Extensions -----------------------------------------------------
|
# Local Extensions -----------------------------------------------------
|
||||||
|
|
||||||
|
|
|
||||||
134
docs/config.rst
134
docs/config.rst
|
|
@ -79,7 +79,7 @@ The following configuration values are used internally by Flask:
|
||||||
|
|
||||||
.. py:data:: TESTING
|
.. 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
|
the app's error handlers. Extensions may also change their behavior to
|
||||||
facilitate easier testing. You should enable this in your own tests.
|
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``
|
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
|
.. py:data:: SESSION_COOKIE_NAME
|
||||||
|
|
||||||
The name of the session cookie. Can be changed in case you already have a
|
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``
|
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
|
.. versionchanged:: 2.3
|
||||||
Not set by default, does not fall back to ``SERVER_NAME``.
|
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``
|
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
|
.. py:data:: SESSION_COOKIE_SAMESITE
|
||||||
|
|
||||||
Restrict how cookies are sent with requests from external sites. Can
|
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``
|
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
|
.. 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
|
If set, ``url_for`` can generate external URLs with only an application
|
||||||
subdomain from the request.
|
context instead of a request context.
|
||||||
|
|
||||||
Must be set for ``url_for`` to generate external URLs outside of a
|
|
||||||
request context.
|
|
||||||
|
|
||||||
Default: ``None``
|
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
|
.. versionchanged:: 2.3
|
||||||
Does not affect ``SESSION_COOKIE_DOMAIN``.
|
Does not affect ``SESSION_COOKIE_DOMAIN``.
|
||||||
|
|
||||||
|
|
@ -319,54 +253,12 @@ The following configuration values are used internally by Flask:
|
||||||
|
|
||||||
.. py:data:: MAX_CONTENT_LENGTH
|
.. py:data:: MAX_CONTENT_LENGTH
|
||||||
|
|
||||||
The maximum number of bytes that will be read during this request. If
|
Don't read more than this many bytes from the incoming request data. If not
|
||||||
this limit is exceeded, a 413 :exc:`~werkzeug.exceptions.RequestEntityTooLarge`
|
set and the request does not specify a ``CONTENT_LENGTH``, no data will be
|
||||||
error is raised. If it is set to ``None``, no limit is enforced at the
|
read for security.
|
||||||
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.
|
|
||||||
|
|
||||||
Default: ``None``
|
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
|
.. py:data:: TEMPLATES_AUTO_RELOAD
|
||||||
|
|
||||||
Reload templates when they are changed. If not set, it will be enabled in
|
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
|
``4093``. Larger cookies may be silently ignored by browsers. Set to
|
||||||
``0`` to disable the warning.
|
``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
|
.. versionadded:: 0.4
|
||||||
``LOGGER_NAME``
|
``LOGGER_NAME``
|
||||||
|
|
||||||
|
|
@ -445,10 +331,6 @@ The following configuration values are used internally by Flask:
|
||||||
.. versionchanged:: 2.3
|
.. versionchanged:: 2.3
|
||||||
``ENV`` was removed.
|
``ENV`` was removed.
|
||||||
|
|
||||||
.. versionadded:: 3.1
|
|
||||||
Added :data:`PROVIDE_AUTOMATIC_OPTIONS` to control the default
|
|
||||||
addition of autogenerated OPTIONS responses.
|
|
||||||
|
|
||||||
|
|
||||||
Configuring from Python Files
|
Configuring from Python Files
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1 @@
|
||||||
Contributing
|
.. include:: ../CONTRIBUTING.rst
|
||||||
============
|
|
||||||
|
|
||||||
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/
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ wrapping the Flask app,
|
||||||
asgi_app = WsgiToAsgi(app)
|
asgi_app = WsgiToAsgi(app)
|
||||||
|
|
||||||
and then serving the ``asgi_app`` with the ASGI server, e.g. using
|
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
|
.. sourcecode:: text
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,80 @@
|
||||||
:orphan:
|
|
||||||
|
|
||||||
eventlet
|
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.
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,15 @@ configurable and production-tested servers.
|
||||||
|
|
||||||
`gevent`_ allows writing asynchronous, coroutine-based code that looks
|
`gevent`_ allows writing asynchronous, coroutine-based code that looks
|
||||||
like standard synchronous Python. It uses `greenlet`_ to enable task
|
like standard synchronous Python. It uses `greenlet`_ to enable task
|
||||||
switching without writing ``async/await`` or using ``asyncio``. This is
|
switching without writing ``async/await`` or using ``asyncio``.
|
||||||
not the same as Python's ``async/await``, or the ASGI server spec.
|
|
||||||
|
: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
|
gevent provides a WSGI server that can handle many connections at once
|
||||||
instead of one per worker process. See :doc:`/gevent` for more
|
instead of one per worker process. You must actually use gevent in your
|
||||||
information about enabling it in your application.
|
own code to see any benefit to using the server.
|
||||||
|
|
||||||
.. _gevent: https://www.gevent.org/
|
.. _gevent: https://www.gevent.org/
|
||||||
.. _greenlet: https://greenlet.readthedocs.io/en/latest/
|
.. _greenlet: https://greenlet.readthedocs.io/en/latest/
|
||||||
|
|
@ -21,7 +24,8 @@ information about enabling it in your application.
|
||||||
Installing
|
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.
|
PyPy>=7.3.7 is required.
|
||||||
|
|
||||||
Create a virtualenv, install your application, then install ``gevent``.
|
Create a virtualenv, install your application, then install ``gevent``.
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ multiple worker implementations for performance tuning.
|
||||||
* It does not support Windows (but does run on WSL).
|
* It does not support Windows (but does run on WSL).
|
||||||
* It is easy to install as it does not require additional dependencies
|
* It is easy to install as it does not require additional dependencies
|
||||||
or compilation.
|
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
|
This page outlines the basics of running Gunicorn. Be sure to read its
|
||||||
`documentation`_ and use ``gunicorn --help`` to understand what features
|
`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.
|
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,
|
The default sync worker is appropriate for many use cases. If you need
|
||||||
long running, concurrent connections, Gunicorn provides an asynchronous worker
|
asynchronous support, Gunicorn provides workers using either `gevent`_
|
||||||
using `gevent`_. This is not the same as Python's ``async/await``, or the ASGI
|
or `eventlet`_. This is not the same as Python's ``async/await``, or the
|
||||||
server spec. See :doc:`/gevent` for more information about enabling it in your
|
ASGI server spec. You must actually use gevent/eventlet in your own code
|
||||||
application.
|
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
|
To use gevent:
|
||||||
required.
|
|
||||||
|
|
||||||
.. code-block:: text
|
.. code-block:: text
|
||||||
|
|
||||||
|
|
@ -114,3 +115,16 @@ required.
|
||||||
Listening at: http://127.0.0.1:8000 (x)
|
Listening at: http://127.0.0.1:8000 (x)
|
||||||
Using worker: gevent
|
Using worker: gevent
|
||||||
Booting worker with pid: x
|
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/
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ discusses platforms that can manage this for you.
|
||||||
mod_wsgi
|
mod_wsgi
|
||||||
uwsgi
|
uwsgi
|
||||||
gevent
|
gevent
|
||||||
|
eventlet
|
||||||
asgi
|
asgi
|
||||||
|
|
||||||
WSGI servers have HTTP servers built-in. However, a dedicated HTTP
|
WSGI servers have HTTP servers built-in. However, a dedicated HTTP
|
||||||
|
|
|
||||||
|
|
@ -119,16 +119,15 @@ IP address in your browser.
|
||||||
Async with gevent
|
Async with gevent
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
The default sync worker is appropriate for most use cases. If you need numerous,
|
The default sync worker is appropriate for many use cases. If you need
|
||||||
long running, concurrent connections, uWSGI provides an asynchronous worker
|
asynchronous support, uWSGI provides a `gevent`_ worker. This is not the
|
||||||
using `gevent`_. This is not the same as Python's ``async/await``, or the ASGI
|
same as Python's ``async/await``, or the ASGI server spec. You must
|
||||||
server spec. See :doc:`/gevent` for more information about enabling it in your
|
actually use gevent in your own code to see any benefit to using the
|
||||||
application.
|
worker.
|
||||||
|
|
||||||
.. _gevent: https://www.gevent.org/
|
When using gevent, greenlet>=1.0 is required, otherwise context locals
|
||||||
|
such as ``request`` will not work as expected. When using PyPy,
|
||||||
When using gevent, greenlet>=1.0 is required. When using PyPy, PyPy>=7.3.7 is
|
PyPy>=7.3.7 is required.
|
||||||
required.
|
|
||||||
|
|
||||||
.. code-block:: text
|
.. code-block:: text
|
||||||
|
|
||||||
|
|
@ -141,3 +140,6 @@ required.
|
||||||
spawned uWSGI worker 1 (pid: x, cores: 100)
|
spawned uWSGI worker 1 (pid: x, cores: 100)
|
||||||
spawned uWSGI http 1 (pid: x)
|
spawned uWSGI http 1 (pid: x)
|
||||||
*** running gevent loop engine [addr:x] ***
|
*** running gevent loop engine [addr:x] ***
|
||||||
|
|
||||||
|
|
||||||
|
.. _gevent: https://www.gevent.org/
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ reverse proxy such as :doc:`nginx` or :doc:`apache-httpd` should be used
|
||||||
in front of Waitress.
|
in front of Waitress.
|
||||||
|
|
||||||
You can bind to all external IPs on a non-privileged port by not
|
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.
|
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
|
``0.0.0.0`` is not a valid address to navigate to, you'd use a specific
|
||||||
|
|
|
||||||
|
|
@ -96,10 +96,10 @@ is ambiguous.
|
||||||
One Template Engine
|
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
|
pluggable template engine interface? You can obviously use a different
|
||||||
template engine, but Flask will still configure Jinja for you. While
|
template engine, but Flask will still configure Jinja2 for you. While
|
||||||
that limitation that Jinja is *always* configured will probably go away,
|
that limitation that Jinja2 is *always* configured will probably go away,
|
||||||
the decision to bundle one template engine and use that will not.
|
the decision to bundle one template engine and use that will not.
|
||||||
|
|
||||||
Template engines are like programming languages and each of those engines
|
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
|
all work the same: you tell the engine to evaluate a template with a set
|
||||||
of variables and take the return value as string.
|
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,
|
extensive filter system, a certain way to do template inheritance,
|
||||||
support for reusable blocks (macros) that can be used from inside
|
support for reusable blocks (macros) that can be used from inside
|
||||||
templates and also from Python code, supports iterative template
|
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
|
When it comes to connecting a template engine with an application or
|
||||||
framework there is more than just rendering templates. For instance,
|
framework there is more than just rendering templates. For instance,
|
||||||
Flask uses Jinja's extensive autoescaping support. Also it provides
|
Flask uses Jinja2's extensive autoescaping support. Also it provides
|
||||||
ways to access macros from Jinja templates.
|
ways to access macros from Jinja2 templates.
|
||||||
|
|
||||||
A template abstraction layer that would not take the unique features of
|
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
|
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.
|
production use on a variety of needs.
|
||||||
|
|
||||||
Why does Flask call itself a microframework and yet it depends on two
|
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
|
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
|
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
|
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.
|
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
|
Flask uses thread local objects (context local objects in fact, they
|
||||||
current app and request data to any code running during a request, CLI command,
|
support greenlet contexts as well) for request, session and an extra
|
||||||
etc. Context locals are specific to the worker handling the activity, such as a
|
object you can put your own things on (:data:`~flask.g`). Why is that and
|
||||||
thread, process, coroutine, or greenlet.
|
isn't that a bad idea?
|
||||||
|
|
||||||
The context and proxies help solve two development issues: circular imports, and
|
Yes it is usually not such a bright idea to use thread locals. They cause
|
||||||
passing around global data. :data:`.current_app` can be used to access the
|
troubles for servers that are not based on the concept of threads and make
|
||||||
application object without needing to import the app object directly, avoiding
|
large applications harder to maintain. However Flask is just not designed
|
||||||
circular import issues. :data:`.request`, :data:`.session`, and :data:`.g` can
|
for large applications or asynchronous servers. Flask wants to make it
|
||||||
be imported to access the current data for the request, rather than needing to
|
quick and easy to write a traditional web application.
|
||||||
pass them as arguments through every single function in your project.
|
|
||||||
|
|
||||||
|
|
||||||
Async/await and ASGI support
|
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
|
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
|
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.
|
It also binds to a few common standard library packages such as logging.
|
||||||
Everything else is up for extensions.
|
Everything else is up for extensions.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ application instance.
|
||||||
It is important that the app is not stored on the extension, don't do
|
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
|
``self.app = app``. The only time the extension should have direct
|
||||||
access to an app is during ``init_app``, otherwise it should use
|
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,
|
This allows the extension to support the application factory pattern,
|
||||||
avoids circular import issues when importing the extension instance
|
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
|
A common pattern is to use :meth:`~Flask.before_request` to initialize
|
||||||
some data or a connection at the beginning of each request, then
|
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
|
: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
|
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
|
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 = SimpleNamespace()
|
||||||
g._hello.user_id = 2
|
g._hello.user_id = 2
|
||||||
|
|
||||||
The data in ``g`` lasts for an application context. An application context is
|
The data in ``g`` lasts for an application context. An application
|
||||||
active during a request, CLI command, or ``with app.app_context()`` block. If
|
context is active when a request context is, or when a CLI command is
|
||||||
you're storing something that should be closed, use
|
run. If you're storing something that should be closed, use
|
||||||
:meth:`~flask.Flask.teardown_appcontext` to ensure that it gets closed when the
|
:meth:`~flask.Flask.teardown_appcontext` to ensure that it gets closed
|
||||||
app context ends. If it should only be valid during a request, or would not be
|
when the application context ends. If it should only be valid during a
|
||||||
used in the CLI outside a request, use :meth:`~flask.Flask.teardown_request`.
|
request, or would not be used in the CLI outside a request, use
|
||||||
|
:meth:`~flask.Flask.teardown_request`.
|
||||||
|
|
||||||
|
|
||||||
Views and Models
|
Views and Models
|
||||||
|
|
@ -293,13 +294,10 @@ ecosystem remain consistent and compatible.
|
||||||
indicate minimum compatibility support. For example,
|
indicate minimum compatibility support. For example,
|
||||||
``sqlalchemy>=1.4``.
|
``sqlalchemy>=1.4``.
|
||||||
9. Indicate the versions of Python supported using ``python_requires=">=version"``.
|
9. Indicate the versions of Python supported using ``python_requires=">=version"``.
|
||||||
Flask and Pallets policy is to support all Python versions that are not
|
Flask itself supports Python >=3.8 as of April 2023, but this will update over time.
|
||||||
within six months of end of life (EOL). See Python's `EOL calendar`_ for
|
|
||||||
timing.
|
|
||||||
|
|
||||||
.. _PyPI: https://pypi.org/search/?c=Framework+%3A%3A+Flask
|
.. _PyPI: https://pypi.org/search/?c=Framework+%3A%3A+Flask
|
||||||
.. _Discord Chat: https://discord.gg/pallets
|
.. _Discord Chat: https://discord.gg/pallets
|
||||||
.. _GitHub Discussions: https://github.com/pallets/flask/discussions
|
.. _GitHub Discussions: https://github.com/pallets/flask/discussions
|
||||||
.. _Official Pallets Themes: https://pypi.org/project/Pallets-Sphinx-Themes/
|
.. _Official Pallets Themes: https://pypi.org/project/Pallets-Sphinx-Themes/
|
||||||
.. _Pallets-Eco: https://github.com/pallets-eco
|
.. _Pallets-Eco: https://github.com/pallets-eco
|
||||||
.. _EOL calendar: https://devguide.python.org/versions/
|
|
||||||
|
|
|
||||||
125
docs/gevent.rst
125
docs/gevent.rst
|
|
@ -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()
|
|
||||||
|
|
@ -3,15 +3,10 @@
|
||||||
Welcome to Flask
|
Welcome to Flask
|
||||||
================
|
================
|
||||||
|
|
||||||
.. image:: _static/flask-name.svg
|
.. image:: _static/flask-horizontal.png
|
||||||
:align: center
|
:align: center
|
||||||
:height: 200px
|
|
||||||
|
|
||||||
Welcome to Flask's documentation. Flask is a lightweight WSGI web application framework.
|
Welcome to Flask's documentation. Get started with :doc:`installation`
|
||||||
It is designed to make getting started quick and easy, with the ability to scale up to
|
|
||||||
complex applications.
|
|
||||||
|
|
||||||
Get started with :doc:`installation`
|
|
||||||
and then get an overview with the :doc:`quickstart`. There is also a
|
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
|
more detailed :doc:`tutorial/index` that shows how to create a small but
|
||||||
complete application with Flask. Common patterns are described in the
|
complete application with Flask. Common patterns are described in the
|
||||||
|
|
@ -52,15 +47,15 @@ community-maintained extensions to add even more functionality.
|
||||||
views
|
views
|
||||||
lifecycle
|
lifecycle
|
||||||
appcontext
|
appcontext
|
||||||
|
reqcontext
|
||||||
blueprints
|
blueprints
|
||||||
extensions
|
extensions
|
||||||
cli
|
cli
|
||||||
server
|
server
|
||||||
shell
|
shell
|
||||||
patterns/index
|
patterns/index
|
||||||
web-security
|
security
|
||||||
deploying/index
|
deploying/index
|
||||||
gevent
|
|
||||||
async-await
|
async-await
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ Installation
|
||||||
Python Version
|
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
|
Dependencies
|
||||||
|
|
@ -51,8 +51,9 @@ use them if you install them.
|
||||||
greenlet
|
greenlet
|
||||||
~~~~~~~~
|
~~~~~~~~
|
||||||
|
|
||||||
You may choose to use :doc:`/gevent` with your application. In this case,
|
You may choose to use gevent or eventlet with your application. In this
|
||||||
greenlet>=1.0 is required. When using PyPy, PyPy>=7.3.7 is required.
|
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
|
These are not minimum supported versions, they only indicate the first
|
||||||
versions that added necessary features. You should use the latest
|
versions that added necessary features. You should use the latest
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
BSD-3-Clause License
|
BSD-3-Clause License
|
||||||
====================
|
====================
|
||||||
|
|
||||||
.. literalinclude:: ../LICENSE.txt
|
.. include:: ../LICENSE.rst
|
||||||
:language: text
|
|
||||||
|
|
|
||||||
|
|
@ -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.
|
parts that you can use to customize its behavior.
|
||||||
|
|
||||||
#. WSGI server calls the Flask object, which calls :meth:`.Flask.wsgi_app`.
|
#. WSGI server calls the Flask object, which calls :meth:`.Flask.wsgi_app`.
|
||||||
#. An :class:`.AppContext` object is created. This converts the WSGI ``environ``
|
#. A :class:`.RequestContext` object is created. This converts the WSGI ``environ``
|
||||||
dict into a :class:`.Request` object.
|
dict into a :class:`.Request` object. It also creates an :class:`AppContext` object.
|
||||||
#. The :doc:`app context <appcontext>` is pushed, which makes
|
#. The :doc:`app context <appcontext>` is pushed, which makes :data:`.current_app` and
|
||||||
:data:`.current_app`, :data:`.g`, :data:`.request`, and :data:`.session`
|
:data:`.g` available.
|
||||||
available.
|
|
||||||
#. The :data:`.appcontext_pushed` signal is sent.
|
#. 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`
|
#. 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,
|
decorator during application setup. If there is no match, the error - usually a 404,
|
||||||
405, or redirect - is stored to be handled later.
|
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.
|
called to handle the error and return a response.
|
||||||
#. Whatever returned a response value - a before request function, the view, or an
|
#. Whatever returned a response value - a before request function, the view, or an
|
||||||
error handler, that value is converted to a :class:`.Response` object.
|
error handler, that value is converted to a :class:`.Response` object.
|
||||||
#. Any :func:`~.after_this_request` decorated functions are called, which can modify
|
#. Any :func:`~.after_this_request` decorated functions are called, then cleared.
|
||||||
the response object. They are then cleared.
|
|
||||||
#. Any :meth:`~.Flask.after_request` decorated functions are called, which can modify
|
#. Any :meth:`~.Flask.after_request` decorated functions are called, which can modify
|
||||||
the response object.
|
the response object.
|
||||||
#. The session is saved, persisting any modified session data using the app's
|
#. 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.
|
#. The response object's status, headers, and body are returned to the WSGI server.
|
||||||
#. Any :meth:`~.Flask.teardown_request` decorated functions are called.
|
#. Any :meth:`~.Flask.teardown_request` decorated functions are called.
|
||||||
#. The :data:`.request_tearing_down` signal is sent.
|
#. 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.
|
#. Any :meth:`~.Flask.teardown_appcontext` decorated functions are called.
|
||||||
#. The :data:`.appcontext_tearing_down` signal is sent.
|
#. The :data:`.appcontext_tearing_down` signal is sent.
|
||||||
#. The app context is popped, :data:`.current_app`, :data:`.g`, :data:`.request`,
|
#. The app context is popped, :data:`.current_app` and :data:`.g` are no longer
|
||||||
and :data:`.session` are no longer available.
|
available.
|
||||||
#. The :data:`.appcontext_popped` signal is sent.
|
#. 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
|
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
|
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
|
a request, such as templates, building URLs, or handling JSON data. See the rest of this
|
||||||
|
|
|
||||||
|
|
@ -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. ::
|
care about separately, instead of configuring only the root logger. ::
|
||||||
|
|
||||||
for logger in (
|
for logger in (
|
||||||
logging.getLogger(app.name),
|
app.logger,
|
||||||
logging.getLogger('sqlalchemy'),
|
logging.getLogger('sqlalchemy'),
|
||||||
logging.getLogger('other_package'),
|
logging.getLogger('other_package'),
|
||||||
):
|
):
|
||||||
|
|
|
||||||
|
|
@ -99,9 +99,9 @@ to the factory like this:
|
||||||
|
|
||||||
.. code-block:: text
|
.. 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.
|
argument ``local_auth=True``. See :doc:`/cli` for more detail.
|
||||||
|
|
||||||
Factory Improvements
|
Factory Improvements
|
||||||
|
|
|
||||||
|
|
@ -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
|
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::
|
however your application is the root you can simply route a redirect::
|
||||||
|
|
||||||
app.add_url_rule(
|
app.add_url_rule('/favicon.ico',
|
||||||
"/favicon.ico",
|
redirect_to=url_for('static', filename='favicon.ico'))
|
||||||
endpoint="favicon",
|
|
||||||
redirect_to=url_for("static", filename="favicon.ico"),
|
|
||||||
)
|
|
||||||
|
|
||||||
If you want to save the extra redirect request you can also write a view
|
If you want to save the extra redirect request you can also write a view
|
||||||
using :func:`~flask.send_from_directory`::
|
using :func:`~flask.send_from_directory`::
|
||||||
|
|
|
||||||
|
|
@ -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
|
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
|
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
|
sending JSON data, the ``Content-Type: application/json`` header must be
|
||||||
sent as well, otherwise Flask will return a 415 Unsupported Media Type
|
sent as well, otherwise Flask will return a 400 error.
|
||||||
error.
|
|
||||||
|
|
||||||
.. code-block:: javascript
|
.. code-block:: javascript
|
||||||
|
|
||||||
|
|
@ -198,7 +197,7 @@ in the previous section. The following example shows how to replace a
|
||||||
const geology_div = getElementById("geology-fact")
|
const geology_div = getElementById("geology-fact")
|
||||||
fetch(geology_url)
|
fetch(geology_url)
|
||||||
.then(response => response.text)
|
.then(response => response.text)
|
||||||
.then(text => geology_div.innerHTML = text)
|
.then(text => geology_div.innerHtml = text)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -245,9 +244,8 @@ Receiving JSON in Views
|
||||||
|
|
||||||
Use the :attr:`~flask.Request.json` property of the
|
Use the :attr:`~flask.Request.json` property of the
|
||||||
:data:`~flask.request` object to decode the request's body as JSON. If
|
: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 body is not valid JSON, or the ``Content-Type`` header is not set to
|
||||||
the ``Content-Type`` header is not set to ``application/json``, a 415
|
``application/json``, a 400 Bad Request error will be raised.
|
||||||
Unsupported Media Type error will be raised.
|
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,8 @@ A running MongoDB server and `Flask-MongoEngine`_ are required. ::
|
||||||
pip install flask-mongoengine
|
pip install flask-mongoengine
|
||||||
|
|
||||||
.. _MongoEngine: http://mongoengine.org
|
.. _MongoEngine: http://mongoengine.org
|
||||||
.. _Flask-MongoEngine: https://docs.mongoengine.org/projects/flask-mongoengine/en/latest/
|
.. _Flask-MongoEngine: https://flask-mongoengine.readthedocs.io
|
||||||
|
|
||||||
|
|
||||||
Configuration
|
Configuration
|
||||||
-------------
|
-------------
|
||||||
|
|
@ -79,7 +80,7 @@ Queries
|
||||||
Use the class ``objects`` attribute to make queries. A keyword argument
|
Use the class ``objects`` attribute to make queries. A keyword argument
|
||||||
looks for an equal value on the field. ::
|
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
|
Query operators may be used by concatenating them with the field name
|
||||||
using a double-underscore. ``objects``, and queries returned by
|
using a double-underscore. ``objects``, and queries returned by
|
||||||
|
|
|
||||||
|
|
@ -131,8 +131,9 @@ Here is an example :file:`database.py` module for your application::
|
||||||
def init_db():
|
def init_db():
|
||||||
metadata.create_all(bind=engine)
|
metadata.create_all(bind=engine)
|
||||||
|
|
||||||
As in the declarative approach, you need to close the session after each app
|
As in the declarative approach, you need to close the session after
|
||||||
context. Put this into your application module::
|
each request or application context shutdown. Put this into your
|
||||||
|
application module::
|
||||||
|
|
||||||
from yourapplication.database import db_session
|
from yourapplication.database import db_session
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
Using SQLite 3 with Flask
|
Using SQLite 3 with Flask
|
||||||
=========================
|
=========================
|
||||||
|
|
||||||
You can implement a few functions to work with a SQLite connection during a
|
In Flask you can easily implement the opening of database connections on
|
||||||
request context. The connection is created the first time it's accessed,
|
demand and closing them when the context dies (usually at the end of the
|
||||||
reused on subsequent access, until it is closed when the request context ends.
|
request).
|
||||||
|
|
||||||
Here is a simple example of how you can use SQLite 3 with Flask::
|
Here is a simple example of how you can use SQLite 3 with Flask::
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,21 +8,6 @@ roundtrip to the filesystem?
|
||||||
|
|
||||||
The answer is by using generators and direct responses.
|
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
|
Basic Usage
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
|
|
@ -44,7 +29,7 @@ debug environments with profilers and other things you might have enabled.
|
||||||
Streaming from Templates
|
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
|
piece, returning an iterator of strings. Flask provides the
|
||||||
:func:`~flask.stream_template` and :func:`~flask.stream_template_string`
|
:func:`~flask.stream_template` and :func:`~flask.stream_template_string`
|
||||||
functions to make this easier to use.
|
functions to make this easier to use.
|
||||||
|
|
@ -64,13 +49,13 @@ the template.
|
||||||
Streaming with Context
|
Streaming with Context
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
The :data:`.request` proxy will not be active while the generator is
|
The :data:`~flask.request` will not be active while the generator is
|
||||||
running, because the app has already returned control to the WSGI server at that
|
running, because the view has already returned at that point. If you try
|
||||||
point. If you try to access ``request``, you'll get a ``RuntimeError``.
|
to access ``request``, you'll get a ``RuntimeError``.
|
||||||
|
|
||||||
If your generator function relies on data in ``request``, use the
|
If your generator function relies on data in ``request``, use the
|
||||||
:func:`.stream_with_context` wrapper. This will keep the request context active
|
:func:`~flask.stream_with_context` wrapper. This will keep the request
|
||||||
during the generator.
|
context active during the generator.
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
arguments will be inserted as HTML attributes. So, for example, you can
|
||||||
call ``render_field(form.username, class='username')`` to add a class to
|
call ``render_field(form.username, class='username')`` to add a class to
|
||||||
the input element. Note that WTForms returns standard Python strings,
|
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.
|
the ``|safe`` filter.
|
||||||
|
|
||||||
Here is the :file:`register.html` template for the function we used above, which
|
Here is the :file:`register.html` template for the function we used above, which
|
||||||
|
|
|
||||||
|
|
@ -139,16 +139,18 @@ how you're using untrusted data.
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
from flask import request
|
|
||||||
from markupsafe import escape
|
from markupsafe import escape
|
||||||
|
|
||||||
@app.route("/hello")
|
@app.route("/<name>")
|
||||||
def hello():
|
def hello(name):
|
||||||
name = request.args.get("name", "Flask")
|
|
||||||
return f"Hello, {escape(name)}!"
|
return f"Hello, {escape(name)}!"
|
||||||
|
|
||||||
If a user submits ``/hello?name=<script>alert("bad")</script>``, escaping causes
|
If a user managed to submit the name ``<script>alert("bad")</script>``,
|
||||||
it to be rendered as text, rather than running the script in the user's browser.
|
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
|
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
|
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`
|
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
|
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
|
.. code-block:: python
|
||||||
|
|
||||||
|
|
@ -352,7 +354,7 @@ Rendering Templates
|
||||||
|
|
||||||
Generating HTML from within Python is not fun, and actually pretty
|
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
|
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.
|
<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
|
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/')
|
||||||
@app.route('/hello/<name>')
|
@app.route('/hello/<name>')
|
||||||
def hello(name=None):
|
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
|
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
|
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
|
/templates
|
||||||
/hello.html
|
/hello.html
|
||||||
|
|
||||||
For templates you can use the full power of Jinja templates. Head over
|
For templates you can use the full power of Jinja2 templates. Head over
|
||||||
to the official `Jinja Template Documentation
|
to the official `Jinja2 Template Documentation
|
||||||
<https://jinja.palletsprojects.com/templates/>`_ for more information.
|
<https://jinja.palletsprojects.com/templates/>`_ for more information.
|
||||||
|
|
||||||
Here is an example template:
|
Here is an example template:
|
||||||
|
|
@ -402,8 +404,8 @@ Here is an example template:
|
||||||
|
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<title>Hello from Flask</title>
|
<title>Hello from Flask</title>
|
||||||
{% if person %}
|
{% if name %}
|
||||||
<h1>Hello {{ person }}!</h1>
|
<h1>Hello {{ name }}!</h1>
|
||||||
{% else %}
|
{% else %}
|
||||||
<h1>Hello, World!</h1>
|
<h1>Hello, World!</h1>
|
||||||
{% endif %}
|
{% 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
|
template inheritance makes it possible to keep certain elements on each
|
||||||
page (like header, navigation and footer).
|
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
|
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
|
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
|
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
|
Accessing Request Data
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
For web applications it's crucial to react to the data a client sends to the
|
For web applications it's crucial to react to the data a client sends to
|
||||||
server. In Flask this information is provided by the global :data:`.request`
|
the server. In Flask this information is provided by the global
|
||||||
object, which is an instance of :class:`.Request`. This object has many
|
:class:`~flask.request` object. If you have some experience with Python
|
||||||
attributes and methods to work with the incoming request data, but here is a
|
you might be wondering how that object can be global and how Flask
|
||||||
broad overview. First it needs to be imported.
|
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
|
from flask import request
|
||||||
|
|
||||||
If you have some experience with Python you might be wondering how that object
|
with app.test_request_context('/hello', method='POST'):
|
||||||
can be global when Flask handles multiple requests at a time. The answer is
|
# now you can do something with the request until the
|
||||||
that :data:`.request` is actually a proxy, pointing at whatever request is
|
# end of the with block, such as basic assertions:
|
||||||
currently being handled by a given worker, which is managed internally by Flask
|
assert request.path == '/hello'
|
||||||
and Python. See :doc:`/appcontext` for much more information.
|
assert request.method == 'POST'
|
||||||
|
|
||||||
The current request method is available in the :attr:`~.Request.method`
|
The other possibility is passing a whole WSGI environment to the
|
||||||
attribute. To access form data (data transmitted in a ``POST`` or ``PUT``
|
:meth:`~flask.Flask.request_context` method::
|
||||||
request), use the :attr:`~flask.Request.form` attribute, which behaves like a
|
|
||||||
dict.
|
|
||||||
|
|
||||||
.. 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():
|
def login():
|
||||||
error = None
|
error = None
|
||||||
|
if request.method == 'POST':
|
||||||
if request.method == "POST":
|
if valid_login(request.form['username'],
|
||||||
if valid_login(request.form["username"], request.form["password"]):
|
request.form['password']):
|
||||||
return store_login(request.form["username"])
|
return log_the_user_in(request.form['username'])
|
||||||
else:
|
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.
|
What happens if the key does not exist in the ``form`` attribute? In that
|
||||||
return render_template("login.html", error=error)
|
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
|
To access parameters submitted in the URL (``?key=value``) you can use the
|
||||||
can catch it like a normal ``KeyError``, otherwise it will return a HTTP 400
|
:attr:`~flask.Request.args` attribute::
|
||||||
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
|
|
||||||
|
|
||||||
searchword = request.args.get('key', '')
|
searchword = request.args.get('key', '')
|
||||||
|
|
||||||
For a full list of methods and attributes of the request object, see the
|
We recommend accessing URL parameters with `get` or by catching the
|
||||||
:class:`~.Request` documentation.
|
: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
|
File Uploads
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,243 @@
|
||||||
:orphan:
|
.. currentmodule:: flask
|
||||||
|
|
||||||
The Request Context
|
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)
|
||||||
|
|
|
||||||
|
|
@ -1,43 +1,9 @@
|
||||||
Security Considerations
|
Security Considerations
|
||||||
=======================
|
=======================
|
||||||
|
|
||||||
Web applications face many types of potential security problems, and it can be
|
Web applications usually face all kinds of security problems and it's very
|
||||||
hard to get everything right, or even to know what "right" is in general. Flask
|
hard to get everything right. Flask tries to solve a few of these things
|
||||||
tries to solve a few of these things by default, but there are other parts you
|
for you, but there are a couple more you have to take care of yourself.
|
||||||
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.
|
|
||||||
|
|
||||||
.. _security-xss:
|
.. _security-xss:
|
||||||
|
|
||||||
|
|
@ -51,12 +17,12 @@ tags. For more information on that have a look at the Wikipedia article
|
||||||
on `Cross-Site Scripting
|
on `Cross-Site Scripting
|
||||||
<https://en.wikipedia.org/wiki/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
|
explicitly told otherwise. This should rule out all XSS problems caused
|
||||||
in templates, but there are still other places where you have to be
|
in templates, but there are still other places where you have to be
|
||||||
careful:
|
careful:
|
||||||
|
|
||||||
- generating HTML without the help of Jinja
|
- generating HTML without the help of Jinja2
|
||||||
- calling :class:`~markupsafe.Markup` on data submitted by users
|
- calling :class:`~markupsafe.Markup` on data submitted by users
|
||||||
- sending out HTML from uploaded files, never do that, use the
|
- sending out HTML from uploaded files, never do that, use the
|
||||||
``Content-Disposition: attachment`` header to prevent that problem.
|
``Content-Disposition: attachment`` header to prevent that problem.
|
||||||
|
|
@ -65,7 +31,7 @@ careful:
|
||||||
trick a browser to execute HTML.
|
trick a browser to execute HTML.
|
||||||
|
|
||||||
Another thing that is very important are unquoted attributes. While
|
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
|
thing it cannot protect you from: XSS by attribute injection. To counter
|
||||||
this possible attack vector, be sure to always quote your attributes with
|
this possible attack vector, be sure to always quote your attributes with
|
||||||
either double or single quotes when using Jinja expressions in them:
|
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
|
The `Flask-Talisman`_ extension can be used to manage HTTPS and the security
|
||||||
headers for you.
|
headers for you.
|
||||||
|
|
||||||
.. _Flask-Talisman: https://github.com/wntrblm/flask-talisman
|
.. _Flask-Talisman: https://github.com/GoogleCloudPlatform/flask-talisman
|
||||||
|
|
||||||
HTTP Strict Transport Security (HSTS)
|
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
|
.. _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
|
This tells the browser to authenticate with the server using only the specific
|
||||||
was made to. This is used, for example, by ``url_for(..., _external=True)`` to
|
certificate key to prevent MITM attacks.
|
||||||
generate full URLs, for use in email or other messages outside the browser
|
|
||||||
window.
|
|
||||||
|
|
||||||
By default the app doesn't know what host(s) it is allowed to be accessed
|
.. warning::
|
||||||
through, and assumes any host is valid. Although browsers do not allow setting
|
Be careful when enabling this, as it is very difficult to undo if you set up
|
||||||
the ``Host`` header, requests made by attackers in other scenarios could set
|
or upgrade your key incorrectly.
|
||||||
the ``Host`` header to a value they want.
|
|
||||||
|
|
||||||
When deploying your application, set :data:`TRUSTED_HOSTS` to restrict what
|
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Public_Key_Pinning
|
||||||
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.
|
|
||||||
|
|
||||||
|
|
||||||
Copy/Paste to Terminal
|
Copy/Paste to Terminal
|
||||||
|
|
@ -76,8 +76,8 @@ following example shows that process id 6847 is using port 5000.
|
||||||
TCP 127.0.0.1:5000 0.0.0.0:0 LISTENING 6847
|
TCP 127.0.0.1:5000 0.0.0.0:0 LISTENING 6847
|
||||||
|
|
||||||
macOS Monterey and later automatically starts a service that uses port
|
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
|
5000. To disable the service, go to System Preferences, Sharing, and
|
||||||
searching for "AirPlay Receiver" in System Settings and toggling it off.
|
disable "AirPlay Receiver".
|
||||||
|
|
||||||
|
|
||||||
Deferred Errors on Reload
|
Deferred Errors on Reload
|
||||||
|
|
|
||||||
|
|
@ -1,37 +1,56 @@
|
||||||
Working with the Shell
|
Working with the Shell
|
||||||
======================
|
======================
|
||||||
|
|
||||||
One of the reasons everybody loves Python is the interactive shell. It allows
|
.. versionadded:: 0.3
|
||||||
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.
|
|
||||||
|
|
||||||
.. 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
|
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
|
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
|
using the :attr:`~flask.Flask.test_request_context` method which creates
|
||||||
us a :class:`~flask.ctx.RequestContext`:
|
us a :class:`~flask.ctx.RequestContext`:
|
||||||
|
|
||||||
>>> ctx = app.test_request_context()
|
>>> ctx = app.test_request_context()
|
||||||
|
|
||||||
Normally you would use the ``with`` statement to make this context active, but
|
Normally you would use the ``with`` statement to make this request object
|
||||||
in the shell it's easier to call :meth:`~.RequestContext.push` and
|
active, but in the shell it's easier to use the
|
||||||
:meth:`~.RequestContext.pop` manually:
|
:meth:`~flask.ctx.RequestContext.push` and
|
||||||
|
:meth:`~flask.ctx.RequestContext.pop` methods by hand:
|
||||||
|
|
||||||
>>> ctx.push()
|
>>> ctx.push()
|
||||||
|
|
||||||
From that point onwards you can work with the request object until you call
|
From that point onwards you can work with the request object until you
|
||||||
``pop``:
|
call `pop`:
|
||||||
|
|
||||||
>>> ctx.pop()
|
>>> ctx.pop()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -144,10 +144,11 @@ function, you can pass ``current_app._get_current_object()`` as sender.
|
||||||
Signals and Flask's Request Context
|
Signals and Flask's Request Context
|
||||||
-----------------------------------
|
-----------------------------------
|
||||||
|
|
||||||
Context-local proxies are available between :data:`~flask.request_started` and
|
Signals fully support :doc:`reqcontext` when receiving signals.
|
||||||
:data:`~flask.request_finished`, so you can rely on :class:`flask.g` and others
|
Context-local variables are consistently available between
|
||||||
as needed. Note the limitations described in :ref:`signals-sending` and the
|
:data:`~flask.request_started` and :data:`~flask.request_finished`, so you can
|
||||||
:data:`~flask.request_tearing_down` signal.
|
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
|
Decorator Based Signal Subscriptions
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,21 @@
|
||||||
Templates
|
Templates
|
||||||
=========
|
=========
|
||||||
|
|
||||||
Flask leverages Jinja as its template engine. You are obviously free to use
|
Flask leverages Jinja2 as its template engine. You are obviously free to use
|
||||||
a different template engine, but you still have to install Jinja to run
|
a different template engine, but you still have to install Jinja2 to run
|
||||||
Flask itself. This requirement is necessary to enable rich extensions.
|
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
|
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
|
Documentation <https://jinja.palletsprojects.com/templates/>`_ for
|
||||||
more information.
|
more information.
|
||||||
|
|
||||||
Jinja Setup
|
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``,
|
- autoescaping is enabled for all templates ending in ``.html``,
|
||||||
``.htm``, ``.xml``, ``.xhtml``, as well as ``.svg`` when using
|
``.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
|
- a template has the ability to opt in/out autoescaping with the
|
||||||
``{% autoescape %}`` tag.
|
``{% autoescape %}`` tag.
|
||||||
- Flask inserts a couple of global functions and helpers into the
|
- 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.
|
default.
|
||||||
|
|
||||||
Standard Context
|
Standard Context
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
The following global variables are available within Jinja templates
|
The following global variables are available within Jinja2 templates
|
||||||
by default:
|
by default:
|
||||||
|
|
||||||
.. data:: config
|
.. data:: config
|
||||||
|
|
@ -137,58 +137,32 @@ using in this block.
|
||||||
|
|
||||||
.. _registering-filters:
|
.. _registering-filters:
|
||||||
|
|
||||||
Registering Filters, Tests, and Globals
|
Registering Filters
|
||||||
---------------------------------------
|
-------------------
|
||||||
|
|
||||||
The Flask app and blueprints provide decorators and methods to register your own
|
If you want to register your own filters in Jinja2 you have two ways to do
|
||||||
filters, tests, and global functions for use in Jinja templates. They all follow
|
that. You can either put them by hand into the
|
||||||
the same pattern, so the following examples only discuss filters.
|
: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
|
The two following examples work the same and both reverse an object::
|
||||||
template filter.
|
|
||||||
|
|
||||||
.. code-block:: python
|
@app.template_filter('reverse')
|
||||||
|
def reverse_filter(s):
|
||||||
|
return s[::-1]
|
||||||
|
|
||||||
@app.template_filter
|
def reverse_filter(s):
|
||||||
def reverse(s):
|
return s[::-1]
|
||||||
return reversed(s)
|
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 %}
|
{% 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
|
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
|
initial page load, or to save memory when rendering a very large
|
||||||
template.
|
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
|
by piece, returning an iterator of strings. Flask provides the
|
||||||
:func:`~flask.stream_template` and :func:`~flask.stream_template_string`
|
:func:`~flask.stream_template` and :func:`~flask.stream_template_string`
|
||||||
functions to make this easier to use.
|
functions to make this easier to use.
|
||||||
|
|
@ -251,11 +225,5 @@ functions to make this easier to use.
|
||||||
return stream_template("timeline.html")
|
return stream_template("timeline.html")
|
||||||
|
|
||||||
These functions automatically apply the
|
These functions automatically apply the
|
||||||
:func:`~flask.stream_with_context` wrapper if a request is active, so that
|
:func:`~flask.stream_with_context` wrapper if a request is active, so
|
||||||
:data:`.request`, :data:`.session`, and :data:`.g` remain available in the
|
that it remains available in the template.
|
||||||
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.
|
|
||||||
|
|
|
||||||
|
|
@ -192,7 +192,7 @@ which records the request that produced that response.
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
def test_logout_redirect(client):
|
def test_logout_redirect(client):
|
||||||
response = client.get("/logout", follow_redirects=True)
|
response = client.get("/logout")
|
||||||
# Check that there was one redirect response.
|
# Check that there was one redirect response.
|
||||||
assert len(response.history) == 1
|
assert len(response.history) == 1
|
||||||
# Check that the second request was to the index page.
|
# 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
|
Tests that depend on an Active Context
|
||||||
--------------------------------------
|
--------------------------------------
|
||||||
|
|
||||||
You may have functions that are called from views or commands, that expect an
|
You may have functions that are called from views or commands, that
|
||||||
active :doc:`app context </appcontext>` because they access :data:`.request`,
|
expect an active :doc:`application context </appcontext>` or
|
||||||
:data:`.session`, :data:`.g`, or :data:`.current_app`. Rather than testing them by
|
:doc:`request context </reqcontext>` because they access ``request``,
|
||||||
making a request or invoking the command, you can create and activate a context
|
``session``, or ``current_app``. Rather than testing them by making a
|
||||||
|
request or invoking the command, you can create and activate a context
|
||||||
directly.
|
directly.
|
||||||
|
|
||||||
Use ``with app.app_context()`` to push an application context. For
|
Use ``with app.app_context()`` to push an application context. For
|
||||||
|
|
|
||||||
|
|
@ -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
|
choose what data appears in the form. When the form hasn't been
|
||||||
submitted, the original ``post`` data appears, but if invalid form data
|
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
|
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.
|
that's automatically available in templates.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,6 @@ response is sent.
|
||||||
:caption: ``flaskr/db.py``
|
:caption: ``flaskr/db.py``
|
||||||
|
|
||||||
import sqlite3
|
import sqlite3
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
import click
|
import click
|
||||||
from flask import current_app, g
|
from flask import current_app, g
|
||||||
|
|
@ -60,17 +59,17 @@ response is sent.
|
||||||
if db is not None:
|
if db is not None:
|
||||||
db.close()
|
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
|
used to store data that might be accessed by multiple functions during
|
||||||
the request. The connection is stored and reused instead of creating a
|
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
|
new connection if ``get_db`` is called a second time in the same
|
||||||
request.
|
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,
|
application handling the request. Since you used an application factory,
|
||||||
there is no application object when writing the rest of your code.
|
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
|
``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
|
:func:`sqlite3.connect` establishes a connection to the file pointed at
|
||||||
by the ``DATABASE`` configuration key. This file doesn't have to exist
|
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()
|
init_db()
|
||||||
click.echo('Initialized the database.')
|
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
|
:meth:`open_resource() <Flask.open_resource>` opens a file relative to
|
||||||
the ``flaskr`` package, which is useful since you won't necessarily know
|
the ``flaskr`` package, which is useful since you won't necessarily know
|
||||||
where that location is when deploying the application later. ``get_db``
|
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
|
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.
|
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
|
Register with the Application
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,10 @@ directory should be treated as a package.
|
||||||
app.config.from_mapping(test_config)
|
app.config.from_mapping(test_config)
|
||||||
|
|
||||||
# ensure the instance folder exists
|
# 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
|
# a simple page that says hello
|
||||||
@app.route('/hello')
|
@app.route('/hello')
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,8 @@ By the end, your project layout will look like this:
|
||||||
│ ├── test_auth.py
|
│ ├── test_auth.py
|
||||||
│ └── test_blog.py
|
│ └── test_blog.py
|
||||||
├── .venv/
|
├── .venv/
|
||||||
└── pyproject.toml
|
├── pyproject.toml
|
||||||
|
└── MANIFEST.in
|
||||||
|
|
||||||
If you're using version control, the following files that are generated
|
If you're using version control, the following files that are generated
|
||||||
while running your project should be ignored. There may be other files
|
while running your project should be ignored. There may be other files
|
||||||
|
|
@ -102,4 +103,8 @@ write. For example, with git:
|
||||||
.coverage
|
.coverage
|
||||||
htmlcov/
|
htmlcov/
|
||||||
|
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
*.egg-info/
|
||||||
|
|
||||||
Continue to :doc:`factory`.
|
Continue to :doc:`factory`.
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ specific sections.
|
||||||
{% block content %}{% endblock %}
|
{% block content %}{% endblock %}
|
||||||
</section>
|
</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
|
``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
|
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
|
are displayed. :func:`url_for` is also automatically available, and is
|
||||||
|
|
|
||||||
|
|
@ -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
|
The tests for the ``login`` view are very similar to those for
|
||||||
``register``. Rather than testing the data in the database,
|
``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
|
.. code-block:: python
|
||||||
:caption: ``tests/test_auth.py``
|
: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
|
assert message in response.data
|
||||||
|
|
||||||
Using ``client`` in a ``with`` block allows accessing context variables
|
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.
|
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.
|
not contain ``user_id`` after logging out.
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
|
||||||
|
|
@ -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
|
password in the same way as the stored hash and securely compares
|
||||||
them. If they match, the password is valid.
|
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
|
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
|
session. The data is stored in a *cookie* that is sent to the
|
||||||
browser, and the browser then sends it back with subsequent requests.
|
browser, and the browser then sends it back with subsequent requests.
|
||||||
Flask securely *signs* the data so that it can't be tampered with.
|
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
|
available on subsequent requests. At the beginning of each request, if
|
||||||
a user is logged in their information should be loaded and made
|
a user is logged in their information should be loaded and made
|
||||||
available to other views.
|
available to other views.
|
||||||
|
|
@ -236,7 +236,7 @@ available to other views.
|
||||||
:meth:`bp.before_app_request() <Blueprint.before_app_request>` registers
|
:meth:`bp.before_app_request() <Blueprint.before_app_request>` registers
|
||||||
a function that runs before the view function, no matter what URL is
|
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
|
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
|
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
|
there is no user id, or if the id doesn't exist, ``g.user`` will be
|
||||||
``None``.
|
``None``.
|
||||||
|
|
@ -245,7 +245,7 @@ there is no user id, or if the id doesn't exist, ``g.user`` will be
|
||||||
Logout
|
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.
|
Then ``load_logged_in_user`` won't load a user on subsequent requests.
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@ name = "flask-example-celery"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
description = "Example Flask application with Celery background tasks."
|
description = "Example Flask application with Celery background tasks."
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
classifiers = ["Private :: Do Not Upload"]
|
requires-python = ">=3.8"
|
||||||
dependencies = ["flask", "celery[redis]"]
|
dependencies = ["flask>=2.2.2", "celery[redis]>=5.2.7"]
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["flit_core<4"]
|
requires = ["flit_core<4"]
|
||||||
|
|
@ -12,6 +12,3 @@ build-backend = "flit_core.buildapi"
|
||||||
|
|
||||||
[tool.flit.module]
|
[tool.flit.module]
|
||||||
name = "task_app"
|
name = "task_app"
|
||||||
|
|
||||||
[tool.ruff]
|
|
||||||
src = ["src"]
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ page. Demonstrates using |fetch|_, |XMLHttpRequest|_, and
|
||||||
.. |jQuery.ajax| replace:: ``jQuery.ajax``
|
.. |jQuery.ajax| replace:: ``jQuery.ajax``
|
||||||
.. _jQuery.ajax: https://api.jquery.com/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
|
Install
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ from flask import jsonify
|
||||||
from flask import render_template
|
from flask import render_template
|
||||||
from flask import request
|
from flask import request
|
||||||
|
|
||||||
from . import app
|
from js_example import app
|
||||||
|
|
||||||
|
|
||||||
@app.route("/", defaults={"js": "fetch"})
|
@app.route("/", defaults={"js": "fetch"})
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,12 @@ name = "js_example"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
description = "Demonstrates making AJAX requests to Flask."
|
description = "Demonstrates making AJAX requests to Flask."
|
||||||
readme = "README.rst"
|
readme = "README.rst"
|
||||||
license = {file = "LICENSE.txt"}
|
license = {file = "LICENSE.rst"}
|
||||||
maintainers = [{name = "Pallets", email = "contact@palletsprojects.com"}]
|
maintainers = [{name = "Pallets", email = "contact@palletsprojects.com"}]
|
||||||
classifiers = ["Private :: Do Not Upload"]
|
|
||||||
dependencies = ["flask"]
|
dependencies = ["flask"]
|
||||||
|
|
||||||
[project.urls]
|
[project.urls]
|
||||||
Documentation = "https://flask.palletsprojects.com/patterns/javascript/"
|
Documentation = "https://flask.palletsprojects.com/patterns/jquery/"
|
||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
test = ["pytest"]
|
test = ["pytest"]
|
||||||
|
|
@ -28,6 +27,3 @@ filterwarnings = ["error"]
|
||||||
[tool.coverage.run]
|
[tool.coverage.run]
|
||||||
branch = true
|
branch = true
|
||||||
source = ["js_example", "tests"]
|
source = ["js_example", "tests"]
|
||||||
|
|
||||||
[tool.ruff]
|
|
||||||
src = ["src"]
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ from flask import template_rendered
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("path", "template_name"),
|
("path", "template_name"),
|
||||||
(
|
(
|
||||||
("/", "fetch.html"),
|
("/", "xhr.html"),
|
||||||
("/plain", "xhr.html"),
|
("/plain", "xhr.html"),
|
||||||
("/fetch", "fetch.html"),
|
("/fetch", "fetch.html"),
|
||||||
("/jquery", "jquery.html"),
|
("/jquery", "jquery.html"),
|
||||||
|
|
|
||||||
|
|
@ -21,20 +21,22 @@ def create_app(test_config=None):
|
||||||
app.config.update(test_config)
|
app.config.update(test_config)
|
||||||
|
|
||||||
# ensure the instance folder exists
|
# 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")
|
@app.route("/hello")
|
||||||
def hello():
|
def hello():
|
||||||
return "Hello, World!"
|
return "Hello, World!"
|
||||||
|
|
||||||
# register the database commands
|
# register the database commands
|
||||||
from . import db
|
from flaskr import db
|
||||||
|
|
||||||
db.init_app(app)
|
db.init_app(app)
|
||||||
|
|
||||||
# apply the blueprints to the app
|
# apply the blueprints to the app
|
||||||
from . import auth
|
from flaskr import auth, blog
|
||||||
from . import blog
|
|
||||||
|
|
||||||
app.register_blueprint(auth.bp)
|
app.register_blueprint(auth.bp)
|
||||||
app.register_blueprint(blog.bp)
|
app.register_blueprint(blog.bp)
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ from flask import url_for
|
||||||
from werkzeug.security import check_password_hash
|
from werkzeug.security import check_password_hash
|
||||||
from werkzeug.security import generate_password_hash
|
from werkzeug.security import generate_password_hash
|
||||||
|
|
||||||
from .db import get_db
|
from flaskr.db import get_db
|
||||||
|
|
||||||
bp = Blueprint("auth", __name__, url_prefix="/auth")
|
bp = Blueprint("auth", __name__, url_prefix="/auth")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ from flask import request
|
||||||
from flask import url_for
|
from flask import url_for
|
||||||
from werkzeug.exceptions import abort
|
from werkzeug.exceptions import abort
|
||||||
|
|
||||||
from .auth import login_required
|
from flaskr.auth import login_required
|
||||||
from .db import get_db
|
from flaskr.db import get_db
|
||||||
|
|
||||||
bp = Blueprint("blog", __name__)
|
bp = Blueprint("blog", __name__)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import sqlite3
|
import sqlite3
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
import click
|
import click
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
|
|
@ -45,9 +44,6 @@ def init_db_command():
|
||||||
click.echo("Initialized the database.")
|
click.echo("Initialized the database.")
|
||||||
|
|
||||||
|
|
||||||
sqlite3.register_converter("timestamp", lambda v: datetime.fromisoformat(v.decode()))
|
|
||||||
|
|
||||||
|
|
||||||
def init_app(app):
|
def init_app(app):
|
||||||
"""Register database functions with the Flask app. This is called by
|
"""Register database functions with the Flask app. This is called by
|
||||||
the application factory.
|
the application factory.
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,8 @@ name = "flaskr"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
description = "The basic blog app built in the Flask tutorial."
|
description = "The basic blog app built in the Flask tutorial."
|
||||||
readme = "README.rst"
|
readme = "README.rst"
|
||||||
license = {file = "LICENSE.txt"}
|
license = {text = "BSD-3-Clause"}
|
||||||
maintainers = [{name = "Pallets", email = "contact@palletsprojects.com"}]
|
maintainers = [{name = "Pallets", email = "contact@palletsprojects.com"}]
|
||||||
classifiers = ["Private :: Do Not Upload"]
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"flask",
|
"flask",
|
||||||
]
|
]
|
||||||
|
|
@ -35,6 +34,3 @@ filterwarnings = ["error"]
|
||||||
[tool.coverage.run]
|
[tool.coverage.run]
|
||||||
branch = true
|
branch = true
|
||||||
source = ["flaskr", "tests"]
|
source = ["flaskr", "tests"]
|
||||||
|
|
||||||
[tool.ruff]
|
|
||||||
src = ["src"]
|
|
||||||
|
|
|
||||||
247
pyproject.toml
247
pyproject.toml
|
|
@ -1,88 +1,50 @@
|
||||||
[project]
|
[project]
|
||||||
name = "Flask"
|
name = "Flask"
|
||||||
version = "3.2.0.dev"
|
|
||||||
description = "A simple framework for building complex web applications."
|
description = "A simple framework for building complex web applications."
|
||||||
readme = "README.md"
|
readme = "README.rst"
|
||||||
license = "BSD-3-Clause"
|
license = {file = "LICENSE.rst"}
|
||||||
license-files = ["LICENSE.txt"]
|
|
||||||
maintainers = [{name = "Pallets", email = "contact@palletsprojects.com"}]
|
maintainers = [{name = "Pallets", email = "contact@palletsprojects.com"}]
|
||||||
classifiers = [
|
classifiers = [
|
||||||
"Development Status :: 5 - Production/Stable",
|
"Development Status :: 5 - Production/Stable",
|
||||||
"Environment :: Web Environment",
|
"Environment :: Web Environment",
|
||||||
"Framework :: Flask",
|
"Framework :: Flask",
|
||||||
"Intended Audience :: Developers",
|
"Intended Audience :: Developers",
|
||||||
|
"License :: OSI Approved :: BSD License",
|
||||||
"Operating System :: OS Independent",
|
"Operating System :: OS Independent",
|
||||||
"Programming Language :: Python",
|
"Programming Language :: Python",
|
||||||
"Topic :: Internet :: WWW/HTTP :: Dynamic Content",
|
"Topic :: Internet :: WWW/HTTP :: Dynamic Content",
|
||||||
"Topic :: Internet :: WWW/HTTP :: WSGI",
|
"Topic :: Internet :: WWW/HTTP :: WSGI",
|
||||||
"Topic :: Internet :: WWW/HTTP :: WSGI :: Application",
|
"Topic :: Internet :: WWW/HTTP :: WSGI :: Application",
|
||||||
"Topic :: Software Development :: Libraries :: Application Frameworks",
|
"Topic :: Software Development :: Libraries :: Application Frameworks",
|
||||||
"Typing :: Typed",
|
|
||||||
]
|
]
|
||||||
requires-python = ">=3.10"
|
requires-python = ">=3.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"blinker>=1.9.0",
|
"Werkzeug>=2.3.7",
|
||||||
|
"Jinja2>=3.1.2",
|
||||||
|
"itsdangerous>=2.1.2",
|
||||||
"click>=8.1.3",
|
"click>=8.1.3",
|
||||||
"itsdangerous>=2.2.0",
|
"blinker>=1.6.2",
|
||||||
"jinja2>=3.1.2",
|
"importlib-metadata>=3.6.0; python_version < '3.10'",
|
||||||
"markupsafe>=2.1.1",
|
|
||||||
"werkzeug>=3.1.0",
|
|
||||||
]
|
]
|
||||||
|
dynamic = ["version"]
|
||||||
|
|
||||||
|
[project.urls]
|
||||||
|
Donate = "https://palletsprojects.com/donate"
|
||||||
|
Documentation = "https://flask.palletsprojects.com/"
|
||||||
|
Changes = "https://flask.palletsprojects.com/changes/"
|
||||||
|
"Source Code" = "https://github.com/pallets/flask/"
|
||||||
|
"Issue Tracker" = "https://github.com/pallets/flask/issues/"
|
||||||
|
Chat = "https://discord.gg/pallets"
|
||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
async = ["asgiref>=3.2"]
|
async = ["asgiref>=3.2"]
|
||||||
dotenv = ["python-dotenv"]
|
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",
|
|
||||||
]
|
|
||||||
|
|
||||||
[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/"
|
|
||||||
Chat = "https://discord.gg/pallets"
|
|
||||||
|
|
||||||
[project.scripts]
|
[project.scripts]
|
||||||
flask = "flask.cli:main"
|
flask = "flask.cli:main"
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["flit_core>=3.11,<4"]
|
requires = ["flit_core<4"]
|
||||||
build-backend = "flit_core.buildapi"
|
build-backend = "flit_core.buildapi"
|
||||||
|
|
||||||
[tool.flit.module]
|
[tool.flit.module]
|
||||||
|
|
@ -92,17 +54,16 @@ name = "flask"
|
||||||
include = [
|
include = [
|
||||||
"docs/",
|
"docs/",
|
||||||
"examples/",
|
"examples/",
|
||||||
|
"requirements/",
|
||||||
"tests/",
|
"tests/",
|
||||||
"CHANGES.rst",
|
"CHANGES.rst",
|
||||||
"uv.lock"
|
"CONTRIBUTING.rst",
|
||||||
|
"tox.ini",
|
||||||
]
|
]
|
||||||
exclude = [
|
exclude = [
|
||||||
"docs/_build/",
|
"docs/_build/",
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.uv]
|
|
||||||
default-groups = ["dev", "pre-commit", "tests", "typing"]
|
|
||||||
|
|
||||||
[tool.pytest.ini_options]
|
[tool.pytest.ini_options]
|
||||||
testpaths = ["tests"]
|
testpaths = ["tests"]
|
||||||
filterwarnings = [
|
filterwarnings = [
|
||||||
|
|
@ -116,19 +77,26 @@ source = ["flask", "tests"]
|
||||||
[tool.coverage.paths]
|
[tool.coverage.paths]
|
||||||
source = ["src", "*/site-packages"]
|
source = ["src", "*/site-packages"]
|
||||||
|
|
||||||
[tool.coverage.report]
|
|
||||||
exclude_also = [
|
|
||||||
"if t.TYPE_CHECKING",
|
|
||||||
"raise NotImplementedError",
|
|
||||||
": \\.{3}",
|
|
||||||
]
|
|
||||||
|
|
||||||
[tool.mypy]
|
[tool.mypy]
|
||||||
python_version = "3.10"
|
python_version = "3.8"
|
||||||
files = ["src", "tests/type_check"]
|
files = ["src/flask"]
|
||||||
show_error_codes = true
|
show_error_codes = true
|
||||||
pretty = true
|
pretty = true
|
||||||
strict = true
|
#strict = true
|
||||||
|
allow_redefinition = true
|
||||||
|
disallow_subclassing_any = true
|
||||||
|
#disallow_untyped_calls = true
|
||||||
|
#disallow_untyped_defs = true
|
||||||
|
#disallow_incomplete_defs = true
|
||||||
|
no_implicit_optional = true
|
||||||
|
local_partial_types = true
|
||||||
|
#no_implicit_reexport = true
|
||||||
|
strict_equality = true
|
||||||
|
warn_redundant_casts = true
|
||||||
|
warn_unused_configs = true
|
||||||
|
warn_unused_ignores = true
|
||||||
|
#warn_return_any = true
|
||||||
|
#warn_unreachable = true
|
||||||
|
|
||||||
[[tool.mypy.overrides]]
|
[[tool.mypy.overrides]]
|
||||||
module = [
|
module = [
|
||||||
|
|
@ -138,140 +106,3 @@ module = [
|
||||||
"importlib_metadata",
|
"importlib_metadata",
|
||||||
]
|
]
|
||||||
ignore_missing_imports = true
|
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"
|
|
||||||
|
|
||||||
[tool.ruff.lint]
|
|
||||||
select = [
|
|
||||||
"B", # flake8-bugbear
|
|
||||||
"E", # pycodestyle error
|
|
||||||
"F", # pyflakes
|
|
||||||
"I", # isort
|
|
||||||
"UP", # pyupgrade
|
|
||||||
"W", # pycodestyle warning
|
|
||||||
]
|
|
||||||
|
|
||||||
[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}]]
|
|
||||||
|
|
|
||||||
1
requirements/build.in
Normal file
1
requirements/build.in
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
build
|
||||||
13
requirements/build.txt
Normal file
13
requirements/build.txt
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
# SHA1:80754af91bfb6d1073585b046fe0a474ce868509
|
||||||
|
#
|
||||||
|
# This file is autogenerated by pip-compile-multi
|
||||||
|
# To update, run:
|
||||||
|
#
|
||||||
|
# pip-compile-multi
|
||||||
|
#
|
||||||
|
build==0.10.0
|
||||||
|
# via -r requirements/build.in
|
||||||
|
packaging==23.1
|
||||||
|
# via build
|
||||||
|
pyproject-hooks==1.0.0
|
||||||
|
# via build
|
||||||
6
requirements/dev.in
Normal file
6
requirements/dev.in
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
-r docs.in
|
||||||
|
-r tests.in
|
||||||
|
-r typing.in
|
||||||
|
pip-compile-multi
|
||||||
|
pre-commit
|
||||||
|
tox
|
||||||
64
requirements/dev.txt
Normal file
64
requirements/dev.txt
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
# SHA1:54b5b77ec8c7a0064ffa93b2fd16cb0130ba177c
|
||||||
|
#
|
||||||
|
# This file is autogenerated by pip-compile-multi
|
||||||
|
# To update, run:
|
||||||
|
#
|
||||||
|
# pip-compile-multi
|
||||||
|
#
|
||||||
|
-r docs.txt
|
||||||
|
-r tests.txt
|
||||||
|
-r typing.txt
|
||||||
|
build==0.10.0
|
||||||
|
# via pip-tools
|
||||||
|
cachetools==5.3.1
|
||||||
|
# via tox
|
||||||
|
cfgv==3.3.1
|
||||||
|
# via pre-commit
|
||||||
|
chardet==5.1.0
|
||||||
|
# via tox
|
||||||
|
click==8.1.3
|
||||||
|
# via
|
||||||
|
# pip-compile-multi
|
||||||
|
# pip-tools
|
||||||
|
colorama==0.4.6
|
||||||
|
# via tox
|
||||||
|
distlib==0.3.6
|
||||||
|
# via virtualenv
|
||||||
|
filelock==3.12.2
|
||||||
|
# via
|
||||||
|
# tox
|
||||||
|
# virtualenv
|
||||||
|
identify==2.5.24
|
||||||
|
# via pre-commit
|
||||||
|
nodeenv==1.8.0
|
||||||
|
# via pre-commit
|
||||||
|
pip-compile-multi==2.6.3
|
||||||
|
# via -r requirements/dev.in
|
||||||
|
pip-tools==6.13.0
|
||||||
|
# via pip-compile-multi
|
||||||
|
platformdirs==3.8.0
|
||||||
|
# via
|
||||||
|
# tox
|
||||||
|
# virtualenv
|
||||||
|
pre-commit==3.3.3
|
||||||
|
# via -r requirements/dev.in
|
||||||
|
pyproject-api==1.5.2
|
||||||
|
# via tox
|
||||||
|
pyproject-hooks==1.0.0
|
||||||
|
# via build
|
||||||
|
pyyaml==6.0
|
||||||
|
# via pre-commit
|
||||||
|
toposort==1.10
|
||||||
|
# via pip-compile-multi
|
||||||
|
tox==4.6.3
|
||||||
|
# via -r requirements/dev.in
|
||||||
|
virtualenv==20.23.1
|
||||||
|
# via
|
||||||
|
# pre-commit
|
||||||
|
# tox
|
||||||
|
wheel==0.40.0
|
||||||
|
# via pip-tools
|
||||||
|
|
||||||
|
# The following packages are considered to be unsafe in a requirements file:
|
||||||
|
# pip
|
||||||
|
# setuptools
|
||||||
5
requirements/docs.in
Normal file
5
requirements/docs.in
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
Pallets-Sphinx-Themes
|
||||||
|
Sphinx
|
||||||
|
sphinx-issues
|
||||||
|
sphinxcontrib-log-cabinet
|
||||||
|
sphinx-tabs
|
||||||
68
requirements/docs.txt
Normal file
68
requirements/docs.txt
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
# SHA1:34fd4ca6516e97c7348e6facdd9c4ebb68209d1c
|
||||||
|
#
|
||||||
|
# This file is autogenerated by pip-compile-multi
|
||||||
|
# To update, run:
|
||||||
|
#
|
||||||
|
# pip-compile-multi
|
||||||
|
#
|
||||||
|
alabaster==0.7.13
|
||||||
|
# via sphinx
|
||||||
|
babel==2.12.1
|
||||||
|
# via sphinx
|
||||||
|
certifi==2023.5.7
|
||||||
|
# via requests
|
||||||
|
charset-normalizer==3.1.0
|
||||||
|
# via requests
|
||||||
|
docutils==0.18.1
|
||||||
|
# via
|
||||||
|
# sphinx
|
||||||
|
# sphinx-tabs
|
||||||
|
idna==3.4
|
||||||
|
# via requests
|
||||||
|
imagesize==1.4.1
|
||||||
|
# via sphinx
|
||||||
|
jinja2==3.1.2
|
||||||
|
# via sphinx
|
||||||
|
markupsafe==2.1.3
|
||||||
|
# via jinja2
|
||||||
|
packaging==23.1
|
||||||
|
# via
|
||||||
|
# pallets-sphinx-themes
|
||||||
|
# sphinx
|
||||||
|
pallets-sphinx-themes==2.1.1
|
||||||
|
# via -r requirements/docs.in
|
||||||
|
pygments==2.15.1
|
||||||
|
# via
|
||||||
|
# sphinx
|
||||||
|
# sphinx-tabs
|
||||||
|
requests==2.31.0
|
||||||
|
# via sphinx
|
||||||
|
snowballstemmer==2.2.0
|
||||||
|
# via sphinx
|
||||||
|
sphinx==7.0.1
|
||||||
|
# via
|
||||||
|
# -r requirements/docs.in
|
||||||
|
# pallets-sphinx-themes
|
||||||
|
# sphinx-issues
|
||||||
|
# sphinx-tabs
|
||||||
|
# sphinxcontrib-log-cabinet
|
||||||
|
sphinx-issues==3.0.1
|
||||||
|
# via -r requirements/docs.in
|
||||||
|
sphinx-tabs==3.4.1
|
||||||
|
# via -r requirements/docs.in
|
||||||
|
sphinxcontrib-applehelp==1.0.4
|
||||||
|
# via sphinx
|
||||||
|
sphinxcontrib-devhelp==1.0.2
|
||||||
|
# via sphinx
|
||||||
|
sphinxcontrib-htmlhelp==2.0.1
|
||||||
|
# via sphinx
|
||||||
|
sphinxcontrib-jsmath==1.0.1
|
||||||
|
# via sphinx
|
||||||
|
sphinxcontrib-log-cabinet==1.0.1
|
||||||
|
# via -r requirements/docs.in
|
||||||
|
sphinxcontrib-qthelp==1.0.3
|
||||||
|
# via sphinx
|
||||||
|
sphinxcontrib-serializinghtml==1.1.5
|
||||||
|
# via sphinx
|
||||||
|
urllib3==2.0.3
|
||||||
|
# via requests
|
||||||
6
requirements/tests-pallets-min.in
Normal file
6
requirements/tests-pallets-min.in
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
Werkzeug==2.3.7
|
||||||
|
Jinja2==3.1.2
|
||||||
|
MarkupSafe==2.1.1
|
||||||
|
itsdangerous==2.1.2
|
||||||
|
click==8.1.3
|
||||||
|
blinker==1.6.2
|
||||||
22
requirements/tests-pallets-min.txt
Normal file
22
requirements/tests-pallets-min.txt
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
# SHA1:fe057f95a98251b053eec8fa27df0feb722c70e8
|
||||||
|
#
|
||||||
|
# This file is autogenerated by pip-compile-multi
|
||||||
|
# To update, run:
|
||||||
|
#
|
||||||
|
# pip-compile-multi
|
||||||
|
#
|
||||||
|
blinker==1.6.2
|
||||||
|
# via -r requirements/tests-pallets-min.in
|
||||||
|
click==8.1.3
|
||||||
|
# via -r requirements/tests-pallets-min.in
|
||||||
|
itsdangerous==2.1.2
|
||||||
|
# via -r requirements/tests-pallets-min.in
|
||||||
|
jinja2==3.1.2
|
||||||
|
# via -r requirements/tests-pallets-min.in
|
||||||
|
markupsafe==2.1.1
|
||||||
|
# via
|
||||||
|
# -r requirements/tests-pallets-min.in
|
||||||
|
# jinja2
|
||||||
|
# werkzeug
|
||||||
|
werkzeug==2.3.7
|
||||||
|
# via -r requirements/tests-pallets-min.in
|
||||||
4
requirements/tests.in
Normal file
4
requirements/tests.in
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
pytest
|
||||||
|
asgiref
|
||||||
|
greenlet ; python_version < "3.11"
|
||||||
|
python-dotenv
|
||||||
19
requirements/tests.txt
Normal file
19
requirements/tests.txt
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
# SHA1:42d37aff22e2f1fc447e20d483e13d6d4e066b10
|
||||||
|
#
|
||||||
|
# This file is autogenerated by pip-compile-multi
|
||||||
|
# To update, run:
|
||||||
|
#
|
||||||
|
# pip-compile-multi
|
||||||
|
#
|
||||||
|
asgiref==3.7.2
|
||||||
|
# via -r requirements/tests.in
|
||||||
|
iniconfig==2.0.0
|
||||||
|
# via pytest
|
||||||
|
packaging==23.1
|
||||||
|
# via pytest
|
||||||
|
pluggy==1.2.0
|
||||||
|
# via pytest
|
||||||
|
pytest==7.4.0
|
||||||
|
# via -r requirements/tests.in
|
||||||
|
python-dotenv==1.0.0
|
||||||
|
# via -r requirements/tests.in
|
||||||
4
requirements/typing.in
Normal file
4
requirements/typing.in
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
mypy
|
||||||
|
types-contextvars
|
||||||
|
types-dataclasses
|
||||||
|
cryptography
|
||||||
23
requirements/typing.txt
Normal file
23
requirements/typing.txt
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
# SHA1:6a354b832686fd3ec017455769a0270953a1e225
|
||||||
|
#
|
||||||
|
# This file is autogenerated by pip-compile-multi
|
||||||
|
# To update, run:
|
||||||
|
#
|
||||||
|
# pip-compile-multi
|
||||||
|
#
|
||||||
|
cffi==1.15.1
|
||||||
|
# via cryptography
|
||||||
|
cryptography==41.0.1
|
||||||
|
# via -r requirements/typing.in
|
||||||
|
mypy==1.4.1
|
||||||
|
# via -r requirements/typing.in
|
||||||
|
mypy-extensions==1.0.0
|
||||||
|
# via mypy
|
||||||
|
pycparser==2.21
|
||||||
|
# via cffi
|
||||||
|
types-contextvars==2.4.7.2
|
||||||
|
# via -r requirements/typing.in
|
||||||
|
types-dataclasses==0.6.6
|
||||||
|
# via -r requirements/typing.in
|
||||||
|
typing-extensions==4.6.3
|
||||||
|
# via mypy
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
from . import json as json
|
from . import json as json
|
||||||
from .app import Flask as Flask
|
from .app import Flask as Flask
|
||||||
|
from .app import Request as Request
|
||||||
|
from .app import Response as Response
|
||||||
from .blueprints import Blueprint as Blueprint
|
from .blueprints import Blueprint as Blueprint
|
||||||
from .config import Config as Config
|
from .config import Config as Config
|
||||||
from .ctx import after_this_request as after_this_request
|
from .ctx import after_this_request as after_this_request
|
||||||
|
|
@ -35,5 +37,66 @@ from .templating import render_template as render_template
|
||||||
from .templating import render_template_string as render_template_string
|
from .templating import render_template_string as render_template_string
|
||||||
from .templating import stream_template as stream_template
|
from .templating import stream_template as stream_template
|
||||||
from .templating import stream_template_string as stream_template_string
|
from .templating import stream_template_string as stream_template_string
|
||||||
from .wrappers import Request as Request
|
|
||||||
from .wrappers import Response as Response
|
__version__ = "2.3.3"
|
||||||
|
|
||||||
|
|
||||||
|
def __getattr__(name):
|
||||||
|
if name == "_app_ctx_stack":
|
||||||
|
import warnings
|
||||||
|
from .globals import __app_ctx_stack
|
||||||
|
|
||||||
|
warnings.warn(
|
||||||
|
"'_app_ctx_stack' is deprecated and will be removed in Flask 2.4.",
|
||||||
|
DeprecationWarning,
|
||||||
|
stacklevel=2,
|
||||||
|
)
|
||||||
|
return __app_ctx_stack
|
||||||
|
|
||||||
|
if name == "_request_ctx_stack":
|
||||||
|
import warnings
|
||||||
|
from .globals import __request_ctx_stack
|
||||||
|
|
||||||
|
warnings.warn(
|
||||||
|
"'_request_ctx_stack' is deprecated and will be removed in Flask 2.4.",
|
||||||
|
DeprecationWarning,
|
||||||
|
stacklevel=2,
|
||||||
|
)
|
||||||
|
return __request_ctx_stack
|
||||||
|
|
||||||
|
if name == "escape":
|
||||||
|
import warnings
|
||||||
|
from markupsafe import escape
|
||||||
|
|
||||||
|
warnings.warn(
|
||||||
|
"'flask.escape' is deprecated and will be removed in Flask 2.4. Import"
|
||||||
|
" 'markupsafe.escape' instead.",
|
||||||
|
DeprecationWarning,
|
||||||
|
stacklevel=2,
|
||||||
|
)
|
||||||
|
return escape
|
||||||
|
|
||||||
|
if name == "Markup":
|
||||||
|
import warnings
|
||||||
|
from markupsafe import Markup
|
||||||
|
|
||||||
|
warnings.warn(
|
||||||
|
"'flask.Markup' is deprecated and will be removed in Flask 2.4. Import"
|
||||||
|
" 'markupsafe.Markup' instead.",
|
||||||
|
DeprecationWarning,
|
||||||
|
stacklevel=2,
|
||||||
|
)
|
||||||
|
return Markup
|
||||||
|
|
||||||
|
if name == "signals_available":
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
warnings.warn(
|
||||||
|
"'signals_available' is deprecated and will be removed in Flask 2.4."
|
||||||
|
" Signals are always available",
|
||||||
|
DeprecationWarning,
|
||||||
|
stacklevel=2,
|
||||||
|
)
|
||||||
|
return True
|
||||||
|
|
||||||
|
raise AttributeError(name)
|
||||||
|
|
|
||||||
1470
src/flask/app.py
1470
src/flask/app.py
File diff suppressed because it is too large
Load diff
|
|
@ -2,127 +2,625 @@ from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import typing as t
|
import typing as t
|
||||||
from datetime import timedelta
|
from collections import defaultdict
|
||||||
|
from functools import update_wrapper
|
||||||
|
|
||||||
from .cli import AppGroup
|
from . import typing as ft
|
||||||
from .globals import current_app
|
from .scaffold import _endpoint_from_view_func
|
||||||
from .helpers import send_from_directory
|
from .scaffold import _sentinel
|
||||||
from .sansio.blueprints import Blueprint as SansioBlueprint
|
from .scaffold import Scaffold
|
||||||
from .sansio.blueprints import BlueprintSetupState as BlueprintSetupState # noqa
|
from .scaffold import setupmethod
|
||||||
from .sansio.scaffold import _sentinel
|
|
||||||
|
|
||||||
if t.TYPE_CHECKING: # pragma: no cover
|
if t.TYPE_CHECKING: # pragma: no cover
|
||||||
from .wrappers import Response
|
from .app import Flask
|
||||||
|
|
||||||
|
DeferredSetupFunction = t.Callable[["BlueprintSetupState"], t.Callable]
|
||||||
|
T_after_request = t.TypeVar("T_after_request", bound=ft.AfterRequestCallable)
|
||||||
|
T_before_request = t.TypeVar("T_before_request", bound=ft.BeforeRequestCallable)
|
||||||
|
T_error_handler = t.TypeVar("T_error_handler", bound=ft.ErrorHandlerCallable)
|
||||||
|
T_teardown = t.TypeVar("T_teardown", bound=ft.TeardownCallable)
|
||||||
|
T_template_context_processor = t.TypeVar(
|
||||||
|
"T_template_context_processor", bound=ft.TemplateContextProcessorCallable
|
||||||
|
)
|
||||||
|
T_template_filter = t.TypeVar("T_template_filter", bound=ft.TemplateFilterCallable)
|
||||||
|
T_template_global = t.TypeVar("T_template_global", bound=ft.TemplateGlobalCallable)
|
||||||
|
T_template_test = t.TypeVar("T_template_test", bound=ft.TemplateTestCallable)
|
||||||
|
T_url_defaults = t.TypeVar("T_url_defaults", bound=ft.URLDefaultCallable)
|
||||||
|
T_url_value_preprocessor = t.TypeVar(
|
||||||
|
"T_url_value_preprocessor", bound=ft.URLValuePreprocessorCallable
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Blueprint(SansioBlueprint):
|
class BlueprintSetupState:
|
||||||
|
"""Temporary holder object for registering a blueprint with the
|
||||||
|
application. An instance of this class is created by the
|
||||||
|
:meth:`~flask.Blueprint.make_setup_state` method and later passed
|
||||||
|
to all register callback functions.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
blueprint: Blueprint,
|
||||||
|
app: Flask,
|
||||||
|
options: t.Any,
|
||||||
|
first_registration: bool,
|
||||||
|
) -> None:
|
||||||
|
#: a reference to the current application
|
||||||
|
self.app = app
|
||||||
|
|
||||||
|
#: a reference to the blueprint that created this setup state.
|
||||||
|
self.blueprint = blueprint
|
||||||
|
|
||||||
|
#: a dictionary with all options that were passed to the
|
||||||
|
#: :meth:`~flask.Flask.register_blueprint` method.
|
||||||
|
self.options = options
|
||||||
|
|
||||||
|
#: as blueprints can be registered multiple times with the
|
||||||
|
#: application and not everything wants to be registered
|
||||||
|
#: multiple times on it, this attribute can be used to figure
|
||||||
|
#: out if the blueprint was registered in the past already.
|
||||||
|
self.first_registration = first_registration
|
||||||
|
|
||||||
|
subdomain = self.options.get("subdomain")
|
||||||
|
if subdomain is None:
|
||||||
|
subdomain = self.blueprint.subdomain
|
||||||
|
|
||||||
|
#: The subdomain that the blueprint should be active for, ``None``
|
||||||
|
#: otherwise.
|
||||||
|
self.subdomain = subdomain
|
||||||
|
|
||||||
|
url_prefix = self.options.get("url_prefix")
|
||||||
|
if url_prefix is None:
|
||||||
|
url_prefix = self.blueprint.url_prefix
|
||||||
|
#: The prefix that should be used for all URLs defined on the
|
||||||
|
#: blueprint.
|
||||||
|
self.url_prefix = url_prefix
|
||||||
|
|
||||||
|
self.name = self.options.get("name", blueprint.name)
|
||||||
|
self.name_prefix = self.options.get("name_prefix", "")
|
||||||
|
|
||||||
|
#: A dictionary with URL defaults that is added to each and every
|
||||||
|
#: URL that was defined with the blueprint.
|
||||||
|
self.url_defaults = dict(self.blueprint.url_values_defaults)
|
||||||
|
self.url_defaults.update(self.options.get("url_defaults", ()))
|
||||||
|
|
||||||
|
def add_url_rule(
|
||||||
|
self,
|
||||||
|
rule: str,
|
||||||
|
endpoint: str | None = None,
|
||||||
|
view_func: t.Callable | None = None,
|
||||||
|
**options: t.Any,
|
||||||
|
) -> None:
|
||||||
|
"""A helper method to register a rule (and optionally a view function)
|
||||||
|
to the application. The endpoint is automatically prefixed with the
|
||||||
|
blueprint's name.
|
||||||
|
"""
|
||||||
|
if self.url_prefix is not None:
|
||||||
|
if rule:
|
||||||
|
rule = "/".join((self.url_prefix.rstrip("/"), rule.lstrip("/")))
|
||||||
|
else:
|
||||||
|
rule = self.url_prefix
|
||||||
|
options.setdefault("subdomain", self.subdomain)
|
||||||
|
if endpoint is None:
|
||||||
|
endpoint = _endpoint_from_view_func(view_func) # type: ignore
|
||||||
|
defaults = self.url_defaults
|
||||||
|
if "defaults" in options:
|
||||||
|
defaults = dict(defaults, **options.pop("defaults"))
|
||||||
|
|
||||||
|
self.app.add_url_rule(
|
||||||
|
rule,
|
||||||
|
f"{self.name_prefix}.{self.name}.{endpoint}".lstrip("."),
|
||||||
|
view_func,
|
||||||
|
defaults=defaults,
|
||||||
|
**options,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Blueprint(Scaffold):
|
||||||
|
"""Represents a blueprint, a collection of routes and other
|
||||||
|
app-related functions that can be registered on a real application
|
||||||
|
later.
|
||||||
|
|
||||||
|
A blueprint is an object that allows defining application functions
|
||||||
|
without requiring an application object ahead of time. It uses the
|
||||||
|
same decorators as :class:`~flask.Flask`, but defers the need for an
|
||||||
|
application by recording them for later registration.
|
||||||
|
|
||||||
|
Decorating a function with a blueprint creates a deferred function
|
||||||
|
that is called with :class:`~flask.blueprints.BlueprintSetupState`
|
||||||
|
when the blueprint is registered on an application.
|
||||||
|
|
||||||
|
See :doc:`/blueprints` for more information.
|
||||||
|
|
||||||
|
:param name: The name of the blueprint. Will be prepended to each
|
||||||
|
endpoint name.
|
||||||
|
:param import_name: The name of the blueprint package, usually
|
||||||
|
``__name__``. This helps locate the ``root_path`` for the
|
||||||
|
blueprint.
|
||||||
|
:param static_folder: A folder with static files that should be
|
||||||
|
served by the blueprint's static route. The path is relative to
|
||||||
|
the blueprint's root path. Blueprint static files are disabled
|
||||||
|
by default.
|
||||||
|
:param static_url_path: The url to serve static files from.
|
||||||
|
Defaults to ``static_folder``. If the blueprint does not have
|
||||||
|
a ``url_prefix``, the app's static route will take precedence,
|
||||||
|
and the blueprint's static files won't be accessible.
|
||||||
|
:param template_folder: A folder with templates that should be added
|
||||||
|
to the app's template search path. The path is relative to the
|
||||||
|
blueprint's root path. Blueprint templates are disabled by
|
||||||
|
default. Blueprint templates have a lower precedence than those
|
||||||
|
in the app's templates folder.
|
||||||
|
:param url_prefix: A path to prepend to all of the blueprint's URLs,
|
||||||
|
to make them distinct from the rest of the app's routes.
|
||||||
|
:param subdomain: A subdomain that blueprint routes will match on by
|
||||||
|
default.
|
||||||
|
:param url_defaults: A dict of default values that blueprint routes
|
||||||
|
will receive by default.
|
||||||
|
:param root_path: By default, the blueprint will automatically set
|
||||||
|
this based on ``import_name``. In certain situations this
|
||||||
|
automatic detection can fail, so the path can be specified
|
||||||
|
manually instead.
|
||||||
|
|
||||||
|
.. versionchanged:: 1.1.0
|
||||||
|
Blueprints have a ``cli`` group to register nested CLI commands.
|
||||||
|
The ``cli_group`` parameter controls the name of the group under
|
||||||
|
the ``flask`` command.
|
||||||
|
|
||||||
|
.. versionadded:: 0.7
|
||||||
|
"""
|
||||||
|
|
||||||
|
_got_registered_once = False
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
name: str,
|
name: str,
|
||||||
import_name: str,
|
import_name: str,
|
||||||
static_folder: str | os.PathLike[str] | None = None,
|
static_folder: str | os.PathLike | None = None,
|
||||||
static_url_path: str | None = None,
|
static_url_path: str | None = None,
|
||||||
template_folder: str | os.PathLike[str] | None = None,
|
template_folder: str | os.PathLike | None = None,
|
||||||
url_prefix: str | None = None,
|
url_prefix: str | None = None,
|
||||||
subdomain: str | None = None,
|
subdomain: str | None = None,
|
||||||
url_defaults: dict[str, t.Any] | None = None,
|
url_defaults: dict | None = None,
|
||||||
root_path: str | None = None,
|
root_path: str | None = None,
|
||||||
cli_group: str | None = _sentinel, # type: ignore
|
cli_group: str | None = _sentinel, # type: ignore
|
||||||
) -> None:
|
):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
name,
|
import_name=import_name,
|
||||||
import_name,
|
static_folder=static_folder,
|
||||||
static_folder,
|
static_url_path=static_url_path,
|
||||||
static_url_path,
|
template_folder=template_folder,
|
||||||
template_folder,
|
root_path=root_path,
|
||||||
url_prefix,
|
|
||||||
subdomain,
|
|
||||||
url_defaults,
|
|
||||||
root_path,
|
|
||||||
cli_group,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
#: The Click command group for registering CLI commands for this
|
if not name:
|
||||||
#: object. The commands are available from the ``flask`` command
|
raise ValueError("'name' may not be empty.")
|
||||||
#: 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
|
if "." in name:
|
||||||
# the app's commands to another CLI tool.
|
raise ValueError("'name' may not contain a dot '.' character.")
|
||||||
self.cli.name = self.name
|
|
||||||
|
|
||||||
def get_send_file_max_age(self, filename: str | None) -> int | None:
|
self.name = name
|
||||||
"""Used by :func:`send_file` to determine the ``max_age`` cache
|
self.url_prefix = url_prefix
|
||||||
value for a given file path if it wasn't passed.
|
self.subdomain = subdomain
|
||||||
|
self.deferred_functions: list[DeferredSetupFunction] = []
|
||||||
|
|
||||||
By default, this returns :data:`SEND_FILE_MAX_AGE_DEFAULT` from
|
if url_defaults is None:
|
||||||
the configuration of :data:`~flask.current_app`. This defaults
|
url_defaults = {}
|
||||||
to ``None``, which tells the browser to use conditional requests
|
|
||||||
instead of a timed cache, which is usually preferable.
|
|
||||||
|
|
||||||
Note this is a duplicate of the same method in the Flask
|
self.url_values_defaults = url_defaults
|
||||||
class.
|
self.cli_group = cli_group
|
||||||
|
self._blueprints: list[tuple[Blueprint, dict]] = []
|
||||||
|
|
||||||
.. versionchanged:: 2.0
|
def _check_setup_finished(self, f_name: str) -> None:
|
||||||
The default configuration is ``None`` instead of 12 hours.
|
if self._got_registered_once:
|
||||||
|
raise AssertionError(
|
||||||
|
f"The setup method '{f_name}' can no longer be called on the blueprint"
|
||||||
|
f" '{self.name}'. It has already been registered at least once, any"
|
||||||
|
" changes will not be applied consistently.\n"
|
||||||
|
"Make sure all imports, decorators, functions, etc. needed to set up"
|
||||||
|
" the blueprint are done before registering it."
|
||||||
|
)
|
||||||
|
|
||||||
.. versionadded:: 0.9
|
@setupmethod
|
||||||
"""
|
def record(self, func: t.Callable) -> None:
|
||||||
value = current_app.config["SEND_FILE_MAX_AGE_DEFAULT"]
|
"""Registers a function that is called when the blueprint is
|
||||||
|
registered on the application. This function is called with the
|
||||||
if value is None:
|
state as argument as returned by the :meth:`make_setup_state`
|
||||||
return None
|
|
||||||
|
|
||||||
if isinstance(value, timedelta):
|
|
||||||
return int(value.total_seconds())
|
|
||||||
|
|
||||||
return value # type: ignore[no-any-return]
|
|
||||||
|
|
||||||
def send_static_file(self, filename: str) -> Response:
|
|
||||||
"""The view function used to serve files from
|
|
||||||
:attr:`static_folder`. A route is automatically registered for
|
|
||||||
this view at :attr:`static_url_path` if :attr:`static_folder` is
|
|
||||||
set.
|
|
||||||
|
|
||||||
Note this is a duplicate of the same method in the Flask
|
|
||||||
class.
|
|
||||||
|
|
||||||
.. versionadded:: 0.5
|
|
||||||
|
|
||||||
"""
|
|
||||||
if not self.has_static_folder:
|
|
||||||
raise RuntimeError("'static_folder' must be set to serve static_files.")
|
|
||||||
|
|
||||||
# send_file only knows to call get_send_file_max_age on the app,
|
|
||||||
# call it here so it works for blueprints too.
|
|
||||||
max_age = self.get_send_file_max_age(filename)
|
|
||||||
return send_from_directory(
|
|
||||||
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.
|
method.
|
||||||
|
|
||||||
: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.
|
|
||||||
|
|
||||||
.. versionchanged:: 3.1
|
|
||||||
Added the ``encoding`` parameter.
|
|
||||||
"""
|
"""
|
||||||
if mode not in {"r", "rt", "rb"}:
|
self.deferred_functions.append(func)
|
||||||
raise ValueError("Resources can only be opened for reading.")
|
|
||||||
|
|
||||||
path = os.path.join(self.root_path, resource)
|
@setupmethod
|
||||||
|
def record_once(self, func: t.Callable) -> None:
|
||||||
|
"""Works like :meth:`record` but wraps the function in another
|
||||||
|
function that will ensure the function is only called once. If the
|
||||||
|
blueprint is registered a second time on the application, the
|
||||||
|
function passed is not called.
|
||||||
|
"""
|
||||||
|
|
||||||
if mode == "rb":
|
def wrapper(state: BlueprintSetupState) -> None:
|
||||||
return open(path, mode) # pyright: ignore
|
if state.first_registration:
|
||||||
|
func(state)
|
||||||
|
|
||||||
return open(path, mode, encoding=encoding)
|
self.record(update_wrapper(wrapper, func))
|
||||||
|
|
||||||
|
def make_setup_state(
|
||||||
|
self, app: Flask, options: dict, first_registration: bool = False
|
||||||
|
) -> BlueprintSetupState:
|
||||||
|
"""Creates an instance of :meth:`~flask.blueprints.BlueprintSetupState`
|
||||||
|
object that is later passed to the register callback functions.
|
||||||
|
Subclasses can override this to return a subclass of the setup state.
|
||||||
|
"""
|
||||||
|
return BlueprintSetupState(self, app, options, first_registration)
|
||||||
|
|
||||||
|
@setupmethod
|
||||||
|
def register_blueprint(self, blueprint: Blueprint, **options: t.Any) -> None:
|
||||||
|
"""Register a :class:`~flask.Blueprint` on this blueprint. Keyword
|
||||||
|
arguments passed to this method will override the defaults set
|
||||||
|
on the blueprint.
|
||||||
|
|
||||||
|
.. versionchanged:: 2.0.1
|
||||||
|
The ``name`` option can be used to change the (pre-dotted)
|
||||||
|
name the blueprint is registered with. This allows the same
|
||||||
|
blueprint to be registered multiple times with unique names
|
||||||
|
for ``url_for``.
|
||||||
|
|
||||||
|
.. versionadded:: 2.0
|
||||||
|
"""
|
||||||
|
if blueprint is self:
|
||||||
|
raise ValueError("Cannot register a blueprint on itself")
|
||||||
|
self._blueprints.append((blueprint, options))
|
||||||
|
|
||||||
|
def register(self, app: Flask, options: dict) -> None:
|
||||||
|
"""Called by :meth:`Flask.register_blueprint` to register all
|
||||||
|
views and callbacks registered on the blueprint with the
|
||||||
|
application. Creates a :class:`.BlueprintSetupState` and calls
|
||||||
|
each :meth:`record` callback with it.
|
||||||
|
|
||||||
|
:param app: The application this blueprint is being registered
|
||||||
|
with.
|
||||||
|
:param options: Keyword arguments forwarded from
|
||||||
|
:meth:`~Flask.register_blueprint`.
|
||||||
|
|
||||||
|
.. versionchanged:: 2.3
|
||||||
|
Nested blueprints now correctly apply subdomains.
|
||||||
|
|
||||||
|
.. versionchanged:: 2.1
|
||||||
|
Registering the same blueprint with the same name multiple
|
||||||
|
times is an error.
|
||||||
|
|
||||||
|
.. versionchanged:: 2.0.1
|
||||||
|
Nested blueprints are registered with their dotted name.
|
||||||
|
This allows different blueprints with the same name to be
|
||||||
|
nested at different locations.
|
||||||
|
|
||||||
|
.. versionchanged:: 2.0.1
|
||||||
|
The ``name`` option can be used to change the (pre-dotted)
|
||||||
|
name the blueprint is registered with. This allows the same
|
||||||
|
blueprint to be registered multiple times with unique names
|
||||||
|
for ``url_for``.
|
||||||
|
"""
|
||||||
|
name_prefix = options.get("name_prefix", "")
|
||||||
|
self_name = options.get("name", self.name)
|
||||||
|
name = f"{name_prefix}.{self_name}".lstrip(".")
|
||||||
|
|
||||||
|
if name in app.blueprints:
|
||||||
|
bp_desc = "this" if app.blueprints[name] is self else "a different"
|
||||||
|
existing_at = f" '{name}'" if self_name != name else ""
|
||||||
|
|
||||||
|
raise ValueError(
|
||||||
|
f"The name '{self_name}' is already registered for"
|
||||||
|
f" {bp_desc} blueprint{existing_at}. Use 'name=' to"
|
||||||
|
f" provide a unique name."
|
||||||
|
)
|
||||||
|
|
||||||
|
first_bp_registration = not any(bp is self for bp in app.blueprints.values())
|
||||||
|
first_name_registration = name not in app.blueprints
|
||||||
|
|
||||||
|
app.blueprints[name] = self
|
||||||
|
self._got_registered_once = True
|
||||||
|
state = self.make_setup_state(app, options, first_bp_registration)
|
||||||
|
|
||||||
|
if self.has_static_folder:
|
||||||
|
state.add_url_rule(
|
||||||
|
f"{self.static_url_path}/<path:filename>",
|
||||||
|
view_func=self.send_static_file,
|
||||||
|
endpoint="static",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Merge blueprint data into parent.
|
||||||
|
if first_bp_registration or first_name_registration:
|
||||||
|
|
||||||
|
def extend(bp_dict, parent_dict):
|
||||||
|
for key, values in bp_dict.items():
|
||||||
|
key = name if key is None else f"{name}.{key}"
|
||||||
|
parent_dict[key].extend(values)
|
||||||
|
|
||||||
|
for key, value in self.error_handler_spec.items():
|
||||||
|
key = name if key is None else f"{name}.{key}"
|
||||||
|
value = defaultdict(
|
||||||
|
dict,
|
||||||
|
{
|
||||||
|
code: {
|
||||||
|
exc_class: func for exc_class, func in code_values.items()
|
||||||
|
}
|
||||||
|
for code, code_values in value.items()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
app.error_handler_spec[key] = value
|
||||||
|
|
||||||
|
for endpoint, func in self.view_functions.items():
|
||||||
|
app.view_functions[endpoint] = func
|
||||||
|
|
||||||
|
extend(self.before_request_funcs, app.before_request_funcs)
|
||||||
|
extend(self.after_request_funcs, app.after_request_funcs)
|
||||||
|
extend(
|
||||||
|
self.teardown_request_funcs,
|
||||||
|
app.teardown_request_funcs,
|
||||||
|
)
|
||||||
|
extend(self.url_default_functions, app.url_default_functions)
|
||||||
|
extend(self.url_value_preprocessors, app.url_value_preprocessors)
|
||||||
|
extend(self.template_context_processors, app.template_context_processors)
|
||||||
|
|
||||||
|
for deferred in self.deferred_functions:
|
||||||
|
deferred(state)
|
||||||
|
|
||||||
|
cli_resolved_group = options.get("cli_group", self.cli_group)
|
||||||
|
|
||||||
|
if self.cli.commands:
|
||||||
|
if cli_resolved_group is None:
|
||||||
|
app.cli.commands.update(self.cli.commands)
|
||||||
|
elif cli_resolved_group is _sentinel:
|
||||||
|
self.cli.name = name
|
||||||
|
app.cli.add_command(self.cli)
|
||||||
|
else:
|
||||||
|
self.cli.name = cli_resolved_group
|
||||||
|
app.cli.add_command(self.cli)
|
||||||
|
|
||||||
|
for blueprint, bp_options in self._blueprints:
|
||||||
|
bp_options = bp_options.copy()
|
||||||
|
bp_url_prefix = bp_options.get("url_prefix")
|
||||||
|
bp_subdomain = bp_options.get("subdomain")
|
||||||
|
|
||||||
|
if bp_subdomain is None:
|
||||||
|
bp_subdomain = blueprint.subdomain
|
||||||
|
|
||||||
|
if state.subdomain is not None and bp_subdomain is not None:
|
||||||
|
bp_options["subdomain"] = bp_subdomain + "." + state.subdomain
|
||||||
|
elif bp_subdomain is not None:
|
||||||
|
bp_options["subdomain"] = bp_subdomain
|
||||||
|
elif state.subdomain is not None:
|
||||||
|
bp_options["subdomain"] = state.subdomain
|
||||||
|
|
||||||
|
if bp_url_prefix is None:
|
||||||
|
bp_url_prefix = blueprint.url_prefix
|
||||||
|
|
||||||
|
if state.url_prefix is not None and bp_url_prefix is not None:
|
||||||
|
bp_options["url_prefix"] = (
|
||||||
|
state.url_prefix.rstrip("/") + "/" + bp_url_prefix.lstrip("/")
|
||||||
|
)
|
||||||
|
elif bp_url_prefix is not None:
|
||||||
|
bp_options["url_prefix"] = bp_url_prefix
|
||||||
|
elif state.url_prefix is not None:
|
||||||
|
bp_options["url_prefix"] = state.url_prefix
|
||||||
|
|
||||||
|
bp_options["name_prefix"] = name
|
||||||
|
blueprint.register(app, bp_options)
|
||||||
|
|
||||||
|
@setupmethod
|
||||||
|
def add_url_rule(
|
||||||
|
self,
|
||||||
|
rule: str,
|
||||||
|
endpoint: str | None = None,
|
||||||
|
view_func: ft.RouteCallable | None = None,
|
||||||
|
provide_automatic_options: bool | None = None,
|
||||||
|
**options: t.Any,
|
||||||
|
) -> None:
|
||||||
|
"""Register a URL rule with the blueprint. See :meth:`.Flask.add_url_rule` for
|
||||||
|
full documentation.
|
||||||
|
|
||||||
|
The URL rule is prefixed with the blueprint's URL prefix. The endpoint name,
|
||||||
|
used with :func:`url_for`, is prefixed with the blueprint's name.
|
||||||
|
"""
|
||||||
|
if endpoint and "." in endpoint:
|
||||||
|
raise ValueError("'endpoint' may not contain a dot '.' character.")
|
||||||
|
|
||||||
|
if view_func and hasattr(view_func, "__name__") and "." in view_func.__name__:
|
||||||
|
raise ValueError("'view_func' name may not contain a dot '.' character.")
|
||||||
|
|
||||||
|
self.record(
|
||||||
|
lambda s: s.add_url_rule(
|
||||||
|
rule,
|
||||||
|
endpoint,
|
||||||
|
view_func,
|
||||||
|
provide_automatic_options=provide_automatic_options,
|
||||||
|
**options,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
@setupmethod
|
||||||
|
def app_template_filter(
|
||||||
|
self, name: str | None = None
|
||||||
|
) -> t.Callable[[T_template_filter], T_template_filter]:
|
||||||
|
"""Register a template filter, available in any template rendered by the
|
||||||
|
application. Equivalent to :meth:`.Flask.template_filter`.
|
||||||
|
|
||||||
|
:param name: the optional name of the filter, otherwise the
|
||||||
|
function name will be used.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def decorator(f: T_template_filter) -> T_template_filter:
|
||||||
|
self.add_app_template_filter(f, name=name)
|
||||||
|
return f
|
||||||
|
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
@setupmethod
|
||||||
|
def add_app_template_filter(
|
||||||
|
self, f: ft.TemplateFilterCallable, name: str | None = None
|
||||||
|
) -> None:
|
||||||
|
"""Register a template filter, available in any template rendered by the
|
||||||
|
application. Works like the :meth:`app_template_filter` decorator. Equivalent to
|
||||||
|
:meth:`.Flask.add_template_filter`.
|
||||||
|
|
||||||
|
:param name: the optional name of the filter, otherwise the
|
||||||
|
function name will be used.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def register_template(state: BlueprintSetupState) -> None:
|
||||||
|
state.app.jinja_env.filters[name or f.__name__] = f
|
||||||
|
|
||||||
|
self.record_once(register_template)
|
||||||
|
|
||||||
|
@setupmethod
|
||||||
|
def app_template_test(
|
||||||
|
self, name: str | None = None
|
||||||
|
) -> t.Callable[[T_template_test], T_template_test]:
|
||||||
|
"""Register a template test, available in any template rendered by the
|
||||||
|
application. Equivalent to :meth:`.Flask.template_test`.
|
||||||
|
|
||||||
|
.. versionadded:: 0.10
|
||||||
|
|
||||||
|
:param name: the optional name of the test, otherwise the
|
||||||
|
function name will be used.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def decorator(f: T_template_test) -> T_template_test:
|
||||||
|
self.add_app_template_test(f, name=name)
|
||||||
|
return f
|
||||||
|
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
@setupmethod
|
||||||
|
def add_app_template_test(
|
||||||
|
self, f: ft.TemplateTestCallable, name: str | None = None
|
||||||
|
) -> None:
|
||||||
|
"""Register a template test, available in any template rendered by the
|
||||||
|
application. Works like the :meth:`app_template_test` decorator. Equivalent to
|
||||||
|
:meth:`.Flask.add_template_test`.
|
||||||
|
|
||||||
|
.. versionadded:: 0.10
|
||||||
|
|
||||||
|
:param name: the optional name of the test, otherwise the
|
||||||
|
function name will be used.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def register_template(state: BlueprintSetupState) -> None:
|
||||||
|
state.app.jinja_env.tests[name or f.__name__] = f
|
||||||
|
|
||||||
|
self.record_once(register_template)
|
||||||
|
|
||||||
|
@setupmethod
|
||||||
|
def app_template_global(
|
||||||
|
self, name: str | None = None
|
||||||
|
) -> t.Callable[[T_template_global], T_template_global]:
|
||||||
|
"""Register a template global, available in any template rendered by the
|
||||||
|
application. Equivalent to :meth:`.Flask.template_global`.
|
||||||
|
|
||||||
|
.. versionadded:: 0.10
|
||||||
|
|
||||||
|
:param name: the optional name of the global, otherwise the
|
||||||
|
function name will be used.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def decorator(f: T_template_global) -> T_template_global:
|
||||||
|
self.add_app_template_global(f, name=name)
|
||||||
|
return f
|
||||||
|
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
@setupmethod
|
||||||
|
def add_app_template_global(
|
||||||
|
self, f: ft.TemplateGlobalCallable, name: str | None = None
|
||||||
|
) -> None:
|
||||||
|
"""Register a template global, available in any template rendered by the
|
||||||
|
application. Works like the :meth:`app_template_global` decorator. Equivalent to
|
||||||
|
:meth:`.Flask.add_template_global`.
|
||||||
|
|
||||||
|
.. versionadded:: 0.10
|
||||||
|
|
||||||
|
:param name: the optional name of the global, otherwise the
|
||||||
|
function name will be used.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def register_template(state: BlueprintSetupState) -> None:
|
||||||
|
state.app.jinja_env.globals[name or f.__name__] = f
|
||||||
|
|
||||||
|
self.record_once(register_template)
|
||||||
|
|
||||||
|
@setupmethod
|
||||||
|
def before_app_request(self, f: T_before_request) -> T_before_request:
|
||||||
|
"""Like :meth:`before_request`, but before every request, not only those handled
|
||||||
|
by the blueprint. Equivalent to :meth:`.Flask.before_request`.
|
||||||
|
"""
|
||||||
|
self.record_once(
|
||||||
|
lambda s: s.app.before_request_funcs.setdefault(None, []).append(f)
|
||||||
|
)
|
||||||
|
return f
|
||||||
|
|
||||||
|
@setupmethod
|
||||||
|
def after_app_request(self, f: T_after_request) -> T_after_request:
|
||||||
|
"""Like :meth:`after_request`, but after every request, not only those handled
|
||||||
|
by the blueprint. Equivalent to :meth:`.Flask.after_request`.
|
||||||
|
"""
|
||||||
|
self.record_once(
|
||||||
|
lambda s: s.app.after_request_funcs.setdefault(None, []).append(f)
|
||||||
|
)
|
||||||
|
return f
|
||||||
|
|
||||||
|
@setupmethod
|
||||||
|
def teardown_app_request(self, f: T_teardown) -> T_teardown:
|
||||||
|
"""Like :meth:`teardown_request`, but after every request, not only those
|
||||||
|
handled by the blueprint. Equivalent to :meth:`.Flask.teardown_request`.
|
||||||
|
"""
|
||||||
|
self.record_once(
|
||||||
|
lambda s: s.app.teardown_request_funcs.setdefault(None, []).append(f)
|
||||||
|
)
|
||||||
|
return f
|
||||||
|
|
||||||
|
@setupmethod
|
||||||
|
def app_context_processor(
|
||||||
|
self, f: T_template_context_processor
|
||||||
|
) -> T_template_context_processor:
|
||||||
|
"""Like :meth:`context_processor`, but for templates rendered by every view, not
|
||||||
|
only by the blueprint. Equivalent to :meth:`.Flask.context_processor`.
|
||||||
|
"""
|
||||||
|
self.record_once(
|
||||||
|
lambda s: s.app.template_context_processors.setdefault(None, []).append(f)
|
||||||
|
)
|
||||||
|
return f
|
||||||
|
|
||||||
|
@setupmethod
|
||||||
|
def app_errorhandler(
|
||||||
|
self, code: type[Exception] | int
|
||||||
|
) -> t.Callable[[T_error_handler], T_error_handler]:
|
||||||
|
"""Like :meth:`errorhandler`, but for every request, not only those handled by
|
||||||
|
the blueprint. Equivalent to :meth:`.Flask.errorhandler`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def decorator(f: T_error_handler) -> T_error_handler:
|
||||||
|
self.record_once(lambda s: s.app.errorhandler(code)(f))
|
||||||
|
return f
|
||||||
|
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
@setupmethod
|
||||||
|
def app_url_value_preprocessor(
|
||||||
|
self, f: T_url_value_preprocessor
|
||||||
|
) -> T_url_value_preprocessor:
|
||||||
|
"""Like :meth:`url_value_preprocessor`, but for every request, not only those
|
||||||
|
handled by the blueprint. Equivalent to :meth:`.Flask.url_value_preprocessor`.
|
||||||
|
"""
|
||||||
|
self.record_once(
|
||||||
|
lambda s: s.app.url_value_preprocessors.setdefault(None, []).append(f)
|
||||||
|
)
|
||||||
|
return f
|
||||||
|
|
||||||
|
@setupmethod
|
||||||
|
def app_url_defaults(self, f: T_url_defaults) -> T_url_defaults:
|
||||||
|
"""Like :meth:`url_defaults`, but for every request, not only those handled by
|
||||||
|
the blueprint. Equivalent to :meth:`.Flask.url_defaults`.
|
||||||
|
"""
|
||||||
|
self.record_once(
|
||||||
|
lambda s: s.app.url_default_functions.setdefault(None, []).append(f)
|
||||||
|
)
|
||||||
|
return f
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue