Compare commits

..

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

267 changed files with 19687 additions and 23658 deletions

23
.appveyor.yml Normal file
View file

@ -0,0 +1,23 @@
environment:
global:
TOXENV: py
matrix:
- PYTHON: C:\Python36
- PYTHON: C:\Python27
init:
- SET PATH=%PYTHON%;%PATH%
install:
- python -m pip install -U pip setuptools wheel tox
build: false
test_script:
- python -m tox
branches:
only:
- master
- /^.*-maintenance$/

View file

@ -1,17 +0,0 @@
{
"name": "pallets/flask",
"image": "mcr.microsoft.com/devcontainers/python:3",
"customizations": {
"vscode": {
"settings": {
"python.defaultInterpreterPath": "${workspaceFolder}/.venv",
"python.terminal.activateEnvInCurrentTerminal": true,
"python.terminal.launchArgs": [
"-X",
"dev"
]
}
}
},
"onCreateCommand": ".devcontainer/on-create-command.sh"
}

View file

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

View file

@ -1,13 +0,0 @@
root = true
[*]
indent_style = space
indent_size = 4
insert_final_newline = true
trim_trailing_whitespace = true
end_of_line = lf
charset = utf-8
max_line_length = 88
[*.{css,html,js,json,jsx,scss,ts,tsx,yaml,yml}]
indent_size = 2

1
.gitattributes vendored Normal file
View file

@ -0,0 +1 @@
CHANGES.rst merge=union

33
.github/ISSUE_TEMPLATE.md vendored Normal file
View file

@ -0,0 +1,33 @@
**This issue tracker is a tool to address bugs in Flask itself.
Please use the #pocoo IRC channel on freenode or Stack Overflow for general
questions about using Flask or issues not related to Flask.**
If you'd like to report a bug in Flask, fill out the template below. Provide
any any extra information that may be useful / related to your problem.
Ideally, create an [MCVE](http://stackoverflow.com/help/mcve), which helps us
understand the problem and helps check that it is not caused by something in
your code.
---
### Expected Behavior
Tell us what should happen.
```python
Paste a minimal example that causes the problem.
```
### Actual Behavior
Tell us what happens instead.
```pytb
Paste the full traceback if there was an exception.
```
### Environment
* Python version:
* Flask version:
* Werkzeug version:

View file

@ -1,27 +0,0 @@
---
name: Bug report
about: Report a bug in Flask (not other projects which depend on Flask)
---
<!--
This issue tracker is a tool to address bugs in Flask itself. Please use
GitHub Discussions or the Pallets Discord for questions about your own code.
Replace this comment with a clear outline of what the bug is.
-->
<!--
Describe how to replicate the bug.
Include a minimal reproducible example that demonstrates the bug.
Include the full traceback if there was an exception.
-->
<!--
Describe the expected behavior that should have happened but didn't.
-->
Environment:
- Python version:
- Flask version:

View file

@ -1,11 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: Security issue
url: https://github.com/pallets/flask/security/advisories/new
about: Do not report security issues publicly. Create a private advisory.
- name: Questions on GitHub Discussions
url: https://github.com/pallets/flask/discussions/
about: Ask questions about your own code on the Discussions tab.
- name: Questions on Discord
url: https://discord.gg/pallets
about: Ask questions about your own code on our Discord chat.

View file

@ -1,15 +0,0 @@
---
name: Feature request
about: Suggest a new feature for Flask
---
<!--
Replace this comment with a description of what the feature should do.
Include details such as links to relevant specs or previous discussions.
-->
<!--
Replace this comment with an example of the problem which this feature
would resolve. Is this problem solvable without changes to Flask, such
as by subclassing or using an extension?
-->

16
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View file

@ -0,0 +1,16 @@
Describe what this patch does to fix the issue.
Link to any relevant issues or pull requests.
<!--
Commit checklist:
* add tests that fail without the patch
* ensure all tests pass with ``pytest``
* add documentation to the relevant docstrings or pages
* add ``versionadded`` or ``versionchanged`` directives to relevant docstrings
* add a changelog entry if this patch changes code
Tests, coverage, and docs will be run automatically when you submit the pull
request, but running them yourself can save time.
-->

View file

@ -1,25 +0,0 @@
<!--
Before opening a PR, open a ticket describing the issue or feature the
PR will address. An issue is not required for fixing typos in
documentation, or other simple non-code changes.
Replace this comment with a description of the change. Describe how it
addresses the linked ticket.
-->
<!--
Link to relevant issues or previous PRs, one per line. Use "fixes" to
automatically close an issue.
fixes #<issue number>
-->
<!--
Ensure each step in CONTRIBUTING.rst is complete, especially the following:
- 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.
-->

View file

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

View file

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

View file

@ -1,62 +0,0 @@
name: Publish
on:
push:
tags: ['*']
permissions: {}
concurrency:
group: publish-${{ github.event.push.ref }}
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
outputs:
artifact-id: ${{ steps.upload-artifact.outputs.artifact-id }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
with:
enable-cache: false
prune-cache: false
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version-file: pyproject.toml
- run: echo "SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)" >> $GITHUB_ENV
- run: uv build
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
id: upload-artifact
with:
name: dist
path: dist/
if-no-files-found: error
create-release:
needs: [build]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
artifact-ids: ${{ needs.build.outputs.artifact-id }}
path: dist/
- name: create release
run: gh release create --draft --repo ${GITHUB_REPOSITORY} ${GITHUB_REF_NAME} dist/*
env:
GH_TOKEN: ${{ github.token }}
publish-pypi:
needs: [build]
environment:
name: publish
url: https://pypi.org/project/Flask/${{ github.ref_name }}
runs-on: ubuntu-latest
permissions:
id-token: write
steps:
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
artifact-ids: ${{ needs.build.outputs.artifact-id }}
path: dist/
- uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
with:
packages-dir: "dist/"

View file

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

View file

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

25
.gitignore vendored
View file

@ -1,8 +1,23 @@
.idea/ .DS_Store
.vscode/ .env
__pycache__/ .flaskenv
*.pyc
*.pyo
env/
env*
dist/ dist/
.coverage* build/
htmlcov/ *.egg
*.egg-info/
_mailinglist
.tox/ .tox/
.cache/
.pytest_cache/
.idea/
docs/_build/ docs/_build/
# Coverage reports
htmlcov/
.coverage
.coverage.*
*,cover

View file

@ -1,23 +0,0 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: 5e2fb545eba1ea9dc051f6f962d52fe8f76a9794 # frozen: v0.15.13
hooks:
- id: ruff-check
- id: ruff-format
- repo: https://github.com/astral-sh/uv-pre-commit
rev: fa60a193803535a9e2accdb3ca4b1b584b1150cb # frozen: 0.11.15
hooks:
- id: uv-lock
- repo: https://github.com/codespell-project/codespell
rev: 2ccb47ff45ad361a21071a7eedda4c37e6ae8c5a # frozen: v2.4.2
hooks:
- id: codespell
args: ['--write-changes']
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: 3e8a8703264a2f4a69428a0aa4dcb512790b2c8c # frozen: v6.0.0
hooks:
- id: check-merge-conflict
- id: debug-statements
- id: fix-byte-order-marker
- id: trailing-whitespace
- id: end-of-file-fixer

View file

@ -1,10 +0,0 @@
version: 2
build:
os: ubuntu-24.04
tools:
python: '3.13'
commands:
- asdf plugin add uv
- asdf install uv latest
- asdf global uv latest
- uv run --group docs sphinx-build -W -b dirhtml docs $READTHEDOCS_OUTPUT/html

55
.travis.yml Normal file
View file

@ -0,0 +1,55 @@
os: linux
sudo: false
language: python
matrix:
include:
- python: 3.6
env: TOXENV=py,simplejson,devel,lowest,codecov
- python: 3.6
env: TOXENV=docs-html
- python: 3.5
env: TOXENV=py,codecov
- python: 3.4
env: TOXENV=py,codecov
- python: 2.7
env: TOXENV=py,simplejson,devel,lowest,codecov
- python: pypy
env: TOXENV=py,codecov
- python: nightly
env: TOXENV=py
- os: osx
language: generic
env: TOXENV=py
allow_failures:
- python: nightly
env: TOXENV=py
- os: osx
language: generic
env: TOXENV=py
fast_finish: true
before_install:
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
brew update;
brew install python3 redis memcached;
virtualenv -p python3 ~/py-env;
. ~/py-env/bin/activate;
fi
install:
- pip install tox
script:
- tox
cache:
- pip
branches:
only:
- master
- /^.*-maintenance$/
notifications:
email: false

12
AUTHORS Normal file
View file

@ -0,0 +1,12 @@
Flask is developed and maintained by the Pallets team and community
contributors. It was created by Armin Ronacher. The core maintainers
are:
- David Lord (davidism)
- Adrian Mönnich (ThiefMaster)
- Armin Ronacher (mitsuhiko)
- Marcus Unterwaditzer (untitaker)
A full list of contributors is available from git with::
git shortlog -sne

File diff suppressed because it is too large Load diff

180
CONTRIBUTING.rst Normal file
View file

@ -0,0 +1,180 @@
How to contribute to Flask
==========================
Thank you for considering contributing to Flask!
Support questions
-----------------
Please, don't use the issue tracker for this. Use one of the following
resources for questions about your own code:
* The IRC channel ``#pocoo`` on FreeNode.
* The IRC channel ``#python`` on FreeNode for more general questions.
* The mailing list flask@python.org for long term discussion or larger issues.
* Ask on `Stack Overflow`_. Search with Google first using:
``site:stackoverflow.com flask {search term, exception message, etc.}``
.. _Stack Overflow: https://stackoverflow.com/questions/tagged/flask?sort=linked
Reporting issues
----------------
- Describe what you expected to happen.
- If possible, include a `minimal, complete, and verifiable 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, Flask, and Werkzeug versions. If possible, check if this
issue is already fixed in the repository.
.. _minimal, complete, and verifiable example: https://stackoverflow.com/help/mcve
Submitting patches
------------------
- Include tests if your patch is supposed to solve a bug, and explain
clearly under which circumstances the bug happens. Make sure the test fails
without your patch.
- Try to follow `PEP8`_, but you may ignore the line length limit if following
it would make the code uglier.
First time setup
~~~~~~~~~~~~~~~~
- Download and install the `latest version of git`_.
- Configure git with your `username`_ and `email`_::
git config --global user.name 'your name'
git config --global user.email 'your email'
- Make sure you have a `GitHub account`_.
- Fork Flask to your GitHub account by clicking the `Fork`_ button.
- `Clone`_ your GitHub fork locally::
git clone https://github.com/{username}/flask
cd flask
- Add the main repository as a remote to update later::
git remote add pallets https://github.com/pallets/flask
git fetch pallets
- Create a virtualenv::
python3 -m venv env
. env/bin/activate
# or "env\Scripts\activate" on Windows
- Install Flask in editable mode with development dependencies::
pip install -e ".[dev]"
.. _GitHub account: https://github.com/join
.. _latest version of git: https://git-scm.com/downloads
.. _username: https://help.github.com/articles/setting-your-username-in-git/
.. _email: https://help.github.com/articles/setting-your-email-in-git/
.. _Fork: https://github.com/pallets/flask/fork
.. _Clone: https://help.github.com/articles/fork-a-repo/#step-2-create-a-local-clone-of-your-fork
Start coding
~~~~~~~~~~~~
- Create a branch to identify the issue you would like to work on (e.g.
``2287-dry-test-suite``)
- Using your favorite editor, make your changes, `committing as you go`_.
- Try to follow `PEP8`_, but you may ignore the line length limit if following
it would make the code uglier.
- Include tests that cover any code changes you make. Make sure the test fails
without your patch. `Run the tests. <contributing-testsuite_>`_.
- Push your commits to GitHub and `create a pull request`_.
- Celebrate 🎉
.. _committing as you go: http://dont-be-afraid-to-commit.readthedocs.io/en/latest/git/commandlinegit.html#commit-your-changes
.. _PEP8: https://pep8.org/
.. _create a pull request: https://help.github.com/articles/creating-a-pull-request/
.. _contributing-testsuite:
Running the tests
~~~~~~~~~~~~~~~~~
Run the basic test suite with::
pytest
This only runs the tests for the current environment. Whether this is relevant
depends on which part of Flask you're working on. Travis-CI will run the full
suite when you submit your pull request.
The full test suite takes a long time to run because it tests multiple
combinations of Python and dependencies. You need to have Python 2.7, 3.4,
3.5 3.6, and PyPy 2.7 installed to run all of the environments. Then run::
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 on the terminal and as an interactive HTML document::
coverage run -m pytest
coverage report
coverage html
# then open htmlcov/index.html
Read more about `coverage <https://coverage.readthedocs.io>`_.
Running the full test suite with ``tox`` will combine the coverage reports
from all runs.
Building the docs
~~~~~~~~~~~~~~~~~
Build the docs in the ``docs`` directory using Sphinx::
cd docs
make html
Open ``_build/html/index.html`` in your browser to view the docs.
Read more about `Sphinx <http://www.sphinx-doc.org>`_.
make targets
~~~~~~~~~~~~
Flask provides a ``Makefile`` with various shortcuts. They will ensure that
all dependencies are installed.
- ``make test`` runs the basic test suite with ``pytest``
- ``make cov`` runs the basic test suite with ``coverage``
- ``make test-all`` runs the full test suite with ``tox``
- ``make docs`` builds the HTML documentation
Caution: zero-padded file modes
-------------------------------
This repository contains several zero-padded file modes that may cause issues
when pushing this repository to git hosts other than GitHub. Fixing this is
destructive to the commit history, so we suggest ignoring these warnings. If it
fails to push and you're using a self-hosted git service like GitLab, you can
turn off repository checks in the admin panel.
These files can also cause issues while cloning. If you have ::
[fetch]
fsckobjects = true
or ::
[receive]
fsckObjects = true
set in your git configuration file, cloning this repository will fail. The only
solution is to set both of the above settings to false while cloning, and then
setting them back to true after the cloning is finished.

31
LICENSE Normal file
View file

@ -0,0 +1,31 @@
Copyright © 2010 by the Pallets team.
Some rights reserved.
Redistribution and use in source and binary forms of the software as
well as documentation, with or without modification, are permitted
provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.

View file

@ -1,28 +0,0 @@
Copyright 2010 Pallets
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

11
MANIFEST.in Normal file
View file

@ -0,0 +1,11 @@
include Makefile CHANGES.rst LICENSE AUTHORS tox.ini
graft artwork
graft tests
graft examples
graft docs
global-exclude *.py[co]
prune docs/_build
prune docs/_themes

35
Makefile Normal file
View file

@ -0,0 +1,35 @@
.PHONY: all install-dev test coverage cov test-all tox docs audit release clean-pyc upload-docs ebook
all: test
install-dev:
pip install -q -e .[dev]
test: clean-pyc install-dev
pytest
coverage: clean-pyc install-dev
coverage run -m pytest
coverage report
coverage html
cov: coverage
test-all: install-dev
tox
tox: test-all
docs: clean-pyc install-dev
$(MAKE) -C docs html
audit:
python setup.py audit
release:
python scripts/make-release.py
clean-pyc:
find . -name '*.pyc' -exec rm -f {} +
find . -name '*.pyo' -exec rm -f {} +
find . -name '*~' -exec rm -f {} +

View file

@ -1,53 +0,0 @@
<div align="center"><img src="https://raw.githubusercontent.com/pallets/flask/refs/heads/stable/docs/_static/flask-name.svg" alt="" height="150"></div>
# Flask
Flask is a lightweight [WSGI] web application framework. It is designed
to make getting started quick and easy, with the ability to scale up to
complex applications. It began as a simple wrapper around [Werkzeug]
and [Jinja], and has become one of the most popular Python web
application frameworks.
Flask offers suggestions, but doesn't enforce any dependencies or
project layout. It is up to the developer to choose the tools and
libraries they want to use. There are many extensions provided by the
community that make adding new functionality easy.
[WSGI]: https://wsgi.readthedocs.io/
[Werkzeug]: https://werkzeug.palletsprojects.com/
[Jinja]: https://jinja.palletsprojects.com/
## A Simple Example
```python
# save this as app.py
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello, World!"
```
```
$ flask run
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
```
## Donate
The Pallets organization develops and supports Flask and the libraries
it uses. In order to grow the community of contributors and users, and
allow the maintainers to devote more time to the projects, [please
donate today].
[please donate today]: https://palletsprojects.com/donate
## Contributing
See our [detailed contributing documentation][contrib] for many ways to
contribute, including reporting issues, requesting features, asking or answering
questions, and making PRs.
[contrib]: https://palletsprojects.com/contributing/

76
README.rst Normal file
View file

@ -0,0 +1,76 @@
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.
Installing
----------
Install and update using `pip`_:
.. code-block:: text
pip install -U Flask
A Simple Example
----------------
.. code-block:: python
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return 'Hello, World!'
.. code-block:: text
$ FLASK_APP=hello.py flask run
* Serving Flask app "hello"
* 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://psfmember.org/civicrm/contribute/transact?reset=1&id=20
Links
-----
* Website: https://www.palletsprojects.com/p/flask/
* Documentation: http://flask.pocoo.org/docs/
* License: `BSD <https://github.com/pallets/flask/blob/master/LICENSE>`_
* Releases: https://pypi.org/project/Flask/
* Code: https://github.com/pallets/flask
* Issue tracker: https://github.com/pallets/flask/issues
* Test status:
* Linux, Mac: https://travis-ci.org/pallets/flask
* Windows: https://ci.appveyor.com/project/pallets/flask
* Test coverage: https://codecov.io/gh/pallets/flask
.. _WSGI: https://wsgi.readthedocs.io
.. _Werkzeug: https://www.palletsprojects.com/p/werkzeug/
.. _Jinja: https://www.palletsprojects.com/p/jinja/
.. _pip: https://pip.pypa.io/en/stable/quickstart/

20
artwork/LICENSE Normal file
View file

@ -0,0 +1,20 @@
Copyright (c) 2010 by Armin Ronacher.
Some rights reserved.
This logo or a modified version may be used by anyone to refer to the
Flask project, but does not indicate endorsement by the project.
Redistribution and use in source (the SVG file) and binary forms (rendered
PNG files etc.) of the image, with or without modification, are permitted
provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice and this list of conditions.
* The names of the contributors to the Flask software (see AUTHORS) may
not be used to endorse or promote products derived from this software
without specific prior written permission.
Note: we would appreciate that you make the image a link to
http://flask.pocoo.org/ if you use it on a web page.

290
artwork/logo-full.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 77 KiB

165
artwork/logo-lineart.svg Normal file
View file

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

After

Width:  |  Height:  |  Size: 7.9 KiB

View file

@ -1,10 +1,10 @@
# Minimal makefile for Sphinx documentation # Minimal makefile for Sphinx documentation
# #
# You can set these variables from the command line, and also # You can set these variables from the command line.
# from the environment for the first two. SPHINXOPTS =
SPHINXOPTS ?= SPHINXBUILD = sphinx-build
SPHINXBUILD ?= sphinx-build SPHINXPROJ = Flask
SOURCEDIR = . SOURCEDIR = .
BUILDDIR = _build BUILDDIR = _build
@ -17,4 +17,4 @@ help:
# Catch-all target: route all unknown targets to Sphinx using the new # Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile %: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

BIN
docs/_static/flask-favicon.ico vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

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

Before

Width:  |  Height:  |  Size: 2 KiB

View file

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

Before

Width:  |  Height:  |  Size: 3.4 KiB

View file

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

Before

Width:  |  Height:  |  Size: 5.2 KiB

BIN
docs/_static/flask.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

BIN
docs/_static/logo-full.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
docs/_static/no.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

BIN
docs/_static/pycharm-runconfig.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
docs/_static/yes.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 B

View file

@ -0,0 +1,47 @@
.. _advanced_foreword:
Foreword for Experienced Programmers
====================================
Thread-Locals in Flask
----------------------
One of the design decisions in Flask was that simple tasks should be simple;
they should not take a lot of code and yet they should not limit you. Because
of that, Flask has a few design choices that some people might find surprising or
unorthodox. For example, Flask uses thread-local objects internally so that you
dont have to pass objects around from function to function within a request in
order to stay threadsafe. This approach is convenient, but requires a valid
request context for dependency injection or when attempting to reuse code which
uses a value pegged to the request. The Flask project is honest about
thread-locals, does not hide them, and calls out in the code and documentation
where they are used.
Develop for the Web with Caution
--------------------------------
Always keep security in mind when building web applications.
If you write a web application, you are probably allowing users to register
and leave their data on your server. The users are entrusting you with data.
And even if you are the only user that might leave data in your application,
you still want that data to be stored securely.
Unfortunately, there are many ways the security of a web application can be
compromised. Flask protects you against one of the most common security
problems of modern web applications: cross-site scripting (XSS). Unless you
deliberately mark insecure HTML as secure, Flask and the underlying Jinja2
template engine have you covered. But there are many more ways to cause
security problems.
The documentation will warn you about aspects of web development that require
attention to security. Some of these security concerns are far more complex
than one might think, and we all sometimes underestimate the likelihood that a
vulnerability will be exploited - until a clever attacker figures out a way to
exploit our applications. And don't think that your application is not
important enough to attract an attacker. Depending on the kind of attack,
chances are that automated bots are probing for ways to fill your database with
spam, links to malicious software, and the like.
Flask is no different from any other framework in that you the developer must
build with caution, watching for exploits when building to your requirements.

View file

@ -1,9 +1,11 @@
.. _api:
API API
=== ===
.. module:: flask .. module:: flask
This part of the documentation covers all the interfaces of Flask. For This part of the documentation covers all the interfaces of Flask. For
parts where Flask depends on external libraries, we document the most parts where Flask depends on external libraries, we document the most
important right here and provide links to the canonical documentation. important right here and provide links to the canonical documentation.
@ -27,28 +29,76 @@ Incoming Request Data
--------------------- ---------------------
.. autoclass:: Request .. autoclass:: Request
:members: :members:
:inherited-members: :inherited-members:
:exclude-members: json_module
.. data:: request .. attribute:: environ
A proxy to the request data for the current request, an instance of The underlying WSGI environment.
:class:`.Request`.
This is only available when a :doc:`request context </appcontext>` is .. attribute:: path
active. .. attribute:: full_path
.. attribute:: script_root
.. attribute:: url
.. attribute:: base_url
.. attribute:: url_root
This is a proxy. See :ref:`context-visibility` for more information. Provides different ways to look at the current `IRI
<http://tools.ietf.org/html/rfc3987>`_. Imagine your application is
listening on the following application root::
http://www.example.com/myapplication
And a user requests the following URI::
http://www.example.com/myapplication/%CF%80/page.html?x=y
In this case the values of the above mentioned attributes would be
the following:
============= ======================================================
`path` ``u'/π/page.html'``
`full_path` ``u'/π/page.html?x=y'``
`script_root` ``u'/myapplication'``
`base_url` ``u'http://www.example.com/myapplication/π/page.html'``
`url` ``u'http://www.example.com/myapplication/π/page.html?x=y'``
`url_root` ``u'http://www.example.com/myapplication/'``
============= ======================================================
.. attribute:: request
To access incoming request data, you can use the global `request`
object. Flask parses incoming request data for you and gives you
access to it through that global object. Internally Flask makes
sure that you always get the correct data for the active thread if you
are in a multithreaded environment.
This is a proxy. See :ref:`notes-on-proxies` for more information.
The request object is an instance of a :class:`~werkzeug.wrappers.Request`
subclass and provides all of the attributes Werkzeug defines. This
just shows a quick overview of the most important ones.
Response Objects Response Objects
---------------- ----------------
.. autoclass:: flask.Response .. autoclass:: flask.Response
:members: :members: set_cookie, max_cookie_size, data, mimetype, is_json, get_json
:inherited-members:
:exclude-members: json_module .. attribute:: headers
A :class:`~werkzeug.datastructures.Headers` object representing the response headers.
.. attribute:: status
A string with a response status.
.. attribute:: status_code
The response status as integer.
Sessions Sessions
-------- --------
@ -60,33 +110,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 on 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
@ -116,9 +173,10 @@ implementation that Flask is using.
.. admonition:: Notice .. admonition:: Notice
The :data:`PERMANENT_SESSION_LIFETIME` config can be an integer or ``timedelta``. The ``PERMANENT_SESSION_LIFETIME`` config key can also be an integer
The :attr:`~flask.Flask.permanent_session_lifetime` attribute is always a starting with Flask 0.8. Either catch this down yourself or use
``timedelta``. the :attr:`~flask.Flask.permanent_session_lifetime` attribute on the
app which converts the result to an integer automatically.
Test Client Test Client
@ -146,24 +204,23 @@ Application Globals
To share data that is valid for one request only from one function to To share data that is valid for one request only from one function to
another, a global variable is not good enough because it would break in 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. During
:meth:`~.Flask.before_request` function could load a user object from a testing, you can use the :ref:`faking-resources` pattern to
session id, then set ``g.user`` to be used in the view function. pre-configure such resources.
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 +234,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
@ -208,6 +266,12 @@ Useful Functions and Classes
.. autofunction:: send_from_directory .. autofunction:: send_from_directory
.. autofunction:: safe_join
.. autofunction:: escape
.. autoclass:: Markup
:members: escape, unescape, striptags
Message Flashing Message Flashing
---------------- ----------------
@ -216,29 +280,58 @@ Message Flashing
.. autofunction:: get_flashed_messages .. autofunction:: get_flashed_messages
JSON Support JSON Support
------------ ------------
.. module:: flask.json .. module:: flask.json
Flask uses Python's built-in :mod:`json` module for handling JSON by Flask uses ``simplejson`` for the JSON implementation. Since simplejson
default. The JSON implementation can be changed by assigning a different is provided by both the standard library as well as extension, Flask will
provider to :attr:`flask.Flask.json_provider_class` or try simplejson first and then fall back to the stdlib json module. On top
:attr:`flask.Flask.json`. The functions provided by ``flask.json`` will of that it will delegate access to the current application's JSON encoders
use methods on ``app.json`` if an app context is active. and decoders for easier customization.
Jinja's ``|tojson`` filter is configured to use the app's JSON provider. So for starters instead of doing::
The filter marks the output with ``|safe``. Use it to render data inside
HTML ``<script>`` tags. try:
import simplejson as json
except ImportError:
import json
You can instead just do this::
from flask import json
For usage examples, read the :mod:`json` documentation in the standard
library. The following extensions are by default applied to the stdlib's
JSON module:
1. ``datetime`` objects are serialized as :rfc:`822` strings.
2. Any object with an ``__html__`` method (like :class:`~flask.Markup`)
will have that method called and then the return value is serialized
as string.
The :func:`~htmlsafe_dumps` function of this json module is also available
as filter called ``|tojson`` in Jinja2. Note that inside ``script``
tags no escaping must take place, so make sure to disable escaping
with ``|safe`` if you intend to use it inside ``script`` tags unless
you are using Flask 0.10 which implies that:
.. sourcecode:: html+jinja .. sourcecode:: html+jinja
<script> <script type=text/javascript>
const names = {{ names|tojson }}; doSomethingWith({{ user.username|tojson|safe }});
renderChart(names, {{ axis_data|tojson }});
</script> </script>
.. admonition:: Auto-Sort JSON Keys
The configuration variable ``JSON_SORT_KEYS`` (:ref:`config`) can be
set to false to stop Flask from auto-sorting keys. By default sorting
is enabled and outside of the app context sorting is turned on.
Notice that disabling key sorting can cause issues when using content
based HTTP caches and Python's hash randomization feature.
.. autofunction:: jsonify .. autofunction:: jsonify
.. autofunction:: dumps .. autofunction:: dumps
@ -249,17 +342,14 @@ HTML ``<script>`` tags.
.. autofunction:: load .. autofunction:: load
.. autoclass:: flask.json.provider.JSONProvider .. autoclass:: JSONEncoder
:members: :members:
:member-order: bysource
.. autoclass:: flask.json.provider.DefaultJSONProvider .. autoclass:: JSONDecoder
:members: :members:
:member-order: bysource
.. automodule:: flask.json.tag .. automodule:: flask.json.tag
Template Rendering Template Rendering
------------------ ------------------
@ -269,10 +359,6 @@ Template Rendering
.. autofunction:: render_template_string .. autofunction:: render_template_string
.. autofunction:: stream_template
.. autofunction:: stream_template_string
.. autofunction:: get_template_attribute .. autofunction:: get_template_attribute
Configuration Configuration
@ -290,31 +376,59 @@ Stream Helpers
Useful Internals Useful Internals
---------------- ----------------
.. autoclass:: flask.ctx.RequestContext
:members:
.. data:: _request_ctx_stack
The internal :class:`~werkzeug.local.LocalStack` that holds
:class:`~flask.ctx.RequestContext` instances. Typically, the
:data:`request` and :data:`session` proxies should be accessed
instead of the stack. It may be useful to access the stack in
extension code.
The following attributes are always present on each layer of the
stack:
`app`
the active Flask application.
`url_adapter`
the URL adapter that was used to match the request.
`request`
the current request object.
`session`
the active session object.
`g`
an object with all the attributes of the :data:`flask.g` object.
`flashes`
an internal cache for the flashed messages.
Example usage::
from flask import _request_ctx_stack
def get_session():
ctx = _request_ctx_stack.top
if ctx is not None:
return ctx.session
.. autoclass:: flask.ctx.AppContext .. autoclass:: flask.ctx.AppContext
:members: :members:
.. data:: flask.globals.app_ctx .. data:: _app_ctx_stack
A proxy to the active :class:`.AppContext`. The internal :class:`~werkzeug.local.LocalStack` that holds
:class:`~flask.ctx.AppContext` instances. Typically, the
:data:`current_app` and :data:`g` proxies should be accessed instead
of the stack. Extensions can access the contexts on the stack as a
namespace to store data.
This is an internal object that is essential to how Flask handles requests. .. versionadded:: 0.9
Accessing this should not be needed in most cases. Most likely you want
:data:`.current_app`, :data:`.g`, :data:`.request`, and :data:`.session` instead.
This is only available when a :doc:`request context </appcontext>` is
active.
This is a proxy. See :ref:`context-visibility` for more information.
.. class:: flask.ctx.RequestContext
.. deprecated:: 3.2
Merged with :class:`AppContext`. This alias will be removed in Flask 4.0.
.. data:: flask.globals.request_ctx
.. deprecated:: 3.2
Merged with :data:`.app_ctx`. This alias will be removed in Flask 4.0.
.. autoclass:: flask.blueprints.BlueprintSetupState .. autoclass:: flask.blueprints.BlueprintSetupState
:members: :members:
@ -324,13 +438,18 @@ Useful Internals
Signals Signals
------- -------
Signals are provided by the `Blinker`_ library. See :doc:`signals` for an introduction. .. versionadded:: 0.6
.. _blinker: https://blinker.readthedocs.io/ .. data:: signals.signals_available
``True`` if the signaling system is available. This is the case
when `blinker`_ is installed.
The following signals exist in Flask:
.. data:: template_rendered .. data:: template_rendered
This signal is sent when a template was successfully rendered. The This signal is sent when a template was successfully rendered. The
signal is invoked with the instance of the template as `template` signal is invoked with the instance of the template as `template`
and the context as dictionary (named `context`). and the context as dictionary (named `context`).
@ -364,7 +483,7 @@ Signals are provided by the `Blinker`_ library. See :doc:`signals` for an introd
.. data:: request_started .. data:: request_started
This signal is sent when the request context is set up, before This signal is sent when the request context is set up, before
any request processing happens. Because the request context is already any request processing happens. Because the request context is already
bound, the subscriber can access the request with the standard global bound, the subscriber can access the request with the standard global
proxies such as :class:`~flask.request`. proxies such as :class:`~flask.request`.
@ -384,7 +503,7 @@ Signals are provided by the `Blinker`_ library. See :doc:`signals` for an introd
Example subscriber:: Example subscriber::
def log_response(sender, response, **extra): def log_response(sender, response, **extra):
sender.logger.debug('Request context is about to close down. ' sender.logger.debug('Request context is about to close down. '
'Response: %s', response) 'Response: %s', response)
from flask import request_finished from flask import request_finished
@ -392,37 +511,23 @@ Signals are provided by the `Blinker`_ library. See :doc:`signals` for an introd
.. data:: got_request_exception .. data:: got_request_exception
This signal is sent when an unhandled exception happens during This signal is sent when an exception happens during request processing.
request processing, including when debugging. The exception is It is sent *before* the standard exception handling kicks in and even
passed to the subscriber as ``exception``. in debug mode, where no exception handling happens. The exception
itself is passed to the subscriber as `exception`.
This signal is not sent for Example subscriber::
:exc:`~werkzeug.exceptions.HTTPException`, or other exceptions that
have error handlers registered, unless the exception was raised from
an error handler.
This example shows how to do some extra logging if a theoretical def log_exception(sender, exception, **extra):
``SecurityException`` was raised: sender.logger.debug('Got exception during processing: %s', exception)
.. code-block:: python
from flask import got_request_exception from flask import got_request_exception
got_request_exception.connect(log_exception, app)
def log_security_exception(sender, exception, **extra):
if not isinstance(exception, SecurityException):
return
security_logger.exception(
f"SecurityException at {request.url!r}",
exc_info=exception,
)
got_request_exception.connect(log_security_exception, app)
.. data:: request_tearing_down .. data:: request_tearing_down
This signal is sent when the request is tearing down. This is always This signal is sent when the request is tearing down. This is always
called, even if an exception is caused. Currently functions listening called, even if an exception is caused. Currently functions listening
to this signal are called after the regular teardown handlers, but this to this signal are called after the regular teardown handlers, but this
is not something you can rely on. is not something you can rely on.
@ -440,8 +545,8 @@ Signals are provided by the `Blinker`_ library. See :doc:`signals` for an introd
.. data:: appcontext_tearing_down .. data:: appcontext_tearing_down
This signal is sent when the app context is tearing down. This is always This signal is sent when the app context is tearing down. This is always
called, even if an exception is caused. Currently functions listening called, even if an exception is caused. Currently functions listening
to this signal are called after the regular teardown handlers, but this to this signal are called after the regular teardown handlers, but this
is not something you can rely on. is not something you can rely on.
@ -458,9 +563,9 @@ Signals are provided by the `Blinker`_ library. See :doc:`signals` for an introd
.. data:: appcontext_pushed .. data:: appcontext_pushed
This signal is sent when an application context is pushed. The sender This signal is sent when an application context is pushed. The sender
is the application. This is usually useful for unittests in order to is the application. This is usually useful for unittests in order to
temporarily hook in information. For instance it can be used to temporarily hook in information. For instance it can be used to
set a resource early onto the `g` object. set a resource early onto the `g` object.
Example usage:: Example usage::
@ -487,15 +592,16 @@ Signals are provided by the `Blinker`_ library. See :doc:`signals` for an introd
.. data:: appcontext_popped .. data:: appcontext_popped
This signal is sent when an application context is popped. The sender This signal is sent when an application context is popped. The sender
is the application. This usually falls in line with the is the application. This usually falls in line with the
:data:`appcontext_tearing_down` signal. :data:`appcontext_tearing_down` signal.
.. versionadded:: 0.10 .. versionadded:: 0.10
.. data:: message_flashed .. data:: message_flashed
This signal is sent when the application is flashing a message. The This signal is sent when the application is flashing a message. The
messages is sent as `message` keyword argument and the category as messages is sent as `message` keyword argument and the category as
`category`. `category`.
@ -510,6 +616,24 @@ Signals are provided by the `Blinker`_ library. See :doc:`signals` for an introd
.. versionadded:: 0.10 .. versionadded:: 0.10
.. class:: signals.Namespace
An alias for :class:`blinker.base.Namespace` if blinker is available,
otherwise a dummy class that creates fake signals. This class is
available for Flask extensions that want to provide the same fallback
system as Flask itself.
.. method:: signal(name, doc=None)
Creates a new signal for this namespace if blinker is available,
otherwise returns a fake signal that has a send method that will
do nothing but will fail with a :exc:`RuntimeError` for all other
operations, including connecting.
.. _blinker: https://pypi.org/project/blinker/
.. _class-based-views:
Class-Based Views Class-Based Views
----------------- -----------------
@ -537,7 +661,7 @@ Generally there are three ways to define rules for the routing system:
which is exposed as :attr:`flask.Flask.url_map`. which is exposed as :attr:`flask.Flask.url_map`.
Variable parts in the route can be specified with angular brackets Variable parts in the route can be specified with angular brackets
(``/user/<username>``). By default a variable part in the URL accepts any (``/user/<username>``). By default a variable part in the URL accepts any
string without a slash however a different converter can be specified as string without a slash however a different converter can be specified as
well by using ``<converter:name>``. well by using ``<converter:name>``.
@ -571,7 +695,7 @@ Here are some examples::
pass pass
An important detail to keep in mind is how Flask deals with trailing An important detail to keep in mind is how Flask deals with trailing
slashes. The idea is to keep each URL unique so the following rules slashes. The idea is to keep each URL unique so the following rules
apply: apply:
1. If a rule ends with a slash and is requested without a slash by the 1. If a rule ends with a slash and is requested without a slash by the
@ -580,11 +704,11 @@ apply:
2. If a rule does not end with a trailing slash and the user requests the 2. If a rule does not end with a trailing slash and the user requests the
page with a trailing slash, a 404 not found is raised. page with a trailing slash, a 404 not found is raised.
This is consistent with how web servers deal with static files. This This is consistent with how web servers deal with static files. This
also makes it possible to use relative link targets safely. also makes it possible to use relative link targets safely.
You can also define multiple rules for the same function. They have to be You can also define multiple rules for the same function. They have to be
unique however. Defaults can also be specified. Here for example is a unique however. Defaults can also be specified. Here for example is a
definition for a URL that accepts an optional page:: definition for a URL that accepts an optional page::
@app.route('/users/', defaults={'page': 1}) @app.route('/users/', defaults={'page': 1})
@ -593,49 +717,39 @@ definition for a URL that accepts an optional page::
pass pass
This specifies that ``/users/`` will be the URL for page one and 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
form with a 308 redirect. In the above example, ``/users/page/1`` will
be redirected to ``/users/``. If your route handles ``GET`` and ``POST``
requests, make sure the default route only handles ``GET``, as redirects
can't preserve form data. ::
@app.route('/region/', defaults={'id': 1})
@app.route('/region/<int:id>', methods=['GET', 'POST'])
def region(id):
pass
Here are the parameters that :meth:`~flask.Flask.route` and Here are the parameters that :meth:`~flask.Flask.route` and
:meth:`~flask.Flask.add_url_rule` accept. The only difference is that :meth:`~flask.Flask.add_url_rule` accept. The only difference is that
with the route parameter the view function is defined with the decorator with the route parameter the view function is defined with the decorator
instead of the `view_func` parameter. instead of the `view_func` parameter.
=============== ========================================================== =============== ==========================================================
`rule` the URL rule as string `rule` the URL rule as string
`endpoint` the endpoint for the registered URL rule. Flask itself `endpoint` the endpoint for the registered URL rule. Flask itself
assumes that the name of the view function is the name assumes that the name of the view function is the name
of the endpoint if not explicitly stated. of the endpoint if not explicitly stated.
`view_func` the function to call when serving a request to the `view_func` the function to call when serving a request to the
provided endpoint. If this is not provided one can provided endpoint. If this is not provided one can
specify the function later by storing it in the specify the function later by storing it in the
:attr:`~flask.Flask.view_functions` dictionary with the :attr:`~flask.Flask.view_functions` dictionary with the
endpoint as key. endpoint as key.
`defaults` A dictionary with defaults for this rule. See the `defaults` A dictionary with defaults for this rule. See the
example above for how defaults work. example above for how defaults work.
`subdomain` specifies the rule for the subdomain in case subdomain `subdomain` specifies the rule for the subdomain in case subdomain
matching is in use. If not specified the default matching is in use. If not specified the default
subdomain is assumed. subdomain is assumed.
`**options` the options to be forwarded to the underlying `**options` the options to be forwarded to the underlying
:class:`~werkzeug.routing.Rule` object. A change to :class:`~werkzeug.routing.Rule` object. A change to
Werkzeug is handling of method options. methods is a list Werkzeug is handling of method options. methods is a list
of methods this rule should be limited to (``GET``, ``POST`` of methods this rule should be limited to (``GET``, ``POST``
etc.). By default a rule just listens for ``GET`` (and etc.). By default a rule just listens for ``GET`` (and
implicitly ``HEAD``). Starting with Flask 0.6, ``OPTIONS`` is implicitly ``HEAD``). Starting with Flask 0.6, ``OPTIONS`` is
implicitly added and handled by the standard request implicitly added and handled by the standard request
handling. They have to be specified as keyword arguments. handling. They have to be specified as keyword arguments.
=============== ========================================================== =============== ==========================================================
.. _view-func-options:
View Function Options View Function Options
--------------------- ---------------------
@ -645,19 +759,19 @@ customize behavior the view function would normally not have control over.
The following attributes can be provided optionally to either override The following attributes can be provided optionally to either override
some defaults to :meth:`~flask.Flask.add_url_rule` or general behavior: some defaults to :meth:`~flask.Flask.add_url_rule` or general behavior:
- `__name__`: The name of a function is by default used as endpoint. If - `__name__`: The name of a function is by default used as endpoint. If
endpoint is provided explicitly this value is used. Additionally this endpoint is provided explicitly this value is used. Additionally this
will be prefixed with the name of the blueprint by default which will be prefixed with the name of the blueprint by default which
cannot be customized from the function itself. cannot be customized from the function itself.
- `methods`: If methods are not provided when the URL rule is added, - `methods`: If methods are not provided when the URL rule is added,
Flask will look on the view function object itself if a `methods` Flask will look on the view function object itself if a `methods`
attribute exists. If it does, it will pull the information for the attribute exists. If it does, it will pull the information for the
methods from there. methods from there.
- `provide_automatic_options`: if this attribute is set Flask will - `provide_automatic_options`: if this attribute is set Flask will
either force enable or disable the automatic implementation of the either force enable or disable the automatic implementation of the
HTTP ``OPTIONS`` response. This can be useful when working with HTTP ``OPTIONS`` response. This can be useful when working with
decorators that want to customize the ``OPTIONS`` response on a per-view decorators that want to customize the ``OPTIONS`` response on a per-view
basis. basis.

View file

@ -1,63 +1,76 @@
The App and Request Context .. currentmodule:: flask
===========================
The context keeps track of data and objects during a request, CLI command, or .. _app-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
because it contains request data in addition to application data. Otherwise, =======================
such as during a CLI command, it is referred to as the "app context". During an
app context, :data:`.current_app` and :data:`.g` are available, while during a
request context :data:`.request` and :data:`.session` are also available.
The application context keeps track of the application-level data during
a request, CLI command, or other activity. Rather than passing the
application around to each function, the :data:`current_app` and
:data:`g` proxies are accessed instead.
This is similar to the :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 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 lifecycle 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 +80,80 @@ 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():
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.
----
If you're writing an extension, :data:`g` should be reserved for user
code. You may store internal data on the context itself, but be sure to
use a sufficiently unique name. The current context is accessed with
:data:`_app_ctx_stack.top <_app_ctx_stack>`. For more information see
:doc:`extensiondev`.
.. _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 If :data:`~signals.signals_available` is true, the following signals are
command, or ``with`` block. Various callbacks and signals registered with the sent: :data:`appcontext_pushed`, :data:`appcontext_tearing_down`, and
app will be run during the context. :data:`appcontext_popped`.
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.

View file

@ -1,119 +0,0 @@
.. _async_await:
Using ``async`` and ``await``
=============================
.. versionadded:: 2.0
Routes, error handlers, before request, after request, and teardown
functions can all be coroutine functions if Flask is installed with the
``async`` extra (``pip install flask[async]``). This allows views to be
defined with ``async def`` and use ``await``.
.. code-block:: python
@app.route("/get-data")
async def get_data():
data = await async_db_query(...)
return jsonify(data)
Pluggable class-based views also support handlers that are implemented as
coroutines. This applies to the :meth:`~flask.views.View.dispatch_request`
method in views that inherit from the :class:`flask.views.View` class, as
well as all the HTTP method handlers in views that inherit from the
:class:`flask.views.MethodView` class.
Performance
-----------
Async functions require an event loop to run. Flask, as a WSGI
application, uses one worker to handle one request/response cycle.
When a request comes in to an async view, Flask will start an event loop
in a thread, run the view function there, then return the result.
Each request still ties up one worker, even for async views. The upside
is that you can run async code within a view, for example to make
multiple concurrent database queries, HTTP requests to an external API,
etc. However, the number of requests your application can handle at one
time will remain the same.
**Async is not inherently faster than sync code.** Async is beneficial
when performing concurrent IO-bound tasks, but will probably not improve
CPU-bound tasks. Traditional Flask views will still be appropriate for
most use cases, but Flask's async support enables writing and using
code that wasn't possible natively before.
Background tasks
----------------
Async functions will run in an event loop until they complete, at
which stage the event loop will stop. This means any additional
spawned tasks that haven't completed when the async function completes
will be cancelled. Therefore you cannot spawn background tasks, for
example via ``asyncio.create_task``.
If you wish to use background tasks it is best to use a task queue to
trigger background work, rather than spawn tasks in a view
function. With that in mind you can spawn asyncio tasks by serving
Flask with an ASGI server and utilising the asgiref WsgiToAsgi adapter
as described in :doc:`deploying/asgi`. This works as the adapter creates
an event loop that runs continually.
When to use Quart instead
-------------------------
Flask's async support is less performant than async-first frameworks due
to the way it is implemented. If you have a mainly async codebase it
would make sense to consider `Quart`_. Quart is a reimplementation of
Flask based on the `ASGI`_ standard instead of WSGI. This allows it to
handle many concurrent requests, long running requests, and websockets
without requiring multiple worker processes or threads.
It has also already been possible to :doc:`run Flask with Gevent </gevent>` to
get many of the benefits of async request handling. Gevent patches low-level
Python functions to accomplish this, whereas ``async``/``await`` and ASGI use
standard, modern Python capabilities. Deciding whether you should use gevent
with Flask, or Quart, or something else is ultimately up to understanding the
specific needs of your project.
.. _Quart: https://quart.palletsprojects.com
.. _ASGI: https://asgi.readthedocs.io
Extensions
----------
Flask extensions predating Flask's async support do not expect async views.
If they provide decorators to add functionality to views, those will probably
not work with async views because they will not await the function or be
awaitable. Other functions they provide will not be awaitable either and
will probably be blocking if called within an async view.
Extension authors can support async functions by utilising the
:meth:`flask.Flask.ensure_sync` method. For example, if the extension
provides a view function decorator add ``ensure_sync`` before calling
the decorated function,
.. code-block:: python
def extension(func):
@wraps(func)
def wrapper(*args, **kwargs):
... # Extension logic
return current_app.ensure_sync(func)(*args, **kwargs)
return wrapper
Check the changelog of the extension you want to use to see if they've
implemented async support, or make a feature request or PR to them.
Other event loops
-----------------
At the moment Flask only supports :mod:`asyncio`. It's possible to override
:meth:`flask.Flask.ensure_sync` to change how async functions are wrapped to use
a different library. See :ref:`gevent-asyncio` for an example.

101
docs/becomingbig.rst Normal file
View file

@ -0,0 +1,101 @@
.. _becomingbig:
Becoming Big
============
Here are your options when growing your codebase or scaling your application.
Read the Source.
----------------
Flask started in part to demonstrate how to build your own framework on top of
existing well-used tools Werkzeug (WSGI) and Jinja (templating), and as it
developed, it became useful to a wide audience. As you grow your codebase,
don't just use Flask -- understand it. Read the source. Flask's code is
written to be read; its documentation is published so you can use its internal
APIs. Flask sticks to documented APIs in upstream libraries, and documents its
internal utilities so that you can find the hook points needed for your
project.
Hook. Extend.
-------------
The :ref:`api` docs are full of available overrides, hook points, and
:ref:`signals`. You can provide custom classes for things like the request and
response objects. Dig deeper on the APIs you use, and look for the
customizations which are available out of the box in a Flask release. Look for
ways in which your project can be refactored into a collection of utilities and
Flask extensions. Explore the many `extensions
<http://flask.pocoo.org/extensions/>`_ in the community, and look for patterns to
build your own extensions if you do not find the tools you need.
Subclass.
---------
The :class:`~flask.Flask` class has many methods designed for subclassing. You
can quickly add or customize behavior by subclassing :class:`~flask.Flask` (see
the linked method docs) and using that subclass wherever you instantiate an
application class. This works well with :ref:`app-factories`. See :doc:`/patterns/subclassing` for an example.
Wrap with middleware.
---------------------
The :ref:`app-dispatch` chapter shows in detail how to apply middleware. You
can introduce WSGI middleware to wrap your Flask instances and introduce fixes
and changes at the layer between your Flask application and your HTTP
server. Werkzeug includes several `middlewares
<http://werkzeug.pocoo.org/docs/middlewares/>`_.
Fork.
-----
If none of the above options work, fork Flask. The majority of code of Flask
is within Werkzeug and Jinja2. These libraries do the majority of the work.
Flask is just the paste that glues those together. For every project there is
the point where the underlying framework gets in the way (due to assumptions
the original developers had). This is natural because if this would not be the
case, the framework would be a very complex system to begin with which causes a
steep learning curve and a lot of user frustration.
This is not unique to Flask. Many people use patched and modified
versions of their framework to counter shortcomings. This idea is also
reflected in the license of Flask. You don't have to contribute any
changes back if you decide to modify the framework.
The downside of forking is of course that Flask extensions will most
likely break because the new framework has a different import name.
Furthermore integrating upstream changes can be a complex process,
depending on the number of changes. Because of that, forking should be
the very last resort.
Scale like a pro.
-----------------
For many web applications the complexity of the code is less an issue than
the scaling for the number of users or data entries expected. Flask by
itself is only limited in terms of scaling by your application code, the
data store you want to use and the Python implementation and webserver you
are running on.
Scaling well means for example that if you double the amount of servers
you get about twice the performance. Scaling bad means that if you add a
new server the application won't perform any better or would not even
support a second server.
There is only one limiting factor regarding scaling in Flask which are
the context local proxies. They depend on context which in Flask is
defined as being either a thread, process or greenlet. If your server
uses some kind of concurrency that is not based on threads or greenlets,
Flask will no longer be able to support these global proxies. However the
majority of servers are using either threads, greenlets or separate
processes to achieve concurrency which are all methods well supported by
the underlying Werkzeug library.
Discuss with the community.
---------------------------
The Flask developers keep the framework accessible to users with codebases big
and small. If you find an obstacle in your way, caused by Flask, don't hesitate
to contact the developers on the mailinglist or IRC channel. The best way for
the Flask and Flask extension developers to improve the tools for larger
applications is getting feedback from users.

View file

@ -1,3 +1,5 @@
.. _blueprints:
Modular Applications with Blueprints Modular Applications with Blueprints
==================================== ====================================
@ -35,9 +37,8 @@ Blueprints in Flask are intended for these cases:
A blueprint in Flask is not a pluggable app because it is not actually an A blueprint in Flask is not a pluggable app because it is not actually an
application -- it's a set of operations which can be registered on an application -- it's a set of operations which can be registered on an
application, even multiple times. Why not have multiple application application, even multiple times. Why not have multiple application
objects? You can do that (see :doc:`/patterns/appdispatch`), but your objects? You can do that (see :ref:`app-dispatch`), but your applications
applications will have separate configs and will be managed at the WSGI will have separate configs and will be managed at the WSGI layer.
layer.
Blueprints instead provide separation at the Flask level, share Blueprints instead provide separation at the Flask level, share
application config, and can change an application object as necessary with application config, and can change an application object as necessary with
@ -69,17 +70,16 @@ implement a blueprint that does simple rendering of static templates::
@simple_page.route('/<page>') @simple_page.route('/<page>')
def show(page): def show(page):
try: try:
return render_template(f'pages/{page}.html') return render_template('pages/%s.html' % page)
except TemplateNotFound: except TemplateNotFound:
abort(404) abort(404)
When you bind a function with the help of the ``@simple_page.route`` When you bind a function with the help of the ``@simple_page.route``
decorator, the blueprint will record the intention of registering the decorator the blueprint will record the intention of registering the
function ``show`` on the application when it's later registered. function `show` on the application when it's later registered.
Additionally it will prefix the endpoint of the function with the Additionally it will prefix the endpoint of the function with the
name of the blueprint which was given to the :class:`Blueprint` name of the blueprint which was given to the :class:`Blueprint`
constructor (in this case also ``simple_page``). The blueprint's name constructor (in this case also ``simple_page``).
does not modify the URL, only the endpoint.
Registering Blueprints Registering Blueprints
---------------------- ----------------------
@ -95,10 +95,9 @@ So how do you register that blueprint? Like this::
If you check the rules registered on the application, you will find If you check the rules registered on the application, you will find
these:: these::
>>> app.url_map [<Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>,
Map([<Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>,
<Rule '/<page>' (HEAD, OPTIONS, GET) -> simple_page.show>, <Rule '/<page>' (HEAD, OPTIONS, GET) -> simple_page.show>,
<Rule '/' (HEAD, OPTIONS, GET) -> simple_page.show>]) <Rule '/' (HEAD, OPTIONS, GET) -> simple_page.show>]
The first one is obviously from the application itself for the static The first one is obviously from the application itself for the static
files. The other two are for the `show` function of the ``simple_page`` files. The other two are for the `show` function of the ``simple_page``
@ -111,53 +110,14 @@ Blueprints however can also be mounted at different locations::
And sure enough, these are the generated rules:: And sure enough, these are the generated rules::
>>> app.url_map [<Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>,
Map([<Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>,
<Rule '/pages/<page>' (HEAD, OPTIONS, GET) -> simple_page.show>, <Rule '/pages/<page>' (HEAD, OPTIONS, GET) -> simple_page.show>,
<Rule '/pages/' (HEAD, OPTIONS, GET) -> simple_page.show>]) <Rule '/pages/' (HEAD, OPTIONS, GET) -> simple_page.show>]
On top of that you can register blueprints multiple times though not every On top of that you can register blueprints multiple times though not every
blueprint might respond properly to that. In fact it depends on how the blueprint might respond properly to that. In fact it depends on how the
blueprint is implemented if it can be mounted more than once. blueprint is implemented if it can be mounted more than once.
Nesting Blueprints
------------------
It is possible to register a blueprint on another blueprint.
.. code-block:: python
parent = Blueprint('parent', __name__, url_prefix='/parent')
child = Blueprint('child', __name__, url_prefix='/child')
parent.register_blueprint(child)
app.register_blueprint(parent)
The child blueprint will gain the parent's name as a prefix to its
name, and child URLs will be prefixed with the parent's URL prefix.
.. code-block:: python
url_for('parent.child.create')
/parent/child/create
In addition a child blueprint's will gain their parent's subdomain,
with their subdomain as prefix if present i.e.
.. code-block:: python
parent = Blueprint('parent', __name__, subdomain='parent')
child = Blueprint('child', __name__, subdomain='child')
parent.register_blueprint(child)
app.register_blueprint(parent)
url_for('parent.child.create', _external=True)
"child.parent.domain.tld"
Blueprint-specific before request functions, etc. registered with the
parent will trigger for the child. If a child does not have an error
handler that can handle a given exception, the parent's will be tried.
Blueprint Resources Blueprint Resources
------------------- -------------------
@ -280,11 +240,10 @@ you can use relative redirects by prefixing the endpoint with a dot only::
This will link to ``admin.index`` for instance in case the current request This will link to ``admin.index`` for instance in case the current request
was dispatched to any other admin blueprint endpoint. was dispatched to any other admin blueprint endpoint.
Error Handlers
--------------
Blueprint Error Handlers Blueprints support the errorhandler decorator just like the :class:`Flask`
------------------------
Blueprints support the ``errorhandler`` decorator just like the :class:`Flask`
application object, so it is easy to make Blueprint-specific custom error application object, so it is easy to make Blueprint-specific custom error
pages. pages.
@ -299,7 +258,7 @@ concerning handlers for 404 and 405 exceptions. These errorhandlers are only
invoked from an appropriate ``raise`` statement or a call to ``abort`` in another invoked from an appropriate ``raise`` statement or a call to ``abort`` in another
of the blueprint's view functions; they are not invoked by, e.g., an invalid URL of the blueprint's view functions; they are not invoked by, e.g., an invalid URL
access. This is because the blueprint does not "own" a certain URL space, so access. This is because the blueprint does not "own" a certain URL space, so
the application instance has no way of knowing which blueprint error handler it the application instance has no way of knowing which blueprint errorhandler it
should run if given an invalid URL. If you would like to execute different should run if given an invalid URL. If you would like to execute different
handling strategies for these errors based on URL prefixes, they may be defined handling strategies for these errors based on URL prefixes, they may be defined
at the application level using the ``request`` proxy object:: at the application level using the ``request`` proxy object::
@ -308,8 +267,8 @@ at the application level using the ``request`` proxy object::
@app.errorhandler(405) @app.errorhandler(405)
def _handle_api_error(ex): def _handle_api_error(ex):
if request.path.startswith('/api/'): if request.path.startswith('/api/'):
return jsonify(error=str(ex)), ex.code return jsonify_error(ex)
else: else:
return ex return ex
See :doc:`/errorhandling`. More information on error handling see :ref:`errorpages`.

View file

@ -1,4 +1 @@
Changes
=======
.. include:: ../CHANGES.rst .. include:: ../CHANGES.rst

View file

@ -1,5 +1,7 @@
.. currentmodule:: flask .. currentmodule:: flask
.. _cli:
Command Line Interface Command Line Interface
====================== ======================
@ -8,61 +10,78 @@ interface, in your virtualenv. Executed from the terminal, this script gives
access to built-in, extension, and application-defined commands. The ``--help`` access to built-in, extension, and application-defined commands. The ``--help``
option will give more information about any commands and options. option will give more information about any commands and options.
.. _Click: https://click.palletsprojects.com/ .. _Click: http://click.pocoo.org/
Application Discovery Application Discovery
--------------------- ---------------------
The ``flask`` command is installed by Flask, not your application; it must be The ``flask`` command is installed by Flask, not your application; it must be
told where to find your application in order to use it. The ``--app`` told where to find your application in order to use it. The ``FLASK_APP``
option is used to specify how to load the application. environment variable is used to specify how to load the application.
While ``--app`` supports a variety of options for specifying your Unix Bash (Linux, Mac, etc.)::
$ export FLASK_APP=hello
$ flask run
Windows CMD::
> set FLASK_APP=hello
> flask run
Windows PowerShell::
> $env:FLASK_APP = "hello"
> flask run
While ``FLASK_APP`` supports a variety of options for specifying your
application, most use cases should be simple. Here are the typical values: application, most use cases should be simple. Here are the typical values:
(nothing) (nothing)
The name "app" or "wsgi" is imported (as a ".py" file, or package), The file :file:`wsgi.py` is imported, automatically detecting an app
automatically detecting an app (``app`` or ``application``) or (``app``). This provides an easy way to create an app from a factory with
factory (``create_app`` or ``make_app``). extra arguments.
``--app hello`` ``FLASK_APP=hello``
The given name is imported, automatically detecting an app (``app`` The name is imported, automatically detecting an app (``app``) or factory
or ``application``) or factory (``create_app`` or ``make_app``). (``create_app``).
---- ----
``--app`` has three parts: an optional path that sets the current working ``FLASK_APP`` has three parts: an optional path that sets the current working
directory, a Python file or dotted import path, and an optional variable directory, a Python file or dotted import path, and an optional variable
name of the instance or factory. If the name is a factory, it can optionally name of the instance or factory. If the name is a factory, it can optionally
be followed by arguments in parentheses. The following values demonstrate these be followed by arguments in parentheses. The following values demonstrate these
parts: parts:
``--app src/hello`` ``FLASK_APP=src/hello``
Sets the current working directory to ``src`` then imports ``hello``. Sets the current working directory to ``src`` then imports ``hello``.
``--app hello.web`` ``FLASK_APP=hello.web``
Imports the path ``hello.web``. Imports the path ``hello.web``.
``--app hello:app2`` ``FLASK_APP=hello:app2``
Uses the ``app2`` Flask instance in ``hello``. Uses the ``app2`` Flask instance in ``hello``.
``--app 'hello:create_app("dev")'`` ``FLASK_APP="hello:create_app('dev')"``
The ``create_app`` factory in ``hello`` is called with the string ``'dev'`` The ``create_app`` factory in ``hello`` is called with the string ``'dev'``
as the argument. as the argument.
If ``--app`` is not set, the command will try to import "app" or If ``FLASK_APP`` is not set, the command will look for a file called
"wsgi" (as a ".py" file, or package) and try to detect an application :file:`wsgi.py` or :file:`app.py` and try to detect an application instance or
instance or factory. factory.
Within the given import, the command looks for an application instance named Within the given import, the command looks for an application instance named
``app`` or ``application``, then any application instance. If no instance is ``app`` or ``application``, then any application instance. If no instance is
found, the command looks for a factory function named ``create_app`` or found, the command looks for a factory function named ``create_app`` or
``make_app`` that returns an instance. ``make_app`` that returns an instance.
If parentheses follow the factory name, their contents are parsed as When calling an application factory, if the factory takes an argument named
Python literals and passed as arguments and keyword arguments to the ``info``, then the :class:`~cli.ScriptInfo` instance is passed as a keyword
function. This means that strings must still be in quotes. argument. If parentheses follow the factory name, their contents are parsed
as Python literals and passes as arguments to the function. This means that
strings must still be in quotes.
Run the Development Server Run the Development Server
@ -71,63 +90,14 @@ Run the Development Server
The :func:`run <cli.run_command>` command will start the development server. It The :func:`run <cli.run_command>` command will start the development server. It
replaces the :meth:`Flask.run` method in most cases. :: replaces the :meth:`Flask.run` method in most cases. ::
$ flask --app hello run $ flask run
* Serving Flask app "hello" * Serving Flask app "hello"
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
.. warning:: Do not use this command to run your application in production. .. warning:: Do not use this command to run your application in production.
Only use the development server during development. The development server Only use the development server during development. The development server
is provided for convenience, but is not designed to be particularly secure, is provided for convenience, but is not designed to be particularly secure,
stable, or efficient. See :doc:`/deploying/index` for how to run in production. stable, or efficient. See :ref:`deployment` for how to run in production.
If another program is already using port 5000, you'll see
``OSError: [Errno 98]`` or ``OSError: [WinError 10013]`` when the
server tries to start. See :ref:`address-already-in-use` for how to
handle that.
Debug Mode
~~~~~~~~~~
In debug mode, the ``flask run`` command will enable the interactive debugger and the
reloader by default, and make errors easier to see and debug. To enable debug mode, use
the ``--debug`` option.
.. code-block:: console
$ flask --app hello run --debug
* Serving Flask app "hello"
* Debug mode: on
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
* Restarting with inotify reloader
* Debugger is active!
* Debugger PIN: 223-456-919
The ``--debug`` option can also be passed to the top level ``flask`` command to enable
debug mode for any command. The following two ``run`` calls are equivalent.
.. code-block:: console
$ flask --app hello --debug run
$ flask --app hello run --debug
Watch and Ignore Files with the Reloader
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When using debug mode, the reloader will trigger whenever your Python code or imported
modules change. The reloader can watch additional files with the ``--extra-files``
option. Multiple paths are separated with ``:``, or ``;`` on Windows.
.. code-block:: text
$ flask run --extra-files file1:dirA/file2:dirB/
* Running on http://127.0.0.1:8000/
* Detected change in '/path/to/file1', reloading
The reloader can also ignore files using :mod:`fnmatch` patterns with the
``--exclude-patterns`` option. Multiple patterns are separated with ``:``, or ``;`` on
Windows.
Open a Shell Open a Shell
@ -138,34 +108,63 @@ shell with the :func:`shell <cli.shell_command>` command. An application
context will be active, and the app instance will be imported. :: context will be active, and the app instance will be imported. ::
$ flask shell $ flask shell
Python 3.10.0 (default, Oct 27 2021, 06:59:51) [GCC 11.1.0] on linux Python 3.6.2 (default, Jul 20 2017, 03:52:27)
App: example [production] [GCC 7.1.1 20170630] on linux
Instance: /home/david/Projects/pallets/flask/instance App: example
Instance: /home/user/Projects/hello/instance
>>> >>>
Use :meth:`~Flask.shell_context_processor` to add other automatic imports. Use :meth:`~Flask.shell_context_processor` to add other automatic imports.
Environments
------------
.. versionadded:: 1.0
The environment in which the Flask app runs is set by the
:envvar:`FLASK_ENV` environment variable. If not set it defaults to
``production``. The other recognized environment is ``development``.
Flask and extensions may choose to enable behaviors based on the
environment.
If the env is set to ``development``, the ``flask`` command will enable
debug mode and ``flask run`` will enable the interactive debugger and
reloader.
::
$ FLASK_ENV=development flask run
* Serving Flask app "hello"
* Environment: development
* Debug mode: on
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
* Restarting with inotify reloader
* Debugger is active!
* Debugger PIN: 223-456-919
Debug Mode
----------
Debug mode will be enabled when :envvar:`FLASK_ENV` is ``development``,
as described above. If you want to control debug mode separately, use
:envvar:`FLASK_DEBUG`. The value ``1`` enables it, ``0`` disables it.
.. _dotenv: .. _dotenv:
Environment Variables From dotenv Environment Variables From dotenv
--------------------------------- ---------------------------------
The ``flask`` command supports setting any option for any command with Rather than setting ``FLASK_APP`` each time you open a new terminal, you can
environment variables. The variables are named like ``FLASK_OPTION`` or use Flask's dotenv support to set environment variables automatically.
``FLASK_COMMAND_OPTION``, for example ``FLASK_APP`` or
``FLASK_RUN_PORT``.
Rather than passing options every time you run a command, or environment
variables every time you open a new terminal, you can use Flask's dotenv
support to set environment variables automatically.
If `python-dotenv`_ is installed, running the ``flask`` command will set If `python-dotenv`_ is installed, running the ``flask`` command will set
environment variables defined in the files ``.env`` and ``.flaskenv``. environment variables defined in the files :file:`.env` and :file:`.flaskenv`.
You can also specify an extra file to load with the ``--env-file`` This can be used to avoid having to set ``FLASK_APP`` manually every time you
option. Dotenv files can be used to avoid having to set ``--app`` or open a new terminal, and to set configuration using environment variables
``FLASK_APP`` manually, and to set configuration using environment similar to how some deployment services work.
variables similar to how some deployment services work.
Variables set on the command line are used over those set in :file:`.env`, Variables set on the command line are used over those set in :file:`.env`,
which are used over those set in :file:`.flaskenv`. :file:`.flaskenv` should be which are used over those set in :file:`.flaskenv`. :file:`.flaskenv` should be
@ -173,7 +172,9 @@ used for public variables, such as ``FLASK_APP``, while :file:`.env` should not
be committed to your repository so that it can set private variables. be committed to your repository so that it can set private variables.
Directories are scanned upwards from the directory you call ``flask`` Directories are scanned upwards from the directory you call ``flask``
from to locate the files. from to locate the files. The current working directory will be set to the
location of the file, with the assumption that that is the top level project
directory.
The files are only loaded by the ``flask`` command or calling The files are only loaded by the ``flask`` command or calling
:meth:`~Flask.run`. If you would like to load these files when running in :meth:`~Flask.run`. If you would like to load these files when running in
@ -190,93 +191,16 @@ environment variables. The variables use the pattern
``FLASK_COMMAND_OPTION``. For example, to set the port for the run ``FLASK_COMMAND_OPTION``. For example, to set the port for the run
command, instead of ``flask run --port 8000``: command, instead of ``flask run --port 8000``:
.. tabs:: .. code-block:: none
.. group-tab:: Bash export FLASK_RUN_PORT=8000
flask run
.. code-block:: text * Running on http://127.0.0.1:8000/
$ export FLASK_RUN_PORT=8000
$ flask run
* Running on http://127.0.0.1:8000/
.. group-tab:: Fish
.. code-block:: text
$ set -x FLASK_RUN_PORT 8000
$ flask run
* Running on http://127.0.0.1:8000/
.. group-tab:: CMD
.. code-block:: text
> set FLASK_RUN_PORT=8000
> flask run
* Running on http://127.0.0.1:8000/
.. group-tab:: Powershell
.. code-block:: text
> $env:FLASK_RUN_PORT = 8000
> flask run
* Running on http://127.0.0.1:8000/
These can be added to the ``.flaskenv`` file just like ``FLASK_APP`` to These can be added to the ``.flaskenv`` file just like ``FLASK_APP`` to
control default command options. control default command options.
Disable dotenv
~~~~~~~~~~~~~~
The ``flask`` command will show a message if it detects dotenv files but
python-dotenv is not installed.
.. code-block:: bash
$ flask run
* Tip: There are .env files present. Do "pip install python-dotenv" to use them.
You can tell Flask not to load dotenv files even when python-dotenv is
installed by setting the ``FLASK_SKIP_DOTENV`` environment variable.
This can be useful if you want to load them manually, or if you're using
a project runner that loads them already. Keep in mind that the
environment variables must be set before the app loads or it won't
configure as expected.
.. tabs::
.. group-tab:: Bash
.. code-block:: text
$ export FLASK_SKIP_DOTENV=1
$ flask run
.. group-tab:: Fish
.. code-block:: text
$ set -x FLASK_SKIP_DOTENV 1
$ flask run
.. group-tab:: CMD
.. code-block:: text
> set FLASK_SKIP_DOTENV=1
> flask run
.. group-tab:: Powershell
.. code-block:: text
> $env:FLASK_SKIP_DOTENV = 1
> flask run
Environment Variables From virtualenv Environment Variables From virtualenv
------------------------------------- -------------------------------------
@ -284,31 +208,13 @@ If you do not want to install dotenv support, you can still set environment
variables by adding them to the end of the virtualenv's :file:`activate` variables by adding them to the end of the virtualenv's :file:`activate`
script. Activating the virtualenv will set the variables. script. Activating the virtualenv will set the variables.
.. tabs:: Unix Bash, :file:`venv/bin/activate`::
.. group-tab:: Bash export FLASK_APP=hello
Unix Bash, :file:`.venv/bin/activate`:: Windows CMD, :file:`venv\\Scripts\\activate.bat`::
$ export FLASK_APP=hello set FLASK_APP=hello
.. group-tab:: Fish
Fish, :file:`.venv/bin/activate.fish`::
$ set -x FLASK_APP hello
.. group-tab:: CMD
Windows CMD, :file:`.venv\\Scripts\\activate.bat`::
> set FLASK_APP=hello
.. group-tab:: Powershell
Windows Powershell, :file:`.venv\\Scripts\\activate.ps1`::
> $env:FLASK_APP = "hello"
It is preferred to use dotenv support over this, since :file:`.flaskenv` can be It is preferred to use dotenv support over this, since :file:`.flaskenv` can be
committed to the repository so that it works automatically wherever the project committed to the repository so that it works automatically wherever the project
@ -321,7 +227,7 @@ Custom Commands
The ``flask`` command is implemented using `Click`_. See that project's The ``flask`` command is implemented using `Click`_. See that project's
documentation for full information about writing commands. documentation for full information about writing commands.
This example adds the command ``create-user`` that takes the argument This example adds the command ``create_user`` that takes the argument
``name``. :: ``name``. ::
import click import click
@ -329,14 +235,14 @@ This example adds the command ``create-user`` that takes the argument
app = Flask(__name__) app = Flask(__name__)
@app.cli.command("create-user") @app.cli.command()
@click.argument("name") @click.argument('name')
def create_user(name): def create_user(name):
... ...
:: ::
$ flask create-user admin flask create_user admin
This example adds the same command, but as ``user create``, a command in a This example adds the same command, but as ``user create``, a command in a
group. This is useful if you want to organize multiple related commands. :: group. This is useful if you want to organize multiple related commands. ::
@ -357,105 +263,62 @@ group. This is useful if you want to organize multiple related commands. ::
:: ::
$ flask user create demo flask user create demo
See :ref:`testing-cli` for an overview of how to test your custom See :ref:`testing-cli` for an overview of how to test your custom
commands. commands.
Registering Commands with Blueprints
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If your application uses blueprints, you can optionally register CLI
commands directly onto them. When your blueprint is registered onto your
application, the associated commands will be available to the ``flask``
command. By default, those commands will be nested in a group matching
the name of the blueprint.
.. code-block:: python
from flask import Blueprint
bp = Blueprint('students', __name__)
@bp.cli.command('create')
@click.argument('name')
def create(name):
...
app.register_blueprint(bp)
.. code-block:: text
$ flask students create alice
You can alter the group name by specifying the ``cli_group`` parameter
when creating the :class:`Blueprint` object, or later with
:meth:`app.register_blueprint(bp, cli_group='...') <Flask.register_blueprint>`.
The following are equivalent:
.. code-block:: python
bp = Blueprint('students', __name__, cli_group='other')
# or
app.register_blueprint(bp, cli_group='other')
.. code-block:: text
$ flask other create alice
Specifying ``cli_group=None`` will remove the nesting and merge the
commands directly to the application's level:
.. code-block:: python
bp = Blueprint('students', __name__, cli_group=None)
# or
app.register_blueprint(bp, cli_group=None)
.. code-block:: text
$ flask create alice
Application Context Application Context
~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~
Commands added using the Flask app's :attr:`~Flask.cli` or Commands added using the Flask app's :attr:`~Flask.cli`
:class:`~flask.cli.FlaskGroup` :meth:`~cli.AppGroup.command` decorator :meth:`~cli.AppGroup.command` decorator will be executed with an application
will be executed with an application context pushed, so your custom context pushed, so your command and extensions have access to the app and its
commands and parameters have access to the app and its configuration. The configuration. If you create a command using the Click :func:`~click.command`
:func:`~cli.with_appcontext` decorator can be used to get the same decorator instead of the Flask decorator, you can use
behavior, but is not needed in most cases. :func:`~cli.with_appcontext` to get the same behavior. ::
.. code-block:: python
import click import click
from flask.cli import with_appcontext from flask.cli import with_appcontext
@click.command() @click.command
@with_appcontext @with_appcontext
def do_work(): def do_work():
... ...
app.cli.add_command(do_work) app.cli.add_command(do_work)
If you're sure a command doesn't need the context, you can disable it::
@app.cli.command(with_appcontext=False)
def do_work():
...
Plugins Plugins
------- -------
Flask will automatically load commands specified in the ``flask.commands`` Flask will automatically load commands specified in the ``flask.commands``
`entry point`_. This is useful for extensions that want to add commands when `entry point`_. This is useful for extensions that want to add commands when
they are installed. Entry points are specified in :file:`pyproject.toml`: they are installed. Entry points are specified in :file:`setup.py` ::
.. code-block:: toml from setuptools import setup
[project.entry-points."flask.commands"] setup(
my-command = "my_extension.commands:cli" name='flask-my-extension',
...,
entry_points={
'flask.commands': [
'my-command=flask_my_extension.commands:cli'
],
},
)
.. _entry point: https://packaging.python.org/tutorials/packaging-projects/#entry-points
Inside :file:`my_extension/commands.py` you can then export a Click .. _entry point: https://packaging.python.org/tutorials/distributing-packages/#entry-points
Inside :file:`flask_my_extension/commands.py` you can then export a Click
object:: object::
import click import click
@ -474,7 +337,7 @@ Custom Scripts
-------------- --------------
When you are using the app factory pattern, it may be more convenient to define When you are using the app factory pattern, it may be more convenient to define
your own Click script. Instead of using ``--app`` and letting Flask load your own Click script. Instead of using ``FLASK_APP`` and letting Flask load
your application, you can create your own Click object and export it as a your application, you can create your own Click object and export it as a
`console script`_ entry point. `console script`_ entry point.
@ -493,15 +356,22 @@ Create an instance of :class:`~cli.FlaskGroup` and pass it the factory::
def cli(): def cli():
"""Management script for the Wiki application.""" """Management script for the Wiki application."""
Define the entry point in :file:`pyproject.toml`: Define the entry point in :file:`setup.py`::
.. code-block:: toml from setuptools import setup
[project.scripts] setup(
wiki = "wiki:cli" name='flask-my-extension',
...,
entry_points={
'console_scripts': [
'wiki=wiki:cli'
],
},
)
Install the application in the virtualenv in editable mode and the custom Install the application in the virtualenv in editable mode and the custom
script is available. Note that you don't need to set ``--app``. :: script is available. Note that you don't need to set ``FLASK_APP``. ::
$ pip install -e . $ pip install -e .
$ wiki run $ wiki run
@ -515,42 +385,58 @@ script is available. Note that you don't need to set ``--app``. ::
The ``flask`` command, being separate from your code, does not have The ``flask`` command, being separate from your code, does not have
this issue and is recommended in most cases. this issue and is recommended in most cases.
.. _console script: https://packaging.python.org/tutorials/packaging-projects/#console-scripts .. _console script: https://packaging.python.org/tutorials/distributing-packages/#console-scripts
PyCharm Integration PyCharm Integration
------------------- -------------------
PyCharm Professional provides a special Flask run configuration to run the development Prior to PyCharm 2018.1, the Flask CLI features weren't yet fully
server. For the Community Edition, and for other commands besides ``run``, you need to integrated into PyCharm. We have to do a few tweaks to get them working
create a custom run configuration. These instructions should be similar for any other smoothly. These instructions should be similar for any other IDE you
IDE you use. might want to use.
In PyCharm, with your project open, click on *Run* from the menu bar and go to *Edit In PyCharm, with your project open, click on *Run* from the menu bar and
Configurations*. You'll see a screen similar to this: go to *Edit Configurations*. You'll be greeted by a screen similar to
this:
.. image:: _static/pycharm-run-config.png .. image:: _static/pycharm-runconfig.png
:align: center :align: center
:class: screenshot :class: screenshot
:alt: Screenshot of PyCharm run configuration. :alt: screenshot of pycharm's run configuration settings
Once you create a configuration for the ``flask run``, you can copy and change it to There's quite a few options to change, but once we've done it for one
call any other command. command, we can easily copy the entire configuration and make a single
tweak to give us access to other commands, including any custom ones you
may implement yourself.
Click the *+ (Add New Configuration)* button and select *Python*. Give the configuration Click the + (*Add New Configuration*) button and select *Python*. Give
a name such as "flask run". the configuration a good descriptive name such as "Run Flask Server".
For the ``flask run`` command, check "Single instance only" since you
can't run the server more than once at the same time.
Click the *Script path* dropdown and change it to *Module name*, then input ``flask``. Select *Module name* from the dropdown (**A**) then input ``flask``.
The *Parameters* field is set to the CLI command to execute along with any arguments. The *Parameters* field (**B**) is set to the CLI command to execute
This example uses ``--app hello run --debug``, which will run the development server in (with any arguments). In this example we use ``run``, which will run
debug mode. ``--app hello`` should be the import or file with your Flask app. the development server.
If you installed your project as a package in your virtualenv, you may uncheck the You can skip this next step if you're using :ref:`dotenv`. We need to
*PYTHONPATH* options. This will more accurately match how you deploy later. add an environment variable (**C**) to identify our application. Click
on the browse button and add an entry with ``FLASK_APP`` on the left and
the Python import or file on the right (``hello`` for example).
Click *OK* to save and close the configuration. Select the configuration in the main Next we need to set the working directory (**D**) to be the folder where
PyCharm window and click the play button next to it to run the server. our application resides.
Now that you have a configuration for ``flask run``, you can copy that configuration and If you have installed your project as a package in your virtualenv, you
change the *Parameters* argument to run a different CLI command. may untick the *PYTHONPATH* options (**E**). This will more accurately
match how you deploy the app later.
Click *Apply* to save the configuration, or *OK* to save and close the
window. Select the configuration in the main PyCharm window and click
the play button next to it to run the server.
Now that we have a configuration which runs ``flask run`` from within
PyCharm, we can copy that configuration and alter the *Script* argument
to run a different CLI command, e.g. ``flask shell``.

View file

@ -1,96 +1,167 @@
import packaging.version # -*- coding: utf-8 -*-
from pallets_sphinx_themes import get_version from __future__ import print_function
from pallets_sphinx_themes import ProjectLink
import inspect
import re
from pallets_sphinx_themes import DocVersion, ProjectLink, get_version
# Project -------------------------------------------------------------- # Project --------------------------------------------------------------
project = "Flask" project = 'Flask'
copyright = "2010 Pallets" copyright = '2010 Pallets Team'
author = "Pallets" author = 'Pallets Team'
release, version = get_version("Flask") 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",
] ]
autodoc_member_order = "bysource"
autodoc_typehints = "description"
autodoc_preserve_defaults = True
extlinks = {
"issue": ("https://github.com/pallets/flask/issues/%s", "#%s"),
"pr": ("https://github.com/pallets/flask/pull/%s", "#%s"),
"ghsa": ("https://github.com/pallets/flask/security/advisories/GHSA-%s", "GHSA-%s"),
}
intersphinx_mapping = { intersphinx_mapping = {
"python": ("https://docs.python.org/3/", None), 'python': ('https://docs.python.org/3/', None),
"werkzeug": ("https://werkzeug.palletsprojects.com/", None), 'werkzeug': ('http://werkzeug.pocoo.org/docs/', None),
"click": ("https://click.palletsprojects.com/", None), 'click': ('http://click.pocoo.org/', None),
"jinja": ("https://jinja.palletsprojects.com/", None), 'jinja': ('http://jinja.pocoo.org/docs/', None),
"itsdangerous": ("https://itsdangerous.palletsprojects.com/", None), 'itsdangerous': ('https://pythonhosted.org/itsdangerous', None),
"sqlalchemy": ("https://docs.sqlalchemy.org/", None), 'sqlalchemy': ('https://docs.sqlalchemy.org/en/latest/', None),
"wtforms": ("https://wtforms.readthedocs.io/", None), 'wtforms': ('https://wtforms.readthedocs.io/en/latest/', None),
"blinker": ("https://blinker.readthedocs.io/", None), 'blinker': ('https://pythonhosted.org/blinker/', None),
} }
# HTML ----------------------------------------------------------------- # HTML -----------------------------------------------------------------
html_theme = "flask" html_theme = 'flask'
html_theme_options = {"index_sidebar_logo": False}
html_context = { html_context = {
"project_links": [ 'project_links': [
ProjectLink("Donate", "https://palletsprojects.com/donate"), ProjectLink('Donate to Pallets', 'https://psfmember.org/civicrm/contribute/transact?reset=1&id=20'),
ProjectLink("PyPI Releases", "https://pypi.org/project/Flask/"), ProjectLink('Flask Website', 'https://palletsprojects.com/p/flask/'),
ProjectLink("Source Code", "https://github.com/pallets/flask/"), ProjectLink('PyPI releases', 'https://pypi.org/project/Flask/'),
ProjectLink("Issue Tracker", "https://github.com/pallets/flask/issues/"), ProjectLink('Source Code', 'https://github.com/pallets/flask/'),
ProjectLink("Chat", "https://discord.gg/pallets"), ProjectLink(
] 'Issue Tracker', 'https://github.com/pallets/flask/issues/'),
],
'versions': [
DocVersion('dev', 'Development', 'unstable'),
DocVersion('1.0', 'Flask 1.0', 'stable'),
DocVersion('0.12', 'Flask 0.12'),
],
'canonical_url': 'http://flask.pocoo.org/docs/{}/'.format(version),
'carbon_ads_args': 'zoneid=1673&serve=C6AILKT&placement=pocooorg',
} }
html_sidebars = { html_sidebars = {
"index": ["project.html", "localtoc.html", "searchbox.html", "ethicalads.html"], 'index': [
"**": ["localtoc.html", "relations.html", "searchbox.html", "ethicalads.html"], 'project.html',
'versions.html',
'searchbox.html',
],
'**': [
'localtoc.html',
'relations.html',
'versions.html',
'carbon_ads.html',
'searchbox.html',
]
}
html_static_path = ['_static']
html_favicon = '_static/flask-favicon.ico'
html_logo = '_static/flask.png'
html_additional_pages = {
'404': '404.html',
} }
singlehtml_sidebars = {"index": ["project.html", "localtoc.html", "ethicalads.html"]}
html_static_path = ["_static"]
html_favicon = "_static/flask-icon.svg"
html_logo = "_static/flask-logo.svg"
html_title = f"Flask Documentation ({version})"
html_show_sourcelink = False html_show_sourcelink = False
gettext_uuid = True # LaTeX ----------------------------------------------------------------
gettext_compact = False
latex_documents = [
(master_doc, 'Flask.tex', 'Flask Documentation', 'Pallets Team', 'manual'),
]
latex_use_modindex = False
latex_elements = {
'papersize': 'a4paper',
'pointsize': '12pt',
'fontpkg': r'\usepackage{mathpazo}',
'preamble': r'\usepackage{flaskstyle}',
}
latex_use_parts = True
latex_additional_files = ['flaskstyle.sty', 'logo.pdf']
# linkcheck ------------------------------------------------------------
linkcheck_anchors = False
# Local Extensions ----------------------------------------------------- # Local Extensions -----------------------------------------------------
def unwrap_decorators():
import sphinx.util.inspect as inspect
import functools
def github_link(name, rawtext, text, lineno, inliner, options=None, content=None): old_getargspec = inspect.getargspec
def getargspec(x):
return old_getargspec(getattr(x, '_original_function', x))
inspect.getargspec = getargspec
old_update_wrapper = functools.update_wrapper
def update_wrapper(wrapper, wrapped, *a, **kw):
rv = old_update_wrapper(wrapper, wrapped, *a, **kw)
rv._original_function = wrapped
return rv
functools.update_wrapper = update_wrapper
unwrap_decorators()
del unwrap_decorators
_internal_mark_re = re.compile(r'^\s*:internal:\s*$(?m)', re.M)
def skip_internal(app, what, name, obj, skip, options):
docstring = inspect.getdoc(obj) or ''
if skip or _internal_mark_re.search(docstring) is not None:
return True
def cut_module_meta(app, what, name, obj, options, lines):
"""Remove metadata from autodoc output."""
if what != 'module':
return
lines[:] = [
line for line in lines
if not line.startswith((':copyright:', ':license:'))
]
def github_link(
name, rawtext, text, lineno, inliner, options=None, content=None
):
app = inliner.document.settings.env.app app = inliner.document.settings.env.app
release = app.config.release release = app.config.release
base_url = "https://github.com/pallets/flask/tree/" base_url = 'https://github.com/pallets/flask/tree/'
if text.endswith(">"): if text.endswith('>'):
words, text = text[:-1].rsplit("<", 1) words, text = text[:-1].rsplit('<', 1)
words = words.strip() words = words.strip()
else: else:
words = None words = None
if packaging.version.parse(release).is_devrelease: if release.endswith('dev'):
url = f"{base_url}main/{text}" url = '{0}master/{1}'.format(base_url, text)
else: else:
url = f"{base_url}{release}/{text}" url = '{0}{1}/{2}'.format(base_url, release, text)
if words is None: if words is None:
words = url words = url
from docutils.nodes import reference from docutils.nodes import reference
from docutils.parsers.rst.roles import set_classes from docutils.parsers.rst.roles import set_classes
options = options or {} options = options or {}
set_classes(options) set_classes(options)
node = reference(rawtext, words, refuri=url, **options) node = reference(rawtext, words, refuri=url, **options)
@ -98,4 +169,6 @@ def github_link(name, rawtext, text, lineno, inliner, options=None, content=None
def setup(app): def setup(app):
app.add_role("gh", github_link) app.connect('autodoc-skip-member', skip_internal)
app.connect('autodoc-process-docstring', cut_module_meta)
app.add_role('gh', github_link)

View file

@ -1,3 +1,5 @@
.. _config:
Configuration Handling Configuration Handling
====================== ======================
@ -7,7 +9,7 @@ toggling the debug mode, setting the secret key, and other such
environment-specific things. environment-specific things.
The way Flask is designed usually requires the configuration to be The way Flask is designed usually requires the configuration to be
available when the application starts up. You can hard code the available when the application starts up. You can hardcode the
configuration in the code, which for many small applications is not configuration in the code, which for many small applications is not
actually that bad, but there are better ways. actually that bad, but there are better ways.
@ -38,26 +40,45 @@ method::
app.config.update( app.config.update(
TESTING=True, TESTING=True,
SECRET_KEY='192b9bdd22ab9ed4d12e236c78afcb9a393ec15f71bbf5dc987d54727823bcbf' SECRET_KEY=b'_5#y2L"F4Q8z\n\xec]/'
) )
Debug Mode Environment and Debug Features
---------- ------------------------------
The :data:`DEBUG` config value is special because it may behave inconsistently if The :data:`ENV` and :data:`DEBUG` config values are special because they
changed after the app has begun setting up. In order to set debug mode reliably, use the may behave inconsistently if changed after the app has begun setting up.
``--debug`` option on the ``flask`` or ``flask run`` command. ``flask run`` will use the In order to set the environment and debug mode reliably, Flask uses
interactive debugger and reloader by default in debug mode. environment variables.
.. code-block:: text The environment is used to indicate to Flask, extensions, and other
programs, like Sentry, what context Flask is running in. It is
controlled with the :envvar:`FLASK_ENV` environment variable and
defaults to ``production``.
$ flask --app hello run --debug Setting :envvar:`FLASK_ENV` to ``development`` will enable debug mode.
``flask run`` will use the interactive debugger and reloader by default
in debug mode. To control this separately from the environment, use the
:envvar:`FLASK_DEBUG` flag.
Using the option is recommended. While it is possible to set :data:`DEBUG` in your .. versionchanged:: 1.0
config or code, this is strongly discouraged. It can't be read early by the Added :envvar:`FLASK_ENV` to control the environment separately
``flask run`` command, and some systems or extensions may have already configured from debug mode. The development environment enables debug mode.
themselves based on a previous value.
To switch Flask to the development environment and enable debug mode,
set :envvar:`FLASK_ENV`::
$ export FLASK_ENV=development
$ flask run
(On Windows, use ``set`` instead of ``export``.)
Using the environment variables as described above is recommended. While
it is possible to set :data:`ENV` and :data:`DEBUG` in your config or
code, this is strongly discouraged. They can't be read early by the
``flask`` command, and some systems or extensions may have already
configured themselves based on a previous value.
Builtin Configuration Values Builtin Configuration Values
@ -65,21 +86,38 @@ Builtin Configuration Values
The following configuration values are used internally by Flask: The following configuration values are used internally by Flask:
.. py:data:: ENV
What environment the app is running in. Flask and extensions may
enable behaviors based on the environment, such as enabling debug
mode. The :attr:`~flask.Flask.env` attribute maps to this config
key. This is set by the :envvar:`FLASK_ENV` environment variable and
may not behave as expected if set in code.
**Do not enable development when deploying in production.**
Default: ``'production'``
.. versionadded:: 1.0
.. py:data:: DEBUG .. py:data:: DEBUG
Whether debug mode is enabled. When using ``flask run`` to start the development Whether debug mode is enabled. When using ``flask run`` to start the
server, an interactive debugger will be shown for unhandled exceptions, and the development server, an interactive debugger will be shown for
server will be reloaded when code changes. The :attr:`~flask.Flask.debug` attribute unhandled exceptions, and the server will be reloaded when code
maps to this config key. This is set with the ``FLASK_DEBUG`` environment variable. changes. The :attr:`~flask.Flask.debug` attribute maps to this
It may not behave as expected if set in code. config key. This is enabled when :data:`ENV` is ``'development'``
and is overridden by the ``FLASK_DEBUG`` environment variable. It
may not behave as expected if set in code.
**Do not enable debug mode when deploying in production.** **Do not enable debug mode when deploying in production.**
Default: ``False`` Default: ``True`` if :data:`ENV` is ``'production'``, or ``False``
otherwise.
.. 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.
@ -93,6 +131,14 @@ The following configuration values are used internally by Flask:
Default: ``None`` Default: ``None``
.. py:data:: PRESERVE_CONTEXT_ON_EXCEPTION
Don't pop the request context when an exception occurs. If not set, this
is true if ``DEBUG`` is true. This allows debuggers to introspect the
request data on errors, and should normally not need to be set directly.
Default: ``None``
.. py:data:: TRAP_HTTP_EXCEPTIONS .. py:data:: TRAP_HTTP_EXCEPTIONS
If there is no handler for an ``HTTPException``-type exception, re-raise it If there is no handler for an ``HTTPException``-type exception, re-raise it
@ -115,35 +161,16 @@ The following configuration values are used internally by Flask:
A secret key that will be used for securely signing the session cookie A secret key that will be used for securely signing the session cookie
and can be used for any other security related needs by extensions or your and can be used for any other security related needs by extensions or your
application. It should be a long random ``bytes`` or ``str``. For application. It should be a long random string of bytes, although unicode
example, copy the output of this to your config:: is accepted too. For example, copy the output of this to your config::
$ python -c 'import secrets; print(secrets.token_hex())' python -c 'import os; print(os.urandom(16))'
'192b9bdd22ab9ed4d12e236c78afcb9a393ec15f71bbf5dc987d54727823bcbf' b'_5#y2L"F4Q8z\n\xec]/'
**Do not reveal the secret key when posting questions or committing code.** **Do not reveal the secret key when posting questions or committing code.**
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
@ -153,23 +180,12 @@ The following configuration values are used internally by Flask:
.. py:data:: SESSION_COOKIE_DOMAIN .. py:data:: SESSION_COOKIE_DOMAIN
The value of the ``Domain`` parameter on the session cookie. If not set, browsers The domain match rule that the session cookie will be valid for. If not
will only send the cookie to the exact domain it was set from. Otherwise, they set, the cookie will be valid for all subdomains of :data:`SERVER_NAME`.
will send it to any subdomain of the given value as well. If ``False``, the cookie's domain will not be set.
Not setting this value is more restricted and secure than setting it.
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
Not set by default, does not fall back to ``SERVER_NAME``.
.. py:data:: SESSION_COOKIE_PATH .. py:data:: SESSION_COOKIE_PATH
The path that the session cookie will be valid for. If not set, the cookie The path that the session cookie will be valid for. If not set, the cookie
@ -192,23 +208,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
@ -250,61 +249,34 @@ The following configuration values are used internally by Flask:
.. py:data:: SEND_FILE_MAX_AGE_DEFAULT .. py:data:: SEND_FILE_MAX_AGE_DEFAULT
When serving files, set the cache control max age to this number of When serving files, set the cache control max age to this number of
seconds. Can be a :class:`datetime.timedelta` or an ``int``. seconds. Can either be a :class:`datetime.timedelta` or an ``int``.
Override this value on a per-file basis using Override this value on a per-file basis using
:meth:`~flask.Flask.get_send_file_max_age` on the application or :meth:`~flask.Flask.get_send_file_max_age` on the application or blueprint.
blueprint.
If ``None``, ``send_file`` tells the browser to use conditional Default: ``timedelta(hours=12)`` (``43200`` seconds)
requests will be used instead of a timed cache, which is usually
preferable.
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, will be used for the session cookie domain if
subdomain from the request. :data:`SESSION_COOKIE_DOMAIN` is not set. Modern web browsers will
not allow setting cookies for domains without a dot. To use a domain
locally, add any names that should route to the app to your
``hosts`` file. ::
Must be set for ``url_for`` to generate external URLs outside of a 127.0.0.1 localhost.dev
request context.
If set, ``url_for`` can generate external URLs with only an application
context instead 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
Does not affect ``SESSION_COOKIE_DOMAIN``.
.. py:data:: APPLICATION_ROOT .. py:data:: APPLICATION_ROOT
Inform the application what path it is mounted under by the application / Inform the application what path it is mounted under by the application /
web server. This is used for generating URLs outside the context of a web server.
request (inside a request, the dispatcher is responsible for setting
``SCRIPT_NAME`` instead; see :doc:`/patterns/appdispatch`
for examples of dispatch configuration).
Will be used for the session cookie path if ``SESSION_COOKIE_PATH`` is not Will be used for the session cookie path if ``SESSION_COOKIE_PATH`` is not
set. set.
@ -319,53 +291,42 @@ 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:: JSON_AS_ASCII
.. py:data:: MAX_FORM_MEMORY_SIZE Serialize objects to ASCII-encoded JSON. If this is disabled, the JSON
will be returned as a Unicode string, or encoded as ``UTF-8`` by
``jsonify``. This has security implications when rendering the JSON in
to JavaScript in templates, and should typically remain enabled.
The maximum size in bytes any non-file form field may be in a Default: ``True``
``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 .. py:data:: JSON_SORT_KEYS
: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`` Sort the keys of JSON objects alphabetically. This is useful for caching
because it ensures the data is serialized the same way no matter what
Python's hash seed is. While not recommended, you can disable this for a
possible performance improvement at the cost of caching.
.. versionadded:: 3.1 Default: ``True``
.. py:data:: MAX_FORM_PARTS .. py:data:: JSONIFY_PRETTYPRINT_REGULAR
The maximum number of fields that may be present in a ``jsonify`` responses will be output with newlines, spaces, and indentation
``multipart/form-data`` body. If this limit is exceeded, a 413 for easier reading by humans. Always enabled in debug mode.
: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 Default: ``False``
: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`` .. py:data:: JSONIFY_MIMETYPE
.. versionadded:: 3.1 The mimetype of ``jsonify`` responses.
Default: ``'application/json'``
.. py:data:: TEMPLATES_AUTO_RELOAD .. py:data:: TEMPLATES_AUTO_RELOAD
@ -388,12 +349,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``
@ -424,7 +379,7 @@ The following configuration values are used internally by Flask:
.. versionchanged:: 1.0 .. versionchanged:: 1.0
``LOGGER_NAME`` and ``LOGGER_HANDLER_POLICY`` were removed. See ``LOGGER_NAME`` and ``LOGGER_HANDLER_POLICY`` were removed. See
:doc:`/logging` for information about configuration. :ref:`logging` for information about configuration.
Added :data:`ENV` to reflect the :envvar:`FLASK_ENV` environment Added :data:`ENV` to reflect the :envvar:`FLASK_ENV` environment
variable. variable.
@ -434,30 +389,17 @@ The following configuration values are used internally by Flask:
Added :data:`MAX_COOKIE_SIZE` to control a warning from Werkzeug. Added :data:`MAX_COOKIE_SIZE` to control a warning from Werkzeug.
.. versionchanged:: 2.2
Removed ``PRESERVE_CONTEXT_ON_EXCEPTION``.
.. versionchanged:: 2.3 Configuring from Files
``JSON_AS_ASCII``, ``JSON_SORT_KEYS``, ``JSONIFY_MIMETYPE``, and ----------------------
``JSONIFY_PRETTYPRINT_REGULAR`` were removed. The default ``app.json`` provider has
equivalent attributes instead.
.. versionchanged:: 2.3 Configuration becomes more useful if you can store it in a separate file,
``ENV`` was removed. ideally located outside the actual application package. This makes
packaging and distributing your application possible via various package
handling tools (:ref:`distribute-deployment`) and finally modifying the
configuration file afterwards.
.. versionadded:: 3.1 So a common pattern is this::
Added :data:`PROVIDE_AUTOMATIC_OPTIONS` to control the default
addition of autogenerated OPTIONS responses.
Configuring from Python Files
-----------------------------
Configuration becomes more useful if you can store it in a separate file, ideally
located outside the actual application package. You can deploy your application, then
separately configure it for the specific deployment.
A common pattern is this::
app = Flask(__name__) app = Flask(__name__)
app.config.from_object('yourapplication.default_settings') app.config.from_object('yourapplication.default_settings')
@ -466,42 +408,18 @@ A common pattern is this::
This first loads the configuration from the This first loads the configuration from the
`yourapplication.default_settings` module and then overrides the values `yourapplication.default_settings` module and then overrides the values
with the contents of the file the :envvar:`YOURAPPLICATION_SETTINGS` with the contents of the file the :envvar:`YOURAPPLICATION_SETTINGS`
environment variable points to. This environment variable can be set environment variable points to. This environment variable can be set on
in the shell before starting the server: Linux or OS X with the export command in the shell before starting the
server::
.. tabs:: $ export YOURAPPLICATION_SETTINGS=/path/to/settings.cfg
$ python run-app.py
* Running on http://127.0.0.1:5000/
* Restarting with reloader...
.. group-tab:: Bash On Windows systems use the `set` builtin instead::
.. code-block:: text >set YOURAPPLICATION_SETTINGS=\path\to\settings.cfg
$ export YOURAPPLICATION_SETTINGS=/path/to/settings.cfg
$ flask run
* Running on http://127.0.0.1:5000/
.. group-tab:: Fish
.. code-block:: text
$ set -x YOURAPPLICATION_SETTINGS /path/to/settings.cfg
$ flask run
* Running on http://127.0.0.1:5000/
.. group-tab:: CMD
.. code-block:: text
> set YOURAPPLICATION_SETTINGS=\path\to\settings.cfg
> flask run
* Running on http://127.0.0.1:5000/
.. group-tab:: Powershell
.. code-block:: text
> $env:YOURAPPLICATION_SETTINGS = "\path\to\settings.cfg"
> flask run
* Running on http://127.0.0.1:5000/
The configuration files themselves are actual Python files. Only values The configuration files themselves are actual Python files. Only values
in uppercase are actually stored in the config object later on. So make in uppercase are actually stored in the config object later on. So make
@ -510,7 +428,8 @@ sure to use uppercase letters for your config keys.
Here is an example of a configuration file:: Here is an example of a configuration file::
# Example configuration # Example configuration
SECRET_KEY = '192b9bdd22ab9ed4d12e236c78afcb9a393ec15f71bbf5dc987d54727823bcbf' DEBUG = False
SECRET_KEY = b'_5#y2L"F4Q8z\n\xec]/'
Make sure to load the configuration very early on, so that extensions have Make sure to load the configuration very early on, so that extensions have
the ability to access the configuration when starting up. There are other the ability to access the configuration when starting up. There are other
@ -518,118 +437,54 @@ methods on the config object as well to load from individual files. For a
complete reference, read the :class:`~flask.Config` object's complete reference, read the :class:`~flask.Config` object's
documentation. documentation.
Configuring from Data Files
---------------------------
It is also possible to load configuration from a file in a format of
your choice using :meth:`~flask.Config.from_file`. For example to load
from a TOML file:
.. code-block:: python
import tomllib
app.config.from_file("config.toml", load=tomllib.load, text=False)
Or from a JSON file:
.. code-block:: python
import json
app.config.from_file("config.json", load=json.load)
Configuring from Environment Variables Configuring from Environment Variables
-------------------------------------- --------------------------------------
In addition to pointing to configuration files using environment In addition to pointing to configuration files using environment variables, you
variables, you may find it useful (or necessary) to control your may find it useful (or necessary) to control your configuration values directly
configuration values directly from the environment. Flask can be from the environment.
instructed to load all environment variables starting with a specific
prefix into the config using :meth:`~flask.Config.from_prefixed_env`.
Environment variables can be set in the shell before starting the Environment variables can be set on Linux or OS X with the export command in
server: the shell before starting the server::
.. tabs:: $ export SECRET_KEY='5f352379324c22463451387a0aec5d2f'
$ export DEBUG=False
$ python run-app.py
* Running on http://127.0.0.1:5000/
* Restarting with reloader...
.. group-tab:: Bash On Windows systems use the `set` builtin instead::
.. code-block:: text >set SECRET_KEY='5f352379324c22463451387a0aec5d2f'
>set DEBUG=False
$ export FLASK_SECRET_KEY="5f352379324c22463451387a0aec5d2f" While this approach is straightforward to use, it is important to remember that
$ export FLASK_MAIL_ENABLED=false environment variables are strings -- they are not automatically deserialized
$ flask run into Python types.
* Running on http://127.0.0.1:5000/
.. group-tab:: Fish Here is an example of a configuration file that uses environment variables::
.. code-block:: text # Example configuration
import os
$ set -x FLASK_SECRET_KEY "5f352379324c22463451387a0aec5d2f" ENVIRONMENT_DEBUG = os.environ.get("DEBUG", default=False)
$ set -x FLASK_MAIL_ENABLED false if ENVIRONMENT_DEBUG.lower() in ("f", "false"):
$ flask run ENVIRONMENT_DEBUG = False
* Running on http://127.0.0.1:5000/
.. group-tab:: CMD DEBUG = ENVIRONMENT_DEBUG
SECRET_KEY = os.environ.get("SECRET_KEY", default=None)
if not SECRET_KEY:
raise ValueError("No secret key set for Flask application")
.. code-block:: text
> set FLASK_SECRET_KEY="5f352379324c22463451387a0aec5d2f" Notice that any value besides an empty string will be interpreted as a boolean
> set FLASK_MAIL_ENABLED=false ``True`` value in Python, which requires care if an environment explicitly sets
> flask run values intended to be ``False``.
* Running on http://127.0.0.1:5000/
.. group-tab:: Powershell
.. code-block:: text
> $env:FLASK_SECRET_KEY = "5f352379324c22463451387a0aec5d2f"
> $env:FLASK_MAIL_ENABLED = "false"
> flask run
* Running on http://127.0.0.1:5000/
The variables can then be loaded and accessed via the config with a key
equal to the environment variable name without the prefix i.e.
.. code-block:: python
app.config.from_prefixed_env()
app.config["SECRET_KEY"] # Is "5f352379324c22463451387a0aec5d2f"
The prefix is ``FLASK_`` by default. This is configurable via the
``prefix`` argument of :meth:`~flask.Config.from_prefixed_env`.
Values will be parsed to attempt to convert them to a more specific type
than strings. By default :func:`json.loads` is used, so any valid JSON
value is possible, including lists and dicts. This is configurable via
the ``loads`` argument of :meth:`~flask.Config.from_prefixed_env`.
When adding a boolean value with the default JSON parsing, only "true"
and "false", lowercase, are valid values. Keep in mind that any
non-empty string is considered ``True`` by Python.
It is possible to set keys in nested dictionaries by separating the
keys with double underscore (``__``). Any intermediate keys that don't
exist on the parent dict will be initialized to an empty dict.
.. code-block:: text
$ export FLASK_MYAPI__credentials__username=user123
.. code-block:: python
app.config["MYAPI"]["credentials"]["username"] # Is "user123"
On Windows, environment variable keys are always uppercase, therefore
the above example would end up as ``MYAPI__CREDENTIALS__USERNAME``.
For even more config loading features, including merging and
case-insensitive Windows support, try a dedicated library such as
Dynaconf_, which includes integration with Flask.
.. _Dynaconf: https://www.dynaconf.com/
Make sure to load the configuration very early on, so that extensions have the
ability to access the configuration when starting up. There are other methods
on the config object as well to load from individual files. For a complete
reference, read the :class:`~flask.Config` class documentation.
Configuration Best Practices Configuration Best Practices
---------------------------- ----------------------------
@ -641,17 +496,13 @@ that experience:
1. Create your application in a function and register blueprints on it. 1. Create your application in a function and register blueprints on it.
That way you can create multiple instances of your application with That way you can create multiple instances of your application with
different configurations attached which makes unit testing a lot different configurations attached which makes unittesting a lot
easier. You can use this to pass in configuration as needed. easier. You can use this to pass in configuration as needed.
2. Do not write code that needs the configuration at import time. If you 2. Do not write code that needs the configuration at import time. If you
limit yourself to request-only accesses to the configuration you can limit yourself to request-only accesses to the configuration you can
reconfigure the object later on as needed. reconfigure the object later on as needed.
3. Make sure to load the configuration very early on, so that
extensions can access the configuration when calling ``init_app``.
.. _config-dev-prod: .. _config-dev-prod:
Development / Production Development / Production
@ -678,22 +529,23 @@ the config file by adding ``from yourapplication.default_settings
import *`` to the top of the file and then overriding the changes by hand. import *`` to the top of the file and then overriding the changes by hand.
You could also inspect an environment variable like You could also inspect an environment variable like
``YOURAPPLICATION_MODE`` and set that to `production`, `development` etc ``YOURAPPLICATION_MODE`` and set that to `production`, `development` etc
and import different hard-coded files based on that. and import different hardcoded files based on that.
An interesting pattern is also to use classes and inheritance for An interesting pattern is also to use classes and inheritance for
configuration:: configuration::
class Config(object): class Config(object):
DEBUG = False
TESTING = False TESTING = False
DATABASE_URI = 'sqlite:///:memory:'
class ProductionConfig(Config): class ProductionConfig(Config):
DATABASE_URI = 'mysql://user@localhost/foo' DATABASE_URI = 'mysql://user@localhost/foo'
class DevelopmentConfig(Config): class DevelopmentConfig(Config):
DATABASE_URI = "sqlite:////tmp/foo.db" DEBUG = True
class TestingConfig(Config): class TestingConfig(Config):
DATABASE_URI = 'sqlite:///:memory:'
TESTING = True TESTING = True
To enable such a config you just have to call into To enable such a config you just have to call into
@ -701,41 +553,6 @@ To enable such a config you just have to call into
app.config.from_object('configmodule.ProductionConfig') app.config.from_object('configmodule.ProductionConfig')
Note that :meth:`~flask.Config.from_object` does not instantiate the class
object. If you need to instantiate the class, such as to access a property,
then you must do so before calling :meth:`~flask.Config.from_object`::
from configmodule import ProductionConfig
app.config.from_object(ProductionConfig())
# Alternatively, import via string:
from werkzeug.utils import import_string
cfg = import_string('configmodule.ProductionConfig')()
app.config.from_object(cfg)
Instantiating the configuration object allows you to use ``@property`` in
your configuration classes::
class Config(object):
"""Base config, uses staging database server."""
TESTING = False
DB_SERVER = '192.168.1.56'
@property
def DATABASE_URI(self): # Note: all caps
return f"mysql://user@{self.DB_SERVER}/foo"
class ProductionConfig(Config):
"""Uses production database server."""
DB_SERVER = '192.168.19.32'
class DevelopmentConfig(Config):
DB_SERVER = 'localhost'
class TestingConfig(Config):
DB_SERVER = 'localhost'
DATABASE_URI = 'sqlite:///:memory:'
There are many different ways and it's up to you how you want to manage There are many different ways and it's up to you how you want to manage
your configuration files. However here a list of good recommendations: your configuration files. However here a list of good recommendations:
@ -749,10 +566,12 @@ your configuration files. However here a list of good recommendations:
code at all. If you are working often on different projects you can code at all. If you are working often on different projects you can
even create your own script for sourcing that activates a virtualenv even create your own script for sourcing that activates a virtualenv
and exports the development configuration for you. and exports the development configuration for you.
- Use a tool like `fabric`_ to push code and configuration separately - Use a tool like `fabric`_ in production to push code and
to the production server(s). configurations separately to the production server(s). For some
details about how to do that, head over to the
:ref:`fabric-deployment` pattern.
.. _fabric: https://www.fabfile.org/ .. _fabric: http://www.fabfile.org/
.. _instance-folders: .. _instance-folders:
@ -800,7 +619,7 @@ locations are used:
- Installed module or package:: - Installed module or package::
$PREFIX/lib/pythonX.Y/site-packages/myapp $PREFIX/lib/python2.X/site-packages/myapp
$PREFIX/var/myapp-instance $PREFIX/var/myapp-instance
``$PREFIX`` is the prefix of your Python installation. This can be ``$PREFIX`` is the prefix of your Python installation. This can be
@ -817,7 +636,7 @@ root” (the default) to “relative to instance folder” via the
app = Flask(__name__, instance_relative_config=True) app = Flask(__name__, instance_relative_config=True)
Here is a full example of how to configure Flask to preload the config Here is a full example of how to configure Flask to preload the config
from a module and then override the config from a file in the instance from a module and then override the config from a file in the config
folder if it exists:: folder if it exists::
app = Flask(__name__, instance_relative_config=True) app = Flask(__name__, instance_relative_config=True)

62
docs/contents.rst.inc Normal file
View file

@ -0,0 +1,62 @@
User's Guide
------------
This part of the documentation, which is mostly prose, begins with some
background information about Flask, then focuses on step-by-step
instructions for web development with Flask.
.. toctree::
:maxdepth: 2
foreword
advanced_foreword
installation
quickstart
tutorial/index
templating
testing
errorhandling
logging
config
signals
views
appcontext
reqcontext
blueprints
extensions
cli
server
shell
patterns/index
deploying/index
becomingbig
API Reference
-------------
If you are looking for information on a specific function, class or
method, this part of the documentation is for you.
.. toctree::
:maxdepth: 2
api
Additional Notes
----------------
Design notes, legal information and changelog are here for the interested.
.. toctree::
:maxdepth: 2
design
htmlfaq
security
unicode
extensiondev
styleguide
upgrading
changelog
license
contributing

View file

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

View file

@ -1,99 +0,0 @@
Debugging Application Errors
============================
In Production
-------------
**Do not run the development server, or enable the built-in debugger, in
a production environment.** The debugger allows executing arbitrary
Python code from the browser. It's protected by a pin, but that should
not be relied on for security.
Use an error logging tool, such as Sentry, as described in
:ref:`error-logging-tools`, or enable logging and notifications as
described in :doc:`/logging`.
If you have access to the server, you could add some code to start an
external debugger if ``request.remote_addr`` matches your IP. Some IDE
debuggers also have a remote mode so breakpoints on the server can be
interacted with locally. Only enable a debugger temporarily.
The Built-In Debugger
---------------------
The built-in Werkzeug development server provides a debugger which shows
an interactive traceback in the browser when an unhandled error occurs
during a request. This debugger should only be used during development.
.. image:: _static/debugger.png
:align: center
:class: screenshot
:alt: screenshot of debugger in action
.. warning::
The debugger allows executing arbitrary Python code from the
browser. It is protected by a pin, but still represents a major
security risk. Do not run the development server or debugger in a
production environment.
The debugger is enabled by default when the development server is run in debug mode.
.. code-block:: text
$ flask --app hello run --debug
When running from Python code, passing ``debug=True`` enables debug mode, which is
mostly equivalent.
.. code-block:: python
app.run(debug=True)
:doc:`/server` and :doc:`/cli` have more information about running the debugger and
debug mode. More information about the debugger can be found in the `Werkzeug
documentation <https://werkzeug.palletsprojects.com/debug/>`__.
External Debuggers
------------------
External debuggers, such as those provided by IDEs, can offer a more
powerful debugging experience than the built-in debugger. They can also
be used to step through code during a request before an error is raised,
or if no error is raised. Some even have a remote mode so you can debug
code running on another machine.
When using an external debugger, the app should still be in debug mode, otherwise Flask
turns unhandled errors into generic 500 error pages. However, the built-in debugger and
reloader should be disabled so they don't interfere with the external debugger.
.. code-block:: text
$ flask --app hello run --debug --no-debugger --no-reload
When running from Python:
.. code-block:: python
app.run(debug=True, use_debugger=False, use_reloader=False)
Disabling these isn't required, an external debugger will continue to work with the
following caveats.
- If the built-in debugger is not disabled, it will catch unhandled exceptions before
the external debugger can.
- If the reloader is not disabled, it could cause an unexpected reload if code changes
during a breakpoint.
- The development server will still catch unhandled exceptions if the built-in
debugger is disabled, otherwise it would crash on any error. If you want that (and
usually you don't) pass ``passthrough_errors=True`` to ``app.run``.
.. code-block:: python
app.run(
debug=True, passthrough_errors=True,
use_debugger=False, use_reloader=False
)

View file

@ -1,66 +0,0 @@
Apache httpd
============
`Apache httpd`_ is a fast, production level HTTP server. When serving
your application with one of the WSGI servers listed in :doc:`index`, it
is often good or necessary to put a dedicated HTTP server in front of
it. This "reverse proxy" can handle incoming requests, TLS, and other
security and performance concerns better than the WSGI server.
httpd can be installed using your system package manager, or a pre-built
executable for Windows. Installing and running httpd itself is outside
the scope of this doc. This page outlines the basics of configuring
httpd to proxy your application. Be sure to read its documentation to
understand what features are available.
.. _Apache httpd: https://httpd.apache.org/
Domain Name
-----------
Acquiring and configuring a domain name is outside the scope of this
doc. In general, you will buy a domain name from a registrar, pay for
server space with a hosting provider, and then point your registrar
at the hosting provider's name servers.
To simulate this, you can also edit your ``hosts`` file, located at
``/etc/hosts`` on Linux. Add a line that associates a name with the
local IP.
Modern Linux systems may be configured to treat any domain name that
ends with ``.localhost`` like this without adding it to the ``hosts``
file.
.. code-block:: python
:caption: ``/etc/hosts``
127.0.0.1 hello.localhost
Configuration
-------------
The httpd configuration is located at ``/etc/httpd/conf/httpd.conf`` on
Linux. It may be different depending on your operating system. Check the
docs and look for ``httpd.conf``.
Remove or comment out any existing ``DocumentRoot`` directive. Add the
config lines below. We'll assume the WSGI server is listening locally at
``http://127.0.0.1:8000``.
.. code-block:: apache
:caption: ``/etc/httpd/conf/httpd.conf``
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
ProxyPass / http://127.0.0.1:8000/
RequestHeader set X-Forwarded-Proto http
RequestHeader set X-Forwarded-Prefix /
The ``LoadModule`` lines might already exist. If so, make sure they are
uncommented instead of adding them manually.
Then :doc:`proxy_fix` so that your application uses the ``X-Forwarded``
headers. ``X-Forwarded-For`` and ``X-Forwarded-Host`` are automatically
set by ``ProxyPass``.

View file

@ -1,27 +0,0 @@
ASGI
====
If you'd like to use an ASGI server you will need to utilise WSGI to
ASGI middleware. The asgiref
`WsgiToAsgi <https://github.com/django/asgiref#wsgi-to-asgi-adapter>`_
adapter is recommended as it integrates with the event loop used for
Flask's :ref:`async_await` support. You can use the adapter by
wrapping the Flask app,
.. code-block:: python
from asgiref.wsgi import WsgiToAsgi
from flask import Flask
app = Flask(__name__)
...
asgi_app = WsgiToAsgi(app)
and then serving the ``asgi_app`` with the ASGI server, e.g. using
`Hypercorn <https://github.com/pgjones/hypercorn>`_,
.. sourcecode:: text
$ hypercorn module:asgi_app

61
docs/deploying/cgi.rst Normal file
View file

@ -0,0 +1,61 @@
CGI
===
If all other deployment methods do not work, CGI will work for sure.
CGI is supported by all major servers but usually has a sub-optimal
performance.
This is also the way you can use a Flask application on Google's `App
Engine`_, where execution happens in a CGI-like environment.
.. admonition:: Watch Out
Please make sure in advance that any ``app.run()`` calls you might
have in your application file are inside an ``if __name__ ==
'__main__':`` block or moved to a separate file. Just make sure it's
not called because this will always start a local WSGI server which
we do not want if we deploy that application to CGI / app engine.
With CGI, you will also have to make sure that your code does not contain
any ``print`` statements, or that ``sys.stdout`` is overridden by something
that doesn't write into the HTTP response.
Creating a `.cgi` file
----------------------
First you need to create the CGI application file. Let's call it
:file:`yourapplication.cgi`::
#!/usr/bin/python
from wsgiref.handlers import CGIHandler
from yourapplication import app
CGIHandler().run(app)
Server Setup
------------
Usually there are two ways to configure the server. Either just copy the
``.cgi`` into a :file:`cgi-bin` (and use `mod_rewrite` or something similar to
rewrite the URL) or let the server point to the file directly.
In Apache for example you can put something like this into the config:
.. sourcecode:: apache
ScriptAlias /app /path/to/the/application.cgi
On shared webhosting, though, you might not have access to your Apache config.
In this case, a file called ``.htaccess``, sitting in the public directory you want
your app to be available, works too but the ``ScriptAlias`` directive won't
work in that case:
.. sourcecode:: apache
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f # Don't interfere with static files
RewriteRule ^(.*)$ /path/to/the/application.cgi/$1 [L]
For more information consult the documentation of your webserver.
.. _App Engine: https://developers.google.com/appengine/

View file

@ -1,8 +0,0 @@
:orphan:
eventlet
========
`Eventlet is no longer maintained.`__ Use :doc:`/deploying/gevent` instead.
__ https://eventlet.readthedocs.io

240
docs/deploying/fastcgi.rst Normal file
View file

@ -0,0 +1,240 @@
.. _deploying-fastcgi:
FastCGI
=======
FastCGI is a deployment option on servers like `nginx`_, `lighttpd`_, and
`cherokee`_; see :ref:`deploying-uwsgi` and :ref:`deploying-wsgi-standalone`
for other options. To use your WSGI application with any of them you will need
a FastCGI server first. The most popular one is `flup`_ which we will use for
this guide. Make sure to have it installed to follow along.
.. admonition:: Watch Out
Please make sure in advance that any ``app.run()`` calls you might
have in your application file are inside an ``if __name__ ==
'__main__':`` block or moved to a separate file. Just make sure it's
not called because this will always start a local WSGI server which
we do not want if we deploy that application to FastCGI.
Creating a `.fcgi` file
-----------------------
First you need to create the FastCGI server file. Let's call it
`yourapplication.fcgi`::
#!/usr/bin/python
from flup.server.fcgi import WSGIServer
from yourapplication import app
if __name__ == '__main__':
WSGIServer(app).run()
This is enough for Apache to work, however nginx and older versions of
lighttpd need a socket to be explicitly passed to communicate with the
FastCGI server. For that to work you need to pass the path to the
socket to the :class:`~flup.server.fcgi.WSGIServer`::
WSGIServer(application, bindAddress='/path/to/fcgi.sock').run()
The path has to be the exact same path you define in the server
config.
Save the :file:`yourapplication.fcgi` file somewhere you will find it again.
It makes sense to have that in :file:`/var/www/yourapplication` or something
similar.
Make sure to set the executable bit on that file so that the servers
can execute it:
.. sourcecode:: text
# chmod +x /var/www/yourapplication/yourapplication.fcgi
Configuring Apache
------------------
The example above is good enough for a basic Apache deployment but your
`.fcgi` file will appear in your application URL e.g.
``example.com/yourapplication.fcgi/news/``. There are few ways to configure
your application so that yourapplication.fcgi does not appear in the URL.
A preferable way is to use the ScriptAlias and SetHandler configuration
directives to route requests to the FastCGI server. The following example
uses FastCgiServer to start 5 instances of the application which will
handle all incoming requests::
LoadModule fastcgi_module /usr/lib64/httpd/modules/mod_fastcgi.so
FastCgiServer /var/www/html/yourapplication/app.fcgi -idle-timeout 300 -processes 5
<VirtualHost *>
ServerName webapp1.mydomain.com
DocumentRoot /var/www/html/yourapplication
AddHandler fastcgi-script fcgi
ScriptAlias / /var/www/html/yourapplication/app.fcgi/
<Location />
SetHandler fastcgi-script
</Location>
</VirtualHost>
These processes will be managed by Apache. If you're using a standalone
FastCGI server, you can use the FastCgiExternalServer directive instead.
Note that in the following the path is not real, it's simply used as an
identifier to other
directives such as AliasMatch::
FastCgiServer /var/www/html/yourapplication -host 127.0.0.1:3000
If you cannot set ScriptAlias, for example on a shared web host, you can use
WSGI middleware to remove yourapplication.fcgi from the URLs. Set .htaccess::
<IfModule mod_fcgid.c>
AddHandler fcgid-script .fcgi
<Files ~ (\.fcgi)>
SetHandler fcgid-script
Options +FollowSymLinks +ExecCGI
</Files>
</IfModule>
<IfModule mod_rewrite.c>
Options +FollowSymlinks
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ yourapplication.fcgi/$1 [QSA,L]
</IfModule>
Set yourapplication.fcgi::
#!/usr/bin/python
#: optional path to your local python site-packages folder
import sys
sys.path.insert(0, '<your_local_path>/lib/python<your_python_version>/site-packages')
from flup.server.fcgi import WSGIServer
from yourapplication import app
class ScriptNameStripper(object):
def __init__(self, app):
self.app = app
def __call__(self, environ, start_response):
environ['SCRIPT_NAME'] = ''
return self.app(environ, start_response)
app = ScriptNameStripper(app)
if __name__ == '__main__':
WSGIServer(app).run()
Configuring lighttpd
--------------------
A basic FastCGI configuration for lighttpd looks like that::
fastcgi.server = ("/yourapplication.fcgi" =>
((
"socket" => "/tmp/yourapplication-fcgi.sock",
"bin-path" => "/var/www/yourapplication/yourapplication.fcgi",
"check-local" => "disable",
"max-procs" => 1
))
)
alias.url = (
"/static/" => "/path/to/your/static/"
)
url.rewrite-once = (
"^(/static($|/.*))$" => "$1",
"^(/.*)$" => "/yourapplication.fcgi$1"
)
Remember to enable the FastCGI, alias and rewrite modules. This configuration
binds the application to ``/yourapplication``. If you want the application to
work in the URL root you have to work around a lighttpd bug with the
:class:`~werkzeug.contrib.fixers.LighttpdCGIRootFix` middleware.
Make sure to apply it only if you are mounting the application the URL
root. Also, see the Lighty docs for more information on `FastCGI and Python
<https://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModFastCGI>`_ (note that
explicitly passing a socket to run() is no longer necessary).
Configuring nginx
-----------------
Installing FastCGI applications on nginx is a bit different because by
default no FastCGI parameters are forwarded.
A basic Flask FastCGI configuration for nginx looks like this::
location = /yourapplication { rewrite ^ /yourapplication/ last; }
location /yourapplication { try_files $uri @yourapplication; }
location @yourapplication {
include fastcgi_params;
fastcgi_split_path_info ^(/yourapplication)(.*)$;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_pass unix:/tmp/yourapplication-fcgi.sock;
}
This configuration binds the application to ``/yourapplication``. If you
want to have it in the URL root it's a bit simpler because you don't
have to figure out how to calculate ``PATH_INFO`` and ``SCRIPT_NAME``::
location / { try_files $uri @yourapplication; }
location @yourapplication {
include fastcgi_params;
fastcgi_param PATH_INFO $fastcgi_script_name;
fastcgi_param SCRIPT_NAME "";
fastcgi_pass unix:/tmp/yourapplication-fcgi.sock;
}
Running FastCGI Processes
-------------------------
Since nginx and others do not load FastCGI apps, you have to do it by
yourself. `Supervisor can manage FastCGI processes.
<http://supervisord.org/configuration.html#fcgi-program-x-section-settings>`_
You can look around for other FastCGI process managers or write a script
to run your `.fcgi` file at boot, e.g. using a SysV ``init.d`` script.
For a temporary solution, you can always run the ``.fcgi`` script inside
GNU screen. See ``man screen`` for details, and note that this is a
manual solution which does not persist across system restart::
$ screen
$ /var/www/yourapplication/yourapplication.fcgi
Debugging
---------
FastCGI deployments tend to be hard to debug on most web servers. Very
often the only thing the server log tells you is something along the
lines of "premature end of headers". In order to debug the application
the only thing that can really give you ideas why it breaks is switching
to the correct user and executing the application by hand.
This example assumes your application is called `application.fcgi` and
that your web server user is `www-data`::
$ su www-data
$ cd /var/www/yourapplication
$ python application.fcgi
Traceback (most recent call last):
File "yourapplication.fcgi", line 4, in <module>
ImportError: No module named yourapplication
In this case the error seems to be "yourapplication" not being on the
python path. Common problems are:
- Relative paths being used. Don't rely on the current working directory.
- The code depending on environment variables that are not set by the
web server.
- Different python interpreters being used.
.. _nginx: https://nginx.org/
.. _lighttpd: https://www.lighttpd.net/
.. _cherokee: http://cherokee-project.com/
.. _flup: https://pypi.org/project/flup/

View file

@ -1,76 +0,0 @@
gevent
======
Prefer using :doc:`gunicorn` or :doc:`uwsgi` with gevent workers rather
than using `gevent`_ directly. Gunicorn and uWSGI provide much more
configurable and production-tested servers.
`gevent`_ allows writing asynchronous, coroutine-based code that looks
like standard synchronous Python. It uses `greenlet`_ to enable task
switching without writing ``async/await`` or using ``asyncio``. This is
not the same as Python's ``async/await``, or the ASGI server spec.
gevent provides a WSGI server that can handle many connections at once
instead of one per worker process. See :doc:`/gevent` for more
information about enabling it in your application.
.. _gevent: https://www.gevent.org/
.. _greenlet: https://greenlet.readthedocs.io/en/latest/
Installing
----------
When using gevent, greenlet>=1.0 is required. When using PyPy,
PyPy>=7.3.7 is required.
Create a virtualenv, install your application, then install ``gevent``.
.. code-block:: text
$ cd hello-app
$ python -m venv .venv
$ . .venv/bin/activate
$ pip install . # install your application
$ pip install gevent
Running
-------
To use gevent to serve your application, write a script that imports its
``WSGIServer``, as well as your app or app factory.
.. code-block:: python
:caption: ``wsgi.py``
from gevent.pywsgi import WSGIServer
from hello import create_app
app = create_app()
http_server = WSGIServer(("127.0.0.1", 8000), app)
http_server.serve_forever()
.. code-block:: text
$ python wsgi.py
No output is shown when the server starts.
Binding Externally
------------------
gevent 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 gevent.
You can bind to all external IPs on a non-privileged port by using
``0.0.0.0`` in the server arguments shown in the previous section. Don't
do this when using a reverse proxy setup, otherwise it will be possible
to bypass the proxy.
``0.0.0.0`` is not a valid address to navigate to, you'd use a specific
IP address in your browser.

View file

@ -1,116 +0,0 @@
Gunicorn
========
`Gunicorn`_ is a pure Python WSGI server with simple configuration and
multiple worker implementations for performance tuning.
* It tends to integrate easily with hosting platforms.
* It does not support Windows (but does run on WSL).
* It is easy to install as it does not require additional dependencies
or compilation.
* It has built-in async worker support using gevent.
This page outlines the basics of running Gunicorn. Be sure to read its
`documentation`_ and use ``gunicorn --help`` to understand what features
are available.
.. _Gunicorn: https://gunicorn.org/
.. _documentation: https://docs.gunicorn.org/
Installing
----------
Gunicorn is easy to install, as it does not require external
dependencies or compilation. It runs on Windows only under WSL.
Create a virtualenv, install your application, then install
``gunicorn``.
.. code-block:: text
$ cd hello-app
$ python -m venv .venv
$ . .venv/bin/activate
$ pip install . # install your application
$ pip install gunicorn
Running
-------
The only required argument to Gunicorn tells it how to load your Flask
application. The syntax is ``{module_import}:{app_variable}``.
``module_import`` is the dotted import name to the module with your
application. ``app_variable`` is the variable with the application. It
can also be a function call (with any arguments) if you're using the
app factory pattern.
.. code-block:: text
# equivalent to 'from hello import app'
$ gunicorn -w 4 'hello:app'
# equivalent to 'from hello import create_app; create_app()'
$ gunicorn -w 4 'hello:create_app()'
Starting gunicorn 20.1.0
Listening at: http://127.0.0.1:8000 (x)
Using worker: sync
Booting worker with pid: x
Booting worker with pid: x
Booting worker with pid: x
Booting worker with pid: x
The ``-w`` option specifies the number of processes to run; a starting
value could be ``CPU * 2``. The default is only 1 worker, which is
probably not what you want for the default worker type.
Logs for each request aren't shown by default, only worker info and
errors are shown. To show access logs on stdout, use the
``--access-logfile=-`` option.
Binding Externally
------------------
Gunicorn 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 Gunicorn.
You can bind to all external IPs on a non-privileged port using the
``-b 0.0.0.0`` option. Don't do this when using a reverse proxy setup,
otherwise it will be possible to bypass the proxy.
.. code-block:: text
$ gunicorn -w 4 -b 0.0.0.0 'hello:create_app()'
Listening at: http://0.0.0.0:8000 (x)
``0.0.0.0`` is not a valid address to navigate to, you'd use a specific
IP address in your browser.
Async with gevent
-----------------
The default sync worker is appropriate for most use cases. If you need numerous,
long running, concurrent connections, Gunicorn provides an asynchronous worker
using `gevent`_. This is not the same as Python's ``async/await``, or the ASGI
server spec. See :doc:`/gevent` for more information about enabling it in your
application.
.. _gevent: https://www.gevent.org/
When using gevent, greenlet>=1.0 is required. When using PyPy, PyPy>=7.3.7 is
required.
.. code-block:: text
$ gunicorn -k gevent 'hello:create_app()'
Starting gunicorn 20.1.0
Listening at: http://127.0.0.1:8000 (x)
Using worker: gevent
Booting worker with pid: x

View file

@ -1,78 +1,38 @@
Deploying to Production .. _deployment:
=======================
After developing your application, you'll want to make it available Deployment Options
publicly to other users. When you're developing locally, you're probably ==================
using the built-in development server, debugger, and reloader. These
should not be used in production. Instead, you should use a dedicated
WSGI server or hosting platform, some of which will be described here.
"Production" means "not development", which applies whether you're While lightweight and easy to use, **Flask's built-in server is not suitable
serving your application publicly to millions of users or privately / for production** as it doesn't scale well. Some of the options available for
locally to a single user. **Do not use the development server when properly running Flask in production are documented here.
deploying to production. It is intended for use only during local
development. It is not designed to be particularly secure, stable, or
efficient.**
Self-Hosted Options If you want to deploy your Flask application to a WSGI server not listed here,
look up the server documentation about how to use a WSGI app with it. Just
remember that your :class:`Flask` application object is the actual WSGI
application.
Hosted options
--------------
- `Deploying Flask on Heroku <https://devcenter.heroku.com/articles/getting-started-with-python>`_
- `Deploying Flask on OpenShift <https://developers.openshift.com/en/python-flask.html>`_
- `Deploying Flask on Webfaction <http://flask.pocoo.org/snippets/65/>`_
- `Deploying Flask on Google App Engine <https://cloud.google.com/appengine/docs/standard/python/getting-started/python-standard-env>`_
- `Deploying Flask on AWS Elastic Beanstalk <https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/create-deploy-python-flask.html>`_
- `Sharing your Localhost Server with Localtunnel <http://flask.pocoo.org/snippets/89/>`_
- `Deploying on Azure (IIS) <https://azure.microsoft.com/documentation/articles/web-sites-python-configure/>`_
- `Deploying on PythonAnywhere <https://help.pythonanywhere.com/pages/Flask/>`_
Self-hosted options
------------------- -------------------
Flask is a WSGI *application*. A WSGI *server* is used to run the
application, converting incoming HTTP requests to the standard WSGI
environ, and converting outgoing WSGI responses to HTTP responses.
The primary goal of these docs is to familiarize you with the concepts
involved in running a WSGI application using a production WSGI server
and HTTP server. There are many WSGI servers and HTTP servers, with many
configuration possibilities. The pages below discuss the most common
servers, and show the basics of running each one. The next section
discusses platforms that can manage this for you.
.. toctree:: .. toctree::
:maxdepth: 1 :maxdepth: 2
gunicorn wsgi-standalone
waitress uwsgi
mod_wsgi mod_wsgi
uwsgi fastcgi
gevent cgi
asgi
WSGI servers have HTTP servers built-in. However, a dedicated HTTP
server may be safer, more efficient, or more capable. Putting an HTTP
server in front of the WSGI server is called a "reverse proxy."
.. toctree::
:maxdepth: 1
proxy_fix
nginx
apache-httpd
This list is not exhaustive, and you should evaluate these and other
servers based on your application's needs. Different servers will have
different capabilities, configuration, and support.
Hosting Platforms
-----------------
There are many services available for hosting web applications without
needing to maintain your own server, networking, domain, etc. Some
services may have a free tier up to a certain time or bandwidth. Many of
these services use one of the WSGI servers described above, or a similar
interface. The links below are for some of the most common platforms,
which have instructions for Flask, WSGI, or Python.
- `PythonAnywhere <https://help.pythonanywhere.com/pages/Flask/>`_
- `Google App Engine <https://cloud.google.com/appengine/docs/standard/python3/building-app>`_
- `Google Cloud Run <https://cloud.google.com/run/docs/quickstarts/build-and-deploy/deploy-python-service>`_
- `AWS Elastic Beanstalk <https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/create-deploy-python-flask.html>`_
- `Microsoft Azure <https://docs.microsoft.com/en-us/azure/app-service/quickstart-python>`_
This list is not exhaustive, and you should evaluate these and other
services based on your application's needs. Different services will have
different capabilities, configuration, pricing, and support.
You'll probably need to :doc:`proxy_fix` when using most hosting
platforms.

View file

@ -1,94 +1,217 @@
mod_wsgi .. _mod_wsgi-deployment:
========
`mod_wsgi`_ is a WSGI server integrated with the `Apache httpd`_ server. mod_wsgi (Apache)
The modern `mod_wsgi-express`_ command makes it easy to configure and =================
start the server without needing to write Apache httpd configuration.
* Tightly integrated with Apache httpd. If you are using the `Apache`_ webserver, consider using `mod_wsgi`_.
* Supports Windows directly.
* Requires a compiler and the Apache development headers to install.
* Does not require a reverse proxy setup.
This page outlines the basics of running mod_wsgi-express, not the more .. admonition:: Watch Out
complex installation and configuration with httpd. Be sure to read the
`mod_wsgi-express`_, `mod_wsgi`_, and `Apache httpd`_ documentation to
understand what features are available.
.. _mod_wsgi-express: https://pypi.org/project/mod-wsgi/ Please make sure in advance that any ``app.run()`` calls you might
.. _mod_wsgi: https://modwsgi.readthedocs.io/ have in your application file are inside an ``if __name__ ==
.. _Apache httpd: https://httpd.apache.org/ '__main__':`` block or moved to a separate file. Just make sure it's
not called because this will always start a local WSGI server which
we do not want if we deploy that application to mod_wsgi.
.. _Apache: https://httpd.apache.org/
Installing Installing `mod_wsgi`
---------- ---------------------
Installing mod_wsgi requires a compiler and the Apache server and If you don't have `mod_wsgi` installed yet you have to either install it
development headers installed. You will get an error if they are not. using a package manager or compile it yourself. The mod_wsgi
How to install them depends on the OS and package manager that you use. `installation instructions`_ cover source installations on UNIX systems.
Create a virtualenv, install your application, then install If you are using Ubuntu/Debian you can apt-get it and activate it as
``mod_wsgi``. follows:
.. code-block:: text .. sourcecode:: text
$ cd hello-app # apt-get install libapache2-mod-wsgi
$ python -m venv .venv
$ . .venv/bin/activate
$ pip install . # install your application
$ pip install mod_wsgi
If you are using a yum based distribution (Fedora, OpenSUSE, etc..) you
can install it as follows:
Running .. sourcecode:: text
-------
The only argument to ``mod_wsgi-express`` specifies a script containing # yum install mod_wsgi
your Flask application, which must be called ``application``. You can
write a small script to import your app with this name, or to create it
if using the app factory pattern.
.. code-block:: python On FreeBSD install `mod_wsgi` by compiling the `www/mod_wsgi` port or by
:caption: ``wsgi.py`` using pkg_add:
from hello import app .. sourcecode:: text
application = app # pkg install ap22-mod_wsgi2
.. code-block:: python If you are using pkgsrc you can install `mod_wsgi` by compiling the
:caption: ``wsgi.py`` `www/ap2-wsgi` package.
from hello import create_app If you encounter segfaulting child processes after the first apache
reload you can safely ignore them. Just restart the server.
application = create_app() Creating a `.wsgi` file
-----------------------
Now run the ``mod_wsgi-express start-server`` command. To run your application you need a :file:`yourapplication.wsgi` file. This file
contains the code `mod_wsgi` is executing on startup to get the application
object. The object called `application` in that file is then used as
application.
.. code-block:: text For most applications the following file should be sufficient::
$ mod_wsgi-express start-server wsgi.py --processes 4 from yourapplication import app as application
The ``--processes`` option specifies the number of worker processes to If you don't have a factory function for application creation but a singleton
run; a starting value could be ``CPU * 2``. instance you can directly import that one as `application`.
Logs for each request aren't show in the terminal. If an error occurs, Store that file somewhere that you will find it again (e.g.:
its information is written to the error log file shown when starting the :file:`/var/www/yourapplication`) and make sure that `yourapplication` and all
server. the libraries that are in use are on the python load path. If you don't
want to install it system wide consider using a `virtual python`_
instance. Keep in mind that you will have to actually install your
application into the virtualenv as well. Alternatively there is the
option to just patch the path in the ``.wsgi`` file before the import::
import sys
sys.path.insert(0, '/path/to/the/application')
Binding Externally Configuring Apache
------------------ ------------------
Unlike the other WSGI servers in these docs, mod_wsgi can be run as The last thing you have to do is to create an Apache configuration file
root to bind to privileged ports like 80 and 443. However, it must be for your application. In this example we are telling `mod_wsgi` to
configured to drop permissions to a different user and group for the execute the application under a different user for security reasons:
worker processes.
For example, if you created a ``hello`` user and group, you should .. sourcecode:: apache
install your virtualenv and application as that user, then tell
mod_wsgi to drop to that user after starting.
.. code-block:: text <VirtualHost *>
ServerName example.com
$ sudo /home/hello/.venv/bin/mod_wsgi-express start-server \ WSGIDaemonProcess yourapplication user=user1 group=group1 threads=5
/home/hello/wsgi.py \ WSGIScriptAlias / /var/www/yourapplication/yourapplication.wsgi
--user hello --group hello --port 80 --processes 4
<Directory /var/www/yourapplication>
WSGIProcessGroup yourapplication
WSGIApplicationGroup %{GLOBAL}
Order deny,allow
Allow from all
</Directory>
</VirtualHost>
Note: WSGIDaemonProcess isn't implemented in Windows and Apache will
refuse to run with the above configuration. On a Windows system, eliminate those lines:
.. sourcecode:: apache
<VirtualHost *>
ServerName example.com
WSGIScriptAlias / C:\yourdir\yourapp.wsgi
<Directory C:\yourdir>
Order deny,allow
Allow from all
</Directory>
</VirtualHost>
Note: There have been some changes in access control configuration for `Apache 2.4`_.
.. _Apache 2.4: https://httpd.apache.org/docs/trunk/upgrading.html
Most notably, the syntax for directory permissions has changed from httpd 2.2
.. sourcecode:: apache
Order allow,deny
Allow from all
to httpd 2.4 syntax
.. sourcecode:: apache
Require all granted
For more information consult the `mod_wsgi documentation`_.
.. _mod_wsgi: https://github.com/GrahamDumpleton/mod_wsgi
.. _installation instructions: https://modwsgi.readthedocs.io/en/develop/installation.html
.. _virtual python: https://pypi.org/project/virtualenv/
.. _mod_wsgi documentation: https://modwsgi.readthedocs.io/en/develop/index.html
Troubleshooting
---------------
If your application does not run, follow this guide to troubleshoot:
**Problem:** application does not run, errorlog shows SystemExit ignored
You have an ``app.run()`` call in your application file that is not
guarded by an ``if __name__ == '__main__':`` condition. Either
remove that :meth:`~flask.Flask.run` call from the file and move it
into a separate :file:`run.py` file or put it into such an if block.
**Problem:** application gives permission errors
Probably caused by your application running as the wrong user. Make
sure the folders the application needs access to have the proper
privileges set and the application runs as the correct user
(``user`` and ``group`` parameter to the `WSGIDaemonProcess`
directive)
**Problem:** application dies with an error on print
Keep in mind that mod_wsgi disallows doing anything with
:data:`sys.stdout` and :data:`sys.stderr`. You can disable this
protection from the config by setting the `WSGIRestrictStdout` to
``off``:
.. sourcecode:: apache
WSGIRestrictStdout Off
Alternatively you can also replace the standard out in the .wsgi file
with a different stream::
import sys
sys.stdout = sys.stderr
**Problem:** accessing resources gives IO errors
Your application probably is a single .py file you symlinked into
the site-packages folder. Please be aware that this does not work,
instead you either have to put the folder into the pythonpath the
file is stored in, or convert your application into a package.
The reason for this is that for non-installed packages, the module
filename is used to locate the resources and for symlinks the wrong
filename is picked up.
Support for Automatic Reloading
-------------------------------
To help deployment tools you can activate support for automatic
reloading. Whenever something changes the ``.wsgi`` file, `mod_wsgi` will
reload all the daemon processes for us.
For that, just add the following directive to your `Directory` section:
.. sourcecode:: apache
WSGIScriptReloading On
Working with Virtual Environments
---------------------------------
Virtual environments have the advantage that they never install the
required dependencies system wide so you have a better control over what
is used where. If you want to use a virtual environment with mod_wsgi
you have to modify your ``.wsgi`` file slightly.
Add the following lines to the top of your ``.wsgi`` file::
activate_this = '/path/to/env/bin/activate_this.py'
execfile(activate_this, dict(__file__=activate_this))
For Python 3 add the following lines to the top of your ``.wsgi`` file::
activate_this = '/path/to/env/bin/activate_this.py'
with open(activate_this) as file_:
exec(file_.read(), dict(__file__=activate_this))
This sets up the load paths according to the settings of the virtual
environment. Keep in mind that the path has to be absolute.

View file

@ -1,69 +0,0 @@
nginx
=====
`nginx`_ is a fast, production level HTTP server. When serving your
application with one of the WSGI servers listed in :doc:`index`, it is
often good or necessary to put a dedicated HTTP server in front of it.
This "reverse proxy" can handle incoming requests, TLS, and other
security and performance concerns better than the WSGI server.
Nginx can be installed using your system package manager, or a pre-built
executable for Windows. Installing and running Nginx itself is outside
the scope of this doc. This page outlines the basics of configuring
Nginx to proxy your application. Be sure to read its documentation to
understand what features are available.
.. _nginx: https://nginx.org/
Domain Name
-----------
Acquiring and configuring a domain name is outside the scope of this
doc. In general, you will buy a domain name from a registrar, pay for
server space with a hosting provider, and then point your registrar
at the hosting provider's name servers.
To simulate this, you can also edit your ``hosts`` file, located at
``/etc/hosts`` on Linux. Add a line that associates a name with the
local IP.
Modern Linux systems may be configured to treat any domain name that
ends with ``.localhost`` like this without adding it to the ``hosts``
file.
.. code-block:: python
:caption: ``/etc/hosts``
127.0.0.1 hello.localhost
Configuration
-------------
The nginx configuration is located at ``/etc/nginx/nginx.conf`` on
Linux. It may be different depending on your operating system. Check the
docs and look for ``nginx.conf``.
Remove or comment out any existing ``server`` section. Add a ``server``
section and use the ``proxy_pass`` directive to point to the address the
WSGI server is listening on. We'll assume the WSGI server is listening
locally at ``http://127.0.0.1:8000``.
.. code-block:: nginx
:caption: ``/etc/nginx.conf``
server {
listen 80;
server_name _;
location / {
proxy_pass http://127.0.0.1:8000/;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Prefix /;
}
}
Then :doc:`proxy_fix` so that your application uses these headers.

View file

@ -1,33 +0,0 @@
Tell Flask it is Behind a Proxy
===============================
When using a reverse proxy, or many Python hosting platforms, the proxy
will intercept and forward all external requests to the local WSGI
server.
From the WSGI server and Flask application's perspectives, requests are
now coming from the HTTP server to the local address, rather than from
the remote address to the external server address.
HTTP servers should set ``X-Forwarded-`` headers to pass on the real
values to the application. The application can then be told to trust and
use those values by wrapping it with the
:doc:`werkzeug:middleware/proxy_fix` middleware provided by Werkzeug.
This middleware should only be used if the application is actually
behind a proxy, and should be configured with the number of proxies that
are chained in front of it. Not all proxies set all the headers. Since
incoming headers can be faked, you must set how many proxies are setting
each header so the middleware knows what to trust.
.. code-block:: python
from werkzeug.middleware.proxy_fix import ProxyFix
app.wsgi_app = ProxyFix(
app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_prefix=1
)
Remember, only apply this middleware if you are behind a proxy, and set
the correct number of proxies that set each header. It can be a security
issue if you get this configuration wrong.

View file

@ -1,143 +1,72 @@
.. _deploying-uwsgi:
uWSGI uWSGI
===== =====
`uWSGI`_ is a fast, compiled server suite with extensive configuration uWSGI is a deployment option on servers like `nginx`_, `lighttpd`_, and
and capabilities beyond a basic server. `cherokee`_; see :ref:`deploying-fastcgi` and :ref:`deploying-wsgi-standalone`
for other options. To use your WSGI application with uWSGI protocol you will
need a uWSGI server first. uWSGI is both a protocol and an application server;
the application server can serve uWSGI, FastCGI, and HTTP protocols.
* It can be very performant due to being a compiled program. The most popular uWSGI server is `uwsgi`_, which we will use for this
* It is complex to configure beyond the basic application, and has so guide. Make sure to have it installed to follow along.
many options that it can be difficult for beginners to understand.
* It does not support Windows (but does run on WSL).
* It requires a compiler to install in some cases.
This page outlines the basics of running uWSGI. Be sure to read its .. admonition:: Watch Out
documentation to understand what features are available.
.. _uWSGI: https://uwsgi-docs.readthedocs.io/en/latest/ Please make sure in advance that any ``app.run()`` calls you might
have in your application file are inside an ``if __name__ ==
'__main__':`` block or moved to a separate file. Just make sure it's
not called because this will always start a local WSGI server which
we do not want if we deploy that application to uWSGI.
Starting your app with uwsgi
----------------------------
Installing `uwsgi` is designed to operate on WSGI callables found in python modules.
----------
uWSGI has multiple ways to install it. The most straightforward is to Given a flask application in myapp.py, use the following command:
install the ``pyuwsgi`` package, which provides precompiled wheels for
common platforms. However, it does not provide SSL support, which can be
provided with a reverse proxy instead.
Create a virtualenv, install your application, then install ``pyuwsgi``. .. sourcecode:: text
.. code-block:: text $ uwsgi -s /tmp/yourapplication.sock --manage-script-name --mount /yourapplication=myapp:app
$ cd hello-app The ``--manage-script-name`` will move the handling of ``SCRIPT_NAME`` to uwsgi,
$ python -m venv .venv since its smarter about that. It is used together with the ``--mount`` directive
$ . .venv/bin/activate which will make requests to ``/yourapplication`` be directed to ``myapp:app``.
$ pip install . # install your application If your application is accessible at root level, you can use a single ``/``
$ pip install pyuwsgi instead of ``/yourapplication``. ``myapp`` refers to the name of the file of
your flask application (without extension) or the module which provides ``app``.
``app`` is the callable inside of your application (usually the line reads
``app = Flask(__name__)``.
If you have a compiler available, you can install the ``uwsgi`` package If you want to deploy your flask application inside of a virtual environment,
instead. Or install the ``pyuwsgi`` package from sdist instead of wheel. you need to also add ``--virtualenv /path/to/virtual/environment``. You might
Either method will include SSL support. also need to add ``--plugin python`` or ``--plugin python3`` depending on which
python version you use for your project.
.. code-block:: text Configuring nginx
$ pip install uwsgi
# or
$ pip install --no-binary pyuwsgi pyuwsgi
Running
-------
The most basic way to run uWSGI is to tell it to start an HTTP server
and import your application.
.. code-block:: text
$ uwsgi --http 127.0.0.1:8000 --master -p 4 -w hello:app
*** Starting uWSGI 2.0.20 (64bit) on [x] ***
*** Operational MODE: preforking ***
mounting hello:app on /
spawned uWSGI master process (pid: x)
spawned uWSGI worker 1 (pid: x, cores: 1)
spawned uWSGI worker 2 (pid: x, cores: 1)
spawned uWSGI worker 3 (pid: x, cores: 1)
spawned uWSGI worker 4 (pid: x, cores: 1)
spawned uWSGI http 1 (pid: x)
If you're using the app factory pattern, you'll need to create a small
Python file to create the app, then point uWSGI at that.
.. code-block:: python
:caption: ``wsgi.py``
from hello import create_app
app = create_app()
.. code-block:: text
$ uwsgi --http 127.0.0.1:8000 --master -p 4 -w wsgi:app
The ``--http`` option starts an HTTP server at 127.0.0.1 port 8000. The
``--master`` option specifies the standard worker manager. The ``-p``
option starts 4 worker processes; a starting value could be ``CPU * 2``.
The ``-w`` option tells uWSGI how to import your application
Binding Externally
------------------
uWSGI should not be run as root with the configuration shown in this doc
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 uWSGI. It is possible to
run uWSGI as root securely, but that is beyond the scope of this doc.
uWSGI has optimized integration with `Nginx uWSGI`_ and
`Apache mod_proxy_uwsgi`_, and possibly other servers, instead of using
a standard HTTP proxy. That configuration is beyond the scope of this
doc, see the links for more information.
.. _Nginx uWSGI: https://uwsgi-docs.readthedocs.io/en/latest/Nginx.html
.. _Apache mod_proxy_uwsgi: https://uwsgi-docs.readthedocs.io/en/latest/Apache.html#mod-proxy-uwsgi
You can bind to all external IPs on a non-privileged port using the
``--http 0.0.0.0:8000`` option. Don't do this when using a reverse proxy
setup, otherwise it will be possible to bypass the proxy.
.. code-block:: text
$ uwsgi --http 0.0.0.0:8000 --master -p 4 -w wsgi:app
``0.0.0.0`` is not a valid address to navigate to, you'd use a specific
IP address in your browser.
Async with gevent
----------------- -----------------
The default sync worker is appropriate for most use cases. If you need numerous, A basic flask nginx configuration looks like this::
long running, concurrent connections, uWSGI provides an asynchronous worker
using `gevent`_. This is not the same as Python's ``async/await``, or the ASGI
server spec. See :doc:`/gevent` for more information about enabling it in your
application.
.. _gevent: https://www.gevent.org/ location = /yourapplication { rewrite ^ /yourapplication/; }
location /yourapplication { try_files $uri @yourapplication; }
location @yourapplication {
include uwsgi_params;
uwsgi_pass unix:/tmp/yourapplication.sock;
}
When using gevent, greenlet>=1.0 is required. When using PyPy, PyPy>=7.3.7 is This configuration binds the application to ``/yourapplication``. If you want
required. to have it in the URL root its a bit simpler::
.. code-block:: text location / { try_files $uri @yourapplication; }
location @yourapplication {
include uwsgi_params;
uwsgi_pass unix:/tmp/yourapplication.sock;
}
$ uwsgi --http 127.0.0.1:8000 --master --gevent 100 -w wsgi:app .. _nginx: https://nginx.org/
.. _lighttpd: https://www.lighttpd.net/
*** Starting uWSGI 2.0.20 (64bit) on [x] *** .. _cherokee: http://cherokee-project.com/
*** Operational MODE: async *** .. _uwsgi: http://projects.unbit.it/uwsgi/
mounting hello:app on /
spawned uWSGI master process (pid: x)
spawned uWSGI worker 1 (pid: x, cores: 100)
spawned uWSGI http 1 (pid: x)
*** running gevent loop engine [addr:x] ***

View file

@ -1,75 +0,0 @@
Waitress
========
`Waitress`_ is a pure Python WSGI server.
* It is easy to configure.
* It supports Windows directly.
* It is easy to install as it does not require additional dependencies
or compilation.
* It does not support streaming requests, full request data is always
buffered.
* It uses a single process with multiple thread workers.
This page outlines the basics of running Waitress. Be sure to read its
documentation and ``waitress-serve --help`` to understand what features
are available.
.. _Waitress: https://docs.pylonsproject.org/projects/waitress/
Installing
----------
Create a virtualenv, install your application, then install
``waitress``.
.. code-block:: text
$ cd hello-app
$ python -m venv .venv
$ . .venv/bin/activate
$ pip install . # install your application
$ pip install waitress
Running
-------
The only required argument to ``waitress-serve`` tells it how to load
your Flask application. The syntax is ``{module}:{app}``. ``module`` is
the dotted import name to the module with your application. ``app`` is
the variable with the application. If you're using the app factory
pattern, use ``--call {module}:{factory}`` instead.
.. code-block:: text
# equivalent to 'from hello import app'
$ waitress-serve --host 127.0.0.1 hello:app
# equivalent to 'from hello import create_app; create_app()'
$ waitress-serve --host 127.0.0.1 --call hello:create_app
Serving on http://127.0.0.1:8080
The ``--host`` option binds the server to local ``127.0.0.1`` only.
Logs for each request aren't shown, only errors are shown. Logging can
be configured through the Python interface instead of the command line.
Binding Externally
------------------
Waitress 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 Waitress.
You can bind to all external IPs on a non-privileged port by not
specifying the ``--host`` option. Don't do this when using a reverse
proxy setup, otherwise it will be possible to bypass the proxy.
``0.0.0.0`` is not a valid address to navigate to, you'd use a specific
IP address in your browser.

View file

@ -0,0 +1,150 @@
.. _deploying-wsgi-standalone:
Standalone WSGI Containers
==========================
There are popular servers written in Python that contain WSGI applications and
serve HTTP. These servers stand alone when they run; you can proxy to them
from your web server. Note the section on :ref:`deploying-proxy-setups` if you
run into issues.
Gunicorn
--------
`Gunicorn`_ 'Green Unicorn' is a WSGI HTTP Server for UNIX. It's a pre-fork
worker model ported from Ruby's Unicorn project. It supports both `eventlet`_
and `greenlet`_. Running a Flask application on this server is quite simple::
gunicorn myproject:app
`Gunicorn`_ provides many command-line options -- see ``gunicorn -h``.
For example, to run a Flask application with 4 worker processes (``-w
4``) binding to localhost port 4000 (``-b 127.0.0.1:4000``)::
gunicorn -w 4 -b 127.0.0.1:4000 myproject:app
.. _Gunicorn: http://gunicorn.org/
.. _eventlet: http://eventlet.net/
.. _greenlet: https://greenlet.readthedocs.io/en/latest/
uWSGI
--------
`uWSGI`_ is a fast application server written in C. It is very configurable
which makes it more complicated to setup than gunicorn.
Running `uWSGI HTTP Router`_::
uwsgi --http 127.0.0.1:5000 --module myproject:app
For a more optimized setup, see `configuring uWSGI and NGINX`_.
.. _uWSGI: http://uwsgi-docs.readthedocs.io/en/latest/
.. _uWSGI HTTP Router: http://uwsgi-docs.readthedocs.io/en/latest/HTTP.html#the-uwsgi-http-https-router
.. _configuring uWSGI and NGINX: uwsgi.html#starting-your-app-with-uwsgi
Gevent
-------
`Gevent`_ is a coroutine-based Python networking library that uses
`greenlet`_ to provide a high-level synchronous API on top of `libev`_
event loop::
from gevent.wsgi import WSGIServer
from yourapplication import app
http_server = WSGIServer(('', 5000), app)
http_server.serve_forever()
.. _Gevent: http://www.gevent.org/
.. _greenlet: https://greenlet.readthedocs.io/en/latest/
.. _libev: http://software.schmorp.de/pkg/libev.html
Twisted Web
-----------
`Twisted Web`_ is the web server shipped with `Twisted`_, a mature,
non-blocking event-driven networking library. Twisted Web comes with a
standard WSGI container which can be controlled from the command line using
the ``twistd`` utility::
twistd web --wsgi myproject.app
This example will run a Flask application called ``app`` from a module named
``myproject``.
Twisted Web supports many flags and options, and the ``twistd`` utility does
as well; see ``twistd -h`` and ``twistd web -h`` for more information. For
example, to run a Twisted Web server in the foreground, on port 8080, with an
application from ``myproject``::
twistd -n web --port tcp:8080 --wsgi myproject.app
.. _Twisted: https://twistedmatrix.com/
.. _Twisted Web: https://twistedmatrix.com/trac/wiki/TwistedWeb
.. _deploying-proxy-setups:
Proxy Setups
------------
If you deploy your application using one of these servers behind an HTTP proxy
you will need to rewrite a few headers in order for the application to work.
The two problematic values in the WSGI environment usually are ``REMOTE_ADDR``
and ``HTTP_HOST``. You can configure your httpd to pass these headers, or you
can fix them in middleware. Werkzeug ships a fixer that will solve some common
setups, but you might want to write your own WSGI middleware for specific
setups.
Here's a simple nginx configuration which proxies to an application served on
localhost at port 8000, setting appropriate headers:
.. sourcecode:: nginx
server {
listen 80;
server_name _;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
location / {
proxy_pass http://127.0.0.1:8000/;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
If your httpd is not providing these headers, the most common setup invokes the
host being set from ``X-Forwarded-Host`` and the remote address from
``X-Forwarded-For``::
from werkzeug.contrib.fixers import ProxyFix
app.wsgi_app = ProxyFix(app.wsgi_app)
.. admonition:: Trusting Headers
Please keep in mind that it is a security issue to use such a middleware in
a non-proxy setup because it will blindly trust the incoming headers which
might be forged by malicious clients.
If you want to rewrite the headers from another header, you might want to
use a fixer like this::
class CustomProxyFix(object):
def __init__(self, app):
self.app = app
def __call__(self, environ, start_response):
host = environ.get('HTTP_X_FHOST', '')
if host:
environ['HTTP_HOST'] = host
return self.app(environ, start_response)
app.wsgi_app = CustomProxyFix(app.wsgi_app)

View file

@ -1,3 +1,5 @@
.. _design:
Design Decisions in Flask Design Decisions in Flask
========================= =========================
@ -39,7 +41,7 @@ the time. There are ways to fake multiple applications with a single
application object, like maintaining a stack of applications, but this application object, like maintaining a stack of applications, but this
causes some problems I won't outline here in detail. Now the question is: causes some problems I won't outline here in detail. Now the question is:
when does a microframework need more than one application at the same when does a microframework need more than one application at the same
time? A good example for this is unit testing. When you want to test time? A good example for this is unittesting. When you want to test
something it can be very helpful to create a minimal application to test something it can be very helpful to create a minimal application to test
specific behavior. When the application object is deleted everything it specific behavior. When the application object is deleted everything it
allocated will be freed again. allocated will be freed again.
@ -74,8 +76,8 @@ there are better ways to do that so that you do not lose the reference
to the application object :meth:`~flask.Flask.wsgi_app`). to the application object :meth:`~flask.Flask.wsgi_app`).
Furthermore this design makes it possible to use a factory function to Furthermore this design makes it possible to use a factory function to
create the application which is very helpful for unit testing and similar create the application which is very helpful for unittesting and similar
things (:doc:`/patterns/appfactories`). things (:ref:`app-factories`).
The Routing System The Routing System
------------------ ------------------
@ -96,10 +98,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,19 +109,19 @@ 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
support for reusable blocks (macros) that can be used from inside for reusable blocks (macros) that can be used from inside templates and
templates and also from Python code, supports iterative template also from Python code, uses Unicode for all operations, supports
rendering, configurable syntax and more. On the other hand an engine iterative template rendering, configurable syntax and more. On the other
like Genshi is based on XML stream evaluation, template inheritance by hand an engine like Genshi is based on XML stream evaluation, template
taking the availability of XPath into account and more. Mako on the inheritance by taking the availability of XPath into account and more.
other hand treats templates similar to Python modules. Mako on the 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
@ -130,27 +132,11 @@ being present. You can easily use your own templating language, but an
extension could still depend on Jinja itself. extension could still depend on Jinja itself.
What does "micro" mean? Micro with Dependencies
----------------------- -----------------------
“Micro” does not mean that your whole web application has to fit into a single
Python file (although it certainly can), nor does it mean that Flask is lacking
in functionality. The "micro" in microframework means Flask aims to keep the
core simple but extensible. Flask won't make many decisions for you, such as
what database to use. Those decisions that it does make, such as what
templating engine to use, are easy to change. Everything else is up to you, so
that Flask can be everything you need and nothing you don't.
By default, Flask does not include a database abstraction layer, form
validation or anything else where different libraries already exist that can
handle that. Instead, Flask supports extensions to add such functionality to
your application as if it was implemented in Flask itself. Numerous extensions
provide database integration, form validation, upload handling, various open
authentication technologies, and more. Flask may be "micro", but it's ready for
production use on a variety of needs.
Why does Flask call itself a microframework and yet it depends on two 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,39 +155,22 @@ 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.
Also see the :ref:`becomingbig` section of the documentation for some
Async/await and ASGI support inspiration for larger applications based on Flask.
----------------------------
Flask supports ``async`` coroutines for view functions by executing the
coroutine on a separate thread instead of using an event loop on the
main thread as an async-first (ASGI) framework would. This is necessary
for Flask to remain backwards compatible with extensions and code built
before ``async`` was introduced into Python. This compromise introduces
a performance cost compared with the ASGI frameworks, due to the
overhead of the threads.
Due to how tied to WSGI Flask's code is, it's not clear if it's possible
to make the ``Flask`` class support ASGI and WSGI at the same time. Work
is currently being done in Werkzeug to work with ASGI, which may
eventually enable support in Flask as well.
See :doc:`/async-await` for more discussion.
What Flask is, What Flask is Not What Flask is, What Flask is Not
@ -209,7 +178,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.
@ -218,12 +187,5 @@ requirements and Flask could not meet those if it would force any of this
into the core. The majority of web applications will need a template into the core. The majority of web applications will need a template
engine in some sort. However not every application needs a SQL database. engine in some sort. However not every application needs a SQL database.
As your codebase grows, you are free to make the design decisions appropriate
for your project. Flask will continue to provide a very simple glue layer to
the best that Python has to offer. You can implement advanced patterns in
SQLAlchemy or another database tool, introduce non-relational data persistence
as appropriate, and take advantage of framework-agnostic tools built for WSGI,
the Python web interface.
The idea of Flask is to build a good foundation for all applications. The idea of Flask is to build a good foundation for all applications.
Everything else is up to you or extensions. Everything else is up to you or extensions.

View file

@ -1,10 +1,14 @@
Handling Application Errors .. _application-errors:
===========================
Applications fail, servers fail. Sooner or later you will see an exception Application Errors
in production. Even if your code is 100% correct, you will still see ==================
exceptions from time to time. Why? Because everything else involved will
fail. Here are some situations where perfectly fine code can lead to server .. versionadded:: 0.3
Applications fail, servers fail. Sooner or later you will see an exception
in production. Even if your code is 100% correct, you will still see
exceptions from time to time. Why? Because everything else involved will
fail. Here are some situations where perfectly fine code can lead to server
errors: errors:
- the client terminated the request early and the application was still - the client terminated the request early and the application was still
@ -16,16 +20,13 @@ errors:
- a programming error in a library you are using - a programming error in a library you are using
- network connection of the server to another system failed - network connection of the server to another system failed
And that's just a small sample of issues you could be facing. So how do we And that's just a small sample of issues you could be facing. So how do we
deal with that sort of problem? By default if your application runs in deal with that sort of problem? By default if your application runs in
production mode, and an exception is raised Flask will display a very simple production mode, Flask will display a very simple page for you and log the
page for you and log the exception to the :attr:`~flask.Flask.logger`. exception to the :attr:`~flask.Flask.logger`.
But there is more you can do, and we will cover some better setups to deal But there is more you can do, and we will cover some better setups to deal
with errors including custom exceptions and 3rd party tools. with errors.
.. _error-logging-tools:
Error Logging Tools Error Logging Tools
------------------- -------------------
@ -33,66 +34,51 @@ Error Logging Tools
Sending error mails, even if just for critical ones, can become Sending error mails, even if just for critical ones, can become
overwhelming if enough users are hitting the error and log files are overwhelming if enough users are hitting the error and log files are
typically never looked at. This is why we recommend using `Sentry typically never looked at. This is why we recommend using `Sentry
<https://sentry.io/>`_ for dealing with application errors. It's <https://www.getsentry.com/>`_ for dealing with application errors. It's
available as a source-available project `on GitHub available as an Open Source project `on GitHub
<https://github.com/getsentry/sentry>`_ and is also available as a `hosted version <https://github.com/getsentry/sentry>`__ and is also available as a `hosted version
<https://sentry.io/signup/>`_ which you can try for free. Sentry <https://getsentry.com/signup/>`_ which you can try for free. Sentry
aggregates duplicate errors, captures the full stack trace and local aggregates duplicate errors, captures the full stack trace and local
variables for debugging, and sends you mails based on new errors or variables for debugging, and sends you mails based on new errors or
frequency thresholds. frequency thresholds.
To use Sentry you need to install the ``sentry-sdk`` client with extra To use Sentry you need to install the `raven` client with extra `flask` dependencies::
``flask`` dependencies.
.. code-block:: text $ pip install raven[flask]
$ pip install sentry-sdk[flask] And then add this to your Flask app::
And then add this to your Flask app: from raven.contrib.flask import Sentry
sentry = Sentry(app, dsn='YOUR_DSN_HERE')
.. code-block:: python Or if you are using factories you can also init it later::
import sentry_sdk from raven.contrib.flask import Sentry
from sentry_sdk.integrations.flask import FlaskIntegration sentry = Sentry(dsn='YOUR_DSN_HERE')
sentry_sdk.init('YOUR_DSN_HERE', integrations=[FlaskIntegration()]) def create_app():
app = Flask(__name__)
sentry.init_app(app)
...
return app
The ``YOUR_DSN_HERE`` value needs to be replaced with the DSN value you The `YOUR_DSN_HERE` value needs to be replaced with the DSN value you get
get from your Sentry installation. from your Sentry installation.
After installation, failures leading to an Internal Server Error Afterwards failures are automatically reported to Sentry and from there
are automatically reported to Sentry and from there you can you can receive error notifications.
receive error notifications.
See also: .. _error-handlers:
- Sentry also supports catching errors from a worker queue Error handlers
(RQ, Celery, etc.) in a similar fashion. See the `Python SDK docs
<https://docs.sentry.io/platforms/python/>`__ for more information.
- `Flask-specific documentation <https://docs.sentry.io/platforms/python/guides/flask/>`__
Error Handlers
-------------- --------------
When an error occurs in Flask, an appropriate `HTTP status code
<https://developer.mozilla.org/en-US/docs/Web/HTTP/Status>`__ will be
returned. 400-499 indicate errors with the client's request data, or
about the data requested. 500-599 indicate errors with the server or
application itself.
You might want to show custom error pages to the user when an error occurs. You might want to show custom error pages to the user when an error occurs.
This can be done by registering error handlers. This can be done by registering error handlers.
An error handler is a function that returns a response when a type of error is An error handler is a normal view function that return a response, but instead
raised, similar to how a view is a function that returns a response when a of being registered for a route, it is registered for an exception or HTTP
request URL is matched. It is passed the instance of the error being handled, status code that would is raised while trying to handle a request.
which is most likely a :exc:`~werkzeug.exceptions.HTTPException`.
The status code of the response will not be set to the handler's code. Make
sure to provide the appropriate HTTP status code when returning a response from
a handler.
Registering Registering
``````````` ```````````
@ -100,9 +86,7 @@ Registering
Register handlers by decorating a function with Register handlers by decorating a function with
:meth:`~flask.Flask.errorhandler`. Or use :meth:`~flask.Flask.errorhandler`. Or use
:meth:`~flask.Flask.register_error_handler` to register the function later. :meth:`~flask.Flask.register_error_handler` to register the function later.
Remember to set the error code when returning the response. Remember to set the error code when returning the response. ::
.. code-block:: python
@app.errorhandler(werkzeug.exceptions.BadRequest) @app.errorhandler(werkzeug.exceptions.BadRequest)
def handle_bad_request(e): def handle_bad_request(e):
@ -118,15 +102,13 @@ when registering handlers. (``BadRequest.code == 400``)
Non-standard HTTP codes cannot be registered by code because they are not known Non-standard HTTP codes cannot be registered by code because they are not known
by Werkzeug. Instead, define a subclass of by Werkzeug. Instead, define a subclass of
:class:`~werkzeug.exceptions.HTTPException` with the appropriate code and :class:`~werkzeug.exceptions.HTTPException` with the appropriate code and
register and raise that exception class. register and raise that exception class. ::
.. code-block:: python
class InsufficientStorage(werkzeug.exceptions.HTTPException): class InsufficientStorage(werkzeug.exceptions.HTTPException):
code = 507 code = 507
description = 'Not enough storage space.' description = 'Not enough storage space.'
app.register_error_handler(InsufficientStorage, handle_507) app.register_error_handler(InsuffcientStorage, handle_507)
raise InsufficientStorage() raise InsufficientStorage()
@ -135,36 +117,20 @@ Handlers can be registered for any exception class, not just
codes. Handlers can be registered for a specific class, or for all subclasses codes. Handlers can be registered for a specific class, or for all subclasses
of a parent class. of a parent class.
Handling Handling
```````` ````````
When building a Flask application you *will* run into exceptions. If some part When an exception is caught by Flask while handling a request, it is first
of your code breaks while handling a request (and you have no error handlers looked up by code. If no handler is registered for the code, it is looked up
registered), a "500 Internal Server Error" by its class hierarchy; the most specific handler is chosen. If no handler is
(:exc:`~werkzeug.exceptions.InternalServerError`) will be returned by default. registered, :class:`~werkzeug.exceptions.HTTPException` subclasses show a
Similarly, "404 Not Found"
(:exc:`~werkzeug.exceptions.NotFound`) error will occur if a request is sent to an unregistered route.
If a route receives an unallowed request method, a "405 Method Not Allowed"
(:exc:`~werkzeug.exceptions.MethodNotAllowed`) will be raised. These are all
subclasses of :class:`~werkzeug.exceptions.HTTPException` and are provided by
default in Flask.
Flask gives you the ability to raise any HTTP exception registered by
Werkzeug. However, the default HTTP exceptions return simple exception
pages. You might want to show custom error pages to the user when an error occurs.
This can be done by registering error handlers.
When Flask catches an exception while handling a request, it is first looked up by code.
If no handler is registered for the code, Flask looks up the error by its class hierarchy; the most specific handler is chosen.
If no handler is registered, :class:`~werkzeug.exceptions.HTTPException` subclasses show a
generic message about their code, while other exceptions are converted to a generic message about their code, while other exceptions are converted to a
generic "500 Internal Server Error". generic 500 Internal Server Error.
For example, if an instance of :exc:`ConnectionRefusedError` is raised, For example, if an instance of :exc:`ConnectionRefusedError` is raised, and a handler
and a handler is registered for :exc:`ConnectionError` and is registered for :exc:`ConnectionError` and :exc:`ConnectionRefusedError`,
:exc:`ConnectionRefusedError`, the more specific :exc:`ConnectionRefusedError` the more specific :exc:`ConnectionRefusedError` handler is called with the
handler is called with the exception instance to generate the response. exception instance to generate the response.
Handlers registered on the blueprint take precedence over those registered Handlers registered on the blueprint take precedence over those registered
globally on the application, assuming a blueprint is handling the request that globally on the application, assuming a blueprint is handling the request that
@ -172,352 +138,78 @@ raises the exception. However, the blueprint cannot handle 404 routing errors
because the 404 occurs at the routing level before the blueprint can be because the 404 occurs at the routing level before the blueprint can be
determined. determined.
.. versionchanged:: 0.11
Generic Exception Handlers Handlers are prioritized by specificity of the exception classes they are
`````````````````````````` registered for instead of the order they are registered in.
It is possible to register error handlers for very generic base classes
such as ``HTTPException`` or even ``Exception``. However, be aware that
these will catch more than you might expect.
For example, an error handler for ``HTTPException`` might be useful for turning
the default HTML errors pages into JSON. However, this
handler will trigger for things you don't cause directly, such as 404
and 405 errors during routing. Be sure to craft your handler carefully
so you don't lose information about the HTTP error.
.. code-block:: python
from flask import json
from werkzeug.exceptions import HTTPException
@app.errorhandler(HTTPException)
def handle_exception(e):
"""Return JSON instead of HTML for HTTP errors."""
# start with the correct headers and status code from the error
response = e.get_response()
# replace the body with JSON
response.data = json.dumps({
"code": e.code,
"name": e.name,
"description": e.description,
})
response.content_type = "application/json"
return response
An error handler for ``Exception`` might seem useful for changing how
all errors, even unhandled ones, are presented to the user. However,
this is similar to doing ``except Exception:`` in Python, it will
capture *all* otherwise unhandled errors, including all HTTP status
codes.
In most cases it will be safer to register handlers for more
specific exceptions. Since ``HTTPException`` instances are valid WSGI
responses, you could also pass them through directly.
.. code-block:: python
from werkzeug.exceptions import HTTPException
@app.errorhandler(Exception)
def handle_exception(e):
# pass through HTTP errors
if isinstance(e, HTTPException):
return e
# now you're handling non-HTTP exceptions only
return render_template("500_generic.html", e=e), 500
Error handlers still respect the exception class hierarchy. If you
register handlers for both ``HTTPException`` and ``Exception``, the
``Exception`` handler will not handle ``HTTPException`` subclasses
because the ``HTTPException`` handler is more specific.
Unhandled Exceptions
````````````````````
When there is no error handler registered for an exception, a 500
Internal Server Error will be returned instead. See
:meth:`flask.Flask.handle_exception` for information about this
behavior.
If there is an error handler registered for ``InternalServerError``,
this will be invoked. As of Flask 1.1.0, this error handler will always
be passed an instance of ``InternalServerError``, not the original
unhandled error.
The original error is available as ``e.original_exception``.
An error handler for "500 Internal Server Error" will be passed uncaught
exceptions in addition to explicit 500 errors. In debug mode, a handler
for "500 Internal Server Error" will not be used. Instead, the
interactive debugger will be shown.
Custom Error Pages
------------------
Sometimes when building a Flask application, you might want to raise a
:exc:`~werkzeug.exceptions.HTTPException` to signal to the user that
something is wrong with the request. Fortunately, Flask comes with a handy
:func:`~flask.abort` function that aborts a request with a HTTP error from
werkzeug as desired. It will also provide a plain black and white error page
for you with a basic description, but nothing fancy.
Depending on the error code it is less or more likely for the user to
actually see such an error.
Consider the code below, we might have a user profile route, and if the user
fails to pass a username we can raise a "400 Bad Request". If the user passes a
username and we can't find it, we raise a "404 Not Found".
.. code-block:: python
from flask import abort, render_template, request
# a username needs to be supplied in the query args
# a successful request would be like /profile?username=jack
@app.route("/profile")
def user_profile():
username = request.arg.get("username")
# if a username isn't supplied in the request, return a 400 bad request
if username is None:
abort(400)
user = get_user(username=username)
# if a user can't be found by their username, return 404 not found
if user is None:
abort(404)
return render_template("profile.html", user=user)
Here is another example implementation for a "404 Page Not Found" exception:
.. code-block:: python
from flask import render_template
@app.errorhandler(404)
def page_not_found(e):
# note that we set the 404 status explicitly
return render_template('404.html'), 404
When using :doc:`/patterns/appfactories`:
.. code-block:: python
from flask import Flask, render_template
def page_not_found(e):
return render_template('404.html'), 404
def create_app(config_filename):
app = Flask(__name__)
app.register_error_handler(404, page_not_found)
return app
An example template might be this:
.. code-block:: html+jinja
{% extends "layout.html" %}
{% block title %}Page Not Found{% endblock %}
{% block body %}
<h1>Page Not Found</h1>
<p>What you were looking for is just not there.
<p><a href="{{ url_for('index') }}">go somewhere nice</a>
{% endblock %}
Further Examples
````````````````
The above examples wouldn't actually be an improvement on the default
exception pages. We can create a custom 500.html template like this:
.. code-block:: html+jinja
{% extends "layout.html" %}
{% block title %}Internal Server Error{% endblock %}
{% block body %}
<h1>Internal Server Error</h1>
<p>Oops... we seem to have made a mistake, sorry!</p>
<p><a href="{{ url_for('index') }}">Go somewhere nice instead</a>
{% endblock %}
It can be implemented by rendering the template on "500 Internal Server Error":
.. code-block:: python
from flask import render_template
@app.errorhandler(500)
def internal_server_error(e):
# note that we set the 500 status explicitly
return render_template('500.html'), 500
When using :doc:`/patterns/appfactories`:
.. code-block:: python
from flask import Flask, render_template
def internal_server_error(e):
return render_template('500.html'), 500
def create_app():
app = Flask(__name__)
app.register_error_handler(500, internal_server_error)
return app
When using :doc:`/blueprints`:
.. code-block:: python
from flask import Blueprint
blog = Blueprint('blog', __name__)
# as a decorator
@blog.errorhandler(500)
def internal_server_error(e):
return render_template('500.html'), 500
# or with register_error_handler
blog.register_error_handler(500, internal_server_error)
Blueprint Error Handlers
------------------------
In :doc:`/blueprints`, most error handlers will work as expected.
However, there is a caveat concerning handlers for 404 and 405
exceptions. These error handlers are only invoked from an appropriate
``raise`` statement or a call to ``abort`` in another of the blueprint's
view functions; they are not invoked by, e.g., an invalid URL access.
This is because the blueprint does not "own" a certain URL space, so
the application instance has no way of knowing which blueprint error
handler it should run if given an invalid URL. If you would like to
execute different handling strategies for these errors based on URL
prefixes, they may be defined at the application level using the
``request`` proxy object.
.. code-block:: python
from flask import jsonify, render_template
# at the application level
# not the blueprint level
@app.errorhandler(404)
def page_not_found(e):
# if a request is in our blog URL space
if request.path.startswith('/blog/'):
# we return a custom blog 404 page
return render_template("blog/404.html"), 404
else:
# otherwise we return our generic site-wide 404 page
return render_template("404.html"), 404
@app.errorhandler(405)
def method_not_allowed(e):
# if a request has the wrong method to our API
if request.path.startswith('/api/'):
# we return a json saying so
return jsonify(message="Method Not Allowed"), 405
else:
# otherwise we return a generic site-wide 405 page
return render_template("405.html"), 405
Returning API Errors as JSON
----------------------------
When building APIs in Flask, some developers realise that the built-in
exceptions are not expressive enough for APIs and that the content type of
:mimetype:`text/html` they are emitting is not very useful for API consumers.
Using the same techniques as above and :func:`~flask.json.jsonify` we can return JSON
responses to API errors. :func:`~flask.abort` is called
with a ``description`` parameter. The error handler will
use that as the JSON error message, and set the status code to 404.
.. code-block:: python
from flask import abort, jsonify
@app.errorhandler(404)
def resource_not_found(e):
return jsonify(error=str(e)), 404
@app.route("/cheese")
def get_one_cheese():
resource = get_resource()
if resource is None:
abort(404, description="Resource not found")
return jsonify(resource)
We can also create custom exception classes. For instance, we can
introduce a new custom exception for an API that can take a proper human readable message,
a status code for the error and some optional payload to give more context
for the error.
This is a simple example:
.. code-block:: python
from flask import jsonify, request
class InvalidAPIUsage(Exception):
status_code = 400
def __init__(self, message, status_code=None, payload=None):
super().__init__()
self.message = message
if status_code is not None:
self.status_code = status_code
self.payload = payload
def to_dict(self):
rv = dict(self.payload or ())
rv['message'] = self.message
return rv
@app.errorhandler(InvalidAPIUsage)
def invalid_api_usage(e):
return jsonify(e.to_dict()), e.status_code
# an API app route for getting user information
# a correct request might be /api/user?user_id=420
@app.route("/api/user")
def user_api(user_id):
user_id = request.arg.get("user_id")
if not user_id:
raise InvalidAPIUsage("No user id provided!")
user = get_user(user_id=user_id)
if not user:
raise InvalidAPIUsage("No such user!", status_code=404)
return jsonify(user.to_dict())
A view can now raise that exception with an error message. Additionally
some extra payload can be provided as a dictionary through the `payload`
parameter.
Logging Logging
------- -------
See :doc:`/logging` for information about how to log exceptions, such as See :ref:`logging` for information on how to log exceptions, such as by
by emailing them to admins. emailing them to admins.
Debugging Debugging Application Errors
--------- ============================
See :doc:`/debugging` for information about how to debug errors in For production applications, configure your application with logging and
development and production. notifications as described in :ref:`application-errors`. This section provides
pointers when debugging deployment configuration and digging deeper with a
full-featured Python debugger.
When in Doubt, Run Manually
---------------------------
Having problems getting your application configured for production? If you
have shell access to your host, verify that you can run your application
manually from the shell in the deployment environment. Be sure to run under
the same user account as the configured deployment to troubleshoot permission
issues. You can use Flask's builtin development server with `debug=True` on
your production host, which is helpful in catching configuration issues, but
**be sure to do this temporarily in a controlled environment.** Do not run in
production with `debug=True`.
.. _working-with-debuggers:
Working with Debuggers
----------------------
To dig deeper, possibly to trace code execution, Flask provides a debugger out
of the box (see :ref:`debug-mode`). If you would like to use another Python
debugger, note that debuggers interfere with each other. You have to set some
options in order to use your favorite debugger:
* ``debug`` - whether to enable debug mode and catch exceptions
* ``use_debugger`` - whether to use the internal Flask debugger
* ``use_reloader`` - whether to reload and fork the process on exception
``debug`` must be True (i.e., exceptions must be caught) in order for the other
two options to have any value.
If you're using Aptana/Eclipse for debugging you'll need to set both
``use_debugger`` and ``use_reloader`` to False.
A possible useful pattern for configuration is to set the following in your
config.yaml (change the block as appropriate for your application, of course)::
FLASK:
DEBUG: True
DEBUG_WITH_APTANA: True
Then in your application's entry-point (main.py), you could have something like::
if __name__ == "__main__":
# To allow aptana to receive errors, set use_debugger=False
app = create_app(config="config.yaml")
if app.debug: use_debugger = True
try:
# Disable Flask's debugger if external debugger is requested
use_debugger = not(app.config.get('DEBUG_WITH_APTANA'))
except:
pass
app.run(use_debugger=use_debugger, debug=app.debug,
use_reloader=use_debugger, host='0.0.0.0')

View file

@ -1,305 +1,345 @@
.. _extension-dev:
Flask Extension Development Flask Extension Development
=========================== ===========================
.. currentmodule:: flask Flask, being a microframework, often requires some repetitive steps to get
a third party library working. Because very often these steps could be
abstracted to support multiple projects the `Flask Extension Registry`_
was created.
Extensions are extra packages that add functionality to a Flask If you want to create your own Flask extension for something that does not
application. While `PyPI`_ contains many Flask extensions, you may not exist yet, this guide to extension development will help you get your
find one that fits your need. If this is the case, you can create your extension running in no time and to feel like users would expect your
own, and publish it for others to use as well. extension to behave.
This guide will show how to create a Flask extension, and some of the .. _Flask Extension Registry: http://flask.pocoo.org/extensions/
common patterns and requirements involved. Since extensions can do
anything, this guide won't be able to cover every possibility.
The best ways to learn about extensions are to look at how other Anatomy of an Extension
extensions you use are written, and discuss with others. Discuss your -----------------------
design ideas with others on our `Discord Chat`_ or
`GitHub Discussions`_.
The best extensions share common patterns, so that anyone familiar with Extensions are all located in a package called ``flask_something``
using one extension won't feel completely lost with another. This can where "something" is the name of the library you want to bridge. So for
only work if collaboration happens early. example if you plan to add support for a library named `simplexml` to
Flask, you would name your extension's package ``flask_simplexml``.
The name of the actual extension (the human readable name) however would
be something like "Flask-SimpleXML". Make sure to include the name
"Flask" somewhere in that name and that you check the capitalization.
This is how users can then register dependencies to your extension in
their :file:`setup.py` files.
But what do extensions look like themselves? An extension has to ensure
that it works with multiple Flask application instances at once. This is
a requirement because many people will use patterns like the
:ref:`app-factories` pattern to create their application as needed to aid
unittests and to support multiple configurations. Because of that it is
crucial that your application supports that kind of behavior.
Most importantly the extension must be shipped with a :file:`setup.py` file and
registered on PyPI. Also the development checkout link should work so
that people can easily install the development version into their
virtualenv without having to download the library by hand.
Flask extensions must be licensed under a BSD, MIT or more liberal license
in order to be listed in the Flask Extension Registry. Keep in mind
that the Flask Extension Registry is a moderated place and libraries will
be reviewed upfront if they behave as required.
"Hello Flaskext!"
-----------------
So let's get started with creating such a Flask extension. The extension
we want to create here will provide very basic support for SQLite3.
First we create the following folder structure::
flask-sqlite3/
flask_sqlite3.py
LICENSE
README
Here's the contents of the most important files:
setup.py
````````
The next file that is absolutely required is the :file:`setup.py` file which is
used to install your Flask extension. The following contents are
something you can work with::
"""
Flask-SQLite3
-------------
This is the description for that library
"""
from setuptools import setup
Naming setup(
------ name='Flask-SQLite3',
version='1.0',
url='http://example.com/flask-sqlite3/',
license='BSD',
author='Your Name',
author_email='your-email@example.com',
description='Very short description',
long_description=__doc__,
py_modules=['flask_sqlite3'],
# if you would be using a package instead use packages instead
# of py_modules:
# packages=['flask_sqlite3'],
zip_safe=False,
include_package_data=True,
platforms='any',
install_requires=[
'Flask'
],
classifiers=[
'Environment :: Web Environment',
'Intended Audience :: Developers',
'License :: OSI Approved :: BSD License',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
'Topic :: Software Development :: Libraries :: Python Modules'
]
)
A Flask extension typically has ``flask`` in its name as a prefix or That's a lot of code but you can really just copy/paste that from existing
suffix. If it wraps another library, it should include the library name extensions and adapt.
as well. This makes it easy to search for extensions, and makes their
purpose clearer.
A general Python packaging recommendation is that the install name from flask_sqlite3.py
the package index and the name used in ``import`` statements should be ````````````````
related. The import name is lowercase, with words separated by
underscores (``_``). The install name is either lower case or title
case, with words separated by dashes (``-``). If it wraps another
library, prefer using the same case as that library's name.
Here are some example install and import names: Now this is where your extension code goes. But how exactly should such
an extension look like? What are the best practices? Continue reading
for some insight.
- ``Flask-Name`` imported as ``flask_name`` Initializing Extensions
- ``flask-name-lower`` imported as ``flask_name_lower`` -----------------------
- ``Flask-ComboName`` imported as ``flask_comboname``
- ``Name-Flask`` imported as ``name_flask`` Many extensions will need some kind of initialization step. For example,
consider an application that's currently connecting to SQLite like the
documentation suggests (:ref:`sqlite3`). So how does the extension
know the name of the application object?
Quite simple: you pass it to it.
There are two recommended ways for an extension to initialize:
initialization functions:
If your extension is called `helloworld` you might have a function
called ``init_helloworld(app[, extra_args])`` that initializes the
extension for that application. It could attach before / after
handlers etc.
classes:
Classes work mostly like initialization functions but can later be
used to further change the behavior. For an example look at how the
`OAuth extension`_ works: there is an `OAuth` object that provides
some helper functions like `OAuth.remote_app` to create a reference to
a remote application that uses OAuth.
What to use depends on what you have in mind. For the SQLite 3 extension
we will use the class-based approach because it will provide users with an
object that handles opening and closing database connections.
When designing your classes, it's important to make them easily reusable
at the module level. This means the object itself must not under any
circumstances store any application specific state and must be shareable
between different applications.
The Extension Code
------------------
Here's the contents of the `flask_sqlite3.py` for copy/paste::
import sqlite3
from flask import current_app, _app_ctx_stack
The Extension Class and Initialization class SQLite3(object):
--------------------------------------
All extensions will need some entry point that initializes the
extension with the application. The most common pattern is to create a
class that represents the extension's configuration and behavior, with
an ``init_app`` method to apply the extension instance to the given
application instance.
.. code-block:: python
class HelloExtension:
def __init__(self, app=None): def __init__(self, app=None):
self.app = app
if app is not None: if app is not None:
self.init_app(app) self.init_app(app)
def init_app(self, app): def init_app(self, app):
app.before_request(...) app.config.setdefault('SQLITE3_DATABASE', ':memory:')
app.teardown_appcontext(self.teardown)
It is important that the app is not stored on the extension, don't do def connect(self):
``self.app = app``. The only time the extension should have direct return sqlite3.connect(current_app.config['SQLITE3_DATABASE'])
access to an app is during ``init_app``, otherwise it should use
:data:`.current_app`.
This allows the extension to support the application factory pattern, def teardown(self, exception):
avoids circular import issues when importing the extension instance ctx = _app_ctx_stack.top
elsewhere in a user's code, and makes testing with different if hasattr(ctx, 'sqlite3_db'):
configurations easier. ctx.sqlite3_db.close()
.. code-block:: python @property
def connection(self):
hello = HelloExtension() ctx = _app_ctx_stack.top
if ctx is not None:
def create_app(): if not hasattr(ctx, 'sqlite3_db'):
app = Flask(__name__) ctx.sqlite3_db = self.connect()
hello.init_app(app) return ctx.sqlite3_db
return app
Above, the ``hello`` extension instance exists independently of the
application. This means that other modules in a user's project can do
``from project import hello`` and use the extension in blueprints before
the app exists.
The :attr:`Flask.extensions` dict can be used to store a reference to
the extension on the application, or some other state specific to the
application. Be aware that this is a single namespace, so use a name
unique to your extension, such as the extension's name without the
"flask" prefix.
Adding Behavior So here's what these lines of code do:
---------------
There are many ways that an extension can add behavior. Any setup 1. The ``__init__`` method takes an optional app object and, if supplied, will
methods that are available on the :class:`Flask` object can be used call ``init_app``.
during an extension's ``init_app`` method. 2. The ``init_app`` method exists so that the ``SQLite3`` object can be
instantiated without requiring an app object. This method supports the
factory pattern for creating applications. The ``init_app`` will set the
configuration for the database, defaulting to an in memory database if
no configuration is supplied. In addition, the ``init_app`` method attaches
the ``teardown`` handler.
3. Next, we define a ``connect`` method that opens a database connection.
4. Finally, we add a ``connection`` property that on first access opens
the database connection and stores it on the context. This is also
the recommended way to handling resources: fetch resources lazily the
first time they are used.
A common pattern is to use :meth:`~Flask.before_request` to initialize Note here that we're attaching our database connection to the top
some data or a connection at the beginning of each request, then application context via ``_app_ctx_stack.top``. Extensions should use
:meth:`~Flask.teardown_request` to clean it up at the end. This can be the top context for storing their own information with a sufficiently
stored on :data:`.g`, discussed more below. complex name.
A more lazy approach is to provide a method that initializes and caches So why did we decide on a class-based approach here? Because using our
the data or connection. For example, a ``ext.get_db`` method could extension looks something like this::
create a database connection the first time it's called, so that a view
that doesn't use the database doesn't create a connection.
Besides doing something before and after every view, your extension from flask import Flask
might want to add some specific views as well. In this case, you could from flask_sqlite3 import SQLite3
define a :class:`Blueprint`, then call :meth:`~Flask.register_blueprint`
during ``init_app`` to add the blueprint to the app.
app = Flask(__name__)
app.config.from_pyfile('the-config.cfg')
db = SQLite3(app)
Configuration Techniques You can then use the database from views like this::
------------------------
There can be multiple levels and sources of configuration for an @app.route('/')
extension. You should consider what parts of your extension fall into def show_all():
each one. cur = db.connection.cursor()
cur.execute(...)
- Configuration per application instance, through ``app.config`` Likewise if you are outside of a request you can use the database by
values. This is configuration that could reasonably change for each pushing an app context::
deployment of an application. A common example is a URL to an
external resource, such as a database. Configuration keys should
start with the extension's name so that they don't interfere with
other extensions.
- Configuration per extension instance, through ``__init__``
arguments. This configuration usually affects how the extension
is used, such that it wouldn't make sense to change it per
deployment.
- Configuration per extension instance, through instance attributes
and decorator methods. It might be more ergonomic to assign to
``ext.value``, or use a ``@ext.register`` decorator to register a
function, after the extension instance has been created.
- Global configuration through class attributes. Changing a class
attribute like ``Ext.connection_class`` can customize default
behavior without making a subclass. This could be combined
per-extension configuration to override defaults.
- Subclassing and overriding methods and attributes. Making the API of
the extension itself something that can be overridden provides a
very powerful tool for advanced customization.
The :class:`~flask.Flask` object itself uses all of these techniques. with app.app_context():
cur = db.connection.cursor()
cur.execute(...)
It's up to you to decide what configuration is appropriate for your At the end of the ``with`` block the teardown handles will be executed
extension, based on what you need and what you want to support. automatically.
Configuration should not be changed after the application setup phase is Additionally, the ``init_app`` method is used to support the factory pattern
complete and the server begins handling requests. Configuration is for creating apps::
global, any changes to it are not guaranteed to be visible to other
workers.
db = Sqlite3()
Data During a Request # Then later on.
--------------------- app = create_app('the-config.cfg')
When writing a Flask application, the :data:`~flask.g` object is used to
store information during a request. For example the
:doc:`tutorial <tutorial/database>` stores a connection to a SQLite
database as ``g.db``. Extensions can also use this, with some care.
Since ``g`` is a single global namespace, extensions must use unique
names that won't collide with user data. For example, use the extension
name as a prefix, or as a namespace.
.. code-block:: python
# an internal prefix with the extension name
g._hello_user_id = 2
# or an internal prefix as a namespace
from types import SimpleNamespace
g._hello = SimpleNamespace()
g._hello.user_id = 2
The data in ``g`` lasts for an application context. An application context is
active during a request, CLI command, or ``with app.app_context()`` block. If
you're storing something that should be closed, use
:meth:`~flask.Flask.teardown_appcontext` to ensure that it gets closed when the
app context ends. If it should only be valid during a request, or would not be
used in the CLI outside a request, use :meth:`~flask.Flask.teardown_request`.
Views and Models
----------------
Your extension views might want to interact with specific models in your
database, or some other extension or data connected to your application.
For example, let's consider a ``Flask-SimpleBlog`` extension that works
with Flask-SQLAlchemy to provide a ``Post`` model and views to write
and read posts.
The ``Post`` model needs to subclass the Flask-SQLAlchemy ``db.Model``
object, but that's only available once you've created an instance of
that extension, not when your extension is defining its views. So how
can the view code, defined before the model exists, access the model?
One method could be to use :doc:`views`. During ``__init__``, create
the model, then create the views by passing the model to the view
class's :meth:`~views.View.as_view` method.
.. code-block:: python
class PostAPI(MethodView):
def __init__(self, model):
self.model = model
def get(self, id):
post = self.model.query.get(id)
return jsonify(post.to_json())
class BlogExtension:
def __init__(self, db):
class Post(db.Model):
id = db.Column(primary_key=True)
title = db.Column(db.String, nullable=False)
self.post_model = Post
def init_app(self, app):
api_view = PostAPI.as_view(model=self.post_model)
db = SQLAlchemy()
blog = BlogExtension(db)
db.init_app(app) db.init_app(app)
blog.init_app(app)
Another technique could be to use an attribute on the extension, such as Keep in mind that supporting this factory pattern for creating apps is required
``self.post_model`` from above. Add the extension to ``app.extensions`` for approved flask extensions (described below).
in ``init_app``, then access
``current_app.extensions["simple_blog"].post_model`` from views.
You may also want to provide base classes so that users can provide .. admonition:: Note on ``init_app``
their own ``Post`` model that conforms to the API your extension
expects. So they could implement ``class Post(blog.BasePost)``, then
set it as ``blog.post_model``.
As you can see, this can get a bit complex. Unfortunately, there's no As you noticed, ``init_app`` does not assign ``app`` to ``self``. This
perfect solution here, only different strategies and tradeoffs depending is intentional! Class based Flask extensions must only store the
on your needs and how much customization you want to offer. Luckily, application on the object when the application was passed to the
this sort of resource dependency is not a common need for most constructor. This tells the extension: I am not interested in using
extensions. Remember, if you need help with design, ask on our multiple applications.
`Discord Chat`_ or `GitHub Discussions`_.
When the extension needs to find the current application and it does
not have a reference to it, it must either use the
:data:`~flask.current_app` context local or change the API in a way
that you can pass the application explicitly.
Recommended Extension Guidelines Using _app_ctx_stack
-------------------------------- --------------------
Flask previously had the concept of "approved extensions", where the In the example above, before every request, a ``sqlite3_db`` variable is
Flask maintainers evaluated the quality, support, and compatibility of assigned to ``_app_ctx_stack.top``. In a view function, this variable is
the extensions before listing them. While the list became too difficult accessible using the ``connection`` property of ``SQLite3``. During the
to maintain over time, the guidelines are still relevant to all teardown of a request, the ``sqlite3_db`` connection is closed. By using
extensions maintained and developed today, as they help the Flask this pattern, the *same* connection to the sqlite3 database is accessible
ecosystem remain consistent and compatible. to anything that needs it for the duration of the request.
1. An extension requires a maintainer. In the event an extension author
would like to move beyond the project, the project should find a new
maintainer and transfer access to the repository, documentation,
PyPI, and any other services. The `Pallets-Eco`_ organization on
GitHub allows for community maintenance with oversight from the
Pallets maintainers.
2. The naming scheme is *Flask-ExtensionName* or *ExtensionName-Flask*.
It must provide exactly one package or module named
``flask_extension_name``.
3. The extension must use an open source license. The Python web
ecosystem tends to prefer BSD or MIT. It must be open source and
publicly available.
4. The extension's API must have the following characteristics:
- It must support multiple applications running in the same Python Learn from Others
process. Use ``current_app`` instead of ``self.app``, store -----------------
configuration and state per application instance.
- It must be possible to use the factory pattern for creating
applications. Use the ``ext.init_app()`` pattern.
5. From a clone of the repository, an extension with its dependencies This documentation only touches the bare minimum for extension
must be installable in editable mode with ``pip install -e .``. development. If you want to learn more, it's a very good idea to check
6. It must ship tests that can be invoked with a common tool like out existing extensions on the `Flask Extension Registry`_. If you feel
``tox -e py``, ``nox -s test`` or ``pytest``. If not using ``tox``, lost there is still the `mailinglist`_ and the `IRC channel`_ to get some
the test dependencies should be specified in a requirements file. ideas for nice looking APIs. Especially if you do something nobody before
The tests must be part of the sdist distribution. you did, it might be a very good idea to get some more input. This not only
7. A link to the documentation or project website must be in the PyPI generates useful feedback on what people might want from an extension, but
metadata or the readme. The documentation should use the Flask theme also avoids having multiple developers working in isolation on pretty much the
from the `Official Pallets Themes`_. same problem.
8. The extension's dependencies should not use upper bounds or assume
any particular version scheme, but should use lower bounds to
indicate minimum compatibility support. For example,
``sqlalchemy>=1.4``.
9. Indicate the versions of Python supported using ``python_requires=">=version"``.
Flask and Pallets policy is to support all Python versions that are not
within six months of end of life (EOL). See Python's `EOL calendar`_ for
timing.
.. _PyPI: https://pypi.org/search/?c=Framework+%3A%3A+Flask Remember: good API design is hard, so introduce your project on the
.. _Discord Chat: https://discord.gg/pallets mailinglist, and let other developers give you a helping hand with
.. _GitHub Discussions: https://github.com/pallets/flask/discussions designing the API.
.. _Official Pallets Themes: https://pypi.org/project/Pallets-Sphinx-Themes/
.. _Pallets-Eco: https://github.com/pallets-eco The best Flask extensions are extensions that share common idioms for the
.. _EOL calendar: https://devguide.python.org/versions/ API. And this can only work if collaboration happens early.
Approved Extensions
-------------------
Flask also has the concept of approved extensions. Approved extensions
are tested as part of Flask itself to ensure extensions do not break on
new releases. These approved extensions are listed on the `Flask
Extension Registry`_ and marked appropriately. If you want your own
extension to be approved you have to follow these guidelines:
0. An approved Flask extension requires a maintainer. In the event an
extension author would like to move beyond the project, the project should
find a new maintainer including full source hosting transition and PyPI
access. If no maintainer is available, give access to the Flask core team.
1. An approved Flask extension must provide exactly one package or module
named ``flask_extensionname``.
2. It must ship a testing suite that can either be invoked with ``make test``
or ``python setup.py test``. For test suites invoked with ``make
test`` the extension has to ensure that all dependencies for the test
are installed automatically. If tests are invoked with ``python setup.py
test``, test dependencies can be specified in the :file:`setup.py` file. The
test suite also has to be part of the distribution.
3. APIs of approved extensions will be checked for the following
characteristics:
- an approved extension has to support multiple applications
running in the same Python process.
- it must be possible to use the factory pattern for creating
applications.
4. The license must be BSD/MIT/WTFPL licensed.
5. The naming scheme for official extensions is *Flask-ExtensionName* or
*ExtensionName-Flask*.
6. Approved extensions must define all their dependencies in the
:file:`setup.py` file unless a dependency cannot be met because it is not
available on PyPI.
7. The documentation must use the ``flask`` theme from the `Official
Pallets Themes`_.
8. The setup.py description (and thus the PyPI description) has to
link to the documentation, website (if there is one) and there
must be a link to automatically install the development version
(``PackageName==dev``).
9. The ``zip_safe`` flag in the setup script must be set to ``False``,
even if the extension would be safe for zipping.
10. An extension currently has to support Python 3.4 and newer and 2.7.
.. _OAuth extension: https://pythonhosted.org/Flask-OAuth/
.. _mailinglist: http://flask.pocoo.org/mailinglist/
.. _IRC channel: http://flask.pocoo.org/community/irc/
.. _Official Pallets Themes: https://pypi.org/project/pallets-sphinx-themes/

View file

@ -1,17 +1,21 @@
.. _extensions:
Extensions Extensions
========== ==========
Extensions are extra packages that add functionality to a Flask Extensions are extra packages that add functionality to a Flask
application. For example, an extension might add support for sending application. For example, an extension might add support for sending
email or connecting to a database. Some extensions add entire new email or connecting to a database. Some extensions add entire new
frameworks to help build certain types of applications, like a REST API. frameworks to help build certain types of applications, like a ReST API.
Finding Extensions Finding Extensions
------------------ ------------------
Flask extensions are usually named "Flask-Foo" or "Foo-Flask". You can Flask extensions are usually named "Flask-Foo" or "Foo-Flask". Many
search PyPI for packages tagged with `Framework :: Flask <pypi_>`_. extensions are listed in the `Extension Registry`_, which can be updated
by extension developers. You can also search PyPI for packages tagged
with `Framework :: Flask <pypi_>`_.
Using Extensions Using Extensions
@ -21,7 +25,7 @@ Consult each extension's documentation for installation, configuration,
and usage instructions. Generally, extensions pull their own and usage instructions. Generally, extensions pull their own
configuration from :attr:`app.config <flask.Flask.config>` and are configuration from :attr:`app.config <flask.Flask.config>` and are
passed an application instance during initialization. For example, passed an application instance during initialization. For example,
an extension called "Flask-Foo" might be used like this:: an extension caled "Flask-Foo" might be used like this::
from flask_foo import Foo from flask_foo import Foo
@ -39,10 +43,11 @@ an extension called "Flask-Foo" might be used like this::
Building Extensions Building Extensions
------------------- -------------------
While `PyPI <pypi_>`_ contains many Flask extensions, you may not find While the `Extension Registry`_ contains many Flask extensions, you may
an extension that fits your need. If this is the case, you can create not find an extension that fits your need. If this is the case, you can
your own, and publish it for others to use as well. Read create your own. Read :ref:`extension-dev` to develop your own Flask
:doc:`extensiondev` to develop your own Flask extension. extension.
.. _Extension Registry: http://flask.pocoo.org/extensions/
.. _pypi: https://pypi.org/search/?c=Framework+%3A%3A+Flask .. _pypi: https://pypi.org/search/?c=Framework+%3A%3A+Flask

118
docs/flaskstyle.sty Normal file
View file

@ -0,0 +1,118 @@
\definecolor{TitleColor}{rgb}{0,0,0}
\definecolor{InnerLinkColor}{rgb}{0,0,0}
\renewcommand{\maketitle}{%
\begin{titlepage}%
\let\footnotesize\small
\let\footnoterule\relax
\ifsphinxpdfoutput
\begingroup
% This \def is required to deal with multi-line authors; it
% changes \\ to ', ' (comma-space), making it pass muster for
% generating document info in the PDF file.
\def\\{, }
\pdfinfo{
/Author (\@author)
/Title (\@title)
}
\endgroup
\fi
\begin{flushright}%
%\sphinxlogo%
{\center
\vspace*{3cm}
\includegraphics{logo.pdf}
\vspace{3cm}
\par
{\rm\Huge \@title \par}%
{\em\LARGE \py@release\releaseinfo \par}
{\large
\@date \par
\py@authoraddress \par
}}%
\end{flushright}%\par
\@thanks
\end{titlepage}%
\cleardoublepage%
\setcounter{footnote}{0}%
\let\thanks\relax\let\maketitle\relax
%\gdef\@thanks{}\gdef\@author{}\gdef\@title{}
}
\fancypagestyle{normal}{
\fancyhf{}
\fancyfoot[LE,RO]{{\thepage}}
\fancyfoot[LO]{{\nouppercase{\rightmark}}}
\fancyfoot[RE]{{\nouppercase{\leftmark}}}
\fancyhead[LE,RO]{{ \@title, \py@release}}
\renewcommand{\headrulewidth}{0.4pt}
\renewcommand{\footrulewidth}{0.4pt}
}
\fancypagestyle{plain}{
\fancyhf{}
\fancyfoot[LE,RO]{{\thepage}}
\renewcommand{\headrulewidth}{0pt}
\renewcommand{\footrulewidth}{0.4pt}
}
\titleformat{\section}{\Large}%
{\py@TitleColor\thesection}{0.5em}{\py@TitleColor}{\py@NormalColor}
\titleformat{\subsection}{\large}%
{\py@TitleColor\thesubsection}{0.5em}{\py@TitleColor}{\py@NormalColor}
\titleformat{\subsubsection}{}%
{\py@TitleColor\thesubsubsection}{0.5em}{\py@TitleColor}{\py@NormalColor}
\titleformat{\paragraph}{\large}%
{\py@TitleColor}{0em}{\py@TitleColor}{\py@NormalColor}
\ChNameVar{\raggedleft\normalsize}
\ChNumVar{\raggedleft \bfseries\Large}
\ChTitleVar{\raggedleft \rm\Huge}
\renewcommand\thepart{\@Roman\c@part}
\renewcommand\part{%
\pagestyle{plain}
\if@noskipsec \leavevmode \fi
\cleardoublepage
\vspace*{6cm}%
\@afterindentfalse
\secdef\@part\@spart}
\def\@part[#1]#2{%
\ifnum \c@secnumdepth >\m@ne
\refstepcounter{part}%
\addcontentsline{toc}{part}{\thepart\hspace{1em}#1}%
\else
\addcontentsline{toc}{part}{#1}%
\fi
{\parindent \z@ %\center
\interlinepenalty \@M
\normalfont
\ifnum \c@secnumdepth >\m@ne
\rm\Large \partname~\thepart
\par\nobreak
\fi
\MakeUppercase{\rm\Huge #2}%
\markboth{}{}\par}%
\nobreak
\vskip 8ex
\@afterheading}
\def\@spart#1{%
{\parindent \z@ %\center
\interlinepenalty \@M
\normalfont
\huge \bfseries #1\par}%
\nobreak
\vskip 3ex
\@afterheading}
% use inconsolata font
\usepackage{inconsolata}
% fix single quotes, for inconsolata. (does not work)
%%\usepackage{textcomp}
%%\begingroup
%% \catcode`'=\active
%% \g@addto@macro\@noligs{\let'\textsinglequote}
%% \endgroup
%%\endinput

57
docs/foreword.rst Normal file
View file

@ -0,0 +1,57 @@
Foreword
========
Read this before you get started with Flask. This hopefully answers some
questions about the purpose and goals of the project, and when you
should or should not be using it.
What does "micro" mean?
-----------------------
“Micro” does not mean that your whole web application has to fit into a single
Python file (although it certainly can), nor does it mean that Flask is lacking
in functionality. The "micro" in microframework means Flask aims to keep the
core simple but extensible. Flask won't make many decisions for you, such as
what database to use. Those decisions that it does make, such as what
templating engine to use, are easy to change. Everything else is up to you, so
that Flask can be everything you need and nothing you don't.
By default, Flask does not include a database abstraction layer, form
validation or anything else where different libraries already exist that can
handle that. Instead, Flask supports extensions to add such functionality to
your application as if it was implemented in Flask itself. Numerous extensions
provide database integration, form validation, upload handling, various open
authentication technologies, and more. Flask may be "micro", but it's ready for
production use on a variety of needs.
Configuration and Conventions
-----------------------------
Flask has many configuration values, with sensible defaults, and a few
conventions when getting started. By convention, templates and static files are
stored in subdirectories within the application's Python source tree, with the
names :file:`templates` and :file:`static` respectively. While this can be changed, you
usually don't have to, especially when getting started.
Growing with Flask
------------------
Once you have Flask up and running, you'll find a variety of extensions
available in the community to integrate your project for production. The Flask
core team reviews extensions and ensures approved extensions do not break with
future releases.
As your codebase grows, you are free to make the design decisions appropriate
for your project. Flask will continue to provide a very simple glue layer to
the best that Python has to offer. You can implement advanced patterns in
SQLAlchemy or another database tool, introduce non-relational data persistence
as appropriate, and take advantage of framework-agnostic tools built for WSGI,
the Python web interface.
Flask includes many hooks to customize its behavior. Should you need more
customization, the Flask class is built for subclassing. If you are interested
in that, check out the :ref:`becomingbig` chapter. If you are curious about
the Flask design principles, head over to the section about :ref:`design`.
Continue to :ref:`installation`, the :ref:`quickstart`, or the
:ref:`advanced_foreword`.

View file

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

207
docs/htmlfaq.rst Normal file
View file

@ -0,0 +1,207 @@
HTML/XHTML FAQ
==============
The Flask documentation and example applications are using HTML5. You
may notice that in many situations, when end tags are optional they are
not used, so that the HTML is cleaner and faster to load. Because there
is much confusion about HTML and XHTML among developers, this document tries
to answer some of the major questions.
History of XHTML
----------------
For a while, it appeared that HTML was about to be replaced by XHTML.
However, barely any websites on the Internet are actual XHTML (which is
HTML processed using XML rules). There are a couple of major reasons
why this is the case. One of them is Internet Explorer's lack of proper
XHTML support. The XHTML spec states that XHTML must be served with the MIME
type :mimetype:`application/xhtml+xml`, but Internet Explorer refuses to read files
with that MIME type.
While it is relatively easy to configure Web servers to serve XHTML properly,
few people do. This is likely because properly using XHTML can be quite
painful.
One of the most important causes of pain is XML's draconian (strict and
ruthless) error handling. When an XML parsing error is encountered,
the browser is supposed to show the user an ugly error message, instead
of attempting to recover from the error and display what it can. Most of
the (X)HTML generation on the web is based on non-XML template engines
(such as Jinja, the one used in Flask) which do not protect you from
accidentally creating invalid XHTML. There are XML based template engines,
such as Kid and the popular Genshi, but they often come with a larger
runtime overhead and are not as straightforward to use because they have
to obey XML rules.
The majority of users, however, assumed they were properly using XHTML.
They wrote an XHTML doctype at the top of the document and self-closed all
the necessary tags (``<br>`` becomes ``<br/>`` or ``<br></br>`` in XHTML).
However, even if the document properly validates as XHTML, what really
determines XHTML/HTML processing in browsers is the MIME type, which as
said before is often not set properly. So the valid XHTML was being treated
as invalid HTML.
XHTML also changed the way JavaScript is used. To properly work with XHTML,
programmers have to use the namespaced DOM interface with the XHTML
namespace to query for HTML elements.
History of HTML5
----------------
Development of the HTML5 specification was started in 2004 under the name
"Web Applications 1.0" by the Web Hypertext Application Technology Working
Group, or WHATWG (which was formed by the major browser vendors Apple,
Mozilla, and Opera) with the goal of writing a new and improved HTML
specification, based on existing browser behavior instead of unrealistic
and backwards-incompatible specifications.
For example, in HTML4 ``<title/Hello/`` theoretically parses exactly the
same as ``<title>Hello</title>``. However, since people were using
XHTML-like tags along the lines of ``<link />``, browser vendors implemented
the XHTML syntax over the syntax defined by the specification.
In 2007, the specification was adopted as the basis of a new HTML
specification under the umbrella of the W3C, known as HTML5. Currently,
it appears that XHTML is losing traction, as the XHTML 2 working group has
been disbanded and HTML5 is being implemented by all major browser vendors.
HTML versus XHTML
-----------------
The following table gives you a quick overview of features available in
HTML 4.01, XHTML 1.1 and HTML5. (XHTML 1.0 is not included, as it was
superseded by XHTML 1.1 and the barely-used XHTML5.)
.. tabularcolumns:: |p{9cm}|p{2cm}|p{2cm}|p{2cm}|
+-----------------------------------------+----------+----------+----------+
| | HTML4.01 | XHTML1.1 | HTML5 |
+=========================================+==========+==========+==========+
| ``<tag/value/`` == ``<tag>value</tag>`` | |Y| [1]_ | |N| | |N| |
+-----------------------------------------+----------+----------+----------+
| ``<br/>`` supported | |N| | |Y| | |Y| [2]_ |
+-----------------------------------------+----------+----------+----------+
| ``<script/>`` supported | |N| | |Y| | |N| |
+-----------------------------------------+----------+----------+----------+
| should be served as `text/html` | |Y| | |N| [3]_ | |Y| |
+-----------------------------------------+----------+----------+----------+
| should be served as | |N| | |Y| | |N| |
| `application/xhtml+xml` | | | |
+-----------------------------------------+----------+----------+----------+
| strict error handling | |N| | |Y| | |N| |
+-----------------------------------------+----------+----------+----------+
| inline SVG | |N| | |Y| | |Y| |
+-----------------------------------------+----------+----------+----------+
| inline MathML | |N| | |Y| | |Y| |
+-----------------------------------------+----------+----------+----------+
| ``<video>`` tag | |N| | |N| | |Y| |
+-----------------------------------------+----------+----------+----------+
| ``<audio>`` tag | |N| | |N| | |Y| |
+-----------------------------------------+----------+----------+----------+
| New semantic tags like ``<article>`` | |N| | |N| | |Y| |
+-----------------------------------------+----------+----------+----------+
.. [1] This is an obscure feature inherited from SGML. It is usually not
supported by browsers, for reasons detailed above.
.. [2] This is for compatibility with server code that generates XHTML for
tags such as ``<br>``. It should not be used in new code.
.. [3] XHTML 1.0 is the last XHTML standard that allows to be served
as `text/html` for backwards compatibility reasons.
.. |Y| image:: _static/yes.png
:alt: Yes
.. |N| image:: _static/no.png
:alt: No
What does "strict" mean?
------------------------
HTML5 has strictly defined parsing rules, but it also specifies exactly
how a browser should react to parsing errors - unlike XHTML, which simply
states parsing should abort. Some people are confused by apparently
invalid syntax that still generates the expected results (for example,
missing end tags or unquoted attribute values).
Some of these work because of the lenient error handling most browsers use
when they encounter a markup error, others are actually specified. The
following constructs are optional in HTML5 by standard, but have to be
supported by browsers:
- Wrapping the document in an ``<html>`` tag
- Wrapping header elements in ``<head>`` or the body elements in
``<body>``
- Closing the ``<p>``, ``<li>``, ``<dt>``, ``<dd>``, ``<tr>``,
``<td>``, ``<th>``, ``<tbody>``, ``<thead>``, or ``<tfoot>`` tags.
- Quoting attributes, so long as they contain no whitespace or
special characters (like ``<``, ``>``, ``'``, or ``"``).
- Requiring boolean attributes to have a value.
This means the following page in HTML5 is perfectly valid:
.. sourcecode:: html
<!doctype html>
<title>Hello HTML5</title>
<div class=header>
<h1>Hello HTML5</h1>
<p class=tagline>HTML5 is awesome
</div>
<ul class=nav>
<li><a href=/index>Index</a>
<li><a href=/downloads>Downloads</a>
<li><a href=/about>About</a>
</ul>
<div class=body>
<h2>HTML5 is probably the future</h2>
<p>
There might be some other things around but in terms of
browser vendor support, HTML5 is hard to beat.
<dl>
<dt>Key 1
<dd>Value 1
<dt>Key 2
<dd>Value 2
</dl>
</div>
New technologies in HTML5
-------------------------
HTML5 adds many new features that make Web applications easier to write
and to use.
- The ``<audio>`` and ``<video>`` tags provide a way to embed audio and
video without complicated add-ons like QuickTime or Flash.
- Semantic elements like ``<article>``, ``<header>``, ``<nav>``, and
``<time>`` that make content easier to understand.
- The ``<canvas>`` tag, which supports a powerful drawing API, reducing
the need for server-generated images to present data graphically.
- New form control types like ``<input type="date">`` that allow user
agents to make entering and validating values easier.
- Advanced JavaScript APIs like Web Storage, Web Workers, Web Sockets,
geolocation, and offline applications.
Many other features have been added, as well. A good guide to new features
in HTML5 is Mark Pilgrim's soon-to-be-published book, `Dive Into HTML5`_.
Not all of them are supported in browsers yet, however, so use caution.
.. _Dive Into HTML5: http://diveintohtml5.info/
What should be used?
--------------------
Currently, the answer is HTML5. There are very few reasons to use XHTML
considering the latest developments in Web browsers. To summarize the
reasons given above:
- Internet Explorer (which, sadly, currently leads in market share)
has poor support for XHTML.
- Many JavaScript libraries also do not support XHTML, due to the more
complicated namespacing API it requires.
- HTML5 adds several new features, including semantic tags and the
long-awaited ``<audio>`` and ``<video>`` tags.
- It has the support of most browser vendors behind it.
- It is much easier to write, and more compact.
For most applications, it is undoubtedly better to use HTML5 than XHTML.

View file

@ -1,89 +1,27 @@
.. rst-class:: hide-header :orphan:
Welcome to Flask Welcome to Flask
================ ================
.. image:: _static/flask-name.svg .. image:: _static/logo-full.png
:align: center :alt: Flask: web development, one drop at a time
:height: 200px :align: right
:align: right
Welcome to Flask's documentation. Flask is a lightweight WSGI web application framework. Welcome to Flask's documentation. Get started with :ref:`installation`
It is designed to make getting started quick and easy, with the ability to scale up to and then get an overview with the :ref:`quickstart`. There is also a
complex applications. more detailed :ref:`tutorial` that shows how to create a small but
Get started with :doc:`installation`
and then get an overview with the :doc:`quickstart`. There is also a
more detailed :doc:`tutorial/index` that shows how to create a small but
complete application with Flask. Common patterns are described in the complete application with Flask. Common patterns are described in the
:doc:`patterns/index` section. The rest of the docs describe each :ref:`patterns` section. The rest of the docs describe each component of
component of Flask in detail, with a full reference in the :doc:`api` Flask in detail, with a full reference in the :ref:`api` section.
section.
Flask depends on the `Werkzeug`_ WSGI toolkit, the `Jinja`_ template engine, and the Flask depends on the `Jinja`_ template engine and the `Werkzeug`_ WSGI
`Click`_ CLI toolkit. Be sure to check their documentation as well as Flask's when toolkit. The documentation for these libraries can be found at:
looking for information.
.. _Werkzeug: https://werkzeug.palletsprojects.com - `Jinja documentation <http://jinja.pocoo.org/docs>`_
.. _Jinja: https://jinja.palletsprojects.com - `Werkzeug documentation <http://werkzeug.pocoo.org/docs>`_
.. _Click: https://click.palletsprojects.com
.. _Jinja: https://www.palletsprojects.com/p/jinja/
.. _Werkzeug: https://www.palletsprojects.com/p/werkzeug/
User's Guide .. include:: contents.rst.inc
------------
Flask provides configuration and conventions, with sensible defaults, to get started.
This section of the documentation explains the different parts of the Flask framework
and how they can be used, customized, and extended. Beyond Flask itself, look for
community-maintained extensions to add even more functionality.
.. toctree::
:maxdepth: 2
installation
quickstart
tutorial/index
templating
testing
errorhandling
debugging
logging
config
signals
views
lifecycle
appcontext
blueprints
extensions
cli
server
shell
patterns/index
web-security
deploying/index
gevent
async-await
API Reference
-------------
If you are looking for information on a specific function, class or
method, this part of the documentation is for you.
.. toctree::
:maxdepth: 2
api
Additional Notes
----------------
.. toctree::
:maxdepth: 2
design
extensiondev
contributing
license
changes

View file

@ -1,12 +1,13 @@
.. _installation:
Installation 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 3. Flask supports Python 3.4
and newer, Python 2.7, and PyPy.
Dependencies Dependencies
------------ ------------
@ -23,15 +24,12 @@ These distributions will be installed automatically when installing Flask.
to protect Flask's session cookie. to protect Flask's session cookie.
* `Click`_ is a framework for writing command line applications. It provides * `Click`_ is a framework for writing command line applications. It provides
the ``flask`` command and allows adding custom management commands. the ``flask`` command and allows adding custom management commands.
* `Blinker`_ provides support for :doc:`signals`.
.. _Werkzeug: https://palletsprojects.com/p/werkzeug/
.. _Jinja: https://palletsprojects.com/p/jinja/
.. _MarkupSafe: https://palletsprojects.com/p/markupsafe/
.. _ItsDangerous: https://palletsprojects.com/p/itsdangerous/
.. _Click: https://palletsprojects.com/p/click/
.. _Blinker: https://blinker.readthedocs.io/
.. _Werkzeug: http://werkzeug.pocoo.org/
.. _Jinja: http://jinja.pocoo.org/
.. _MarkupSafe: https://pypi.org/project/MarkupSafe/
.. _ItsDangerous: https://pythonhosted.org/itsdangerous/
.. _Click: http://click.pocoo.org/
Optional dependencies Optional dependencies
~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~
@ -39,26 +37,20 @@ Optional dependencies
These distributions will not be installed automatically. Flask will detect and These distributions will not be installed automatically. Flask will detect and
use them if you install them. use them if you install them.
* `Blinker`_ provides support for :ref:`signals`.
* `SimpleJSON`_ is a fast JSON implementation that is compatible with
Python's ``json`` module. It is preferred for JSON operations if it is
installed.
* `python-dotenv`_ enables support for :ref:`dotenv` when running ``flask`` * `python-dotenv`_ enables support for :ref:`dotenv` when running ``flask``
commands. commands.
* `Watchdog`_ provides a faster, more efficient reloader for the development * `Watchdog`_ provides a faster, more efficient reloader for the development
server. server.
.. _Blinker: https://pythonhosted.org/blinker/
.. _SimpleJSON: https://simplejson.readthedocs.io/
.. _python-dotenv: https://github.com/theskumar/python-dotenv#readme .. _python-dotenv: https://github.com/theskumar/python-dotenv#readme
.. _watchdog: https://pythonhosted.org/watchdog/ .. _watchdog: https://pythonhosted.org/watchdog/
greenlet
~~~~~~~~
You may choose to use :doc:`/gevent` with your application. In this case,
greenlet>=1.0 is required. When using PyPy, PyPy>=7.3.7 is required.
These are not minimum supported versions, they only indicate the first
versions that added necessary features. You should use the latest
versions of each.
Virtual environments Virtual environments
-------------------- --------------------
@ -74,35 +66,43 @@ Virtual environments are independent groups of Python libraries, one for each
project. Packages installed for one project will not affect other projects or project. Packages installed for one project will not affect other projects or
the operating system's packages. the operating system's packages.
Python comes bundled with the :mod:`venv` module to create virtual Python 3 comes bundled with the :mod:`venv` module to create virtual
environments. environments. If you're using a modern version of Python, you can continue on
to the next section.
If you're using Python 2, see :ref:`install-install-virtualenv` first.
.. _install-create-env: .. _install-create-env:
Create an environment Create an environment
~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~
Create a project folder and a :file:`.venv` folder within: Create a project folder and a :file:`venv` folder within:
.. tabs:: .. code-block:: sh
.. group-tab:: macOS/Linux mkdir myproject
cd myproject
python3 -m venv venv
.. code-block:: text On Windows:
$ mkdir myproject .. code-block:: bat
$ cd myproject
$ python3 -m venv .venv
.. group-tab:: Windows py -3 -m venv venv
.. code-block:: text If you needed to install virtualenv because you are on an older version of
Python, use the following command instead:
> mkdir myproject .. code-block:: sh
> cd myproject
> py -3 -m venv .venv
virtualenv venv
On Windows:
.. code-block:: bat
\Python27\Scripts\virtualenv.exe venv
.. _install-activate-env: .. _install-activate-env:
@ -111,33 +111,73 @@ Activate the environment
Before you work on your project, activate the corresponding environment: Before you work on your project, activate the corresponding environment:
.. tabs:: .. code-block:: sh
.. group-tab:: macOS/Linux . venv/bin/activate
.. code-block:: text On Windows:
$ . .venv/bin/activate .. code-block:: bat
.. group-tab:: Windows venv\Scripts\activate
.. code-block:: text
> .venv\Scripts\activate
Your shell prompt will change to show the name of the activated
environment.
Your shell prompt will change to show the name of the activated environment.
Install Flask Install Flask
------------- -------------
Within the activated environment, use the following command to install Within the activated environment, use the following command to install Flask:
Flask:
.. code-block:: sh .. code-block:: sh
$ pip install Flask pip install Flask
Flask is now installed. Check out the :doc:`/quickstart` or go to the Living on the edge
:doc:`Documentation Overview </index>`. ~~~~~~~~~~~~~~~~~~
If you want to work with the latest Flask code before it's released, install or
update the code from the master branch:
.. code-block:: sh
pip install -U https://github.com/pallets/flask/archive/master.tar.gz
.. _install-install-virtualenv:
Install virtualenv
------------------
If you are using Python 2, the venv module is not available. Instead,
install `virtualenv`_.
On Linux, virtualenv is provided by your package manager:
.. code-block:: sh
# Debian, Ubuntu
sudo apt-get install python-virtualenv
# CentOS, Fedora
sudo yum install python-virtualenv
# Arch
sudo pacman -S python-virtualenv
If you are on Mac OS X or Windows, download `get-pip.py`_, then:
.. code-block:: sh
sudo python2 Downloads/get-pip.py
sudo python2 -m pip install virtualenv
On Windows, as an administrator:
.. code-block:: bat
\Python27\python.exe Downloads\get-pip.py
\Python27\python.exe -m pip install virtualenv
Now you can continue to :ref:`install-create-env`.
.. _virtualenv: https://virtualenv.pypa.io/
.. _get-pip.py: https://bootstrap.pypa.io/get-pip.py

6
docs/latexindex.rst Normal file
View file

@ -0,0 +1,6 @@
:orphan:
Flask Documentation
===================
.. include:: contents.rst.inc

View file

@ -1,5 +1,48 @@
BSD-3-Clause License License
==================== =======
.. literalinclude:: ../LICENSE.txt Flask is licensed under a three clause BSD License. It basically means:
:language: text do whatever you want with it as long as the copyright in Flask sticks
around, the conditions are not modified and the disclaimer is present.
Furthermore you must not use the names of the authors to promote derivatives
of the software without written consent.
The full license text can be found below (:ref:`flask-license`). For the
documentation and artwork different licenses apply.
.. _authors:
Authors
-------
.. include:: ../AUTHORS
General License Definitions
---------------------------
The following section contains the full license texts for Flask and the
documentation.
- "AUTHORS" hereby refers to all the authors listed in the
:ref:`authors` section.
- The ":ref:`flask-license`" applies to all the source code shipped as
part of Flask (Flask itself as well as the examples and the unittests)
as well as documentation.
- The ":ref:`artwork-license`" applies to the project's Horn-Logo.
.. _flask-license:
Flask License
-------------
.. include:: ../LICENSE
.. _artwork-license:
Flask Artwork License
---------------------
.. include:: ../artwork/LICENSE

View file

@ -1,171 +0,0 @@
Application Structure and Lifecycle
===================================
Flask makes it pretty easy to write a web application. But there are quite a few
different parts to an application and to each request it handles. Knowing what happens
during application setup, serving, and handling requests will help you know what's
possible in Flask and how to structure your application.
Application Setup
-----------------
The first step in creating a Flask application is creating the application object. Each
Flask application is an instance of the :class:`.Flask` class, which collects all
configuration, extensions, and views.
.. code-block:: python
from flask import Flask
app = Flask(__name__)
app.config.from_mapping(
SECRET_KEY="dev",
)
app.config.from_prefixed_env()
@app.route("/")
def index():
return "Hello, World!"
This is known as the "application setup phase", it's the code you write that's outside
any view functions or other handlers. It can be split up between different modules and
sub-packages, but all code that you want to be part of your application must be imported
in order for it to be registered.
All application setup must be completed before you start serving your application and
handling requests. This is because WSGI servers divide work between multiple workers, or
can be distributed across multiple machines. If the configuration changed in one worker,
there's no way for Flask to ensure consistency between other workers.
Flask tries to help developers catch some of these setup ordering issues by showing an
error if setup-related methods are called after requests are handled. In that case
you'll see this error:
The setup method 'route' can no longer be called on the application. It has already
handled its first request, any changes will not be applied consistently.
Make sure all imports, decorators, functions, etc. needed to set up the application
are done before running it.
However, it is not possible for Flask to detect all cases of out-of-order setup. In
general, don't do anything to modify the ``Flask`` app object and ``Blueprint`` objects
from within view functions that run during requests. This includes:
- Adding routes, view functions, and other request handlers with ``@app.route``,
``@app.errorhandler``, ``@app.before_request``, etc.
- Registering blueprints.
- Loading configuration with ``app.config``.
- Setting up the Jinja template environment with ``app.jinja_env``.
- Setting a session interface, instead of the default itsdangerous cookie.
- Setting a JSON provider with ``app.json``, instead of the default provider.
- Creating and initializing Flask extensions.
Serving the Application
-----------------------
Flask is a WSGI application framework. The other half of WSGI is the WSGI server. During
development, Flask, through Werkzeug, provides a development WSGI server with the
``flask run`` CLI command. When you are done with development, use a production server
to serve your application, see :doc:`deploying/index`.
Regardless of what server you're using, it will follow the :pep:`3333` WSGI spec. The
WSGI server will be told how to access your Flask application object, which is the WSGI
application. Then it will start listening for HTTP requests, translate the request data
into a WSGI environ, and call the WSGI application with that data. The WSGI application
will return data that is translated into an HTTP response.
#. Browser or other client makes HTTP request.
#. WSGI server receives request.
#. WSGI server converts HTTP data to WSGI ``environ`` dict.
#. WSGI server calls WSGI application with the ``environ``.
#. Flask, the WSGI application, does all its internal processing to route the request
to a view function, handle errors, etc.
#. Flask translates View function return into WSGI response data, passes it to WSGI
server.
#. WSGI server creates and send an HTTP response.
#. Client receives the HTTP response.
Middleware
~~~~~~~~~~
The WSGI application above is a callable that behaves in a certain way. Middleware
is a WSGI application that wraps another WSGI application. It's a similar concept to
Python decorators. The outermost middleware will be called by the server. It can modify
the data passed to it, then call the WSGI application (or further middleware) that it
wraps, and so on. And it can take the return value of that call and modify it further.
From the WSGI server's perspective, there is one WSGI application, the one it calls
directly. Typically, Flask is the "real" application at the end of the chain of
middleware. But even Flask can call further WSGI applications, although that's an
advanced, uncommon use case.
A common middleware you'll see used with Flask is Werkzeug's
:class:`~werkzeug.middleware.proxy_fix.ProxyFix`, which modifies the request to look
like it came directly from a client even if it passed through HTTP proxies on the way.
There are other middleware that can handle serving static files, authentication, etc.
How a Request is Handled
------------------------
For us, the interesting part of the steps above is when Flask gets called by the WSGI
server (or middleware). At that point, it will do quite a lot to handle the request and
generate the response. At the most basic, it will match the URL to a view function, call
the view function, and pass the return value back to the server. But there are many more
parts that you can use to customize its behavior.
#. WSGI server calls the Flask object, which calls :meth:`.Flask.wsgi_app`.
#. An :class:`.AppContext` object is created. This converts the WSGI ``environ``
dict into a :class:`.Request` object.
#. The :doc:`app context <appcontext>` is pushed, which makes
:data:`.current_app`, :data:`.g`, :data:`.request`, and :data:`.session`
available.
#. The :data:`.appcontext_pushed` signal is sent.
#. The URL is matched against the URL rules registered with the :meth:`~.Flask.route`
decorator during application setup. If there is no match, the error - usually a 404,
405, or redirect - is stored to be handled later.
#. The :data:`.request_started` signal is sent.
#. Any :meth:`~.Flask.url_value_preprocessor` decorated functions are called.
#. Any :meth:`~.Flask.before_request` decorated functions are called. If any of
these function returns a value it is treated as the response immediately.
#. If the URL didn't match a route a few steps ago, that error is raised now.
#. The :meth:`~.Flask.route` decorated view function associated with the matched URL
is called and returns a value to be used as the response.
#. If any step so far raised an exception, and there is an :meth:`~.Flask.errorhandler`
decorated function that matches the exception class or HTTP error code, it is
called to handle the error and return a response.
#. Whatever returned a response value - a before request function, the view, or an
error handler, that value is converted to a :class:`.Response` object.
#. Any :func:`~.after_this_request` decorated functions are called, which can modify
the response object. They are then cleared.
#. Any :meth:`~.Flask.after_request` decorated functions are called, which can modify
the response object.
#. The session is saved, persisting any modified session data using the app's
:attr:`~.Flask.session_interface`.
#. The :data:`.request_finished` signal is sent.
#. If any step so far raised an exception, and it was not handled by an error handler
function, it is handled now. HTTP exceptions are treated as responses with their
corresponding status code, other exceptions are converted to a generic 500 response.
The :data:`.got_request_exception` signal is sent.
#. The response object's status, headers, and body are returned to the WSGI server.
#. Any :meth:`~.Flask.teardown_request` decorated functions are called.
#. The :data:`.request_tearing_down` signal is sent.
#. Any :meth:`~.Flask.teardown_appcontext` decorated functions are called.
#. The :data:`.appcontext_tearing_down` signal is sent.
#. The app context is popped, :data:`.current_app`, :data:`.g`, :data:`.request`,
and :data:`.session` are no longer available.
#. The :data:`.appcontext_popped` signal is sent.
When executing a CLI command or plain app context without request data, the same
order of steps is followed, omitting the steps that refer to the request.
A :class:`Blueprint` can add handlers for these events that are specific to the
blueprint. The handlers for a blueprint will run if the blueprint
owns the route that matches the request.
There are even more decorators and customization points than this, but that aren't part
of every request lifecycle. They're more specific to certain things you might use during
a request, such as templates, building URLs, or handling JSON data. See the rest of this
documentation, as well as the :doc:`api` to explore further.

View file

@ -1,12 +1,12 @@
.. _logging:
Logging Logging
======= =======
Flask uses standard Python :mod:`logging`. Messages about your Flask Flask uses standard Python :mod:`logging`. All Flask-related messages are
application are logged with :meth:`app.logger <flask.Flask.logger>`, logged under the ``'flask'`` logger namespace.
which takes the same name as :attr:`app.name <flask.Flask.name>`. This :meth:`Flask.logger <flask.Flask.logger>` returns the logger named
logger can also be used to log your own messages. ``'flask.app'``, and can be used to log messages for your application. ::
.. code-block:: python
@app.route('/login', methods=['POST']) @app.route('/login', methods=['POST'])
def login(): def login():
@ -20,9 +20,6 @@ logger can also be used to log your own messages.
app.logger.info('%s failed to log in', user.username) app.logger.info('%s failed to log in', user.username)
abort(401) abort(401)
If you don't configure logging, Python's default log level is usually
'warning'. Nothing below the configured level will be visible.
Basic Configuration Basic Configuration
------------------- -------------------
@ -120,18 +117,13 @@ your own fields that can be used in messages. You can change the formatter for
Flask's default handler, the mail handler defined above, or any other Flask's default handler, the mail handler defined above, or any other
handler. :: handler. ::
from flask import has_request_context, request from flask import request
from flask.logging import default_handler from flask.logging import default_handler
class RequestFormatter(logging.Formatter): class RequestFormatter(logging.Formatter):
def format(self, record): def format(self, record):
if has_request_context(): record.url = request.url
record.url = request.url record.remote_addr = request.remote_addr
record.remote_addr = request.remote_addr
else:
record.url = None
record.remote_addr = None
return super().format(record) return super().format(record)
formatter = RequestFormatter( formatter = RequestFormatter(
@ -159,7 +151,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'),
): ):

BIN
docs/logo.pdf Normal file

Binary file not shown.

View file

@ -1,35 +1,36 @@
@ECHO OFF @ECHO OFF
pushd %~dp0 pushd %~dp0
REM Command file for Sphinx documentation REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" ( if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build set SPHINXBUILD=sphinx-build
) )
set SOURCEDIR=. set SOURCEDIR=.
set BUILDDIR=_build set BUILDDIR=_build
set SPHINXPROJ=Flask
if "%1" == "" goto help
if "%1" == "" goto help
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 ( %SPHINXBUILD% >NUL 2>NUL
echo. if errorlevel 9009 (
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.
echo.installed, then set the SPHINXBUILD environment variable to point echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.installed, then set the SPHINXBUILD environment variable to point
echo.may add the Sphinx directory to PATH. echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo. echo.may add the Sphinx directory to PATH.
echo.If you don't have Sphinx installed, grab it from echo.
echo.http://sphinx-doc.org/ echo.If you don't have Sphinx installed, grab it from
exit /b 1 echo.http://sphinx-doc.org/
) exit /b 1
)
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% :help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
:end
popd :end
popd

View file

@ -0,0 +1,63 @@
Implementing API Exceptions
===========================
It's very common to implement RESTful APIs on top of Flask. One of the
first things that developers run into is the realization that the builtin
exceptions are not expressive enough for APIs and that the content type of
:mimetype:`text/html` they are emitting is not very useful for API consumers.
The better solution than using ``abort`` to signal errors for invalid API
usage is to implement your own exception type and install an error handler
for it that produces the errors in the format the user is expecting.
Simple Exception Class
----------------------
The basic idea is to introduce a new exception that can take a proper
human readable message, a status code for the error and some optional
payload to give more context for the error.
This is a simple example::
from flask import jsonify
class InvalidUsage(Exception):
status_code = 400
def __init__(self, message, status_code=None, payload=None):
Exception.__init__(self)
self.message = message
if status_code is not None:
self.status_code = status_code
self.payload = payload
def to_dict(self):
rv = dict(self.payload or ())
rv['message'] = self.message
return rv
A view can now raise that exception with an error message. Additionally
some extra payload can be provided as a dictionary through the `payload`
parameter.
Registering an Error Handler
----------------------------
At that point views can raise that error, but it would immediately result
in an internal server error. The reason for this is that there is no
handler registered for this error class. That however is easy to add::
@app.errorhandler(InvalidUsage)
def handle_invalid_usage(error):
response = jsonify(error.to_dict())
response.status_code = error.status_code
return response
Usage in Views
--------------
Here is how a view can use that functionality::
@app.route('/foo')
def get_foo():
raise InvalidUsage('This view is gone', status_code=410)

View file

@ -1,3 +1,5 @@
.. _app-dispatch:
Application Dispatching Application Dispatching
======================= =======================
@ -8,30 +10,44 @@ Django and a Flask application in the same interpreter side by side if
you want. The usefulness of this depends on how the applications work you want. The usefulness of this depends on how the applications work
internally. internally.
The fundamental difference from :doc:`packages` is that in this case you The fundamental difference from the :ref:`module approach
are running the same or different Flask applications that are entirely <larger-applications>` is that in this case you are running the same or
isolated from each other. They run different configurations and are different Flask applications that are entirely isolated from each other.
dispatched on the WSGI level. They run different configurations and are dispatched on the WSGI level.
Working with this Document Working with this Document
-------------------------- --------------------------
Each of the techniques and examples below results in an ``application`` Each of the techniques and examples below results in an ``application`` object
object that can be run with any WSGI server. For development, use the that can be run with any WSGI server. For production, see :ref:`deployment`.
``flask run`` command to start a development server. For production, see For development, Werkzeug provides a builtin server for development available
:doc:`/deploying/index`. at :func:`werkzeug.serving.run_simple`::
.. code-block:: python from werkzeug.serving import run_simple
run_simple('localhost', 5000, application, use_reloader=True)
Note that :func:`run_simple <werkzeug.serving.run_simple>` is not intended for
use in production. Use a :ref:`full-blown WSGI server <deployment>`.
In order to use the interactive debugger, debugging must be enabled both on
the application and the simple server. Here is the "hello world" example with
debugging and :func:`run_simple <werkzeug.serving.run_simple>`::
from flask import Flask from flask import Flask
from werkzeug.serving import run_simple
app = Flask(__name__) app = Flask(__name__)
app.debug = True
@app.route('/') @app.route('/')
def hello_world(): def hello_world():
return 'Hello World!' return 'Hello World!'
if __name__ == '__main__':
run_simple('localhost', 5000, app,
use_reloader=True, use_debugger=True, use_evalex=True)
Combining Applications Combining Applications
---------------------- ----------------------
@ -44,16 +60,14 @@ are combined by the dispatcher middleware into a larger one that is
dispatched based on prefix. dispatched based on prefix.
For example you could have your main application run on ``/`` and your For example you could have your main application run on ``/`` and your
backend interface on ``/backend``. backend interface on ``/backend``::
.. code-block:: python from werkzeug.wsgi import DispatcherMiddleware
from werkzeug.middleware.dispatcher import DispatcherMiddleware
from frontend_app import application as frontend from frontend_app import application as frontend
from backend_app import application as backend from backend_app import application as backend
application = DispatcherMiddleware(frontend, { application = DispatcherMiddleware(frontend, {
'/backend': backend '/backend': backend
}) })
@ -65,7 +79,7 @@ with different configurations. Assuming the application is created inside
a function and you can call that function to instantiate it, that is a function and you can call that function to instantiate it, that is
really easy to implement. In order to develop your application to support really easy to implement. In order to develop your application to support
creating new instances in functions have a look at the creating new instances in functions have a look at the
:doc:`appfactories` pattern. :ref:`app-factories` pattern.
A very common example would be creating applications per subdomain. For A very common example would be creating applications per subdomain. For
instance you configure your webserver to dispatch all requests for all instance you configure your webserver to dispatch all requests for all
@ -77,13 +91,11 @@ the dynamic application creation.
The perfect level for abstraction in that regard is the WSGI layer. You The perfect level for abstraction in that regard is the WSGI layer. You
write your own WSGI application that looks at the request that comes and write your own WSGI application that looks at the request that comes and
delegates it to your Flask application. If that application does not delegates it to your Flask application. If that application does not
exist yet, it is dynamically created and remembered. exist yet, it is dynamically created and remembered::
.. code-block:: python
from threading import Lock from threading import Lock
class SubdomainDispatcher: class SubdomainDispatcher(object):
def __init__(self, domain, create_app): def __init__(self, domain, create_app):
self.domain = domain self.domain = domain
@ -107,9 +119,7 @@ exist yet, it is dynamically created and remembered.
return app(environ, start_response) return app(environ, start_response)
This dispatcher can then be used like this: This dispatcher can then be used like this::
.. code-block:: python
from myapplication import create_app, get_user_for_subdomain from myapplication import create_app, get_user_for_subdomain
from werkzeug.exceptions import NotFound from werkzeug.exceptions import NotFound
@ -135,14 +145,12 @@ Dispatch by Path
Dispatching by a path on the URL is very similar. Instead of looking at Dispatching by a path on the URL is very similar. Instead of looking at
the ``Host`` header to figure out the subdomain one simply looks at the the ``Host`` header to figure out the subdomain one simply looks at the
request path up to the first slash. request path up to the first slash::
.. code-block:: python
from threading import Lock from threading import Lock
from wsgiref.util import shift_path_info from werkzeug.wsgi import pop_path_info, peek_path_info
class PathDispatcher: class PathDispatcher(object):
def __init__(self, default_app, create_app): def __init__(self, default_app, create_app):
self.default_app = default_app self.default_app = default_app
@ -160,24 +168,15 @@ request path up to the first slash.
return app return app
def __call__(self, environ, start_response): def __call__(self, environ, start_response):
app = self.get_application(_peek_path_info(environ)) app = self.get_application(peek_path_info(environ))
if app is not None: if app is not None:
shift_path_info(environ) pop_path_info(environ)
else: else:
app = self.default_app app = self.default_app
return app(environ, start_response) return app(environ, start_response)
def _peek_path_info(environ):
segments = environ.get("PATH_INFO", "").lstrip("/").split("/", 1)
if segments:
return segments[0]
return None
The big difference between this and the subdomain one is that this one The big difference between this and the subdomain one is that this one
falls back to another application if the creator function returns ``None``. falls back to another application if the creator function returns ``None``::
.. code-block:: python
from myapplication import create_app, default_app, get_user_for_prefix from myapplication import create_app, default_app, get_user_for_prefix

View file

@ -1,8 +1,10 @@
.. _app-factories:
Application Factories Application Factories
===================== =====================
If you are already using packages and blueprints for your application If you are already using packages and blueprints for your application
(:doc:`/blueprints`) there are a couple of really nice ways to further improve (:ref:`blueprints`) there are a couple of really nice ways to further improve
the experience. A common pattern is creating the application object when the experience. A common pattern is creating the application object when
the blueprint is imported. But if you move the creation of this object the blueprint is imported. But if you move the creation of this object
into a function, you can then create multiple instances of this app later. into a function, you can then create multiple instances of this app later.
@ -58,7 +60,7 @@ Factories & Extensions
It's preferable to create your extensions and app factories so that the It's preferable to create your extensions and app factories so that the
extension object does not initially get bound to the application. extension object does not initially get bound to the application.
Using `Flask-SQLAlchemy <https://flask-sqlalchemy.palletsprojects.com/>`_, Using `Flask-SQLAlchemy <http://flask-sqlalchemy.pocoo.org/>`_,
as an example, you should not do something along those lines:: as an example, you should not do something along those lines::
def create_app(config_filename): def create_app(config_filename):
@ -87,22 +89,19 @@ For more information about the design of extensions refer to :doc:`/extensiondev
Using Applications Using Applications
------------------ ------------------
To run such an application, you can use the :command:`flask` command: To run such an application, you can use the :command:`flask` command::
.. code-block:: text export FLASK_APP=myapp
flask run
Flask will automatically detect the factory (``create_app`` or ``make_app``)
in ``myapp``. You can also pass arguments to the factory like this::
$ flask --app hello run export FLASK_APP="myapp:create_app('dev')"
flask run
Flask will automatically detect the factory if it is named
``create_app`` or ``make_app`` in ``hello``. You can also pass arguments Then the ``create_app`` factory in ``myapp`` is called with the string
to the factory like this: ``'dev'`` as the argument. See :doc:`/cli` for more detail.
.. code-block:: text
$ flask --app 'hello:create_app(local_auth=True)' run
Then the ``create_app`` factory in ``hello`` is called with the keyword
argument ``local_auth=True``. See :doc:`/cli` for more detail.
Factory Improvements Factory Improvements
-------------------- --------------------

View file

@ -1,3 +1,5 @@
.. _caching-pattern:
Caching Caching
======= =======
@ -8,9 +10,60 @@ still be good enough if they were 5 minutes old. So then the idea is that
you actually put the result of that calculation into a cache for some you actually put the result of that calculation into a cache for some
time. time.
Flask itself does not provide caching for you, but `Flask-Caching`_, an Flask itself does not provide caching for you, but Werkzeug, one of the
extension for Flask does. Flask-Caching supports various backends, and it is libraries it is based on, has some very basic cache support. It supports
even possible to develop your own caching backend. multiple cache backends, normally you want to use a memcached server.
Setting up a Cache
------------------
.. _Flask-Caching: https://flask-caching.readthedocs.io/en/latest/ You create a cache object once and keep it around, similar to how
:class:`~flask.Flask` objects are created. If you are using the
development server you can create a
:class:`~werkzeug.contrib.cache.SimpleCache` object, that one is a simple
cache that keeps the item stored in the memory of the Python interpreter::
from werkzeug.contrib.cache import SimpleCache
cache = SimpleCache()
If you want to use memcached, make sure to have one of the memcache modules
supported (you get them from `PyPI <https://pypi.org/>`_) and a
memcached server running somewhere. This is how you connect to such an
memcached server then::
from werkzeug.contrib.cache import MemcachedCache
cache = MemcachedCache(['127.0.0.1:11211'])
If you are using App Engine, you can connect to the App Engine memcache
server easily::
from werkzeug.contrib.cache import GAEMemcachedCache
cache = GAEMemcachedCache()
Using a Cache
-------------
Now how can one use such a cache? There are two very important
operations: :meth:`~werkzeug.contrib.cache.BaseCache.get` and
:meth:`~werkzeug.contrib.cache.BaseCache.set`. This is how to use them:
To get an item from the cache call
:meth:`~werkzeug.contrib.cache.BaseCache.get` with a string as key name.
If something is in the cache, it is returned. Otherwise that function
will return ``None``::
rv = cache.get('my-item')
To add items to the cache, use the :meth:`~werkzeug.contrib.cache.BaseCache.set`
method instead. The first argument is the key and the second the value
that should be set. Also a timeout can be provided after which the cache
will automatically remove item.
Here a full example how this looks like normally::
def get_my_item():
rv = cache.get('my-item')
if rv is None:
rv = calculate_value()
cache.set('my-item', rv, timeout=5 * 60)
return rv

View file

@ -1,242 +1,101 @@
Background Tasks with Celery Celery Background Tasks
============================ =======================
If your application has a long running task, such as processing some uploaded data or If your application has a long running task, such as processing some uploaded
sending email, you don't want to wait for it to finish during a request. Instead, use a data or sending email, you don't want to wait for it to finish during a
task queue to send the necessary data to another process that will run the task in the request. Instead, use a task queue to send the necessary data to another
background while the request returns immediately. process that will run the task in the background while the request returns
immediately.
`Celery`_ is a powerful task queue that can be used for simple background tasks as well
as complex multi-stage programs and schedules. This guide will show you how to configure
Celery using Flask. Read Celery's `First Steps with Celery`_ guide to learn how to use
Celery itself.
.. _Celery: https://celery.readthedocs.io
.. _First Steps with Celery: https://celery.readthedocs.io/en/latest/getting-started/first-steps-with-celery.html
The Flask repository contains `an example <https://github.com/pallets/flask/tree/main/examples/celery>`_
based on the information on this page, which also shows how to use JavaScript to submit
tasks and poll for progress and results.
Celery is a powerful task queue that can be used for simple background tasks
as well as complex multi-stage programs and schedules. This guide will show you
how to configure Celery using Flask, but assumes you've already read the
`First Steps with Celery <http://docs.celeryproject.org/en/latest/getting-started/first-steps-with-celery.html>`_
guide in the Celery documentation.
Install Install
------- -------
Install Celery from PyPI, for example using pip: Celery is a separate Python package. Install it from PyPI using pip::
.. code-block:: text
$ pip install celery $ pip install celery
Configure
---------
Integrate Celery with Flask The first thing you need is a Celery instance, this is called the celery
--------------------------- application. It serves the same purpose as the :class:`~flask.Flask`
object in Flask, just for Celery. Since this instance is used as the
entry-point for everything you want to do in Celery, like creating tasks
and managing workers, it must be possible for other modules to import it.
You can use Celery without any integration with Flask, but it's convenient to configure For instance you can place this in a ``tasks`` module. While you can use
it through Flask's config, and to let tasks access the Flask application. Celery without any reconfiguration with Flask, it becomes a bit nicer by
subclassing tasks and adding support for Flask's application contexts and
hooking it up with the Flask configuration.
Celery uses similar ideas to Flask, with a ``Celery`` app object that has configuration This is all that is necessary to properly integrate Celery with Flask::
and registers tasks. While creating a Flask app, use the following code to create and
configure a Celery app as well.
.. code-block:: python from celery import Celery
from celery import Celery, Task def make_celery(app):
celery = Celery(
app.import_name,
backend=app.config['CELERY_RESULT_BACKEND'],
broker=app.config['CELERY_BROKER_URL']
)
celery.conf.update(app.config)
def celery_init_app(app: Flask) -> Celery: class ContextTask(celery.Task):
class FlaskTask(Task): def __call__(self, *args, **kwargs):
def __call__(self, *args: object, **kwargs: object) -> object:
with app.app_context(): with app.app_context():
return self.run(*args, **kwargs) return self.run(*args, **kwargs)
celery_app = Celery(app.name, task_cls=FlaskTask) celery.Task = ContextTask
celery_app.config_from_object(app.config["CELERY"]) return celery
celery_app.set_default()
app.extensions["celery"] = celery_app
return celery_app
This creates and returns a ``Celery`` app object. Celery `configuration`_ is taken from The function creates a new Celery object, configures it with the broker
the ``CELERY`` key in the Flask configuration. The Celery app is set as the default, so from the application config, updates the rest of the Celery config from
that it is seen during each request. The ``Task`` subclass automatically runs task the Flask config and then creates a subclass of the task that wraps the
functions with a Flask app context active, so that services like your database task execution in an application context.
connections are available.
.. _configuration: https://celery.readthedocs.io/en/stable/userguide/configuration.html An example task
---------------
Here's a basic ``example.py`` that configures Celery to use Redis for communication. We Let's write a task that adds two numbers together and returns the result. We
enable a result backend, but ignore results by default. This allows us to store results configure Celery's broker and backend to use Redis, create a ``celery``
only for tasks where we care about the result. application using the factor from above, and then use it to define the task. ::
.. code-block:: python
from flask import Flask from flask import Flask
app = Flask(__name__) flask_app = Flask(__name__)
app.config.from_mapping( flask_app.config.update(
CELERY=dict( CELERY_BROKER_URL='redis://localhost:6379',
broker_url="redis://localhost", CELERY_RESULT_BACKEND='redis://localhost:6379'
result_backend="redis://localhost",
task_ignore_result=True,
),
) )
celery_app = celery_init_app(app) celery = make_celery(flask_app)
Point the ``celery worker`` command at this and it will find the ``celery_app`` object. @celery.task()
def add_together(a, b):
.. code-block:: text
$ celery -A example worker --loglevel INFO
You can also run the ``celery beat`` command to run tasks on a schedule. See Celery's
docs for more information about defining schedules.
.. code-block:: text
$ celery -A example beat --loglevel INFO
Application Factory
-------------------
When using the Flask application factory pattern, call the ``celery_init_app`` function
inside the factory. It sets ``app.extensions["celery"]`` to the Celery app object, which
can be used to get the Celery app from the Flask app returned by the factory.
.. code-block:: python
def create_app() -> Flask:
app = Flask(__name__)
app.config.from_mapping(
CELERY=dict(
broker_url="redis://localhost",
result_backend="redis://localhost",
task_ignore_result=True,
),
)
app.config.from_prefixed_env()
celery_init_app(app)
return app
To use ``celery`` commands, Celery needs an app object, but that's no longer directly
available. Create a ``make_celery.py`` file that calls the Flask app factory and gets
the Celery app from the returned Flask app.
.. code-block:: python
from example import create_app
flask_app = create_app()
celery_app = flask_app.extensions["celery"]
Point the ``celery`` command to this file.
.. code-block:: text
$ celery -A make_celery worker --loglevel INFO
$ celery -A make_celery beat --loglevel INFO
Defining Tasks
--------------
Using ``@celery_app.task`` to decorate task functions requires access to the
``celery_app`` object, which won't be available when using the factory pattern. It also
means that the decorated tasks are tied to the specific Flask and Celery app instances,
which could be an issue during testing if you change configuration for a test.
Instead, use Celery's ``@shared_task`` decorator. This creates task objects that will
access whatever the "current app" is, which is a similar concept to Flask's blueprints
and app context. This is why we called ``celery_app.set_default()`` above.
Here's an example task that adds two numbers together and returns the result.
.. code-block:: python
from celery import shared_task
@shared_task(ignore_result=False)
def add_together(a: int, b: int) -> int:
return a + b return a + b
Earlier, we configured Celery to ignore task results by default. Since we want to know This task can now be called in the background::
the return value of this task, we set ``ignore_result=False``. On the other hand, a task
that didn't need a result, such as sending an email, wouldn't set this.
result = add_together.delay(23, 42)
result.wait() # 65
Calling Tasks Run a worker
------------- ------------
The decorated function becomes a task object with methods to call it in the background. If you jumped in and already executed the above code you will be
The simplest way is to use the ``delay(*args, **kwargs)`` method. See Celery's docs for disappointed to learn that ``.wait()`` will never actually return.
more methods. That's because you also need to run a Celery worker to receive and execute the
task. ::
A Celery worker must be running to run the task. Starting a worker is shown in the $ celery -A your_application.celery worker
previous sections.
.. code-block:: python The ``your_application`` string has to point to your application's package
or module that creates the ``celery`` object.
from flask import request Now that the worker is running, ``wait`` will return the result once the task
is finished.
@app.post("/add")
def start_add() -> dict[str, object]:
a = request.form.get("a", type=int)
b = request.form.get("b", type=int)
result = add_together.delay(a, b)
return {"result_id": result.id}
The route doesn't get the task's result immediately. That would defeat the purpose by
blocking the response. Instead, we return the running task's result id, which we can use
later to get the result.
Getting Results
---------------
To fetch the result of the task we started above, we'll add another route that takes the
result id we returned before. We return whether the task is finished (ready), whether it
finished successfully, and what the return value (or error) was if it is finished.
.. code-block:: python
from celery.result import AsyncResult
@app.get("/result/<id>")
def task_result(id: str) -> dict[str, object]:
result = AsyncResult(id)
return {
"ready": result.ready(),
"successful": result.successful(),
"value": result.result if result.ready() else None,
}
Now you can start the task using the first route, then poll for the result using the
second route. This keeps the Flask request workers from being blocked waiting for tasks
to finish.
The Flask repository contains `an example <https://github.com/pallets/flask/tree/main/examples/celery>`_
using JavaScript to submit tasks and poll for progress and results.
Passing Data to Tasks
---------------------
The "add" task above took two integers as arguments. To pass arguments to tasks, Celery
has to serialize them to a format that it can pass to other processes. Therefore,
passing complex objects is not recommended. For example, it would be impossible to pass
a SQLAlchemy model object, since that object is probably not serializable and is tied to
the session that queried it.
Pass the minimal amount of data necessary to fetch or recreate any complex data within
the task. Consider a task that will run when the logged in user asks for an archive of
their data. The Flask request knows the logged in user, and has the user object queried
from the database. It got that by querying the database for a given id, so the task can
do the same thing. Pass the user's id rather than the user object.
.. code-block:: python
@shared_task
def generate_user_archive(user_id: str) -> None:
user = db.session.get(User, user_id)
...
generate_user_archive.delay(current_user.id)

View file

@ -1,3 +1,5 @@
.. _deferred-callbacks:
Deferred Request Callbacks Deferred Request Callbacks
========================== ==========================
@ -14,7 +16,7 @@ response object.
One way is to avoid the situation. Very often that is possible. For instance One way is to avoid the situation. Very often that is possible. For instance
you can try to move that logic into a :meth:`~flask.Flask.after_request` you can try to move that logic into a :meth:`~flask.Flask.after_request`
callback instead. However, sometimes moving code there makes it callback instead. However, sometimes moving code there makes it more
more complicated or awkward to reason about. more complicated or awkward to reason about.
As an alternative, you can use :func:`~flask.after_this_request` to register As an alternative, you can use :func:`~flask.after_this_request` to register
@ -39,6 +41,5 @@ user in a cookie in a :meth:`~flask.Flask.before_request` callback::
@after_this_request @after_this_request
def remember_language(response): def remember_language(response):
response.set_cookie('user_lang', language) response.set_cookie('user_lang', language)
return response
g.language = language g.language = language

View file

@ -0,0 +1,177 @@
.. _distribute-deployment:
Deploying with Setuptools
=========================
`Setuptools`_, is an extension library that is commonly used to
distribute Python libraries and extensions. It extends distutils, a basic
module installation system shipped with Python to also support various more
complex constructs that make larger applications easier to distribute:
- **support for dependencies**: a library or application can declare a
list of other libraries it depends on which will be installed
automatically for you.
- **package registry**: setuptools registers your package with your
Python installation. This makes it possible to query information
provided by one package from another package. The best known feature of
this system is the entry point support which allows one package to
declare an "entry point" that another package can hook into to extend the
other package.
- **installation manager**: :command:`pip` can install other libraries for you.
If you have Python 2 (>=2.7.9) or Python 3 (>=3.4) installed from python.org,
you will already have pip and setuptools on your system. Otherwise, you
will need to install them yourself.
Flask itself, and all the libraries you can find on PyPI are distributed with
either setuptools or distutils.
In this case we assume your application is called
:file:`yourapplication.py` and you are not using a module, but a :ref:`package
<larger-applications>`. If you have not yet converted your application into
a package, head over to the :ref:`larger-applications` pattern to see
how this can be done.
A working deployment with setuptools is the first step into more complex
and more automated deployment scenarios. If you want to fully automate
the process, also read the :ref:`fabric-deployment` chapter.
Basic Setup Script
------------------
Because you have Flask installed, you have setuptools available on your system.
Flask already depends upon setuptools.
Standard disclaimer applies: :ref:`you better use a virtualenv
<virtualenv>`.
Your setup code always goes into a file named :file:`setup.py` next to your
application. The name of the file is only convention, but because
everybody will look for a file with that name, you better not change it.
A basic :file:`setup.py` file for a Flask application looks like this::
from setuptools import setup
setup(
name='Your Application',
version='1.0',
long_description=__doc__,
packages=['yourapplication'],
include_package_data=True,
zip_safe=False,
install_requires=['Flask']
)
Please keep in mind that you have to list subpackages explicitly. If you
want setuptools to lookup the packages for you automatically, you can use
the ``find_packages`` function::
from setuptools import setup, find_packages
setup(
...
packages=find_packages()
)
Most parameters to the ``setup`` function should be self explanatory,
``include_package_data`` and ``zip_safe`` might not be.
``include_package_data`` tells setuptools to look for a :file:`MANIFEST.in` file
and install all the entries that match as package data. We will use this
to distribute the static files and templates along with the Python module
(see :ref:`distributing-resources`). The ``zip_safe`` flag can be used to
force or prevent zip Archive creation. In general you probably don't want
your packages to be installed as zip files because some tools do not
support them and they make debugging a lot harder.
Tagging Builds
--------------
It is useful to distinguish between release and development builds. Add a
:file:`setup.cfg` file to configure these options. ::
[egg_info]
tag_build = .dev
tag_date = 1
[aliases]
release = egg_info -Db ''
Running ``python setup.py sdist`` will create a development package
with ".dev" and the current date appended: ``flaskr-1.0.dev20160314.tar.gz``.
Running ``python setup.py release sdist`` will create a release package
with only the version: ``flaskr-1.0.tar.gz``.
.. _distributing-resources:
Distributing Resources
----------------------
If you try to install the package you just created, you will notice that
folders like :file:`static` or :file:`templates` are not installed for you. The
reason for this is that setuptools does not know which files to add for
you. What you should do, is to create a :file:`MANIFEST.in` file next to your
:file:`setup.py` file. This file lists all the files that should be added to
your tarball::
recursive-include yourapplication/templates *
recursive-include yourapplication/static *
Don't forget that even if you enlist them in your :file:`MANIFEST.in` file, they
won't be installed for you unless you set the `include_package_data`
parameter of the ``setup`` function to ``True``!
Declaring Dependencies
----------------------
Dependencies are declared in the ``install_requires`` parameter as a list.
Each item in that list is the name of a package that should be pulled from
PyPI on installation. By default it will always use the most recent
version, but you can also provide minimum and maximum version
requirements. Here some examples::
install_requires=[
'Flask>=0.2',
'SQLAlchemy>=0.6',
'BrokenPackage>=0.7,<=1.0'
]
As mentioned earlier, dependencies are pulled from PyPI. What if you
want to depend on a package that cannot be found on PyPI and won't be
because it is an internal package you don't want to share with anyone?
Just do it as if there was a PyPI entry and provide a list of
alternative locations where setuptools should look for tarballs::
dependency_links=['http://example.com/yourfiles']
Make sure that page has a directory listing and the links on the page are
pointing to the actual tarballs with their correct filenames as this is
how setuptools will find the files. If you have an internal company
server that contains the packages, provide the URL to that server.
Installing / Developing
-----------------------
To install your application (ideally into a virtualenv) just run the
:file:`setup.py` script with the ``install`` parameter. It will install your
application into the virtualenv's site-packages folder and also download
and install all dependencies::
$ python setup.py install
If you are developing on the package and also want the requirements to be
installed, you can use the ``develop`` command instead::
$ python setup.py develop
This has the advantage of just installing a link to the site-packages
folder instead of copying the data over. You can then continue to work on
the code without having to run ``install`` again after each change.
.. _pip: https://pypi.org/project/pip/
.. _Setuptools: https://pypi.org/project/setuptools/

View file

@ -0,0 +1,99 @@
.. _errorpages:
Custom Error Pages
==================
Flask comes with a handy :func:`~flask.abort` function that aborts a
request with an HTTP error code early. It will also provide a plain black
and white error page for you with a basic description, but nothing fancy.
Depending on the error code it is less or more likely for the user to
actually see such an error.
Common Error Codes
------------------
The following error codes are some that are often displayed to the user,
even if the application behaves correctly:
*404 Not Found*
The good old "chap, you made a mistake typing that URL" message. So
common that even novices to the internet know that 404 means: damn,
the thing I was looking for is not there. It's a very good idea to
make sure there is actually something useful on a 404 page, at least a
link back to the index.
*403 Forbidden*
If you have some kind of access control on your website, you will have
to send a 403 code for disallowed resources. So make sure the user
is not lost when they try to access a forbidden resource.
*410 Gone*
Did you know that there the "404 Not Found" has a brother named "410
Gone"? Few people actually implement that, but the idea is that
resources that previously existed and got deleted answer with 410
instead of 404. If you are not deleting documents permanently from
the database but just mark them as deleted, do the user a favour and
use the 410 code instead and display a message that what they were
looking for was deleted for all eternity.
*500 Internal Server Error*
Usually happens on programming errors or if the server is overloaded.
A terribly good idea is to have a nice page there, because your
application *will* fail sooner or later (see also:
:ref:`application-errors`).
Error Handlers
--------------
An error handler is a function that returns a response when a type of error is
raised, similar to how a view is a function that returns a response when a
request URL is matched. It is passed the instance of the error being handled,
which is most likely a :exc:`~werkzeug.exceptions.HTTPException`. An error
handler for "500 Internal Server Error" will be passed uncaught exceptions in
addition to explicit 500 errors.
An error handler is registered with the :meth:`~flask.Flask.errorhandler`
decorator or the :meth:`~flask.Flask.register_error_handler` method. A handler
can be registered for a status code, like 404, or for an exception class.
The status code of the response will not be set to the handler's code. Make
sure to provide the appropriate HTTP status code when returning a response from
a handler.
A handler for "500 Internal Server Error" will not be used when running in
debug mode. Instead, the interactive debugger will be shown.
Here is an example implementation for a "404 Page Not Found" exception::
from flask import render_template
@app.errorhandler(404)
def page_not_found(e):
# note that we set the 404 status explicitly
return render_template('404.html'), 404
When using the :ref:`application factory pattern <app-factories>`::
from flask import Flask, render_template
def page_not_found(e):
return render_template('404.html'), 404
def create_app(config_filename):
app = Flask(__name__)
app.register_error_handler(404, page_not_found)
return app
An example template might be this:
.. sourcecode:: html+jinja
{% extends "layout.html" %}
{% block title %}Page Not Found{% endblock %}
{% block body %}
<h1>Page Not Found</h1>
<p>What you were looking for is just not there.
<p><a href="{{ url_for('index') }}">go somewhere nice</a>
{% endblock %}

186
docs/patterns/fabric.rst Normal file
View file

@ -0,0 +1,186 @@
.. _fabric-deployment:
Deploying with Fabric
=====================
`Fabric`_ is a tool for Python similar to Makefiles but with the ability
to execute commands on a remote server. In combination with a properly
set up Python package (:ref:`larger-applications`) and a good concept for
configurations (:ref:`config`) it is very easy to deploy Flask
applications to external servers.
Before we get started, here a quick checklist of things we have to ensure
upfront:
- Fabric 1.0 has to be installed locally. This tutorial assumes the
latest version of Fabric.
- The application already has to be a package and requires a working
:file:`setup.py` file (:ref:`distribute-deployment`).
- In the following example we are using `mod_wsgi` for the remote
servers. You can of course use your own favourite server there, but
for this example we chose Apache + `mod_wsgi` because it's very easy
to setup and has a simple way to reload applications without root
access.
Creating the first Fabfile
--------------------------
A fabfile is what controls what Fabric executes. It is named :file:`fabfile.py`
and executed by the `fab` command. All the functions defined in that file
will show up as `fab` subcommands. They are executed on one or more
hosts. These hosts can be defined either in the fabfile or on the command
line. In this case we will add them to the fabfile.
This is a basic first example that has the ability to upload the current
source code to the server and install it into a pre-existing
virtual environment::
from fabric.api import *
# the user to use for the remote commands
env.user = 'appuser'
# the servers where the commands are executed
env.hosts = ['server1.example.com', 'server2.example.com']
def pack():
# build the package
local('python setup.py sdist --formats=gztar', capture=False)
def deploy():
# figure out the package name and version
dist = local('python setup.py --fullname', capture=True).strip()
filename = '%s.tar.gz' % dist
# upload the package to the temporary folder on the server
put('dist/%s' % filename, '/tmp/%s' % filename)
# install the package in the application's virtualenv with pip
run('/var/www/yourapplication/env/bin/pip install /tmp/%s' % filename)
# remove the uploaded package
run('rm -r /tmp/%s' % filename)
# touch the .wsgi file to trigger a reload in mod_wsgi
run('touch /var/www/yourapplication.wsgi')
Running Fabfiles
----------------
Now how do you execute that fabfile? You use the `fab` command. To
deploy the current version of the code on the remote server you would use
this command::
$ fab pack deploy
However this requires that our server already has the
:file:`/var/www/yourapplication` folder created and
:file:`/var/www/yourapplication/env` to be a virtual environment. Furthermore
are we not creating the configuration or ``.wsgi`` file on the server. So
how do we bootstrap a new server into our infrastructure?
This now depends on the number of servers we want to set up. If we just
have one application server (which the majority of applications will
have), creating a command in the fabfile for this is overkill. But
obviously you can do that. In that case you would probably call it
`setup` or `bootstrap` and then pass the servername explicitly on the
command line::
$ fab -H newserver.example.com bootstrap
To setup a new server you would roughly do these steps:
1. Create the directory structure in :file:`/var/www`::
$ mkdir /var/www/yourapplication
$ cd /var/www/yourapplication
$ virtualenv --distribute env
2. Upload a new :file:`application.wsgi` file to the server and the
configuration file for the application (eg: :file:`application.cfg`)
3. Create a new Apache config for ``yourapplication`` and activate it.
Make sure to activate watching for changes of the ``.wsgi`` file so
that we can automatically reload the application by touching it.
(See :ref:`mod_wsgi-deployment` for more information)
So now the question is, where do the :file:`application.wsgi` and
:file:`application.cfg` files come from?
The WSGI File
-------------
The WSGI file has to import the application and also to set an environment
variable so that the application knows where to look for the config. This
is a short example that does exactly that::
import os
os.environ['YOURAPPLICATION_CONFIG'] = '/var/www/yourapplication/application.cfg'
from yourapplication import app
The application itself then has to initialize itself like this to look for
the config at that environment variable::
app = Flask(__name__)
app.config.from_object('yourapplication.default_config')
app.config.from_envvar('YOURAPPLICATION_CONFIG')
This approach is explained in detail in the :ref:`config` section of the
documentation.
The Configuration File
----------------------
Now as mentioned above, the application will find the correct
configuration file by looking up the ``YOURAPPLICATION_CONFIG`` environment
variable. So we have to put the configuration in a place where the
application will able to find it. Configuration files have the unfriendly
quality of being different on all computers, so you do not version them
usually.
A popular approach is to store configuration files for different servers
in a separate version control repository and check them out on all
servers. Then symlink the file that is active for the server into the
location where it's expected (eg: :file:`/var/www/yourapplication`).
Either way, in our case here we only expect one or two servers and we can
upload them ahead of time by hand.
First Deployment
----------------
Now we can do our first deployment. We have set up the servers so that
they have their virtual environments and activated apache configs. Now we
can pack up the application and deploy it::
$ fab pack deploy
Fabric will now connect to all servers and run the commands as written
down in the fabfile. First it will execute pack so that we have our
tarball ready and then it will execute deploy and upload the source code
to all servers and install it there. Thanks to the :file:`setup.py` file we
will automatically pull in the required libraries into our virtual
environment.
Next Steps
----------
From that point onwards there is so much that can be done to make
deployment actually fun:
- Create a `bootstrap` command that initializes new servers. It could
initialize a new virtual environment, setup apache appropriately etc.
- Put configuration files into a separate version control repository
and symlink the active configs into place.
- You could also put your application code into a repository and check
out the latest version on the server and then install. That way you
can also easily go back to older versions.
- hook in testing functionality so that you can deploy to an external
server and run the test suite.
Working with Fabric is fun and you will notice that it's quite magical to
type ``fab deploy`` and see your application being deployed automatically
to one or more remote servers.
.. _Fabric: http://www.fabfile.org/

View file

@ -24,11 +24,8 @@ the root path of the domain you either need to configure the web server to
serve the icon at the root or if you can't do that you're out of luck. If 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`::

View file

@ -1,3 +1,5 @@
.. _uploading-files:
Uploading Files Uploading Files
=============== ===============
@ -23,7 +25,7 @@ bootstrapping code for our application::
from werkzeug.utils import secure_filename from werkzeug.utils import secure_filename
UPLOAD_FOLDER = '/path/to/the/uploads' UPLOAD_FOLDER = '/path/to/the/uploads'
ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'} ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'])
app = Flask(__name__) app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
@ -37,7 +39,7 @@ Why do we limit the extensions that are allowed? You probably don't want
your users to be able to upload everything there if the server is directly your users to be able to upload everything there if the server is directly
sending out the data to the client. That way you can make sure that users sending out the data to the client. That way you can make sure that users
are not able to upload HTML files that would cause XSS problems (see are not able to upload HTML files that would cause XSS problems (see
:ref:`security-xss`). Also make sure to disallow ``.php`` files if the server :ref:`xss`). Also make sure to disallow ``.php`` files if the server
executes them, but who has PHP installed on their server, right? :) executes them, but who has PHP installed on their server, right? :)
Next the functions that check if an extension is valid and that uploads Next the functions that check if an extension is valid and that uploads
@ -55,15 +57,16 @@ the file and redirects the user to the URL for the uploaded file::
flash('No file part') flash('No file part')
return redirect(request.url) return redirect(request.url)
file = request.files['file'] file = request.files['file']
# If the user does not select a file, the browser submits an # if user does not select file, browser also
# empty file without a filename. # submit an empty part without filename
if file.filename == '': if file.filename == '':
flash('No selected file') flash('No selected file')
return redirect(request.url) return redirect(request.url)
if file and allowed_file(file.filename): if file and allowed_file(file.filename):
filename = secure_filename(file.filename) filename = secure_filename(file.filename)
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
return redirect(url_for('download_file', name=filename)) return redirect(url_for('upload_file',
filename=filename))
return ''' return '''
<!doctype html> <!doctype html>
<title>Upload new File</title> <title>Upload new File</title>
@ -101,28 +104,31 @@ before storing it directly on the filesystem.
>>> secure_filename('../../../../home/username/.bashrc') >>> secure_filename('../../../../home/username/.bashrc')
'home_username_.bashrc' 'home_username_.bashrc'
We want to be able to serve the uploaded files so they can be downloaded Now one last thing is missing: the serving of the uploaded files. In the
by users. We'll define a ``download_file`` view to serve files in the :func:`upload_file()` we redirect the user to
upload folder by name. ``url_for("download_file", name=name)`` generates ``url_for('uploaded_file', filename=filename)``, that is, ``/uploads/filename``.
download URLs. So we write the :func:`uploaded_file` function to return the file of that name. As
of Flask 0.5 we can use a function that does that for us::
.. code-block:: python
from flask import send_from_directory from flask import send_from_directory
@app.route('/uploads/<name>') @app.route('/uploads/<filename>')
def download_file(name): def uploaded_file(filename):
return send_from_directory(app.config["UPLOAD_FOLDER"], name) return send_from_directory(app.config['UPLOAD_FOLDER'],
filename)
If you're using middleware or the HTTP server to serve files, you can Alternatively you can register `uploaded_file` as `build_only` rule and
register the ``download_file`` endpoint as ``build_only`` so ``url_for`` use the :class:`~werkzeug.wsgi.SharedDataMiddleware`. This also works with
will work without a view function. older versions of Flask::
.. code-block:: python from werkzeug import SharedDataMiddleware
app.add_url_rule('/uploads/<filename>', 'uploaded_file',
build_only=True)
app.wsgi_app = SharedDataMiddleware(app.wsgi_app, {
'/uploads': app.config['UPLOAD_FOLDER']
})
app.add_url_rule( If you now run the application everything should work as expected.
"/uploads/<name>", endpoint="download_file", build_only=True
)
Improving Uploads Improving Uploads
@ -131,17 +137,17 @@ Improving Uploads
.. versionadded:: 0.6 .. versionadded:: 0.6
So how exactly does Flask handle uploads? Well it will store them in the So how exactly does Flask handle uploads? Well it will store them in the
webserver's memory if the files are reasonably small, otherwise in a webserver's memory if the files are reasonable small otherwise in a
temporary location (as returned by :func:`tempfile.gettempdir`). But how temporary location (as returned by :func:`tempfile.gettempdir`). But how
do you specify the maximum file size after which an upload is aborted? By do you specify the maximum file size after which an upload is aborted? By
default Flask will happily accept file uploads with an unlimited amount of default Flask will happily accept file uploads to an unlimited amount of
memory, but you can limit that by setting the ``MAX_CONTENT_LENGTH`` memory, but you can limit that by setting the ``MAX_CONTENT_LENGTH``
config key:: config key::
from flask import Flask, Request from flask import Flask, Request
app = Flask(__name__) app = Flask(__name__)
app.config['MAX_CONTENT_LENGTH'] = 16 * 1000 * 1000 app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024
The code above will limit the maximum allowed payload to 16 megabytes. The code above will limit the maximum allowed payload to 16 megabytes.
If a larger file is transmitted, Flask will raise a If a larger file is transmitted, Flask will raise a
@ -163,9 +169,10 @@ Upload Progress Bars
A while ago many developers had the idea to read the incoming file in A while ago many developers had the idea to read the incoming file in
small chunks and store the upload progress in the database to be able to small chunks and store the upload progress in the database to be able to
poll the progress with JavaScript from the client. The client asks the poll the progress with JavaScript from the client. Long story short: the
server every 5 seconds how much it has transmitted, but this is client asks the server every 5 seconds how much it has transmitted
something it should already know. already. Do you realize the irony? The client is asking for something it
should already know.
An Easier Solution An Easier Solution
------------------ ------------------
@ -175,8 +182,9 @@ are JavaScript libraries like jQuery_ that have form plugins to ease the
construction of progress bar. construction of progress bar.
Because the common pattern for file uploads exists almost unchanged in all Because the common pattern for file uploads exists almost unchanged in all
applications dealing with uploads, there are also some Flask extensions that applications dealing with uploads, there is also a Flask extension called
implement a full fledged upload mechanism that allows controlling which `Flask-Uploads`_ that implements a full fledged upload mechanism with white and
file extensions are allowed to be uploaded. blacklisting of extensions and more.
.. _jQuery: https://jquery.com/ .. _jQuery: https://jquery.com/
.. _Flask-Uploads: https://pythonhosted.org/Flask-Uploads/

View file

@ -1,3 +1,5 @@
.. _message-flashing-pattern:
Message Flashing Message Flashing
================ ================
@ -101,7 +103,7 @@ error messages could be displayed with a red background.
To flash a message with a different category, just use the second argument To flash a message with a different category, just use the second argument
to the :func:`~flask.flash` function:: to the :func:`~flask.flash` function::
flash('Invalid password provided', 'error') flash(u'Invalid password provided', 'error')
Inside the template you then have to tell the Inside the template you then have to tell the
:func:`~flask.get_flashed_messages` function to also return the :func:`~flask.get_flashed_messages` function to also return the

View file

@ -1,16 +1,17 @@
.. _patterns:
Patterns for Flask Patterns for Flask
================== ==================
Certain features and interactions are common enough that you will find Certain things are common enough that the chances are high you will find
them in most web applications. For example, many applications use a them in most web applications. For example quite a lot of applications
relational database and user authentication. They will open a database are using relational databases and user authentication. In that case,
connection at the beginning of the request and get the information for chances are they will open a database connection at the beginning of the
the logged in user. At the end of the request, the database connection request and get the information of the currently logged in user. At the
is closed. end of the request, the database connection is closed again.
These types of patterns may be a bit outside the scope of Flask itself, There are more user contributed snippets and patterns in the `Flask
but Flask makes it easy to implement them. Some common patterns are Snippet Archives <http://flask.pocoo.org/snippets/>`_.
collected in the following pages.
.. toctree:: .. toctree::
:maxdepth: 2 :maxdepth: 2
@ -18,7 +19,10 @@ collected in the following pages.
packages packages
appfactories appfactories
appdispatch appdispatch
apierrors
urlprocessors urlprocessors
distribute
fabric
sqlite3 sqlite3
sqlalchemy sqlalchemy
fileuploads fileuploads
@ -27,9 +31,10 @@ collected in the following pages.
wtforms wtforms
templateinheritance templateinheritance
flashing flashing
javascript jquery
errorpages
lazyloading lazyloading
mongoengine mongokit
favicon favicon
streaming streaming
deferredcallbacks deferredcallbacks
@ -37,4 +42,3 @@ collected in the following pages.
requestchecksum requestchecksum
celery celery
subclassing subclassing
singlepageapplications

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