Compare commits
No commits in common. "main" and "0.8.1" have entirely different histories.
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
27
.github/ISSUE_TEMPLATE/bug-report.md
vendored
|
|
@ -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:
|
||||
11
.github/ISSUE_TEMPLATE/config.yml
vendored
|
|
@ -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.
|
||||
15
.github/ISSUE_TEMPLATE/feature-request.md
vendored
|
|
@ -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?
|
||||
-->
|
||||
25
.github/pull_request_template.md
vendored
|
|
@ -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.
|
||||
-->
|
||||
26
.github/workflows/lock.yaml
vendored
|
|
@ -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
|
||||
29
.github/workflows/pre-commit.yaml
vendored
|
|
@ -1,29 +0,0 @@
|
|||
name: pre-commit
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches: [main, stable]
|
||||
permissions: {}
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
|
||||
with:
|
||||
enable-cache: true
|
||||
prune-cache: false
|
||||
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
id: setup-python
|
||||
with:
|
||||
python-version-file: pyproject.toml
|
||||
- uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
|
||||
with:
|
||||
path: ~/.cache/pre-commit
|
||||
key: pre-commit|${{ hashFiles('pyproject.toml', '.pre-commit-config.yaml') }}
|
||||
- run: uv run --locked --no-default-groups --group pre-commit pre-commit run --show-diff-on-failure --color=always --all-files
|
||||
62
.github/workflows/publish.yaml
vendored
|
|
@ -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/"
|
||||
63
.github/workflows/tests.yaml
vendored
|
|
@ -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
|
||||
22
.github/workflows/zizmor.yaml
vendored
|
|
@ -1,22 +0,0 @@
|
|||
name: GitHub Actions security analysis with zizmor
|
||||
on:
|
||||
pull_request:
|
||||
paths: ["**/*.yaml?"]
|
||||
push:
|
||||
branches: [main, stable]
|
||||
paths: ["**/*.yaml?"]
|
||||
permissions: {}
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
jobs:
|
||||
zizmor:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: zizmorcore/zizmor-action@71321a20a9ded102f6e9ce5718a2fcec2c4f70d8 # v0.5.2
|
||||
with:
|
||||
advanced-security: false
|
||||
annotations: true
|
||||
18
.gitignore
vendored
|
|
@ -1,8 +1,10 @@
|
|||
.idea/
|
||||
.vscode/
|
||||
__pycache__/
|
||||
dist/
|
||||
.coverage*
|
||||
htmlcov/
|
||||
.tox/
|
||||
docs/_build/
|
||||
.DS_Store
|
||||
*.pyc
|
||||
*.pyo
|
||||
env
|
||||
env*
|
||||
dist
|
||||
*.egg
|
||||
*.egg-info
|
||||
_mailinglist
|
||||
.tox
|
||||
|
|
|
|||
3
.gitmodules
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
[submodule "docs/_themes"]
|
||||
path = docs/_themes
|
||||
url = git://github.com/mitsuhiko/flask-sphinx-themes.git
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
30
AUTHORS
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
Flask is written and maintained by Armin Ronacher and
|
||||
various contributors:
|
||||
|
||||
Development Lead
|
||||
````````````````
|
||||
|
||||
- Armin Ronacher <armin.ronacher@active-4.com>
|
||||
|
||||
Patches and Suggestions
|
||||
```````````````````````
|
||||
|
||||
- Adam Zapletal
|
||||
- Ali Afshar
|
||||
- Chris Edgemon
|
||||
- Chris Grindstaff
|
||||
- Christopher Grebs
|
||||
- Florent Xicluna
|
||||
- Georg Brandl
|
||||
- Justin Quick
|
||||
- Kenneth Reitz
|
||||
- Marian Sigler
|
||||
- Matt Campell
|
||||
- Matthew Frazier
|
||||
- Michael van Tellingen
|
||||
- Ron DuPlain
|
||||
- Sebastien Estienne
|
||||
- Simon Sapin
|
||||
- Stephane Wirtel
|
||||
- Thomas Schranz
|
||||
- Zhao Xiaohong
|
||||
302
CHANGES
Normal file
|
|
@ -0,0 +1,302 @@
|
|||
Flask Changelog
|
||||
===============
|
||||
|
||||
Here you can see the full list of changes between each Flask release.
|
||||
|
||||
Version 0.8.1
|
||||
-------------
|
||||
|
||||
Bugfix release, released on July 1th 2012
|
||||
|
||||
- Fixed an issue with the undocumented `flask.session` module to not
|
||||
work properly on Python 2.5. It should not be used but did cause
|
||||
some problems for package managers.
|
||||
|
||||
Version 0.8
|
||||
-----------
|
||||
|
||||
Released on September 29th 2011, codename Rakija
|
||||
|
||||
- Refactored session support into a session interface so that
|
||||
the implementation of the sessions can be changed without
|
||||
having to override the Flask class.
|
||||
- Empty session cookies are now deleted properly automatically.
|
||||
- View functions can now opt out of getting the automatic
|
||||
OPTIONS implementation.
|
||||
- HTTP exceptions and Bad Request errors can now be trapped so that they
|
||||
show up normally in the traceback.
|
||||
- Flask in debug mode is now detecting some common problems and tries to
|
||||
warn you about them.
|
||||
- Flask in debug mode will now complain with an assertion error if a view
|
||||
was attached after the first request was handled. This gives earlier
|
||||
feedback when users forget to import view code ahead of time.
|
||||
- Added the ability to register callbacks that are only triggered once at
|
||||
the beginning of the first request. (:meth:`Flask.before_first_request`)
|
||||
- Malformed JSON data will now trigger a bad request HTTP exception instead
|
||||
of a value error which usually would result in a 500 internal server
|
||||
error if not handled. This is a backwards incompatible change.
|
||||
- Applications now not only have a root path where the resources and modules
|
||||
are located but also an instane path which is the designated place to
|
||||
drop files that are modified at runtime (uploads etc.). Also this is
|
||||
conceptionally only instance depending and outside version control so it's
|
||||
the perfect place to put configuration files etc. For more information
|
||||
see :ref:`instance-folders`.
|
||||
- Added the ``APPLICATION_ROOT`` configuration variable.
|
||||
- Implemented :meth:`~flask.testing.TestClient.session_transaction` to
|
||||
easily modify sessions from the test environment.
|
||||
- Refactored test client internally. The ``APPLICATION_ROOT`` configuration
|
||||
variable as well as ``SERVER_NAME`` are now properly used by the test client
|
||||
as defaults.
|
||||
- Added :attr:`flask.views.View.decorators` to support simpler decorating of
|
||||
pluggable (class based) views.
|
||||
- Fixed an issue where the test client if used with the with statement did not
|
||||
trigger the execution of the teardown handlers.
|
||||
- Added finer control over the session cookie parameters.
|
||||
- HEAD requests to a method view now automatically dispatch to the `get`
|
||||
method if no handler was implemented.
|
||||
- Implemented the virtual :mod:`flask.ext` package to import extensions from.
|
||||
- The context preservation on exceptions is now an integral component of
|
||||
Flask itself and no longer of the test client. This cleaned up some
|
||||
internal logic and lowers the odds of runaway request contexts in unittests.
|
||||
|
||||
Version 0.7.3
|
||||
-------------
|
||||
|
||||
Bugfix release, release date to be decided
|
||||
|
||||
- Fixed the Jinja2 environment's list_templates method not returning the
|
||||
correct names when blueprints or modules were involved.
|
||||
|
||||
Version 0.7.2
|
||||
-------------
|
||||
|
||||
Bugfix release, released on July 6th 2011
|
||||
|
||||
- Fixed an issue with URL processors not properly working on
|
||||
blueprints.
|
||||
|
||||
Version 0.7.1
|
||||
-------------
|
||||
|
||||
Bugfix release, released on June 29th 2011
|
||||
|
||||
- Added missing future import that broke 2.5 compatibility.
|
||||
- Fixed an infinite redirect issue with blueprints.
|
||||
|
||||
Version 0.7
|
||||
-----------
|
||||
|
||||
Released on June 28th 2011, codename Grappa
|
||||
|
||||
- Added :meth:`~flask.Flask.make_default_options_response`
|
||||
which can be used by subclasses to alter the default
|
||||
behaviour for `OPTIONS` responses.
|
||||
- Unbound locals now raise a proper :exc:`RuntimeError` instead
|
||||
of an :exc:`AttributeError`.
|
||||
- Mimetype guessing and etag support based on file objects is now
|
||||
deprecated for :func:`flask.send_file` because it was unreliable.
|
||||
Pass filenames instead or attach your own etags and provide a
|
||||
proper mimetype by hand.
|
||||
- Static file handling for modules now requires the name of the
|
||||
static folder to be supplied explicitly. The previous autodetection
|
||||
was not reliable and caused issues on Google's App Engine. Until
|
||||
1.0 the old behaviour will continue to work but issue dependency
|
||||
warnings.
|
||||
- fixed a problem for Flask to run on jython.
|
||||
- added a `PROPAGATE_EXCEPTIONS` configuration variable that can be
|
||||
used to flip the setting of exception propagation which previously
|
||||
was linked to `DEBUG` alone and is now linked to either `DEBUG` or
|
||||
`TESTING`.
|
||||
- Flask no longer internally depends on rules being added through the
|
||||
`add_url_rule` function and can now also accept regular werkzeug
|
||||
rules added to the url map.
|
||||
- Added an `endpoint` method to the flask application object which
|
||||
allows one to register a callback to an arbitrary endpoint with
|
||||
a decorator.
|
||||
- Use Last-Modified for static file sending instead of Date which
|
||||
was incorrectly introduced in 0.6.
|
||||
- Added `create_jinja_loader` to override the loader creation process.
|
||||
- Implemented a silent flag for `config.from_pyfile`.
|
||||
- Added `teardown_request` decorator, for functions that should run at the end
|
||||
of a request regardless of whether an exception occurred. Also the behavior
|
||||
for `after_request` was changed. It's now no longer executed when an exception
|
||||
is raised. See :ref:`upgrading-to-new-teardown-handling`
|
||||
- Implemented :func:`flask.has_request_context`
|
||||
- Deprecated `init_jinja_globals`. Override the
|
||||
:meth:`~flask.Flask.create_jinja_environment` method instead to
|
||||
achieve the same functionality.
|
||||
- Added :func:`flask.safe_join`
|
||||
- The automatic JSON request data unpacking now looks at the charset
|
||||
mimetype parameter.
|
||||
- Don't modify the session on :func:`flask.get_flashed_messages` if there
|
||||
are no messages in the session.
|
||||
- `before_request` handlers are now able to abort requests with errors.
|
||||
- it is not possible to define user exception handlers. That way you can
|
||||
provide custom error messages from a central hub for certain errors that
|
||||
might occur during request processing (for instance database connection
|
||||
errors, timeouts from remote resources etc.).
|
||||
- Blueprints can provide blueprint specific error handlers.
|
||||
- Implemented generic :ref:`views` (class based views).
|
||||
|
||||
Version 0.6.1
|
||||
-------------
|
||||
|
||||
Bugfix release, released on December 31st 2010
|
||||
|
||||
- Fixed an issue where the default `OPTIONS` response was
|
||||
not exposing all valid methods in the `Allow` header.
|
||||
- Jinja2 template loading syntax now allows "./" in front of
|
||||
a template load path. Previously this caused issues with
|
||||
module setups.
|
||||
- Fixed an issue where the subdomain setting for modules was
|
||||
ignored for the static folder.
|
||||
- Fixed a security problem that allowed clients to download arbitrary files
|
||||
if the host server was a windows based operating system and the client
|
||||
uses backslashes to escape the directory the files where exposed from.
|
||||
|
||||
Version 0.6
|
||||
-----------
|
||||
|
||||
Released on July 27th 2010, codename Whisky
|
||||
|
||||
- after request functions are now called in reverse order of
|
||||
registration.
|
||||
- OPTIONS is now automatically implemented by Flask unless the
|
||||
application explicitly adds 'OPTIONS' as method to the URL rule.
|
||||
In this case no automatic OPTIONS handling kicks in.
|
||||
- static rules are now even in place if there is no static folder
|
||||
for the module. This was implemented to aid GAE which will
|
||||
remove the static folder if it's part of a mapping in the .yml
|
||||
file.
|
||||
- the :attr:`~flask.Flask.config` is now available in the templates
|
||||
as `config`.
|
||||
- context processors will no longer override values passed directly
|
||||
to the render function.
|
||||
- added the ability to limit the incoming request data with the
|
||||
new ``MAX_CONTENT_LENGTH`` configuration value.
|
||||
- the endpoint for the :meth:`flask.Module.add_url_rule` method
|
||||
is now optional to be consistent with the function of the
|
||||
same name on the application object.
|
||||
- added a :func:`flask.make_response` function that simplifies
|
||||
creating response object instances in views.
|
||||
- added signalling support based on blinker. This feature is currently
|
||||
optional and supposed to be used by extensions and applications. If
|
||||
you want to use it, make sure to have `blinker`_ installed.
|
||||
- refactored the way URL adapters are created. This process is now
|
||||
fully customizable with the :meth:`~flask.Flask.create_url_adapter`
|
||||
method.
|
||||
- modules can now register for a subdomain instead of just an URL
|
||||
prefix. This makes it possible to bind a whole module to a
|
||||
configurable subdomain.
|
||||
|
||||
.. _blinker: http://pypi.python.org/pypi/blinker
|
||||
|
||||
Version 0.5.2
|
||||
-------------
|
||||
|
||||
Bugfix Release, released on July 15th 2010
|
||||
|
||||
- fixed another issue with loading templates from directories when
|
||||
modules were used.
|
||||
|
||||
Version 0.5.1
|
||||
-------------
|
||||
|
||||
Bugfix Release, released on July 6th 2010
|
||||
|
||||
- fixes an issue with template loading from directories when modules
|
||||
where used.
|
||||
|
||||
Version 0.5
|
||||
-----------
|
||||
|
||||
Released on July 6th 2010, codename Calvados
|
||||
|
||||
- fixed a bug with subdomains that was caused by the inability to
|
||||
specify the server name. The server name can now be set with
|
||||
the `SERVER_NAME` config key. This key is now also used to set
|
||||
the session cookie cross-subdomain wide.
|
||||
- autoescaping is no longer active for all templates. Instead it
|
||||
is only active for ``.html``, ``.htm``, ``.xml`` and ``.xhtml``.
|
||||
Inside templates this behaviour can be changed with the
|
||||
``autoescape`` tag.
|
||||
- refactored Flask internally. It now consists of more than a
|
||||
single file.
|
||||
- :func:`flask.send_file` now emits etags and has the ability to
|
||||
do conditional responses builtin.
|
||||
- (temporarily) dropped support for zipped applications. This was a
|
||||
rarely used feature and led to some confusing behaviour.
|
||||
- added support for per-package template and static-file directories.
|
||||
- removed support for `create_jinja_loader` which is no longer used
|
||||
in 0.5 due to the improved module support.
|
||||
- added a helper function to expose files from any directory.
|
||||
|
||||
Version 0.4
|
||||
-----------
|
||||
|
||||
Released on June 18th 2010, codename Rakia
|
||||
|
||||
- added the ability to register application wide error handlers
|
||||
from modules.
|
||||
- :meth:`~flask.Flask.after_request` handlers are now also invoked
|
||||
if the request dies with an exception and an error handling page
|
||||
kicks in.
|
||||
- test client has not the ability to preserve the request context
|
||||
for a little longer. This can also be used to trigger custom
|
||||
requests that do not pop the request stack for testing.
|
||||
- because the Python standard library caches loggers, the name of
|
||||
the logger is configurable now to better support unittests.
|
||||
- added `TESTING` switch that can activate unittesting helpers.
|
||||
- the logger switches to `DEBUG` mode now if debug is enabled.
|
||||
|
||||
Version 0.3.1
|
||||
-------------
|
||||
|
||||
Bugfix release, released on May 28th 2010
|
||||
|
||||
- fixed a error reporting bug with :meth:`flask.Config.from_envvar`
|
||||
- removed some unused code from flask
|
||||
- release does no longer include development leftover files (.git
|
||||
folder for themes, built documentation in zip and pdf file and
|
||||
some .pyc files)
|
||||
|
||||
Version 0.3
|
||||
-----------
|
||||
|
||||
Released on May 28th 2010, codename Schnaps
|
||||
|
||||
- added support for categories for flashed messages.
|
||||
- the application now configures a :class:`logging.Handler` and will
|
||||
log request handling exceptions to that logger when not in debug
|
||||
mode. This makes it possible to receive mails on server errors
|
||||
for example.
|
||||
- added support for context binding that does not require the use of
|
||||
the with statement for playing in the console.
|
||||
- the request context is now available within the with statement making
|
||||
it possible to further push the request context or pop it.
|
||||
- added support for configurations.
|
||||
|
||||
Version 0.2
|
||||
-----------
|
||||
|
||||
Released on May 12th 2010, codename Jägermeister
|
||||
|
||||
- various bugfixes
|
||||
- integrated JSON support
|
||||
- added :func:`~flask.get_template_attribute` helper function.
|
||||
- :meth:`~flask.Flask.add_url_rule` can now also register a
|
||||
view function.
|
||||
- refactored internal request dispatching.
|
||||
- server listens on 127.0.0.1 by default now to fix issues with chrome.
|
||||
- added external URL support.
|
||||
- added support for :func:`~flask.send_file`
|
||||
- module support and internal request handling refactoring
|
||||
to better support pluggable applications.
|
||||
- sessions can be set to be permanent now on a per-session basis.
|
||||
- better error reporting on missing secret keys.
|
||||
- added support for Google Appengine.
|
||||
|
||||
Version 0.1
|
||||
-----------
|
||||
|
||||
First public preview release.
|
||||
1665
CHANGES.rst
33
LICENSE
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
Copyright (c) 2010 by Armin Ronacher and contributors. See AUTHORS
|
||||
for more details.
|
||||
|
||||
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.
|
||||
|
||||
* The names of the contributors may not 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 OWNER
|
||||
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.
|
||||
28
LICENSE.txt
|
|
@ -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.
|
||||
16
MANIFEST.in
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
include Makefile CHANGES LICENSE AUTHORS run-tests.py
|
||||
recursive-include artwork *
|
||||
recursive-include tests *
|
||||
recursive-include examples *
|
||||
recursive-include docs *
|
||||
recursive-exclude docs *.pyc
|
||||
recursive-exclude docs *.pyo
|
||||
recursive-exclude tests *.pyc
|
||||
recursive-exclude tests *.pyo
|
||||
recursive-exclude examples *.pyc
|
||||
recursive-exclude examples *.pyo
|
||||
recursive-include flask/testsuite/static *
|
||||
recursive-include flask/testsuite/templates *
|
||||
recursive-include flask/testsuite/test_apps *
|
||||
prune docs/_build
|
||||
prune docs/_themes/.git
|
||||
34
Makefile
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
.PHONY: clean-pyc ext-test test upload-docs docs audit
|
||||
|
||||
all: clean-pyc test
|
||||
|
||||
test:
|
||||
python run-tests.py
|
||||
|
||||
audit:
|
||||
python setup.py audit
|
||||
|
||||
release:
|
||||
python scripts/make-release.py
|
||||
|
||||
tox-test:
|
||||
PYTHONDONTWRITEBYTECODE= tox
|
||||
|
||||
ext-test:
|
||||
python tests/flaskext_test.py --browse
|
||||
|
||||
clean-pyc:
|
||||
find . -name '*.pyc' -exec rm -f {} +
|
||||
find . -name '*.pyo' -exec rm -f {} +
|
||||
find . -name '*~' -exec rm -f {} +
|
||||
|
||||
upload-docs:
|
||||
$(MAKE) -C docs html dirhtml latex
|
||||
$(MAKE) -C docs/_build/latex all-pdf
|
||||
cd docs/_build/; mv html flask-docs; zip -r flask-docs.zip flask-docs; mv flask-docs html
|
||||
rsync -a docs/_build/dirhtml/ pocoo.org:/var/www/flask.pocoo.org/docs/
|
||||
rsync -a docs/_build/latex/Flask.pdf pocoo.org:/var/www/flask.pocoo.org/docs/flask-docs.pdf
|
||||
rsync -a docs/_build/flask-docs.zip pocoo.org:/var/www/flask.pocoo.org/docs/flask-docs.zip
|
||||
|
||||
docs:
|
||||
$(MAKE) -C docs html
|
||||
52
README
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
// Flask //
|
||||
|
||||
web development, one drop at a time
|
||||
|
||||
|
||||
~ What is Flask?
|
||||
|
||||
Flask is a microframework for Python based on Werkzeug
|
||||
and Jinja2. It's intended for small scale applications
|
||||
and was developed with best intentions in mind.
|
||||
|
||||
~ Is it ready?
|
||||
|
||||
It's still not 1.0 but it's shaping up nicely and is
|
||||
already widely used. Consider the API to slightly
|
||||
improve over time but we don't plan on breaking it.
|
||||
|
||||
~ What do I need?
|
||||
|
||||
Jinja 2.4 and Werkzeug 0.6.1. `pip` or `easy_install` will
|
||||
install them for you if you do `easy_install Flask`.
|
||||
I encourage you to use a virtualenv. Check the docs for
|
||||
complete installation and usage instructions.
|
||||
|
||||
~ Where are the docs?
|
||||
|
||||
Go to http://flask.pocoo.org/docs/ for a prebuilt version
|
||||
of the current documentation. Otherwise build them yourself
|
||||
from the sphinx sources in the docs folder.
|
||||
|
||||
~ Where are the tests?
|
||||
|
||||
Good that you're asking. The tests are in the
|
||||
flask/testsuite package. To run the tests use the
|
||||
`run-tests.py` file:
|
||||
|
||||
$ python run-tests.py
|
||||
|
||||
If it's not enough output for you, you can use the
|
||||
`--verbose` flag:
|
||||
|
||||
$ python run-tests.py --verbose
|
||||
|
||||
If you just want one particular testcase to run you can
|
||||
provide it on the command line:
|
||||
|
||||
$ python run-tests.py test_to_run
|
||||
|
||||
~ Where can I get help?
|
||||
|
||||
Either use the #pocoo IRC channel on irc.freenode.net or
|
||||
ask on the mailinglist: http://flask.pocoo.org/mailinglist/
|
||||
53
README.md
|
|
@ -1,53 +0,0 @@
|
|||
<div align="center"><img src="https://raw.githubusercontent.com/pallets/flask/refs/heads/stable/docs/_static/flask-name.svg" alt="" height="150"></div>
|
||||
|
||||
# Flask
|
||||
|
||||
Flask is a lightweight [WSGI] web application framework. It is designed
|
||||
to make getting started quick and easy, with the ability to scale up to
|
||||
complex applications. It began as a simple wrapper around [Werkzeug]
|
||||
and [Jinja], and has become one of the most popular Python web
|
||||
application frameworks.
|
||||
|
||||
Flask offers suggestions, but doesn't enforce any dependencies or
|
||||
project layout. It is up to the developer to choose the tools and
|
||||
libraries they want to use. There are many extensions provided by the
|
||||
community that make adding new functionality easy.
|
||||
|
||||
[WSGI]: https://wsgi.readthedocs.io/
|
||||
[Werkzeug]: https://werkzeug.palletsprojects.com/
|
||||
[Jinja]: https://jinja.palletsprojects.com/
|
||||
|
||||
## A Simple Example
|
||||
|
||||
```python
|
||||
# save this as app.py
|
||||
from flask import Flask
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.route("/")
|
||||
def hello():
|
||||
return "Hello, World!"
|
||||
```
|
||||
|
||||
```
|
||||
$ flask run
|
||||
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
## Donate
|
||||
|
||||
The Pallets organization develops and supports Flask and the libraries
|
||||
it uses. In order to grow the community of contributors and users, and
|
||||
allow the maintainers to devote more time to the projects, [please
|
||||
donate today].
|
||||
|
||||
[please donate today]: https://palletsprojects.com/donate
|
||||
|
||||
## Contributing
|
||||
|
||||
See our [detailed contributing documentation][contrib] for many ways to
|
||||
contribute, including reporting issues, requesting features, asking or answering
|
||||
questions, and making PRs.
|
||||
|
||||
[contrib]: https://palletsprojects.com/contributing/
|
||||
20
artwork/LICENSE
Normal 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
|
After Width: | Height: | Size: 77 KiB |
1
docs/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
_build
|
||||
124
docs/Makefile
|
|
@ -1,20 +1,118 @@
|
|||
# Minimal makefile for Sphinx documentation
|
||||
# Makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line, and also
|
||||
# from the environment for the first two.
|
||||
SPHINXOPTS ?=
|
||||
SPHINXBUILD ?= sphinx-build
|
||||
SOURCEDIR = .
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
PAPER =
|
||||
BUILDDIR = _build
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
# Internal variables.
|
||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
||||
PAPEROPT_letter = -D latex_paper_size=letter
|
||||
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||
|
||||
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp epub latex changes linkcheck doctest
|
||||
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
@echo "Please use \`make <target>' where <target> is one of"
|
||||
@echo " html to make standalone HTML files"
|
||||
@echo " dirhtml to make HTML files named index.html in directories"
|
||||
@echo " singlehtml to make a single large HTML file"
|
||||
@echo " pickle to make pickle files"
|
||||
@echo " json to make JSON files"
|
||||
@echo " htmlhelp to make HTML files and a HTML help project"
|
||||
@echo " qthelp to make HTML files and a qthelp project"
|
||||
@echo " devhelp to make HTML files and a Devhelp project"
|
||||
@echo " epub to make an epub"
|
||||
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
||||
@echo " latexpdf to make LaTeX files and run them through pdflatex"
|
||||
@echo " changes to make an overview of all changed/added/deprecated items"
|
||||
@echo " linkcheck to check all external links for integrity"
|
||||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
||||
|
||||
.PHONY: help Makefile
|
||||
clean:
|
||||
-rm -rf $(BUILDDIR)/*
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
html:
|
||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
||||
|
||||
dirhtml:
|
||||
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
|
||||
|
||||
singlehtml:
|
||||
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
|
||||
|
||||
pickle:
|
||||
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
|
||||
@echo
|
||||
@echo "Build finished; now you can process the pickle files."
|
||||
|
||||
json:
|
||||
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
|
||||
@echo
|
||||
@echo "Build finished; now you can process the JSON files."
|
||||
|
||||
htmlhelp:
|
||||
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
||||
".hhp project file in $(BUILDDIR)/htmlhelp."
|
||||
|
||||
qthelp:
|
||||
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
|
||||
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
|
||||
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Flask.qhcp"
|
||||
@echo "To view the help file:"
|
||||
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Flask.qhc"
|
||||
|
||||
devhelp:
|
||||
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) _build/devhelp
|
||||
@echo
|
||||
@echo "Build finished."
|
||||
@echo "To view the help file:"
|
||||
@echo "# mkdir -p $$HOME/.local/share/devhelp/Flask"
|
||||
@echo "# ln -s _build/devhelp $$HOME/.local/share/devhelp/Flask"
|
||||
@echo "# devhelp"
|
||||
|
||||
epub:
|
||||
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
|
||||
@echo
|
||||
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
|
||||
|
||||
latex:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo
|
||||
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
|
||||
@echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
|
||||
"run these through (pdf)latex."
|
||||
|
||||
latexpdf: latex
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) _build/latex
|
||||
@echo "Running LaTeX files through pdflatex..."
|
||||
make -C _build/latex all-pdf
|
||||
@echo "pdflatex finished; the PDF files are in _build/latex."
|
||||
|
||||
changes:
|
||||
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
|
||||
@echo
|
||||
@echo "The overview file is in $(BUILDDIR)/changes."
|
||||
|
||||
linkcheck:
|
||||
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
|
||||
@echo
|
||||
@echo "Link check complete; look for any errors in the above output " \
|
||||
"or in $(BUILDDIR)/linkcheck/output.txt."
|
||||
|
||||
doctest:
|
||||
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
|
||||
@echo "Testing of doctests in the sources finished, look at the " \
|
||||
"results in $(BUILDDIR)/doctest/output.txt."
|
||||
|
|
|
|||
BIN
docs/_static/debugger.png
vendored
|
Before Width: | Height: | Size: 203 KiB After Width: | Height: | Size: 121 KiB |
15
docs/_static/flask-icon.svg
vendored
|
|
@ -1,15 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 500 500" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<rect id="Icon" x="0" y="0" width="500" height="500" style="fill:none;"/>
|
||||
<clipPath id="_clip1">
|
||||
<rect x="0" y="0" width="500" height="500"/>
|
||||
</clipPath>
|
||||
<g clip-path="url(#_clip1)">
|
||||
<g>
|
||||
<path d="M224.446,59.975c-0.056,-4.151 -0.483,-5.543 -2.7,-6.823c-2.104,-1.393 -5.288,-1.421 -8.329,-0.085l-204.674,87.64c-3.042,1.336 -5.913,4.008 -7.448,6.908c-1.535,2.899 -1.705,5.97 -0.511,8.158l17.084,31.384l0.228,0.369c1.847,2.928 6.026,3.696 10.29,1.82l1.251,-0.54c5.344,22.4 14.1,50.429 25.783,70.413l178.294,-79.794c-2.559,-23.14 -9.552,-89.602 -9.268,-119.479l0,0.029Z" style="fill:#3babc3;fill-rule:nonzero;"/>
|
||||
<path d="M238.603,205.776l-171.698,76.838c10.091,19.132 22.542,39.428 37.722,58.986c50.429,-25.698 100.887,-51.396 151.316,-77.094c-3.269,-8.471 -6.452,-17.653 -17.34,-58.73Z" style="fill:#3babc3;fill-rule:nonzero;"/>
|
||||
<path d="M497.601,388.846l-12.139,-18.535c-1.819,-2.018 -4.633,-2.786 -7.106,-1.791l-15.578,5.999c-1.848,-2.047 -4.52,-2.815 -7.135,-1.791c-5.089,1.99 -10.206,4.008 -15.294,5.998c-1.649,0.625 -2.104,1.847 -1.791,3.439l0.995,4.861c-28.711,3.099 -77.236,1.564 -120.701,-32.577c-19.216,-15.066 -37.239,-36.386 -52.277,-66.206l-144.75,73.768c26.466,29.08 59.697,54.864 100.973,70.385c57.422,21.633 130.593,23.679 222.838,-13.475l0.512,2.616c0.455,2.928 3.98,6.026 8.755,4.15l15.323,-5.97c5.258,-1.99 5.287,-6.026 4.519,-8.641l19.729,-7.704c2.217,-0.853 9.096,-6.169 3.183,-14.526l-0.056,-0Z" style="fill:#3babc3;fill-rule:nonzero;"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2 KiB |
17
docs/_static/flask-logo.svg
vendored
|
|
@ -1,17 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 500 500" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<rect id="Logo" x="0" y="0" width="500" height="500" style="fill:none;"/>
|
||||
<g>
|
||||
<path id="Box" d="M500,50l0,400c0,27.596 -22.404,50 -50,50l-400,0c-27.596,0 -50,-22.404 -50,-50l0,-400c0,-27.596 22.404,-50 50,-50l400,0c27.596,0 50,22.404 50,50Z" style="fill:url(#_Linear1);"/>
|
||||
<path id="Shadow" d="M500,398.646l0,51.354c0,27.596 -22.404,50 -50,50l-94.111,0l-170.452,-170.451c13.541,12.279 29.511,22.845 48.242,29.888c34.453,12.98 78.356,14.208 133.703,-8.084l0.307,1.569c0.273,1.757 2.388,3.616 5.253,2.49l9.193,-3.582c3.156,-1.194 3.173,-3.616 2.712,-5.185l11.837,-4.622c1.331,-0.512 5.458,-3.701 1.91,-8.716l-0.034,0l-7.283,-11.12c-1.091,-1.211 -2.78,-1.672 -4.264,-1.075l-9.346,3.599c-1.109,-1.228 -2.712,-1.688 -4.281,-1.074c-3.054,1.194 -6.124,2.404 -9.177,3.598c-0.989,0.376 -1.262,1.109 -1.074,2.064l0.597,2.917c-17.227,1.859 -46.342,0.938 -72.421,-19.547c-11.53,-9.039 -22.343,-21.831 -31.366,-39.723l-83.923,42.769l-13.246,-10.755c30.258,-15.419 60.532,-30.837 90.79,-46.256c-1.961,-5.083 -3.872,-10.592 -10.404,-35.238l-98.082,43.893l-11.828,-11.828l106.976,-47.876c-1.534,-13.88 -5.728,-53.736 -5.56,-71.67l-0,-0.017c-0.027,-1.894 -0.184,-2.827 -0.85,-3.504l266.182,266.182Zm-388.562,-185.436c1.272,1.164 3.414,1.356 5.594,0.397l0.75,-0.324c0.645,2.703 1.373,5.543 2.181,8.452l-8.525,-8.525Z" style="fill:#3b808b;"/>
|
||||
<g id="Icon">
|
||||
<path d="M234.668,135.985c-0.034,-2.49 -0.29,-3.326 -1.62,-4.094c-1.263,-0.835 -3.173,-0.852 -4.998,-0.051l-122.804,52.584c-1.825,0.802 -3.548,2.405 -4.469,4.145c-0.921,1.74 -1.023,3.582 -0.307,4.895l10.251,18.83l0.136,0.222c1.109,1.757 3.616,2.217 6.175,1.091l0.75,-0.324c3.207,13.441 8.46,30.258 15.47,42.248l106.976,-47.876c-1.535,-13.884 -5.731,-53.761 -5.56,-71.687l-0,0.017Z" style="fill:#fff;fill-rule:nonzero;"/>
|
||||
<path d="M243.162,223.466l-103.019,46.103c6.055,11.478 13.525,23.656 22.633,35.391c30.258,-15.419 60.532,-30.837 90.79,-46.256c-1.961,-5.083 -3.872,-10.592 -10.404,-35.238Z" style="fill:#fff;fill-rule:nonzero;"/>
|
||||
<path d="M398.56,333.307l-7.283,-11.12c-1.091,-1.211 -2.78,-1.672 -4.264,-1.075l-9.346,3.599c-1.109,-1.228 -2.712,-1.688 -4.281,-1.074c-3.054,1.194 -6.124,2.404 -9.177,3.598c-0.989,0.376 -1.262,1.109 -1.074,2.064l0.597,2.917c-17.227,1.859 -46.342,0.938 -72.421,-19.547c-11.53,-9.039 -22.343,-21.831 -31.366,-39.723l-86.85,44.26c15.879,17.449 35.818,32.919 60.584,42.231c34.453,12.98 78.356,14.208 133.703,-8.084l0.307,1.569c0.273,1.757 2.388,3.616 5.253,2.49l9.193,-3.582c3.156,-1.194 3.173,-3.616 2.712,-5.185l11.837,-4.622c1.331,-0.512 5.458,-3.701 1.91,-8.716l-0.034,0Z" style="fill:#fff;fill-rule:nonzero;"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="_Linear1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(3.06162e-14,500,-500,3.06162e-14,267.59,0)"><stop offset="0" style="stop-color:#bdddeb;stop-opacity:1"/><stop offset="1" style="stop-color:#53a9d1;stop-opacity:1"/></linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.4 KiB |
23
docs/_static/flask-name.svg
vendored
|
|
@ -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
|
After Width: | Height: | Size: 9.7 KiB |
BIN
docs/_static/flaskr.png
vendored
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
docs/_static/logo-full.png
vendored
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
docs/_static/no.png
vendored
Normal file
|
After Width: | Height: | Size: 317 B |
BIN
docs/_static/pycharm-run-config.png
vendored
|
Before Width: | Height: | Size: 97 KiB |
BIN
docs/_static/touch-icon.png
vendored
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
docs/_static/yes.png
vendored
Normal file
|
After Width: | Height: | Size: 277 B |
22
docs/_templates/sidebarintro.html
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<h3>About Flask</h3>
|
||||
<p>
|
||||
Flask is a micro webdevelopment framework for Python. You are currently
|
||||
looking at the documentation of the development version. Things are
|
||||
not stable yet, but if you have some feedback,
|
||||
<a href="mailto:armin.ronacher@active-4.com">let me know</a>.
|
||||
</p>
|
||||
<h3>Other Formats</h3>
|
||||
<p>
|
||||
You can download the documentation in other formats as well:
|
||||
</p>
|
||||
<ul>
|
||||
<li><a href="http://flask.pocoo.org/docs/flask-docs.pdf">as PDF</a>
|
||||
<li><a href="http://flask.pocoo.org/docs/flask-docs.zip">as zipped HTML</a>
|
||||
</ul>
|
||||
<h3>Useful Links</h3>
|
||||
<ul>
|
||||
<li><a href="http://flask.pocoo.org/">The Flask Website</a></li>
|
||||
<li><a href="http://pypi.python.org/pypi/Flask">Flask @ PyPI</a></li>
|
||||
<li><a href="http://github.com/mitsuhiko/flask">Flask @ github</a></li>
|
||||
<li><a href="http://github.com/mitsuhiko/flask/issues">Issue Tracker</a></li>
|
||||
</ul>
|
||||
3
docs/_templates/sidebarlogo.html
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<p class="logo"><a href="{{ pathto(master_doc) }}">
|
||||
<img class="logo" src="{{ pathto('_static/flask.png', 1) }}" alt="Logo"/>
|
||||
</a></p>
|
||||
1
docs/_themes
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 3d964b660442e23faedf801caed6e3c7bd42d5c9
|
||||
666
docs/api.rst
|
|
@ -1,9 +1,11 @@
|
|||
.. _api:
|
||||
|
||||
API
|
||||
===
|
||||
|
||||
.. 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
|
||||
important right here and provide links to the canonical documentation.
|
||||
|
||||
|
|
@ -27,66 +29,170 @@ Incoming Request Data
|
|||
---------------------
|
||||
|
||||
.. autoclass:: Request
|
||||
:members:
|
||||
:inherited-members:
|
||||
:exclude-members: json_module
|
||||
:members:
|
||||
|
||||
.. data:: request
|
||||
.. attribute:: form
|
||||
|
||||
A proxy to the request data for the current request, an instance of
|
||||
:class:`.Request`.
|
||||
A :class:`~werkzeug.datastructures.MultiDict` with the parsed form data from `POST`
|
||||
or `PUT` requests. Please keep in mind that file uploads will not
|
||||
end up here, but instead in the :attr:`files` attribute.
|
||||
|
||||
This is only available when a :doc:`request context </appcontext>` is
|
||||
active.
|
||||
.. attribute:: args
|
||||
|
||||
This is a proxy. See :ref:`context-visibility` for more information.
|
||||
A :class:`~werkzeug.datastructures.MultiDict` with the parsed contents of the query
|
||||
string. (The part in the URL after the question mark).
|
||||
|
||||
.. attribute:: values
|
||||
|
||||
A :class:`~werkzeug.datastructures.CombinedMultiDict` with the contents of both
|
||||
:attr:`form` and :attr:`args`.
|
||||
|
||||
.. attribute:: cookies
|
||||
|
||||
A :class:`dict` with the contents of all cookies transmitted with
|
||||
the request.
|
||||
|
||||
.. attribute:: stream
|
||||
|
||||
If the incoming form data was not encoded with a known mimetype
|
||||
the data is stored unmodified in this stream for consumption. Most
|
||||
of the time it is a better idea to use :attr:`data` which will give
|
||||
you that data as a string. The stream only returns the data once.
|
||||
|
||||
.. attribute:: headers
|
||||
|
||||
The incoming request headers as a dictionary like object.
|
||||
|
||||
.. attribute:: data
|
||||
|
||||
Contains the incoming request data as string in case it came with
|
||||
a mimetype Flask does not handle.
|
||||
|
||||
.. attribute:: files
|
||||
|
||||
A :class:`~werkzeug.datastructures.MultiDict` with files uploaded as part of a
|
||||
`POST` or `PUT` request. Each file is stored as
|
||||
:class:`~werkzeug.datastructures.FileStorage` object. It basically behaves like a
|
||||
standard file object you know from Python, with the difference that
|
||||
it also has a :meth:`~werkzeug.datastructures.FileStorage.save` function that can
|
||||
store the file on the filesystem.
|
||||
|
||||
.. attribute:: environ
|
||||
|
||||
The underlying WSGI environment.
|
||||
|
||||
.. attribute:: method
|
||||
|
||||
The current request method (``POST``, ``GET`` etc.)
|
||||
|
||||
.. attribute:: path
|
||||
.. attribute:: script_root
|
||||
.. attribute:: url
|
||||
.. attribute:: base_url
|
||||
.. attribute:: url_root
|
||||
|
||||
Provides different ways to look at the current URL. Imagine your
|
||||
application is listening on the following URL::
|
||||
|
||||
http://www.example.com/myapplication
|
||||
|
||||
And a user requests the following URL::
|
||||
|
||||
http://www.example.com/myapplication/page.html?x=y
|
||||
|
||||
In this case the values of the above mentioned attributes would be
|
||||
the following:
|
||||
|
||||
============= ======================================================
|
||||
`path` ``/page.html``
|
||||
`script_root` ``/myapplication``
|
||||
`base_url` ``http://www.example.com/myapplication/page.html``
|
||||
`url` ``http://www.example.com/myapplication/page.html?x=y``
|
||||
`url_root` ``http://www.example.com/myapplication/``
|
||||
============= ======================================================
|
||||
|
||||
.. attribute:: is_xhr
|
||||
|
||||
`True` if the request was triggered via a JavaScript
|
||||
`XMLHttpRequest`. This only works with libraries that support the
|
||||
``X-Requested-With`` header and set it to `XMLHttpRequest`.
|
||||
Libraries that do that are prototype, jQuery and Mochikit and
|
||||
probably some more.
|
||||
|
||||
.. class:: request
|
||||
|
||||
To access incoming request data, you can use the global `request`
|
||||
object. Flask parses incoming request data for you and gives you
|
||||
access to it through that global object. Internally Flask makes
|
||||
sure that you always get the correct data for the active thread if you
|
||||
are in a multithreaded environment.
|
||||
|
||||
This is 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
|
||||
----------------
|
||||
|
||||
.. autoclass:: flask.Response
|
||||
:members:
|
||||
:inherited-members:
|
||||
:exclude-members: json_module
|
||||
:members: set_cookie, data, mimetype
|
||||
|
||||
.. attribute:: headers
|
||||
|
||||
A :class:`Headers` object representing the response headers.
|
||||
|
||||
.. attribute:: status_code
|
||||
|
||||
The response status as integer.
|
||||
|
||||
|
||||
Sessions
|
||||
--------
|
||||
|
||||
If you have set :attr:`Flask.secret_key` (or configured it from
|
||||
:data:`SECRET_KEY`) you can use sessions in Flask applications. A session makes
|
||||
it possible to remember information from one request to another. The way Flask
|
||||
does this is by using a signed cookie. The user can look at the session
|
||||
contents, but can't modify it unless they know the secret key, so make sure to
|
||||
set that to something complex and unguessable.
|
||||
If you have the :attr:`Flask.secret_key` set you can use sessions in Flask
|
||||
applications. A session basically makes it possible to remember
|
||||
information from one request to another. The way Flask does this is by
|
||||
using a signed cookie. So the user can look at the session contents, but
|
||||
not modify it unless they know the secret key, so make sure to set that
|
||||
to something complex and unguessable.
|
||||
|
||||
To access the current session you can use the :data:`.session` proxy.
|
||||
To access the current session you can use the :class:`session` object:
|
||||
|
||||
.. data:: session
|
||||
.. class:: session
|
||||
|
||||
A proxy to the session data for the current request, an instance of
|
||||
:class:`.SessionMixin`.
|
||||
The session object works pretty much like an ordinary dict, with the
|
||||
difference that it keeps track on modifications.
|
||||
|
||||
This is only available when a :doc:`request context </appcontext>` is
|
||||
active.
|
||||
This is a proxy. See :ref:`notes-on-proxies` for more information.
|
||||
|
||||
This is a proxy. See :ref:`context-visibility` for more information.
|
||||
The following attributes are interesting:
|
||||
|
||||
The session object works like a dict but tracks assignment and access to its
|
||||
keys. It cannot track modifications to mutable values, you need to set
|
||||
:attr:`~.SessionMixin.modified` manually when modifying a list, dict, etc.
|
||||
.. attribute:: new
|
||||
|
||||
.. code-block:: python
|
||||
`True` if the session is new, `False` otherwise.
|
||||
|
||||
# appending to a list is not detected
|
||||
session["numbers"].append(42)
|
||||
.. attribute:: modified
|
||||
|
||||
`True` if the session object detected a modification. Be advised
|
||||
that modifications on mutable structures are not picked up
|
||||
automatically, in that situation you have to explicitly set the
|
||||
attribute to `True` yourself. Here an example::
|
||||
|
||||
# this change is not picked up because a mutable object (here
|
||||
# a list) is changed.
|
||||
session['objects'].append(42)
|
||||
# so mark it as modified yourself
|
||||
session.modified = True
|
||||
|
||||
The session is persisted across requests using a cookie. By default the
|
||||
users's browser will clear the cookie when it is closed. Set
|
||||
:attr:`~.SessionMixin.permanent` to ``True`` to persist the cookie for
|
||||
:data:`PERMANENT_SESSION_LIFETIME`.
|
||||
.. attribute:: permanent
|
||||
|
||||
If set to `True` the session lives for
|
||||
:attr:`~flask.Flask.permanent_session_lifetime` seconds. The
|
||||
default is 31 days. If set to `False` (which is the default) the
|
||||
session will be deleted when the user closes the browser.
|
||||
|
||||
|
||||
Session Interface
|
||||
|
|
@ -105,9 +211,6 @@ implementation that Flask is using.
|
|||
.. autoclass:: SecureCookieSessionInterface
|
||||
:members:
|
||||
|
||||
.. autoclass:: SecureCookieSession
|
||||
:members:
|
||||
|
||||
.. autoclass:: NullSession
|
||||
:members:
|
||||
|
||||
|
|
@ -116,9 +219,10 @@ implementation that Flask is using.
|
|||
|
||||
.. admonition:: Notice
|
||||
|
||||
The :data:`PERMANENT_SESSION_LIFETIME` config can be an integer or ``timedelta``.
|
||||
The :attr:`~flask.Flask.permanent_session_lifetime` attribute is always a
|
||||
``timedelta``.
|
||||
The ``PERMANENT_SESSION_LIFETIME`` config key can also be an integer
|
||||
starting with Flask 0.8. Either catch this down yourself or use
|
||||
the :attr:`~flask.Flask.permanent_session_lifetime` attribute on the
|
||||
app which converts the result to an integer automatically.
|
||||
|
||||
|
||||
Test Client
|
||||
|
|
@ -130,15 +234,6 @@ Test Client
|
|||
:members:
|
||||
|
||||
|
||||
Test CLI Runner
|
||||
---------------
|
||||
|
||||
.. currentmodule:: flask.testing
|
||||
|
||||
.. autoclass:: FlaskCliRunner
|
||||
:members:
|
||||
|
||||
|
||||
Application Globals
|
||||
-------------------
|
||||
|
||||
|
|
@ -146,30 +241,17 @@ Application Globals
|
|||
|
||||
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
|
||||
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
|
||||
different values for each request. In a nutshell: it does the right
|
||||
thing, like it does for :data:`.request` and :data:`.session`.
|
||||
different values for each request. In a nutshell: it does the right
|
||||
thing, like it does for :class:`request` and :class:`session`.
|
||||
|
||||
.. data:: g
|
||||
|
||||
A proxy to a namespace object used to store data during a single request or
|
||||
app context. An instance of :attr:`.Flask.app_ctx_globals_class`, which
|
||||
defaults to :class:`._AppCtxGlobals`.
|
||||
Just store on this whatever you want. For example a database
|
||||
connection or the user that is currently logged in.
|
||||
|
||||
This is a good place to store resources during a request. For example, a
|
||||
:meth:`~.Flask.before_request` function could load a user object from a
|
||||
session id, then set ``g.user`` to be used in the view function.
|
||||
|
||||
This is only available when an :doc:`app context </appcontext>` is active.
|
||||
|
||||
This is a proxy. See :ref:`context-visibility` for more information.
|
||||
|
||||
.. versionchanged:: 0.10
|
||||
Bound to the application context instead of the request context.
|
||||
|
||||
.. autoclass:: flask.ctx._AppCtxGlobals
|
||||
:members:
|
||||
This is a proxy. See :ref:`notes-on-proxies` for more information.
|
||||
|
||||
|
||||
Useful Functions and Classes
|
||||
|
|
@ -177,37 +259,38 @@ Useful Functions and Classes
|
|||
|
||||
.. data:: current_app
|
||||
|
||||
A proxy to the :class:`.Flask` application handling the current request or
|
||||
other activity.
|
||||
Points to the application handling the request. This is useful for
|
||||
extensions that want to support multiple applications running side
|
||||
by side.
|
||||
|
||||
This is useful to access the application without needing to import it, or if
|
||||
it can't be imported, such as when using the application factory pattern or
|
||||
in blueprints and extensions.
|
||||
|
||||
This is only available when an :doc:`app context </appcontext>` is active.
|
||||
|
||||
This is a proxy. See :ref:`context-visibility` for more information.
|
||||
This is a proxy. See :ref:`notes-on-proxies` for more information.
|
||||
|
||||
.. autofunction:: has_request_context
|
||||
|
||||
.. autofunction:: copy_current_request_context
|
||||
|
||||
.. autofunction:: has_app_context
|
||||
|
||||
.. autofunction:: url_for
|
||||
|
||||
.. autofunction:: abort
|
||||
.. function:: abort(code)
|
||||
|
||||
Raises an :exc:`~werkzeug.exceptions.HTTPException` for the given
|
||||
status code. For example to abort request handling with a page not
|
||||
found exception, you would call ``abort(404)``.
|
||||
|
||||
:param code: the HTTP error code.
|
||||
|
||||
.. autofunction:: redirect
|
||||
|
||||
.. autofunction:: make_response
|
||||
|
||||
.. autofunction:: after_this_request
|
||||
|
||||
.. autofunction:: send_file
|
||||
|
||||
.. autofunction:: send_from_directory
|
||||
|
||||
.. autofunction:: safe_join
|
||||
|
||||
.. autofunction:: escape
|
||||
|
||||
.. autoclass:: Markup
|
||||
:members: escape, unescape, striptags
|
||||
|
||||
Message Flashing
|
||||
----------------
|
||||
|
|
@ -216,63 +299,47 @@ Message Flashing
|
|||
|
||||
.. autofunction:: get_flashed_messages
|
||||
|
||||
|
||||
JSON Support
|
||||
------------
|
||||
|
||||
.. module:: flask.json
|
||||
|
||||
Flask uses Python's built-in :mod:`json` module for handling JSON by
|
||||
default. The JSON implementation can be changed by assigning a different
|
||||
provider to :attr:`flask.Flask.json_provider_class` or
|
||||
:attr:`flask.Flask.json`. The functions provided by ``flask.json`` will
|
||||
use methods on ``app.json`` if an app context is active.
|
||||
|
||||
Jinja's ``|tojson`` filter is configured to use the app's JSON provider.
|
||||
The filter marks the output with ``|safe``. Use it to render data inside
|
||||
HTML ``<script>`` tags.
|
||||
|
||||
.. sourcecode:: html+jinja
|
||||
|
||||
<script>
|
||||
const names = {{ names|tojson }};
|
||||
renderChart(names, {{ axis_data|tojson }});
|
||||
</script>
|
||||
Returning JSON
|
||||
--------------
|
||||
|
||||
.. autofunction:: jsonify
|
||||
|
||||
.. autofunction:: dumps
|
||||
.. data:: json
|
||||
|
||||
.. autofunction:: dump
|
||||
If JSON support is picked up, this will be the module that Flask is
|
||||
using to parse and serialize JSON. So instead of doing this yourself::
|
||||
|
||||
.. autofunction:: loads
|
||||
try:
|
||||
import simplejson as json
|
||||
except ImportError:
|
||||
import json
|
||||
|
||||
.. autofunction:: load
|
||||
You can instead just do this::
|
||||
|
||||
.. autoclass:: flask.json.provider.JSONProvider
|
||||
:members:
|
||||
:member-order: bysource
|
||||
from flask import json
|
||||
|
||||
.. autoclass:: flask.json.provider.DefaultJSONProvider
|
||||
:members:
|
||||
:member-order: bysource
|
||||
For usage examples, read the :mod:`json` documentation.
|
||||
|
||||
.. automodule:: flask.json.tag
|
||||
The :func:`~json.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:
|
||||
|
||||
.. sourcecode:: html+jinja
|
||||
|
||||
<script type=text/javascript>
|
||||
doSomethingWith({{ user.username|tojson|safe }});
|
||||
</script>
|
||||
|
||||
Note that the ``|tojson`` filter escapes forward slashes properly.
|
||||
|
||||
Template Rendering
|
||||
------------------
|
||||
|
||||
.. currentmodule:: flask
|
||||
|
||||
.. autofunction:: render_template
|
||||
|
||||
.. autofunction:: render_template_string
|
||||
|
||||
.. autofunction:: stream_template
|
||||
|
||||
.. autofunction:: stream_template_string
|
||||
|
||||
.. autofunction:: get_template_attribute
|
||||
|
||||
Configuration
|
||||
|
|
@ -281,237 +348,131 @@ Configuration
|
|||
.. autoclass:: Config
|
||||
:members:
|
||||
|
||||
Extensions
|
||||
----------
|
||||
|
||||
Stream Helpers
|
||||
--------------
|
||||
.. data:: flask.ext
|
||||
|
||||
.. autofunction:: stream_with_context
|
||||
This module acts as redirect import module to Flask extensions. It was
|
||||
added in 0.8 as the canonical way to import Flask extensions and makes
|
||||
it possible for us to have more flexibility in how we distribute
|
||||
extensions.
|
||||
|
||||
If you want to use an extension named “Flask-Foo” you would import it
|
||||
from :data:`~flask.ext` as follows::
|
||||
|
||||
from flask.ext import foo
|
||||
|
||||
.. versionadded:: 0.8
|
||||
|
||||
Useful Internals
|
||||
----------------
|
||||
|
||||
.. autoclass:: flask.ctx.AppContext
|
||||
.. autoclass:: flask.ctx.RequestContext
|
||||
:members:
|
||||
|
||||
.. data:: flask.globals.app_ctx
|
||||
.. data:: _request_ctx_stack
|
||||
|
||||
A proxy to the active :class:`.AppContext`.
|
||||
The internal :class:`~werkzeug.local.LocalStack` that is used to implement
|
||||
all the context local objects used in Flask. This is a documented
|
||||
instance and can be used by extensions and application code but the
|
||||
use is discouraged in general.
|
||||
|
||||
This is an internal object that is essential to how Flask handles requests.
|
||||
Accessing this should not be needed in most cases. Most likely you want
|
||||
:data:`.current_app`, :data:`.g`, :data:`.request`, and :data:`.session` instead.
|
||||
The following attributes are always present on each layer of the
|
||||
stack:
|
||||
|
||||
This is only available when a :doc:`request context </appcontext>` is
|
||||
active.
|
||||
`app`
|
||||
the active Flask application.
|
||||
|
||||
This is a proxy. See :ref:`context-visibility` for more information.
|
||||
`url_adapter`
|
||||
the URL adapter that was used to match the request.
|
||||
|
||||
.. class:: flask.ctx.RequestContext
|
||||
`request`
|
||||
the current request object.
|
||||
|
||||
.. deprecated:: 3.2
|
||||
Merged with :class:`AppContext`. This alias will be removed in Flask 4.0.
|
||||
`session`
|
||||
the active session object.
|
||||
|
||||
.. data:: flask.globals.request_ctx
|
||||
`g`
|
||||
an object with all the attributes of the :data:`flask.g` object.
|
||||
|
||||
.. deprecated:: 3.2
|
||||
Merged with :data:`.app_ctx`. This alias will be removed in Flask 4.0.
|
||||
`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.blueprints.BlueprintSetupState
|
||||
:members:
|
||||
|
||||
.. _core-signals-list:
|
||||
|
||||
Signals
|
||||
-------
|
||||
|
||||
Signals are provided by the `Blinker`_ library. See :doc:`signals` for an introduction.
|
||||
.. when modifying this list, also update the one in signals.rst
|
||||
|
||||
.. _blinker: https://blinker.readthedocs.io/
|
||||
.. versionadded:: 0.6
|
||||
|
||||
.. data:: signals_available
|
||||
|
||||
`True` if the signalling system is available. This is the case
|
||||
when `blinker`_ is installed.
|
||||
|
||||
.. 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`
|
||||
and the context as dictionary (named `context`).
|
||||
|
||||
Example subscriber::
|
||||
|
||||
def log_template_renders(sender, template, context, **extra):
|
||||
sender.logger.debug('Rendering template "%s" with context %s',
|
||||
template.name or 'string template',
|
||||
context)
|
||||
|
||||
from flask import template_rendered
|
||||
template_rendered.connect(log_template_renders, app)
|
||||
|
||||
.. data:: flask.before_render_template
|
||||
:noindex:
|
||||
|
||||
This signal is sent before template rendering process. The
|
||||
signal is invoked with the instance of the template as `template`
|
||||
and the context as dictionary (named `context`).
|
||||
|
||||
Example subscriber::
|
||||
|
||||
def log_template_renders(sender, template, context, **extra):
|
||||
sender.logger.debug('Rendering template "%s" with context %s',
|
||||
template.name or 'string template',
|
||||
context)
|
||||
|
||||
from flask import before_render_template
|
||||
before_render_template.connect(log_template_renders, app)
|
||||
|
||||
.. data:: request_started
|
||||
|
||||
This signal is sent when the request context is set up, before
|
||||
any request processing happens. Because the request context is already
|
||||
This signal is sent before any request processing started but when the
|
||||
request context was set up. Because the request context is already
|
||||
bound, the subscriber can access the request with the standard global
|
||||
proxies such as :class:`~flask.request`.
|
||||
|
||||
Example subscriber::
|
||||
|
||||
def log_request(sender, **extra):
|
||||
sender.logger.debug('Request context is set up')
|
||||
|
||||
from flask import request_started
|
||||
request_started.connect(log_request, app)
|
||||
|
||||
.. data:: request_finished
|
||||
|
||||
This signal is sent right before the response is sent to the client.
|
||||
It is passed the response to be sent named `response`.
|
||||
|
||||
Example subscriber::
|
||||
|
||||
def log_response(sender, response, **extra):
|
||||
sender.logger.debug('Request context is about to close down. '
|
||||
'Response: %s', response)
|
||||
|
||||
from flask import request_finished
|
||||
request_finished.connect(log_response, app)
|
||||
|
||||
.. data:: got_request_exception
|
||||
|
||||
This signal is sent when an unhandled exception happens during
|
||||
request processing, including when debugging. The exception is
|
||||
passed to the subscriber as ``exception``.
|
||||
|
||||
This signal is not sent for
|
||||
: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
|
||||
``SecurityException`` was raised:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import got_request_exception
|
||||
|
||||
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)
|
||||
This signal is sent when an exception happens during request processing.
|
||||
It is sent *before* the standard exception handling kicks in and even
|
||||
in debug mode, where no exception handling happens. The exception
|
||||
itself is passed to the subscriber as `exception`.
|
||||
|
||||
.. data:: request_tearing_down
|
||||
|
||||
This signal is sent when the request is tearing down. This is always
|
||||
called, even if an exception is caused. Currently functions listening
|
||||
to this signal are called after the regular teardown handlers, but this
|
||||
is not something you can rely on.
|
||||
This signal is sent when the application is tearing down the request.
|
||||
This is always called, even if an error happened. No arguments are
|
||||
provided.
|
||||
|
||||
Example subscriber::
|
||||
.. currentmodule:: None
|
||||
|
||||
def close_db_connection(sender, **extra):
|
||||
session.close()
|
||||
.. class:: flask.signals.Namespace
|
||||
|
||||
from flask import request_tearing_down
|
||||
request_tearing_down.connect(close_db_connection, app)
|
||||
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.
|
||||
|
||||
As of Flask 0.9, this will also be passed an `exc` keyword argument
|
||||
that has a reference to the exception that caused the teardown if
|
||||
there was one.
|
||||
.. method:: signal(name, doc=None)
|
||||
|
||||
.. data:: appcontext_tearing_down
|
||||
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.
|
||||
|
||||
This signal is sent when the app context is tearing down. This is always
|
||||
called, even if an exception is caused. Currently functions listening
|
||||
to this signal are called after the regular teardown handlers, but this
|
||||
is not something you can rely on.
|
||||
.. _blinker: http://pypi.python.org/pypi/blinker
|
||||
|
||||
Example subscriber::
|
||||
|
||||
def close_db_connection(sender, **extra):
|
||||
session.close()
|
||||
|
||||
from flask import appcontext_tearing_down
|
||||
appcontext_tearing_down.connect(close_db_connection, app)
|
||||
|
||||
This will also be passed an `exc` keyword argument that has a reference
|
||||
to the exception that caused the teardown if there was one.
|
||||
|
||||
.. data:: appcontext_pushed
|
||||
|
||||
This signal is sent when an application context is pushed. The sender
|
||||
is the application. This is usually useful for unittests in order to
|
||||
temporarily hook in information. For instance it can be used to
|
||||
set a resource early onto the `g` object.
|
||||
|
||||
Example usage::
|
||||
|
||||
from contextlib import contextmanager
|
||||
from flask import appcontext_pushed
|
||||
|
||||
@contextmanager
|
||||
def user_set(app, user):
|
||||
def handler(sender, **kwargs):
|
||||
g.user = user
|
||||
with appcontext_pushed.connected_to(handler, app):
|
||||
yield
|
||||
|
||||
And in the testcode::
|
||||
|
||||
def test_user_me(self):
|
||||
with user_set(app, 'john'):
|
||||
c = app.test_client()
|
||||
resp = c.get('/users/me')
|
||||
assert resp.data == 'username=john'
|
||||
|
||||
.. versionadded:: 0.10
|
||||
|
||||
.. data:: appcontext_popped
|
||||
|
||||
This signal is sent when an application context is popped. The sender
|
||||
is the application. This usually falls in line with the
|
||||
:data:`appcontext_tearing_down` signal.
|
||||
|
||||
.. versionadded:: 0.10
|
||||
|
||||
.. data:: message_flashed
|
||||
|
||||
This signal is sent when the application is flashing a message. The
|
||||
messages is sent as `message` keyword argument and the category as
|
||||
`category`.
|
||||
|
||||
Example subscriber::
|
||||
|
||||
recorded = []
|
||||
def record(sender, message, category, **extra):
|
||||
recorded.append((message, category))
|
||||
|
||||
from flask import message_flashed
|
||||
message_flashed.connect(record, app)
|
||||
|
||||
.. versionadded:: 0.10
|
||||
|
||||
|
||||
Class-Based Views
|
||||
Class Based Views
|
||||
-----------------
|
||||
|
||||
.. versionadded:: 0.7
|
||||
|
|
@ -537,7 +498,7 @@ Generally there are three ways to define rules for the routing system:
|
|||
which is exposed as :attr:`flask.Flask.url_map`.
|
||||
|
||||
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
|
||||
well by using ``<converter:name>``.
|
||||
|
||||
|
|
@ -546,16 +507,12 @@ Variable parts are passed to the view function as keyword arguments.
|
|||
The following converters are available:
|
||||
|
||||
=========== ===============================================
|
||||
`string` accepts any text without a slash (the default)
|
||||
`unicode` accepts any text without a slash (the default)
|
||||
`int` accepts integers
|
||||
`float` like `int` but for floating point values
|
||||
`path` like the default but also accepts slashes
|
||||
`any` matches one of the items provided
|
||||
`uuid` accepts UUID strings
|
||||
=========== ===============================================
|
||||
|
||||
Custom converters can be defined using :attr:`flask.Flask.url_map`.
|
||||
|
||||
Here are some examples::
|
||||
|
||||
@app.route('/')
|
||||
|
|
@ -571,7 +528,7 @@ Here are some examples::
|
|||
pass
|
||||
|
||||
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:
|
||||
|
||||
1. If a rule ends with a slash and is requested without a slash by the
|
||||
|
|
@ -580,11 +537,11 @@ apply:
|
|||
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.
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
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
|
||||
definition for a URL that accepts an optional page::
|
||||
|
||||
@app.route('/users/', defaults={'page': 1})
|
||||
|
|
@ -593,49 +550,39 @@ definition for a URL that accepts an optional page::
|
|||
pass
|
||||
|
||||
This specifies that ``/users/`` will be the URL for page one and
|
||||
``/users/page/N`` will be the URL for page ``N``.
|
||||
|
||||
If a URL contains a default value, it will be redirected to its simpler
|
||||
form with a 308 redirect. In the above example, ``/users/page/1`` will
|
||||
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
|
||||
``/users/page/N`` will be the URL for page `N`.
|
||||
|
||||
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
|
||||
instead of the `view_func` parameter.
|
||||
|
||||
=============== ==========================================================
|
||||
`rule` the URL rule as string
|
||||
`endpoint` the endpoint for the registered URL rule. Flask itself
|
||||
`rule` the URL roule as string
|
||||
`endpoint` the endpoint for the registered URL rule. Flask itself
|
||||
assumes that the name of the view function is the name
|
||||
of the endpoint if not explicitly stated.
|
||||
`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
|
||||
:attr:`~flask.Flask.view_functions` dictionary with the
|
||||
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.
|
||||
`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.
|
||||
`**options` the options to be forwarded to the underlying
|
||||
:class:`~werkzeug.routing.Rule` object. A change to
|
||||
Werkzeug is handling of method options. methods is a list
|
||||
of methods this rule should be limited to (``GET``, ``POST``
|
||||
etc.). By default a rule just listens for ``GET`` (and
|
||||
implicitly ``HEAD``). Starting with Flask 0.6, ``OPTIONS`` is
|
||||
:class:`~werkzeug.routing.Rule` object. A change to
|
||||
Werkzeug is handling of method options. methods is a list
|
||||
of methods this rule should be limited to (`GET`, `POST`
|
||||
etc.). By default a rule just listens for `GET` (and
|
||||
implicitly `HEAD`). Starting with Flask 0.6, `OPTIONS` is
|
||||
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
|
||||
---------------------
|
||||
|
|
@ -645,26 +592,22 @@ customize behavior the view function would normally not have control over.
|
|||
The following attributes can be provided optionally to either override
|
||||
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
|
||||
endpoint is provided explicitly this value is used. Additionally this
|
||||
- `__name__`: The name of a function is by default used as endpoint. If
|
||||
endpoint is provided explicitly this value is used. Additionally this
|
||||
will be prefixed with the name of the blueprint by default which
|
||||
cannot be customized from the function itself.
|
||||
|
||||
- `methods`: If methods are not provided when the URL rule is added,
|
||||
Flask will look on the view function object itself if a `methods`
|
||||
attribute exists. If it does, it will pull the information for the
|
||||
Flask will look on the view function object itself is an `methods`
|
||||
attribute exists. If it does, it will pull the information for the
|
||||
methods from there.
|
||||
|
||||
- `provide_automatic_options`: if this attribute is set Flask will
|
||||
either force enable or disable the automatic implementation of the
|
||||
HTTP ``OPTIONS`` response. This can be useful when working with
|
||||
decorators that want to customize the ``OPTIONS`` response on a per-view
|
||||
HTTP `OPTIONS` response. This can be useful when working with
|
||||
decorators that want to customize the `OPTIONS` response on a per-view
|
||||
basis.
|
||||
|
||||
- `required_methods`: if this attribute is set, Flask will always add
|
||||
these methods when registering a URL rule even if the methods were
|
||||
explicitly overridden in the ``route()`` call.
|
||||
|
||||
Full example::
|
||||
|
||||
def index():
|
||||
|
|
@ -679,30 +622,3 @@ Full example::
|
|||
|
||||
.. versionadded:: 0.8
|
||||
The `provide_automatic_options` functionality was added.
|
||||
|
||||
Command Line Interface
|
||||
----------------------
|
||||
|
||||
.. currentmodule:: flask.cli
|
||||
|
||||
.. autoclass:: FlaskGroup
|
||||
:members:
|
||||
|
||||
.. autoclass:: AppGroup
|
||||
:members:
|
||||
|
||||
.. autoclass:: ScriptInfo
|
||||
:members:
|
||||
|
||||
.. autofunction:: load_dotenv
|
||||
|
||||
.. autofunction:: with_appcontext
|
||||
|
||||
.. autofunction:: pass_script_info
|
||||
|
||||
Marks a function so that an instance of :class:`ScriptInfo` is passed
|
||||
as first argument to the click callback.
|
||||
|
||||
.. autodata:: run_command
|
||||
|
||||
.. autodata:: shell_command
|
||||
|
|
|
|||
|
|
@ -1,187 +0,0 @@
|
|||
The App and Request Context
|
||||
===========================
|
||||
|
||||
The context keeps track of data and objects during a request, CLI command, or
|
||||
other activity. Rather than passing this data around to every function, the
|
||||
:data:`.current_app`, :data:`.g`, :data:`.request`, and :data:`.session` proxies
|
||||
are accessed instead.
|
||||
|
||||
When handling a request, the context is referred to as the "request context"
|
||||
because it contains request data in addition to application data. Otherwise,
|
||||
such as during a CLI command, it is referred to as the "app context". During an
|
||||
app context, :data:`.current_app` and :data:`.g` are available, while during a
|
||||
request context :data:`.request` and :data:`.session` are also available.
|
||||
|
||||
|
||||
Purpose of the Context
|
||||
----------------------
|
||||
|
||||
The context and proxies help solve two development issues: circular imports, and
|
||||
passing around global data during a request.
|
||||
|
||||
The :class:`.Flask` application object has attributes, such as
|
||||
:attr:`~.Flask.config`, that are useful to access within views and other
|
||||
functions. However, importing the ``app`` instance within the modules in your
|
||||
project is prone to circular import issues. When using the
|
||||
:doc:`app factory pattern </patterns/appfactories>` or writing reusable
|
||||
:doc:`blueprints </blueprints>` or :doc:`extensions </extensions>` there won't
|
||||
be an ``app`` instance to import at all.
|
||||
|
||||
When the application handles a request, it creates a :class:`.Request` object.
|
||||
Because a *worker* handles only one request at a time, the request data can be
|
||||
considered global to that worker during that request. Passing it as an argument
|
||||
through every function during the request becomes verbose and redundant.
|
||||
|
||||
Flask solves these issues with the *active context* pattern. Rather than
|
||||
importing an ``app`` directly, or having to pass it and the request through to
|
||||
every single function, you import and access the proxies, which point to the
|
||||
currently active application and request data. This is sometimes referred to
|
||||
as "context local" data.
|
||||
|
||||
|
||||
Context During Setup
|
||||
--------------------
|
||||
|
||||
If you try to access :data:`.current_app`, :data:`.g`, or anything that uses it,
|
||||
outside an app context, you'll get this error message:
|
||||
|
||||
.. code-block:: pytb
|
||||
|
||||
RuntimeError: Working outside of application context.
|
||||
|
||||
Attempted to use functionality that expected a current application to be
|
||||
set. To solve this, set up an app context using 'with app.app_context()'.
|
||||
See the documentation on app context for more information.
|
||||
|
||||
If you see that error while configuring your application, such as when
|
||||
initializing an extension, you can push a context manually since you have direct
|
||||
access to the ``app``. Use :meth:`.Flask.app_context` in a ``with`` block.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def create_app():
|
||||
app = Flask(__name__)
|
||||
|
||||
with app.app_context():
|
||||
init_db()
|
||||
|
||||
return app
|
||||
|
||||
If you see that error somewhere else in your code not related to setting up the
|
||||
application, it most likely indicates that you should move that code into a view
|
||||
function or CLI command.
|
||||
|
||||
|
||||
Context During Testing
|
||||
----------------------
|
||||
|
||||
See :doc:`/testing` for detailed information about managing the context during
|
||||
tests.
|
||||
|
||||
If you try to access :data:`.request`, :data:`.session`, or anything that uses
|
||||
it, outside a request context, you'll get this error message:
|
||||
|
||||
.. code-block:: pytb
|
||||
|
||||
RuntimeError: Working outside of request context.
|
||||
|
||||
Attempted to use functionality that expected an active HTTP request. See the
|
||||
documentation on request context for more information.
|
||||
|
||||
This will probably only happen during tests. If you see that error somewhere
|
||||
else in your code not related to testing, it most likely indicates that you
|
||||
should move that code into a view function.
|
||||
|
||||
The primary way to solve this is to use :meth:`.Flask.test_client` to simulate
|
||||
a full request.
|
||||
|
||||
If you only want to unit test one function, rather than a full request, use
|
||||
:meth:`.Flask.test_request_context` in a ``with`` block.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def generate_report(year):
|
||||
format = request.args.get("format")
|
||||
...
|
||||
|
||||
with app.test_request_context(
|
||||
"/make_report/2017", query_string={"format": "short"}
|
||||
):
|
||||
generate_report()
|
||||
|
||||
|
||||
.. _context-visibility:
|
||||
|
||||
Visibility of the Context
|
||||
-------------------------
|
||||
|
||||
The context will have the same lifetime as an activity, such as a request, CLI
|
||||
command, or ``with`` block. Various callbacks and signals registered with the
|
||||
app will be run during the context.
|
||||
|
||||
When a Flask application handles a request, it pushes a request context
|
||||
to set the active application and request data. When it handles a CLI command,
|
||||
it pushes an app context to set the active application. When the activity ends,
|
||||
it pops that context. Proxy objects like :data:`.request`, :data:`.session`,
|
||||
:data:`.g`, and :data:`.current_app`, are accessible while the context is pushed
|
||||
and active, and are not accessible after the context is popped.
|
||||
|
||||
The context is unique to each thread (or other worker type). The proxies cannot
|
||||
be passed to another worker, which has a different context space and will not
|
||||
know about the active context in the parent's space.
|
||||
|
||||
Besides being scoped to each worker, the proxy object has a separate type and
|
||||
identity than the proxied real object. In some cases you'll need access to the
|
||||
real object, rather than the proxy. Use the
|
||||
:meth:`~.LocalProxy._get_current_object` method in those cases.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
app = current_app._get_current_object()
|
||||
my_signal.send(app)
|
||||
|
||||
|
||||
Lifecycle of the Context
|
||||
------------------------
|
||||
|
||||
Flask dispatches a request in multiple stages which can affect the request,
|
||||
response, and how errors are handled. See :doc:`/lifecycle` for a list of all
|
||||
the steps, callbacks, and signals during each request. The following are the
|
||||
steps directly related to the context.
|
||||
|
||||
- The app context is pushed, the proxies are available.
|
||||
- The :data:`.appcontext_pushed` signal is sent.
|
||||
- The request is dispatched.
|
||||
- Any :meth:`.Flask.teardown_request` decorated functions are called.
|
||||
- The :data:`.request_tearing_down` signal is sent.
|
||||
- Any :meth:`.Flask.teardown_appcontext` decorated functions are called.
|
||||
- The :data:`.appcontext_tearing_down` signal is sent.
|
||||
- The app context is popped, the proxies are no longer available.
|
||||
- The :data:`.appcontext_popped` signal is sent.
|
||||
|
||||
The teardown callbacks are called by the context when it is popped. They are
|
||||
called even if there is an unhandled exception during dispatch. They may be
|
||||
called multiple times in some test scenarios. This means there is no guarantee
|
||||
that any other parts of the request dispatch have run. Be sure to write these
|
||||
functions in a way that does not depend on other callbacks. All callbacks are
|
||||
called even if any raise an error.
|
||||
|
||||
|
||||
How the Context Works
|
||||
---------------------
|
||||
|
||||
Context locals are implemented using Python's :mod:`contextvars` and Werkzeug's
|
||||
:class:`~werkzeug.local.LocalProxy`. Python's contextvars are a low level
|
||||
structure to manage data local to a thread or coroutine. ``LocalProxy`` wraps
|
||||
the contextvar so that access to any attributes and methods is forwarded to the
|
||||
object stored in the contextvar.
|
||||
|
||||
The context is tracked like a stack, with the active context at the top of the
|
||||
stack. Flask manages pushing and popping contexts during requests, CLI commands,
|
||||
testing, ``with`` blocks, etc. The proxies access attributes on the active
|
||||
context.
|
||||
|
||||
Because it is a stack, other contexts may be pushed to change the proxies during
|
||||
an already active context. This is not a common pattern, but can be used in
|
||||
advanced use cases. For example, a Flask application can be used as WSGI
|
||||
middleware, calling another wrapped Flask app from a view.
|
||||
|
|
@ -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.
|
||||
88
docs/becomingbig.rst
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
.. _becomingbig:
|
||||
|
||||
Becoming Big
|
||||
============
|
||||
|
||||
Your application is becoming more and more complex? If you suddenly
|
||||
realize that Flask does things in a way that does not work out for your
|
||||
application there are ways to deal with that.
|
||||
|
||||
Flask is powered by Werkzeug and Jinja2, two libraries that are in use at
|
||||
a number of large websites out there and all Flask does is bring those
|
||||
two together. Being a microframework Flask does not do much more than
|
||||
combining existing libraries - there is not a lot of code involved.
|
||||
What that means for large applications is that it's very easy to take the
|
||||
code from Flask and put it into a new module within the applications and
|
||||
expand on that.
|
||||
|
||||
Flask is designed to be extended and modified in a couple of different
|
||||
ways:
|
||||
|
||||
- Flask extensions. For a lot of reusable functionality you can create
|
||||
extensions. For extensions a number of hooks exist throughout Flask
|
||||
with signals and callback functions.
|
||||
|
||||
- Subclassing. The majority of functionality can be changed by creating
|
||||
a new subclass of the :class:`~flask.Flask` class and overriding
|
||||
methods provided for this exact purpose.
|
||||
|
||||
- Forking. If nothing else works out you can just take the Flask
|
||||
codebase at a given point and copy/paste it into your application
|
||||
and change it. Flask is designed with that in mind and makes this
|
||||
incredible easy. You just have to take the package and copy it
|
||||
into your application's code and rename it (for example to
|
||||
`framework`). Then you can start modifying the code in there.
|
||||
|
||||
Why consider Forking?
|
||||
---------------------
|
||||
|
||||
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.
|
||||
|
||||
Scaling 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.
|
||||
|
||||
Dialogue with the Community
|
||||
---------------------------
|
||||
|
||||
The Flask developers are very interested to keep everybody happy, so as
|
||||
soon as 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 it for larger
|
||||
applications is getting feedback from users.
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
.. _blueprints:
|
||||
|
||||
Modular Applications with Blueprints
|
||||
====================================
|
||||
|
||||
.. currentmodule:: flask
|
||||
|
||||
.. versionadded:: 0.7
|
||||
|
||||
Flask uses a concept of *blueprints* for making application components and
|
||||
|
|
@ -35,9 +35,8 @@ Blueprints in Flask are intended for these cases:
|
|||
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, even multiple times. Why not have multiple application
|
||||
objects? You can do that (see :doc:`/patterns/appdispatch`), but your
|
||||
applications will have separate configs and will be managed at the WSGI
|
||||
layer.
|
||||
objects? You can do that (see :ref:`app-dispatch`), but your applications
|
||||
will have separate configs and will be managed at the WSGI layer.
|
||||
|
||||
Blueprints instead provide separation at the Flask level, share
|
||||
application config, and can change an application object as necessary with
|
||||
|
|
@ -62,24 +61,22 @@ implement a blueprint that does simple rendering of static templates::
|
|||
from flask import Blueprint, render_template, abort
|
||||
from jinja2 import TemplateNotFound
|
||||
|
||||
simple_page = Blueprint('simple_page', __name__,
|
||||
template_folder='templates')
|
||||
simple_page = Blueprint('simple_page', __name__)
|
||||
|
||||
@simple_page.route('/', defaults={'page': 'index'})
|
||||
@simple_page.route('/<page>')
|
||||
def show(page):
|
||||
try:
|
||||
return render_template(f'pages/{page}.html')
|
||||
return render_template('pages/%s.html' % page)
|
||||
except TemplateNotFound:
|
||||
abort(404)
|
||||
|
||||
When you bind a function with the help of the ``@simple_page.route``
|
||||
decorator, the blueprint will record the intention of registering the
|
||||
function ``show`` on the application when it's later registered.
|
||||
decorator the blueprint will record the intention of registering the
|
||||
function `show` on the application when it's later registered.
|
||||
Additionally it will prefix the endpoint of the function with the
|
||||
name of the blueprint which was given to the :class:`Blueprint`
|
||||
constructor (in this case also ``simple_page``). The blueprint's name
|
||||
does not modify the URL, only the endpoint.
|
||||
constructor (in this case also ``simple_page``).
|
||||
|
||||
Registering Blueprints
|
||||
----------------------
|
||||
|
|
@ -95,12 +92,11 @@ So how do you register that blueprint? Like this::
|
|||
If you check the rules registered on the application, you will find
|
||||
these::
|
||||
|
||||
>>> app.url_map
|
||||
Map([<Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>,
|
||||
[<Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>,
|
||||
<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 ifself for the static
|
||||
files. The other two are for the `show` function of the ``simple_page``
|
||||
blueprint. As you can see, they are also prefixed with the name of the
|
||||
blueprint and separated by a dot (``.``).
|
||||
|
|
@ -111,53 +107,14 @@ Blueprints however can also be mounted at different locations::
|
|||
|
||||
And sure enough, these are the generated rules::
|
||||
|
||||
>>> app.url_map
|
||||
Map([<Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>,
|
||||
[<Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>,
|
||||
<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
|
||||
blueprint might respond properly to that. In fact it depends on how the
|
||||
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
|
||||
-------------------
|
||||
|
||||
|
|
@ -191,31 +148,23 @@ To quickly open sources from this folder you can use the
|
|||
Static Files
|
||||
````````````
|
||||
|
||||
A blueprint can expose a folder with static files by providing the path
|
||||
to the folder on the filesystem with the ``static_folder`` argument.
|
||||
It is either an absolute path or relative to the blueprint's location::
|
||||
A blueprint can expose a folder with static files by providing a path to a
|
||||
folder on the filesystem via the `static_folder` keyword argument. It can
|
||||
either be an absolute path or one relative to the folder of the
|
||||
blueprint::
|
||||
|
||||
admin = Blueprint('admin', __name__, static_folder='static')
|
||||
|
||||
By default the rightmost part of the path is where it is exposed on the
|
||||
web. This can be changed with the ``static_url_path`` argument. Because the
|
||||
folder is called ``static`` here it will be available at the
|
||||
``url_prefix`` of the blueprint + ``/static``. If the blueprint
|
||||
has the prefix ``/admin``, the static URL will be ``/admin/static``.
|
||||
web. Because the folder is called ``static`` here it will be available at
|
||||
the location of the blueprint + ``/static``. Say the blueprint is
|
||||
registered for ``/admin`` the static folder will be at ``/admin/static``.
|
||||
|
||||
The endpoint is named ``blueprint_name.static``. You can generate URLs
|
||||
to it with :func:`url_for` like you would with the static folder of the
|
||||
application::
|
||||
The endpoint is named `blueprint_name.static` so you can generate URLs to
|
||||
it like you would do to the static folder of the application::
|
||||
|
||||
url_for('admin.static', filename='style.css')
|
||||
|
||||
However, if the blueprint does not have a ``url_prefix``, it is not
|
||||
possible to access the blueprint's static folder. This is because the
|
||||
URL would be ``/static`` in this case, and the application's ``/static``
|
||||
route takes precedence. Unlike template folders, blueprint static
|
||||
folders are not searched if the file does not exist in the application
|
||||
static folder.
|
||||
|
||||
Templates
|
||||
`````````
|
||||
|
||||
|
|
@ -224,43 +173,16 @@ the `template_folder` parameter to the :class:`Blueprint` constructor::
|
|||
|
||||
admin = Blueprint('admin', __name__, template_folder='templates')
|
||||
|
||||
For static files, the path can be absolute or relative to the blueprint
|
||||
resource folder.
|
||||
|
||||
The template folder is added to the search path of templates but with a lower
|
||||
priority than the actual application's template folder. That way you can
|
||||
easily override templates that a blueprint provides in the actual application.
|
||||
This also means that if you don't want a blueprint template to be accidentally
|
||||
overridden, make sure that no other blueprint or actual application template
|
||||
has the same relative path. When multiple blueprints provide the same relative
|
||||
template path the first blueprint registered takes precedence over the others.
|
||||
|
||||
As for static files, the path can be absolute or relative to the blueprint
|
||||
resource folder. The template folder is added to the searchpath of
|
||||
templates but with a lower priority than the actual application's template
|
||||
folder. That way you can easily override templates that a blueprint
|
||||
provides in the actual application.
|
||||
|
||||
So if you have a blueprint in the folder ``yourapplication/admin`` and you
|
||||
want to render the template ``'admin/index.html'`` and you have provided
|
||||
``templates`` as a `template_folder` you will have to create a file like
|
||||
this: :file:`yourapplication/admin/templates/admin/index.html`. The reason
|
||||
for the extra ``admin`` folder is to avoid getting our template overridden
|
||||
by a template named ``index.html`` in the actual application template
|
||||
folder.
|
||||
|
||||
To further reiterate this: if you have a blueprint named ``admin`` and you
|
||||
want to render a template called :file:`index.html` which is specific to this
|
||||
blueprint, the best idea is to lay out your templates like this::
|
||||
|
||||
yourpackage/
|
||||
blueprints/
|
||||
admin/
|
||||
templates/
|
||||
admin/
|
||||
index.html
|
||||
__init__.py
|
||||
|
||||
And then when you want to render the template, use :file:`admin/index.html` as
|
||||
the name to look up the template by. If you encounter problems loading
|
||||
the correct templates enable the ``EXPLAIN_TEMPLATE_LOADING`` config
|
||||
variable which will instruct Flask to print out the steps it goes through
|
||||
to locate templates on every ``render_template`` call.
|
||||
this: ``yourapplication/admin/templates/admin/index.html``.
|
||||
|
||||
Building URLs
|
||||
-------------
|
||||
|
|
@ -279,37 +201,3 @@ 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
|
||||
was dispatched to any other admin blueprint endpoint.
|
||||
|
||||
|
||||
Blueprint Error Handlers
|
||||
------------------------
|
||||
|
||||
Blueprints support the ``errorhandler`` decorator just like the :class:`Flask`
|
||||
application object, so it is easy to make Blueprint-specific custom error
|
||||
pages.
|
||||
|
||||
Here is an example for a "404 Page Not Found" exception::
|
||||
|
||||
@simple_page.errorhandler(404)
|
||||
def page_not_found(e):
|
||||
return render_template('pages/404.html')
|
||||
|
||||
Most errorhandlers will simply work as expected; however, there is a caveat
|
||||
concerning handlers for 404 and 405 exceptions. These errorhandlers 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::
|
||||
|
||||
@app.errorhandler(404)
|
||||
@app.errorhandler(405)
|
||||
def _handle_api_error(ex):
|
||||
if request.path.startswith('/api/'):
|
||||
return jsonify(error=str(ex)), ex.code
|
||||
else:
|
||||
return ex
|
||||
|
||||
See :doc:`/errorhandling`.
|
||||
|
|
|
|||
1
docs/changelog.rst
Normal file
|
|
@ -0,0 +1 @@
|
|||
.. include:: ../CHANGES
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
Changes
|
||||
=======
|
||||
|
||||
.. include:: ../CHANGES.rst
|
||||
556
docs/cli.rst
|
|
@ -1,556 +0,0 @@
|
|||
.. currentmodule:: flask
|
||||
|
||||
Command Line Interface
|
||||
======================
|
||||
|
||||
Installing Flask installs the ``flask`` script, a `Click`_ command line
|
||||
interface, in your virtualenv. Executed from the terminal, this script gives
|
||||
access to built-in, extension, and application-defined commands. The ``--help``
|
||||
option will give more information about any commands and options.
|
||||
|
||||
.. _Click: https://click.palletsprojects.com/
|
||||
|
||||
|
||||
Application Discovery
|
||||
---------------------
|
||||
|
||||
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``
|
||||
option is used to specify how to load the application.
|
||||
|
||||
While ``--app`` supports a variety of options for specifying your
|
||||
application, most use cases should be simple. Here are the typical values:
|
||||
|
||||
(nothing)
|
||||
The name "app" or "wsgi" is imported (as a ".py" file, or package),
|
||||
automatically detecting an app (``app`` or ``application``) or
|
||||
factory (``create_app`` or ``make_app``).
|
||||
|
||||
``--app hello``
|
||||
The given name is imported, automatically detecting an app (``app``
|
||||
or ``application``) or factory (``create_app`` or ``make_app``).
|
||||
|
||||
----
|
||||
|
||||
``--app`` has three parts: an optional path that sets the current working
|
||||
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
|
||||
be followed by arguments in parentheses. The following values demonstrate these
|
||||
parts:
|
||||
|
||||
``--app src/hello``
|
||||
Sets the current working directory to ``src`` then imports ``hello``.
|
||||
|
||||
``--app hello.web``
|
||||
Imports the path ``hello.web``.
|
||||
|
||||
``--app hello:app2``
|
||||
Uses the ``app2`` Flask instance in ``hello``.
|
||||
|
||||
``--app 'hello:create_app("dev")'``
|
||||
The ``create_app`` factory in ``hello`` is called with the string ``'dev'``
|
||||
as the argument.
|
||||
|
||||
If ``--app`` is not set, the command will try to import "app" or
|
||||
"wsgi" (as a ".py" file, or package) and try to detect an application
|
||||
instance or factory.
|
||||
|
||||
Within the given import, the command looks for an application instance named
|
||||
``app`` or ``application``, then any application instance. If no instance is
|
||||
found, the command looks for a factory function named ``create_app`` or
|
||||
``make_app`` that returns an instance.
|
||||
|
||||
If parentheses follow the factory name, their contents are parsed as
|
||||
Python literals and passed as arguments and keyword arguments to the
|
||||
function. This means that strings must still be in quotes.
|
||||
|
||||
|
||||
Run the Development Server
|
||||
--------------------------
|
||||
|
||||
The :func:`run <cli.run_command>` command will start the development server. It
|
||||
replaces the :meth:`Flask.run` method in most cases. ::
|
||||
|
||||
$ flask --app hello run
|
||||
* Serving Flask app "hello"
|
||||
* 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.
|
||||
Only use the development server during development. The development server
|
||||
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.
|
||||
|
||||
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
|
||||
------------
|
||||
|
||||
To explore the data in your application, you can start an interactive Python
|
||||
shell with the :func:`shell <cli.shell_command>` command. An application
|
||||
context will be active, and the app instance will be imported. ::
|
||||
|
||||
$ flask shell
|
||||
Python 3.10.0 (default, Oct 27 2021, 06:59:51) [GCC 11.1.0] on linux
|
||||
App: example [production]
|
||||
Instance: /home/david/Projects/pallets/flask/instance
|
||||
>>>
|
||||
|
||||
Use :meth:`~Flask.shell_context_processor` to add other automatic imports.
|
||||
|
||||
|
||||
.. _dotenv:
|
||||
|
||||
Environment Variables From dotenv
|
||||
---------------------------------
|
||||
|
||||
The ``flask`` command supports setting any option for any command with
|
||||
environment variables. The variables are named like ``FLASK_OPTION`` or
|
||||
``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
|
||||
environment variables defined in the files ``.env`` and ``.flaskenv``.
|
||||
You can also specify an extra file to load with the ``--env-file``
|
||||
option. Dotenv files can be used to avoid having to set ``--app`` or
|
||||
``FLASK_APP`` manually, and to set configuration using environment
|
||||
variables similar to how some deployment services work.
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
Directories are scanned upwards from the directory you call ``flask``
|
||||
from to locate the files.
|
||||
|
||||
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
|
||||
production, you should call :func:`~cli.load_dotenv` manually.
|
||||
|
||||
.. _python-dotenv: https://github.com/theskumar/python-dotenv#readme
|
||||
|
||||
|
||||
Setting Command Options
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Click is configured to load default values for command options from
|
||||
environment variables. The variables use the pattern
|
||||
``FLASK_COMMAND_OPTION``. For example, to set the port for the run
|
||||
command, instead of ``flask run --port 8000``:
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. group-tab:: Bash
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ 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
|
||||
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
|
||||
-------------------------------------
|
||||
|
||||
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`
|
||||
script. Activating the virtualenv will set the variables.
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. group-tab:: Bash
|
||||
|
||||
Unix Bash, :file:`.venv/bin/activate`::
|
||||
|
||||
$ export 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
|
||||
committed to the repository so that it works automatically wherever the project
|
||||
is checked out.
|
||||
|
||||
|
||||
Custom Commands
|
||||
---------------
|
||||
|
||||
The ``flask`` command is implemented using `Click`_. See that project's
|
||||
documentation for full information about writing commands.
|
||||
|
||||
This example adds the command ``create-user`` that takes the argument
|
||||
``name``. ::
|
||||
|
||||
import click
|
||||
from flask import Flask
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.cli.command("create-user")
|
||||
@click.argument("name")
|
||||
def create_user(name):
|
||||
...
|
||||
|
||||
::
|
||||
|
||||
$ flask create-user admin
|
||||
|
||||
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. ::
|
||||
|
||||
import click
|
||||
from flask import Flask
|
||||
from flask.cli import AppGroup
|
||||
|
||||
app = Flask(__name__)
|
||||
user_cli = AppGroup('user')
|
||||
|
||||
@user_cli.command('create')
|
||||
@click.argument('name')
|
||||
def create_user(name):
|
||||
...
|
||||
|
||||
app.cli.add_command(user_cli)
|
||||
|
||||
::
|
||||
|
||||
$ flask user create demo
|
||||
|
||||
See :ref:`testing-cli` for an overview of how to test your custom
|
||||
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
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Commands added using the Flask app's :attr:`~Flask.cli` or
|
||||
:class:`~flask.cli.FlaskGroup` :meth:`~cli.AppGroup.command` decorator
|
||||
will be executed with an application context pushed, so your custom
|
||||
commands and parameters have access to the app and its configuration. The
|
||||
:func:`~cli.with_appcontext` decorator can be used to get the same
|
||||
behavior, but is not needed in most cases.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import click
|
||||
from flask.cli import with_appcontext
|
||||
|
||||
@click.command()
|
||||
@with_appcontext
|
||||
def do_work():
|
||||
...
|
||||
|
||||
app.cli.add_command(do_work)
|
||||
|
||||
|
||||
Plugins
|
||||
-------
|
||||
|
||||
Flask will automatically load commands specified in the ``flask.commands``
|
||||
`entry point`_. This is useful for extensions that want to add commands when
|
||||
they are installed. Entry points are specified in :file:`pyproject.toml`:
|
||||
|
||||
.. code-block:: toml
|
||||
|
||||
[project.entry-points."flask.commands"]
|
||||
my-command = "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
|
||||
object::
|
||||
|
||||
import click
|
||||
|
||||
@click.command()
|
||||
def cli():
|
||||
...
|
||||
|
||||
Once that package is installed in the same virtualenv as your Flask project,
|
||||
you can run ``flask my-command`` to invoke the command.
|
||||
|
||||
|
||||
.. _custom-scripts:
|
||||
|
||||
Custom Scripts
|
||||
--------------
|
||||
|
||||
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 application, you can create your own Click object and export it as a
|
||||
`console script`_ entry point.
|
||||
|
||||
Create an instance of :class:`~cli.FlaskGroup` and pass it the factory::
|
||||
|
||||
import click
|
||||
from flask import Flask
|
||||
from flask.cli import FlaskGroup
|
||||
|
||||
def create_app():
|
||||
app = Flask('wiki')
|
||||
# other setup
|
||||
return app
|
||||
|
||||
@click.group(cls=FlaskGroup, create_app=create_app)
|
||||
def cli():
|
||||
"""Management script for the Wiki application."""
|
||||
|
||||
Define the entry point in :file:`pyproject.toml`:
|
||||
|
||||
.. code-block:: toml
|
||||
|
||||
[project.scripts]
|
||||
wiki = "wiki:cli"
|
||||
|
||||
Install the application in the virtualenv in editable mode and the custom
|
||||
script is available. Note that you don't need to set ``--app``. ::
|
||||
|
||||
$ pip install -e .
|
||||
$ wiki run
|
||||
|
||||
.. admonition:: Errors in Custom Scripts
|
||||
|
||||
When using a custom script, if you introduce an error in your
|
||||
module-level code, the reloader will fail because it can no longer
|
||||
load the entry point.
|
||||
|
||||
The ``flask`` command, being separate from your code, does not have
|
||||
this issue and is recommended in most cases.
|
||||
|
||||
.. _console script: https://packaging.python.org/tutorials/packaging-projects/#console-scripts
|
||||
|
||||
|
||||
PyCharm Integration
|
||||
-------------------
|
||||
|
||||
PyCharm Professional provides a special Flask run configuration to run the development
|
||||
server. For the Community Edition, and for other commands besides ``run``, you need to
|
||||
create a custom run configuration. These instructions should be similar for any other
|
||||
IDE you use.
|
||||
|
||||
In PyCharm, with your project open, click on *Run* from the menu bar and go to *Edit
|
||||
Configurations*. You'll see a screen similar to this:
|
||||
|
||||
.. image:: _static/pycharm-run-config.png
|
||||
:align: center
|
||||
:class: screenshot
|
||||
:alt: Screenshot of PyCharm run configuration.
|
||||
|
||||
Once you create a configuration for the ``flask run``, you can copy and change it to
|
||||
call any other command.
|
||||
|
||||
Click the *+ (Add New Configuration)* button and select *Python*. Give the configuration
|
||||
a name such as "flask run".
|
||||
|
||||
Click the *Script path* dropdown and change it to *Module name*, then input ``flask``.
|
||||
|
||||
The *Parameters* field is set to the CLI command to execute along with any arguments.
|
||||
This example uses ``--app hello run --debug``, which will run the development server in
|
||||
debug mode. ``--app hello`` should be the import or file with your Flask app.
|
||||
|
||||
If you installed your project as a package in your virtualenv, you may uncheck the
|
||||
*PYTHONPATH* options. This will more accurately match how you deploy later.
|
||||
|
||||
Click *OK* to save and close the configuration. Select the configuration in the main
|
||||
PyCharm window and click the play button next to it to run the server.
|
||||
|
||||
Now that you have a configuration for ``flask run``, you can copy that configuration and
|
||||
change the *Parameters* argument to run a different CLI command.
|
||||
330
docs/conf.py
|
|
@ -1,101 +1,269 @@
|
|||
import packaging.version
|
||||
from pallets_sphinx_themes import get_version
|
||||
from pallets_sphinx_themes import ProjectLink
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Flask documentation build configuration file, created by
|
||||
# sphinx-quickstart on Tue Apr 6 15:24:58 2010.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
# Project --------------------------------------------------------------
|
||||
import sys, os
|
||||
|
||||
project = "Flask"
|
||||
copyright = "2010 Pallets"
|
||||
author = "Pallets"
|
||||
release, version = get_version("Flask")
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
sys.path.append(os.path.abspath('_themes'))
|
||||
sys.path.append(os.path.abspath('.'))
|
||||
|
||||
# General --------------------------------------------------------------
|
||||
# -- General configuration -----------------------------------------------------
|
||||
|
||||
default_role = "code"
|
||||
extensions = [
|
||||
"sphinx.ext.autodoc",
|
||||
"sphinx.ext.extlinks",
|
||||
"sphinx.ext.intersphinx",
|
||||
"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 = {
|
||||
"python": ("https://docs.python.org/3/", None),
|
||||
"werkzeug": ("https://werkzeug.palletsprojects.com/", None),
|
||||
"click": ("https://click.palletsprojects.com/", None),
|
||||
"jinja": ("https://jinja.palletsprojects.com/", None),
|
||||
"itsdangerous": ("https://itsdangerous.palletsprojects.com/", None),
|
||||
"sqlalchemy": ("https://docs.sqlalchemy.org/", None),
|
||||
"wtforms": ("https://wtforms.readthedocs.io/", None),
|
||||
"blinker": ("https://blinker.readthedocs.io/", None),
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx',
|
||||
'flaskdocext']
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The encoding of source files.
|
||||
#source_encoding = 'utf-8-sig'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'Flask'
|
||||
copyright = u'2010, Armin Ronacher'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
import pkg_resources
|
||||
try:
|
||||
release = pkg_resources.get_distribution('Flask').version
|
||||
except pkg_resources.DistributionNotFound:
|
||||
print 'To build the documentation, The distribution information of Flask'
|
||||
print 'Has to be available. Either install the package into your'
|
||||
print 'development environment or run "setup.py develop" to setup the'
|
||||
print 'metadata. A virtualenv is recommended!'
|
||||
sys.exit(1)
|
||||
del pkg_resources
|
||||
|
||||
if 'dev' in release:
|
||||
release = release.split('dev')[0] + 'dev'
|
||||
version = '.'.join(release.split('.')[:2])
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#language = None
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
#today = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
#today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = ['_build']
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||
#default_role = None
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
#add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
#add_module_names = True
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
#show_authors = False
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
#modindex_common_prefix = []
|
||||
|
||||
|
||||
# -- Options for HTML output ---------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. Major themes that come with
|
||||
# Sphinx are currently 'default' and 'sphinxdoc'.
|
||||
html_theme = 'flask'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
html_theme_options = {
|
||||
'touch_icon': 'touch-icon.png'
|
||||
}
|
||||
|
||||
# HTML -----------------------------------------------------------------
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
html_theme_path = ['_themes']
|
||||
|
||||
html_theme = "flask"
|
||||
html_theme_options = {"index_sidebar_logo": False}
|
||||
html_context = {
|
||||
"project_links": [
|
||||
ProjectLink("Donate", "https://palletsprojects.com/donate"),
|
||||
ProjectLink("PyPI Releases", "https://pypi.org/project/Flask/"),
|
||||
ProjectLink("Source Code", "https://github.com/pallets/flask/"),
|
||||
ProjectLink("Issue Tracker", "https://github.com/pallets/flask/issues/"),
|
||||
ProjectLink("Chat", "https://discord.gg/pallets"),
|
||||
]
|
||||
}
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
#html_title = None
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
#html_short_title = None
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar. Do not set, template magic!
|
||||
#html_logo = None
|
||||
|
||||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
#html_favicon = None
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
#html_last_updated_fmt = '%b %d, %Y'
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
#html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
html_sidebars = {
|
||||
"index": ["project.html", "localtoc.html", "searchbox.html", "ethicalads.html"],
|
||||
"**": ["localtoc.html", "relations.html", "searchbox.html", "ethicalads.html"],
|
||||
'index': ['sidebarintro.html', 'sourcelink.html', 'searchbox.html'],
|
||||
'**': ['sidebarlogo.html', 'localtoc.html', 'relations.html',
|
||||
'sourcelink.html', 'searchbox.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
|
||||
|
||||
gettext_uuid = True
|
||||
gettext_compact = False
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
#html_additional_pages = {}
|
||||
|
||||
# Local Extensions -----------------------------------------------------
|
||||
# If false, no module index is generated.
|
||||
html_use_modindex = False
|
||||
|
||||
# If false, no index is generated.
|
||||
#html_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
#html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
#html_show_sourcelink = True
|
||||
|
||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||
html_show_sphinx = False
|
||||
|
||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||
#html_show_copyright = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
#html_use_opensearch = ''
|
||||
|
||||
# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
#html_file_suffix = ''
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'Flaskdoc'
|
||||
|
||||
|
||||
def github_link(name, rawtext, text, lineno, inliner, options=None, content=None):
|
||||
app = inliner.document.settings.env.app
|
||||
release = app.config.release
|
||||
base_url = "https://github.com/pallets/flask/tree/"
|
||||
# -- Options for LaTeX output --------------------------------------------------
|
||||
|
||||
if text.endswith(">"):
|
||||
words, text = text[:-1].rsplit("<", 1)
|
||||
words = words.strip()
|
||||
else:
|
||||
words = None
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||
latex_documents = [
|
||||
('latexindex', 'Flask.tex', u'Flask Documentation',
|
||||
u'Armin Ronacher', 'manual'),
|
||||
]
|
||||
|
||||
if packaging.version.parse(release).is_devrelease:
|
||||
url = f"{base_url}main/{text}"
|
||||
else:
|
||||
url = f"{base_url}{release}/{text}"
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#latex_appendices = []
|
||||
|
||||
if words is None:
|
||||
words = url
|
||||
# If false, no module index is generated.
|
||||
latex_use_modindex = False
|
||||
|
||||
from docutils.nodes import reference
|
||||
from docutils.parsers.rst.roles import set_classes
|
||||
latex_elements = {
|
||||
'fontpkg': r'\usepackage{mathpazo}',
|
||||
'papersize': 'a4paper',
|
||||
'pointsize': '12pt',
|
||||
'preamble': r'\usepackage{flaskstyle}'
|
||||
}
|
||||
latex_use_parts = True
|
||||
|
||||
options = options or {}
|
||||
set_classes(options)
|
||||
node = reference(rawtext, words, refuri=url, **options)
|
||||
return [node], []
|
||||
latex_additional_files = ['flaskstyle.sty', 'logo.pdf']
|
||||
|
||||
|
||||
def setup(app):
|
||||
app.add_role("gh", github_link)
|
||||
# -- Options for Epub output ---------------------------------------------------
|
||||
|
||||
# Bibliographic Dublin Core info.
|
||||
#epub_title = ''
|
||||
#epub_author = ''
|
||||
#epub_publisher = ''
|
||||
#epub_copyright = ''
|
||||
|
||||
# The language of the text. It defaults to the language option
|
||||
# or en if the language is not set.
|
||||
#epub_language = ''
|
||||
|
||||
# The scheme of the identifier. Typical schemes are ISBN or URL.
|
||||
#epub_scheme = ''
|
||||
|
||||
# The unique identifier of the text. This can be a ISBN number
|
||||
# or the project homepage.
|
||||
#epub_identifier = ''
|
||||
|
||||
# A unique identification for the text.
|
||||
#epub_uid = ''
|
||||
|
||||
# HTML files that should be inserted before the pages created by sphinx.
|
||||
# The format is a list of tuples containing the path and title.
|
||||
#epub_pre_files = []
|
||||
|
||||
# HTML files shat should be inserted after the pages created by sphinx.
|
||||
# The format is a list of tuples containing the path and title.
|
||||
#epub_post_files = []
|
||||
|
||||
# A list of files that should not be packed into the epub file.
|
||||
#epub_exclude_files = []
|
||||
|
||||
# The depth of the table of contents in toc.ncx.
|
||||
#epub_tocdepth = 3
|
||||
|
||||
intersphinx_mapping = {
|
||||
'http://docs.python.org/dev': None,
|
||||
'http://werkzeug.pocoo.org/docs/': None,
|
||||
'http://www.sqlalchemy.org/docs/': None,
|
||||
'http://wtforms.simplecodes.com/docs/0.5/': None,
|
||||
'http://discorporate.us/projects/Blinker/docs/1.1/': None
|
||||
}
|
||||
|
||||
pygments_style = 'flask_theme_support.FlaskyStyle'
|
||||
|
||||
# fall back if theme is not there
|
||||
try:
|
||||
__import__('flask_theme_support')
|
||||
except ImportError, e:
|
||||
print '-' * 74
|
||||
print 'Warning: Flask themes unavailable. Building with default theme'
|
||||
print 'If you want the Flask themes, run this command and build again:'
|
||||
print
|
||||
print ' git submodule update --init'
|
||||
print '-' * 74
|
||||
|
||||
pygments_style = 'tango'
|
||||
html_theme = 'default'
|
||||
html_theme_options = {}
|
||||
|
|
|
|||
743
docs/config.rst
|
|
@ -1,13 +1,17 @@
|
|||
.. _config:
|
||||
|
||||
Configuration Handling
|
||||
======================
|
||||
|
||||
.. versionadded:: 0.3
|
||||
|
||||
Applications need some kind of configuration. There are different settings
|
||||
you might want to change depending on the application environment like
|
||||
toggling the debug mode, setting the secret key, and other such
|
||||
environment-specific things.
|
||||
|
||||
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
|
||||
actually that bad, but there are better ways.
|
||||
|
||||
|
|
@ -18,7 +22,6 @@ object. This is the place where Flask itself puts certain configuration
|
|||
values and also where extensions can put their configuration values. But
|
||||
this is also where you can have your own configuration.
|
||||
|
||||
|
||||
Configuration Basics
|
||||
--------------------
|
||||
|
||||
|
|
@ -26,373 +29,123 @@ The :attr:`~flask.Flask.config` is actually a subclass of a dictionary and
|
|||
can be modified just like any dictionary::
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config['TESTING'] = True
|
||||
app.config['DEBUG'] = True
|
||||
|
||||
Certain configuration values are also forwarded to the
|
||||
:attr:`~flask.Flask` object so you can read and write them from there::
|
||||
|
||||
app.testing = True
|
||||
app.debug = True
|
||||
|
||||
To update multiple keys at once you can use the :meth:`dict.update`
|
||||
method::
|
||||
|
||||
app.config.update(
|
||||
TESTING=True,
|
||||
SECRET_KEY='192b9bdd22ab9ed4d12e236c78afcb9a393ec15f71bbf5dc987d54727823bcbf'
|
||||
DEBUG=True,
|
||||
SECRET_KEY='...'
|
||||
)
|
||||
|
||||
|
||||
Debug Mode
|
||||
----------
|
||||
|
||||
The :data:`DEBUG` config value is special because it may behave inconsistently if
|
||||
changed after the app has begun setting up. In order to set debug mode reliably, use the
|
||||
``--debug`` option on the ``flask`` or ``flask run`` command. ``flask run`` will use the
|
||||
interactive debugger and reloader by default in debug mode.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ flask --app hello run --debug
|
||||
|
||||
Using the option is recommended. While it is possible to set :data:`DEBUG` in your
|
||||
config or code, this is strongly discouraged. It can't be read early by the
|
||||
``flask run`` command, and some systems or extensions may have already configured
|
||||
themselves based on a previous value.
|
||||
|
||||
|
||||
Builtin Configuration Values
|
||||
----------------------------
|
||||
|
||||
The following configuration values are used internally by Flask:
|
||||
|
||||
.. py:data:: DEBUG
|
||||
|
||||
Whether debug mode is enabled. When using ``flask run`` to start the development
|
||||
server, an interactive debugger will be shown for unhandled exceptions, and the
|
||||
server will be reloaded when code changes. The :attr:`~flask.Flask.debug` attribute
|
||||
maps to this config key. This is set with the ``FLASK_DEBUG`` environment variable.
|
||||
It may not behave as expected if set in code.
|
||||
|
||||
**Do not enable debug mode when deploying in production.**
|
||||
|
||||
Default: ``False``
|
||||
|
||||
.. py:data:: TESTING
|
||||
|
||||
Enable testing mode. Exceptions are propagated rather than handled by
|
||||
the app's error handlers. Extensions may also change their behavior to
|
||||
facilitate easier testing. You should enable this in your own tests.
|
||||
|
||||
Default: ``False``
|
||||
|
||||
.. py:data:: PROPAGATE_EXCEPTIONS
|
||||
|
||||
Exceptions are re-raised rather than being handled by the app's error
|
||||
handlers. If not set, this is implicitly true if ``TESTING`` or ``DEBUG``
|
||||
is enabled.
|
||||
|
||||
Default: ``None``
|
||||
|
||||
.. py:data:: TRAP_HTTP_EXCEPTIONS
|
||||
|
||||
If there is no handler for an ``HTTPException``-type exception, re-raise it
|
||||
to be handled by the interactive debugger instead of returning it as a
|
||||
simple error response.
|
||||
|
||||
Default: ``False``
|
||||
|
||||
.. py:data:: TRAP_BAD_REQUEST_ERRORS
|
||||
|
||||
Trying to access a key that doesn't exist from request dicts like ``args``
|
||||
and ``form`` will return a 400 Bad Request error page. Enable this to treat
|
||||
the error as an unhandled exception instead so that you get the interactive
|
||||
debugger. This is a more specific version of ``TRAP_HTTP_EXCEPTIONS``. If
|
||||
unset, it is enabled in debug mode.
|
||||
|
||||
Default: ``None``
|
||||
|
||||
.. py:data:: SECRET_KEY
|
||||
|
||||
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
|
||||
application. It should be a long random ``bytes`` or ``str``. For
|
||||
example, copy the output of this to your config::
|
||||
|
||||
$ python -c 'import secrets; print(secrets.token_hex())'
|
||||
'192b9bdd22ab9ed4d12e236c78afcb9a393ec15f71bbf5dc987d54727823bcbf'
|
||||
|
||||
**Do not reveal the secret key when posting questions or committing code.**
|
||||
|
||||
Default: ``None``
|
||||
|
||||
.. py:data:: SECRET_KEY_FALLBACKS
|
||||
|
||||
A list of old secret keys that can still be used for unsigning. This allows
|
||||
a project to implement key rotation without invalidating active sessions or
|
||||
other recently-signed secrets.
|
||||
|
||||
Keys should be removed after an appropriate period of time, as checking each
|
||||
additional key adds some overhead.
|
||||
|
||||
Order should not matter, but the default implementation will test the last
|
||||
key in the list first, so it might make sense to order oldest to newest.
|
||||
|
||||
Flask's built-in secure cookie session supports this. Extensions that use
|
||||
:data:`SECRET_KEY` may not support this yet.
|
||||
|
||||
Default: ``None``
|
||||
|
||||
.. versionadded:: 3.1
|
||||
|
||||
.. py:data:: SESSION_COOKIE_NAME
|
||||
|
||||
The name of the session cookie. Can be changed in case you already have a
|
||||
cookie with the same name.
|
||||
|
||||
Default: ``'session'``
|
||||
|
||||
.. py:data:: SESSION_COOKIE_DOMAIN
|
||||
|
||||
The value of the ``Domain`` parameter on the session cookie. If not set, browsers
|
||||
will only send the cookie to the exact domain it was set from. Otherwise, they
|
||||
will send it to any subdomain of the given value as well.
|
||||
|
||||
Not setting this value is more restricted and secure than setting it.
|
||||
|
||||
Default: ``None``
|
||||
|
||||
.. 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
|
||||
|
||||
The path that the session cookie will be valid for. If not set, the cookie
|
||||
will be valid underneath ``APPLICATION_ROOT`` or ``/`` if that is not set.
|
||||
|
||||
Default: ``None``
|
||||
|
||||
.. py:data:: SESSION_COOKIE_HTTPONLY
|
||||
|
||||
Browsers will not allow JavaScript access to cookies marked as "HTTP only"
|
||||
for security.
|
||||
|
||||
Default: ``True``
|
||||
|
||||
.. py:data:: SESSION_COOKIE_SECURE
|
||||
|
||||
Browsers will only send cookies with requests over HTTPS if the cookie is
|
||||
marked "secure". The application must be served over HTTPS for this to make
|
||||
sense.
|
||||
|
||||
Default: ``False``
|
||||
|
||||
.. py:data:: SESSION_COOKIE_PARTITIONED
|
||||
|
||||
Browsers will send cookies based on the top-level document's domain, rather
|
||||
than only the domain of the document setting the cookie. This prevents third
|
||||
party cookies set in iframes from "leaking" between separate sites.
|
||||
|
||||
Browsers are beginning to disallow non-partitioned third party cookies, so
|
||||
you need to mark your cookies partitioned if you expect them to work in such
|
||||
embedded situations.
|
||||
|
||||
Enabling this implicitly enables :data:`SESSION_COOKIE_SECURE` as well, as
|
||||
it is only valid when served over HTTPS.
|
||||
|
||||
Default: ``False``
|
||||
|
||||
.. versionadded:: 3.1
|
||||
|
||||
.. py:data:: SESSION_COOKIE_SAMESITE
|
||||
|
||||
Restrict how cookies are sent with requests from external sites. Can
|
||||
be set to ``'Lax'`` (recommended) or ``'Strict'``.
|
||||
See :ref:`security-cookie`.
|
||||
|
||||
Default: ``None``
|
||||
|
||||
.. versionadded:: 1.0
|
||||
|
||||
.. py:data:: PERMANENT_SESSION_LIFETIME
|
||||
|
||||
If ``session.permanent`` is true, the cookie's expiration will be set this
|
||||
number of seconds in the future. Can either be a
|
||||
:class:`datetime.timedelta` or an ``int``.
|
||||
|
||||
Flask's default cookie implementation validates that the cryptographic
|
||||
signature is not older than this value.
|
||||
|
||||
Default: ``timedelta(days=31)`` (``2678400`` seconds)
|
||||
|
||||
.. py:data:: SESSION_REFRESH_EACH_REQUEST
|
||||
|
||||
Control whether the cookie is sent with every response when
|
||||
``session.permanent`` is true. Sending the cookie every time (the default)
|
||||
can more reliably keep the session from expiring, but uses more bandwidth.
|
||||
Non-permanent sessions are not affected.
|
||||
|
||||
Default: ``True``
|
||||
|
||||
.. py:data:: USE_X_SENDFILE
|
||||
|
||||
When serving files, set the ``X-Sendfile`` header instead of serving the
|
||||
data with Flask. Some web servers, such as Apache, recognize this and serve
|
||||
the data more efficiently. This only makes sense when using such a server.
|
||||
|
||||
Default: ``False``
|
||||
|
||||
.. py:data:: SEND_FILE_MAX_AGE_DEFAULT
|
||||
|
||||
When serving files, set the cache control max age to this number of
|
||||
seconds. Can be a :class:`datetime.timedelta` or an ``int``.
|
||||
Override this value on a per-file basis using
|
||||
:meth:`~flask.Flask.get_send_file_max_age` on the application or
|
||||
blueprint.
|
||||
|
||||
If ``None``, ``send_file`` tells the browser to use conditional
|
||||
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
|
||||
|
||||
Inform the application what host and port it is bound to.
|
||||
|
||||
Must be set if ``subdomain_matching`` is enabled, to be able to extract the
|
||||
subdomain from the request.
|
||||
|
||||
Must be set for ``url_for`` to generate external URLs outside of a
|
||||
request context.
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
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
|
||||
set.
|
||||
|
||||
Default: ``'/'``
|
||||
|
||||
.. py:data:: PREFERRED_URL_SCHEME
|
||||
|
||||
Use this scheme for generating external URLs when not in a request context.
|
||||
|
||||
Default: ``'http'``
|
||||
|
||||
.. py:data:: MAX_CONTENT_LENGTH
|
||||
|
||||
The maximum number of bytes that will be read during this request. If
|
||||
this limit is exceeded, a 413 :exc:`~werkzeug.exceptions.RequestEntityTooLarge`
|
||||
error is raised. If it is set to ``None``, no limit is enforced at the
|
||||
Flask application level. However, if it is ``None`` and the request has no
|
||||
``Content-Length`` header and the WSGI server does not indicate that it
|
||||
terminates the stream, then no data is read to avoid an infinite stream.
|
||||
|
||||
Each request defaults to this config. It can be set on a specific
|
||||
:attr:`.Request.max_content_length` to apply the limit to that specific
|
||||
view. This should be set appropriately based on an application's or view's
|
||||
specific needs.
|
||||
|
||||
Default: ``None``
|
||||
|
||||
.. versionadded:: 0.6
|
||||
|
||||
.. py:data:: MAX_FORM_MEMORY_SIZE
|
||||
|
||||
The maximum size in bytes any non-file form field may be in a
|
||||
``multipart/form-data`` body. If this limit is exceeded, a 413
|
||||
:exc:`~werkzeug.exceptions.RequestEntityTooLarge` error is raised. If it is
|
||||
set to ``None``, no limit is enforced at the Flask application level.
|
||||
|
||||
Each request defaults to this config. It can be set on a specific
|
||||
:attr:`.Request.max_form_memory_parts` to apply the limit to that specific
|
||||
view. This should be set appropriately based on an application's or view's
|
||||
specific needs.
|
||||
|
||||
Default: ``500_000``
|
||||
|
||||
.. versionadded:: 3.1
|
||||
|
||||
.. py:data:: MAX_FORM_PARTS
|
||||
|
||||
The maximum number of fields that may be present in a
|
||||
``multipart/form-data`` body. If this limit is exceeded, a 413
|
||||
:exc:`~werkzeug.exceptions.RequestEntityTooLarge` error is raised. If it
|
||||
is set to ``None``, no limit is enforced at the Flask application level.
|
||||
|
||||
Each request defaults to this config. It can be set on a specific
|
||||
:attr:`.Request.max_form_parts` to apply the limit to that specific view.
|
||||
This should be set appropriately based on an application's or view's
|
||||
specific needs.
|
||||
|
||||
Default: ``1_000``
|
||||
|
||||
.. versionadded:: 3.1
|
||||
|
||||
.. py:data:: TEMPLATES_AUTO_RELOAD
|
||||
|
||||
Reload templates when they are changed. If not set, it will be enabled in
|
||||
debug mode.
|
||||
|
||||
Default: ``None``
|
||||
|
||||
.. py:data:: EXPLAIN_TEMPLATE_LOADING
|
||||
|
||||
Log debugging information tracing how a template file was loaded. This can
|
||||
be useful to figure out why a template was not loaded or the wrong file
|
||||
appears to be loaded.
|
||||
|
||||
Default: ``False``
|
||||
|
||||
.. py:data:: MAX_COOKIE_SIZE
|
||||
|
||||
Warn if cookie headers are larger than this many bytes. Defaults to
|
||||
``4093``. Larger cookies may be silently ignored by browsers. Set to
|
||||
``0`` to disable the warning.
|
||||
|
||||
.. py:data:: PROVIDE_AUTOMATIC_OPTIONS
|
||||
|
||||
Set to ``False`` to disable the automatic addition of OPTIONS
|
||||
responses. This can be overridden per route by altering the
|
||||
``provide_automatic_options`` attribute.
|
||||
.. tabularcolumns:: |p{6.5cm}|p{8.5cm}|
|
||||
|
||||
================================= =========================================
|
||||
``DEBUG`` enable/disable debug mode
|
||||
``TESTING`` enable/disable testing mode
|
||||
``PROPAGATE_EXCEPTIONS`` explicitly enable or disable the
|
||||
propagation of exceptions. If not set or
|
||||
explicitly set to `None` this is
|
||||
implicitly true if either `TESTING` or
|
||||
`DEBUG` is true.
|
||||
``PRESERVE_CONTEXT_ON_EXCEPTION`` By default if the application is in
|
||||
debug mode the request context is not
|
||||
popped on exceptions to enable debuggers
|
||||
to introspect the data. This can be
|
||||
disabled by this key. You can also use
|
||||
this setting to force-enable it for non
|
||||
debug execution which might be useful to
|
||||
debug production applications (but also
|
||||
very risky).
|
||||
``SECRET_KEY`` the secret key
|
||||
``SESSION_COOKIE_NAME`` the name of the session cookie
|
||||
``SESSION_COOKIE_DOMAIN`` the domain for the session cookie. If
|
||||
this is not set, the cookie will be
|
||||
valid for all subdomains of
|
||||
``SERVER_NAME``.
|
||||
``SESSION_COOKIE_PATH`` the path for the session cookie. If
|
||||
this is not set the cookie will be valid
|
||||
for all of ``APPLICATION_ROOT`` or if
|
||||
that is not set for ``'/'``.
|
||||
``SESSION_COOKIE_HTTPONLY`` controls if the cookie should be set
|
||||
with the httponly flag. Defaults to
|
||||
`True`.
|
||||
``SESSION_COOKIE_SECURE`` controls if the cookie should be set
|
||||
with the secure flag. Defaults to
|
||||
`False`.
|
||||
``PERMANENT_SESSION_LIFETIME`` the lifetime of a permanent session as
|
||||
:class:`datetime.timedelta` object.
|
||||
Starting with Flask 0.8 this can also be
|
||||
an integer representing seconds.
|
||||
``USE_X_SENDFILE`` enable/disable x-sendfile
|
||||
``LOGGER_NAME`` the name of the logger
|
||||
``SERVER_NAME`` the name and port number of the server.
|
||||
Required for subdomain support (e.g.:
|
||||
``'myapp.dev:5000'``) Note that
|
||||
localhost does not support subdomains so
|
||||
setting this to “localhost” does not
|
||||
help.
|
||||
``APPLICATION_ROOT`` If the application does not occupy
|
||||
a whole domain or subdomain this can
|
||||
be set to the path where the application
|
||||
is configured to live. This is for
|
||||
session cookie as path value. If
|
||||
domains are used, this should be
|
||||
``None``.
|
||||
``MAX_CONTENT_LENGTH`` If set to a value in bytes, Flask will
|
||||
reject incoming requests with a
|
||||
content length greater than this by
|
||||
returning a 413 status code.
|
||||
``TRAP_HTTP_EXCEPTIONS`` If this is set to ``True`` Flask will
|
||||
not execute the error handlers of HTTP
|
||||
exceptions but instead treat the
|
||||
exception like any other and bubble it
|
||||
through the exception stack. This is
|
||||
helpful for hairy debugging situations
|
||||
where you have to find out where an HTTP
|
||||
exception is coming from.
|
||||
``TRAP_BAD_REQUEST_ERRORS`` Werkzeug's internal data structures that
|
||||
deal with request specific data will
|
||||
raise special key errors that are also
|
||||
bad request exceptions. Likewise many
|
||||
operations can implicitly fail with a
|
||||
BadRequest exception for consistency.
|
||||
Since it's nice for debugging to know
|
||||
why exactly it failed this flag can be
|
||||
used to debug those situations. If this
|
||||
config is set to ``True`` you will get
|
||||
a regular traceback instead.
|
||||
================================= =========================================
|
||||
|
||||
.. admonition:: More on ``SERVER_NAME``
|
||||
|
||||
The ``SERVER_NAME`` key is used for the subdomain support. Because
|
||||
Flask cannot guess the subdomain part without the knowledge of the
|
||||
actual server name, this is required if you want to work with
|
||||
subdomains. This is also used for the session cookie.
|
||||
|
||||
Please keep in mind that not only Flask has the problem of not knowing
|
||||
what subdomains are, your web browser does as well. Most modern web
|
||||
browsers will not allow cross-subdomain cookies to be set on a
|
||||
server name without dots in it. So if your server name is
|
||||
``'localhost'`` you will not be able to set a cookie for
|
||||
``'localhost'`` and every subdomain of it. Please chose a different
|
||||
server name in that case, like ``'myapplication.local'`` and add
|
||||
this name + the subdomains you want to use into your host config
|
||||
or setup a local `bind`_.
|
||||
|
||||
.. _bind: https://www.isc.org/software/bind
|
||||
|
||||
.. versionadded:: 0.4
|
||||
``LOGGER_NAME``
|
||||
|
|
@ -412,52 +165,16 @@ The following configuration values are used internally by Flask:
|
|||
``SESSION_COOKIE_PATH``, ``SESSION_COOKIE_HTTPONLY``,
|
||||
``SESSION_COOKIE_SECURE``
|
||||
|
||||
.. versionadded:: 0.9
|
||||
``PREFERRED_URL_SCHEME``
|
||||
Configuring from Files
|
||||
----------------------
|
||||
|
||||
.. versionadded:: 0.10
|
||||
``JSON_AS_ASCII``, ``JSON_SORT_KEYS``, ``JSONIFY_PRETTYPRINT_REGULAR``
|
||||
Configuration becomes more useful if you can store it in a separate file,
|
||||
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:: 0.11
|
||||
``SESSION_REFRESH_EACH_REQUEST``, ``TEMPLATES_AUTO_RELOAD``,
|
||||
``LOGGER_HANDLER_POLICY``, ``EXPLAIN_TEMPLATE_LOADING``
|
||||
|
||||
.. versionchanged:: 1.0
|
||||
``LOGGER_NAME`` and ``LOGGER_HANDLER_POLICY`` were removed. See
|
||||
:doc:`/logging` for information about configuration.
|
||||
|
||||
Added :data:`ENV` to reflect the :envvar:`FLASK_ENV` environment
|
||||
variable.
|
||||
|
||||
Added :data:`SESSION_COOKIE_SAMESITE` to control the session
|
||||
cookie's ``SameSite`` option.
|
||||
|
||||
Added :data:`MAX_COOKIE_SIZE` to control a warning from Werkzeug.
|
||||
|
||||
.. versionchanged:: 2.2
|
||||
Removed ``PRESERVE_CONTEXT_ON_EXCEPTION``.
|
||||
|
||||
.. versionchanged:: 2.3
|
||||
``JSON_AS_ASCII``, ``JSON_SORT_KEYS``, ``JSONIFY_MIMETYPE``, and
|
||||
``JSONIFY_PRETTYPRINT_REGULAR`` were removed. The default ``app.json`` provider has
|
||||
equivalent attributes instead.
|
||||
|
||||
.. versionchanged:: 2.3
|
||||
``ENV`` was removed.
|
||||
|
||||
.. versionadded:: 3.1
|
||||
Added :data:`PROVIDE_AUTOMATIC_OPTIONS` to control the default
|
||||
addition of autogenerated OPTIONS responses.
|
||||
|
||||
|
||||
Configuring from Python Files
|
||||
-----------------------------
|
||||
|
||||
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::
|
||||
So a common pattern is this::
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config.from_object('yourapplication.default_settings')
|
||||
|
|
@ -466,42 +183,18 @@ A common pattern is this::
|
|||
This first loads the configuration from the
|
||||
`yourapplication.default_settings` module and then overrides the values
|
||||
with the contents of the file the :envvar:`YOURAPPLICATION_SETTINGS`
|
||||
environment variable points to. This environment variable can be set
|
||||
in the shell before starting the server:
|
||||
environment variable points to. This environment variable can be set on
|
||||
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
|
||||
|
||||
$ 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/
|
||||
>set YOURAPPLICATION_SETTINGS=\path\to\settings.cfg
|
||||
|
||||
The configuration files themselves are actual Python files. Only values
|
||||
in uppercase are actually stored in the config object later on. So make
|
||||
|
|
@ -510,7 +203,8 @@ sure to use uppercase letters for your config keys.
|
|||
Here is an example of a configuration file::
|
||||
|
||||
# Example configuration
|
||||
SECRET_KEY = '192b9bdd22ab9ed4d12e236c78afcb9a393ec15f71bbf5dc987d54727823bcbf'
|
||||
DEBUG = False
|
||||
SECRET_KEY = '?\xbf,\xb4\x8d\xa3"<\x9c\xb0@\x0f5\xab,w\xee\x8d$0\x13\x8b83'
|
||||
|
||||
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
|
||||
|
|
@ -519,118 +213,6 @@ complete reference, read the :class:`~flask.Config` object's
|
|||
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
|
||||
--------------------------------------
|
||||
|
||||
In addition to pointing to configuration files using environment
|
||||
variables, you may find it useful (or necessary) to control your
|
||||
configuration values directly from the environment. Flask can be
|
||||
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
|
||||
server:
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. group-tab:: Bash
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ export FLASK_SECRET_KEY="5f352379324c22463451387a0aec5d2f"
|
||||
$ export FLASK_MAIL_ENABLED=false
|
||||
$ flask run
|
||||
* Running on http://127.0.0.1:5000/
|
||||
|
||||
.. group-tab:: Fish
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ set -x FLASK_SECRET_KEY "5f352379324c22463451387a0aec5d2f"
|
||||
$ set -x FLASK_MAIL_ENABLED false
|
||||
$ flask run
|
||||
* Running on http://127.0.0.1:5000/
|
||||
|
||||
.. group-tab:: CMD
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
> set FLASK_SECRET_KEY="5f352379324c22463451387a0aec5d2f"
|
||||
> set FLASK_MAIL_ENABLED=false
|
||||
> flask run
|
||||
* 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/
|
||||
|
||||
|
||||
Configuration Best Practices
|
||||
----------------------------
|
||||
|
||||
|
|
@ -639,20 +221,15 @@ a little harder. There is no single 100% solution for this problem in
|
|||
general, but there are a couple of things you can keep in mind to improve
|
||||
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
|
||||
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.
|
||||
|
||||
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
|
||||
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:
|
||||
|
||||
Development / Production
|
||||
------------------------
|
||||
|
|
@ -668,32 +245,33 @@ in the example above::
|
|||
app.config.from_object('yourapplication.default_settings')
|
||||
app.config.from_envvar('YOURAPPLICATION_SETTINGS')
|
||||
|
||||
Then you just have to add a separate :file:`config.py` file and export
|
||||
Then you just have to add a separate `config.py` file and export
|
||||
``YOURAPPLICATION_SETTINGS=/path/to/config.py`` and you are done. However
|
||||
there are alternative ways as well. For example you could use imports or
|
||||
subclassing.
|
||||
|
||||
What is very popular in the Django world is to make the import explicit in
|
||||
the config file by adding ``from yourapplication.default_settings
|
||||
the config file by adding an ``from yourapplication.default_settings
|
||||
import *`` to the top of the file and then overriding the changes by hand.
|
||||
You could also inspect an environment variable like
|
||||
``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
|
||||
configuration::
|
||||
|
||||
class Config(object):
|
||||
DEBUG = False
|
||||
TESTING = False
|
||||
DATABASE_URI = 'sqlite://:memory:'
|
||||
|
||||
class ProductionConfig(Config):
|
||||
DATABASE_URI = 'mysql://user@localhost/foo'
|
||||
|
||||
|
||||
class DevelopmentConfig(Config):
|
||||
DATABASE_URI = "sqlite:////tmp/foo.db"
|
||||
DEBUG = True
|
||||
|
||||
class TestingConfig(Config):
|
||||
DATABASE_URI = 'sqlite:///:memory:'
|
||||
TESTING = True
|
||||
|
||||
To enable such a config you just have to call into
|
||||
|
|
@ -701,58 +279,25 @@ To enable such a config you just have to call into
|
|||
|
||||
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
|
||||
your configuration files. However here a list of good recommendations:
|
||||
|
||||
- Keep a default configuration in version control. Either populate the
|
||||
- keep a default configuration in version control. Either populate the
|
||||
config with this default configuration or import it in your own
|
||||
configuration files before overriding values.
|
||||
- Use an environment variable to switch between the configurations.
|
||||
- use an environment variable to switch between the configurations.
|
||||
This can be done from outside the Python interpreter and makes
|
||||
development and deployment much easier because you can quickly and
|
||||
easily switch between different configs without having to touch the
|
||||
code at all. If you are working often on different projects you can
|
||||
even create your own script for sourcing that activates a virtualenv
|
||||
and exports the development configuration for you.
|
||||
- Use a tool like `fabric`_ to push code and configuration separately
|
||||
to the production server(s).
|
||||
- Use a tool like `fabric`_ in production to push code and
|
||||
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://fabfile.org/
|
||||
|
||||
|
||||
.. _instance-folders:
|
||||
|
|
@ -800,7 +345,7 @@ locations are used:
|
|||
|
||||
- Installed module or package::
|
||||
|
||||
$PREFIX/lib/pythonX.Y/site-packages/myapp
|
||||
$PREFIX/lib/python2.X/site-packages/myapp
|
||||
$PREFIX/var/myapp-instance
|
||||
|
||||
``$PREFIX`` is the prefix of your Python installation. This can be
|
||||
|
|
@ -817,7 +362,7 @@ root” (the default) to “relative to instance folder” via the
|
|||
app = Flask(__name__, instance_relative_config=True)
|
||||
|
||||
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::
|
||||
|
||||
app = Flask(__name__, instance_relative_config=True)
|
||||
|
|
@ -830,7 +375,7 @@ file from the instance folder with :meth:`Flask.open_instance_resource`.
|
|||
|
||||
Example usage for both::
|
||||
|
||||
filename = os.path.join(app.instance_path, 'application.cfg')
|
||||
filename = os.path.join(app.instance_root, 'application.cfg')
|
||||
with open(filename) as f:
|
||||
config = f.read()
|
||||
|
||||
|
|
|
|||
56
docs/contents.rst.inc
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
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
|
||||
installation
|
||||
quickstart
|
||||
tutorial/index
|
||||
templating
|
||||
testing
|
||||
errorhandling
|
||||
config
|
||||
signals
|
||||
views
|
||||
reqcontext
|
||||
blueprints
|
||||
extensions
|
||||
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
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
Contributing
|
||||
============
|
||||
|
||||
See the Pallets `detailed contributing documentation <contrib_>`_ for many ways
|
||||
to contribute, including reporting issues, requesting features, asking or
|
||||
answering questions, and making PRs.
|
||||
|
||||
.. _contrib: https://palletsprojects.com/contributing/
|
||||
|
|
@ -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
|
||||
)
|
||||
|
|
@ -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``.
|
||||
|
|
@ -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
|
||||
46
docs/deploying/cgi.rst
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
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.
|
||||
|
||||
Creating a `.cgi` file
|
||||
----------------------
|
||||
|
||||
First you need to create the CGI application file. Let's call it
|
||||
`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 `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 a like like this into the config:
|
||||
|
||||
.. sourcecode:: apache
|
||||
|
||||
ScriptAlias /app /path/to/the/application.cgi
|
||||
|
||||
For more information consult the documentation of your webserver.
|
||||
|
||||
.. _App Engine: http://code.google.com/appengine/
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
:orphan:
|
||||
|
||||
eventlet
|
||||
========
|
||||
|
||||
`Eventlet is no longer maintained.`__ Use :doc:`/deploying/gevent` instead.
|
||||
|
||||
__ https://eventlet.readthedocs.io
|
||||
164
docs/deploying/fastcgi.rst
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
.. _deploying-fastcgi:
|
||||
|
||||
FastCGI
|
||||
=======
|
||||
|
||||
FastCGI is a deployment option on servers like `nginx`_, `lighttpd`_,
|
||||
and `cherokee`_; see :ref:`deploying-uwsgi` and
|
||||
:ref:`deploying-other-servers` 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 `yourapplication.fcgi` file somewhere you will find it again.
|
||||
It makes sense to have that in `/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 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 <http://redmine.lighttpd.net/wiki/lighttpd/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 webservers. 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 webserver 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: http://nginx.org/
|
||||
.. _lighttpd: http://www.lighttpd.net/
|
||||
.. _cherokee: http://www.cherokee-project.com/
|
||||
.. _flup: http://trac.saddi.com/flup
|
||||
|
|
@ -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.
|
||||
|
|
@ -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
|
||||
|
|
@ -1,78 +1,23 @@
|
|||
Deploying to Production
|
||||
=======================
|
||||
.. _deployment:
|
||||
|
||||
After developing your application, you'll want to make it available
|
||||
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.
|
||||
Deployment Options
|
||||
==================
|
||||
|
||||
"Production" means "not development", which applies whether you're
|
||||
serving your application publicly to millions of users or privately /
|
||||
locally to a single user. **Do not use the development server when
|
||||
deploying to production. It is intended for use only during local
|
||||
development. It is not designed to be particularly secure, stable, or
|
||||
efficient.**
|
||||
Depending on what you have available there are multiple ways to run
|
||||
Flask applications. You can use the builtin server during development,
|
||||
but you should use a full deployment option for production applications.
|
||||
(Do not use the builtin development server in production.) Several
|
||||
options are available and documented here.
|
||||
|
||||
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.
|
||||
If you have a different WSGI server 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.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:maxdepth: 2
|
||||
|
||||
gunicorn
|
||||
waitress
|
||||
mod_wsgi
|
||||
uwsgi
|
||||
gevent
|
||||
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.
|
||||
mod_wsgi
|
||||
cgi
|
||||
fastcgi
|
||||
uwsgi
|
||||
others
|
||||
|
|
|
|||
|
|
@ -1,94 +1,167 @@
|
|||
mod_wsgi
|
||||
========
|
||||
.. _mod_wsgi-deployment:
|
||||
|
||||
`mod_wsgi`_ is a WSGI server integrated with the `Apache httpd`_ server.
|
||||
The modern `mod_wsgi-express`_ command makes it easy to configure and
|
||||
start the server without needing to write Apache httpd configuration.
|
||||
mod_wsgi (Apache)
|
||||
=================
|
||||
|
||||
* Tightly integrated with Apache httpd.
|
||||
* Supports Windows directly.
|
||||
* Requires a compiler and the Apache development headers to install.
|
||||
* Does not require a reverse proxy setup.
|
||||
If you are using the `Apache`_ webserver, consider using `mod_wsgi`_.
|
||||
|
||||
This page outlines the basics of running mod_wsgi-express, not the more
|
||||
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.
|
||||
.. admonition:: Watch Out
|
||||
|
||||
.. _mod_wsgi-express: https://pypi.org/project/mod-wsgi/
|
||||
.. _mod_wsgi: https://modwsgi.readthedocs.io/
|
||||
.. _Apache httpd: https://httpd.apache.org/
|
||||
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 mod_wsgi.
|
||||
|
||||
.. _Apache: http://httpd.apache.org/
|
||||
|
||||
Installing
|
||||
----------
|
||||
Installing `mod_wsgi`
|
||||
---------------------
|
||||
|
||||
Installing mod_wsgi requires a compiler and the Apache server and
|
||||
development headers installed. You will get an error if they are not.
|
||||
How to install them depends on the OS and package manager that you use.
|
||||
If you don't have `mod_wsgi` installed yet you have to either install it
|
||||
using a package manager or compile it yourself. The mod_wsgi
|
||||
`installation instructions`_ cover source installations on UNIX systems.
|
||||
|
||||
Create a virtualenv, install your application, then install
|
||||
``mod_wsgi``.
|
||||
If you are using Ubuntu/Debian you can apt-get it and activate it as
|
||||
follows:
|
||||
|
||||
.. code-block:: text
|
||||
.. sourcecode:: text
|
||||
|
||||
$ cd hello-app
|
||||
$ python -m venv .venv
|
||||
$ . .venv/bin/activate
|
||||
$ pip install . # install your application
|
||||
$ pip install mod_wsgi
|
||||
# apt-get install libapache2-mod-wsgi
|
||||
|
||||
On FreeBSD install `mod_wsgi` by compiling the `www/mod_wsgi` port or by
|
||||
using pkg_add:
|
||||
|
||||
Running
|
||||
-------
|
||||
.. sourcecode:: text
|
||||
|
||||
The only argument to ``mod_wsgi-express`` specifies a script containing
|
||||
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.
|
||||
# pkg_add -r mod_wsgi
|
||||
|
||||
.. code-block:: python
|
||||
:caption: ``wsgi.py``
|
||||
If you are using pkgsrc you can install `mod_wsgi` by compiling the
|
||||
`www/ap2-wsgi` package.
|
||||
|
||||
from hello import app
|
||||
If you encounter segfaulting child processes after the first apache
|
||||
reload you can safely ignore them. Just restart the server.
|
||||
|
||||
application = app
|
||||
Creating a `.wsgi` file
|
||||
-----------------------
|
||||
|
||||
.. code-block:: python
|
||||
:caption: ``wsgi.py``
|
||||
To run your application you need a `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.
|
||||
|
||||
from hello import create_app
|
||||
For most applications the following file should be sufficient::
|
||||
|
||||
application = create_app()
|
||||
from yourapplication import app as application
|
||||
|
||||
Now run the ``mod_wsgi-express start-server`` command.
|
||||
If you don't have a factory function for application creation but a singleton
|
||||
instance you can directly import that one as `application`.
|
||||
|
||||
.. code-block:: text
|
||||
Store that file somewhere that you will find it again (e.g.:
|
||||
`/var/www/yourapplication`) and make sure that `yourapplication` and all
|
||||
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.
|
||||
|
||||
$ mod_wsgi-express start-server wsgi.py --processes 4
|
||||
|
||||
The ``--processes`` option specifies the number of worker processes to
|
||||
run; a starting value could be ``CPU * 2``.
|
||||
|
||||
Logs for each request aren't show in the terminal. If an error occurs,
|
||||
its information is written to the error log file shown when starting the
|
||||
server.
|
||||
|
||||
|
||||
Binding Externally
|
||||
Configuring Apache
|
||||
------------------
|
||||
|
||||
Unlike the other WSGI servers in these docs, mod_wsgi can be run as
|
||||
root to bind to privileged ports like 80 and 443. However, it must be
|
||||
configured to drop permissions to a different user and group for the
|
||||
worker processes.
|
||||
The last thing you have to do is to create an Apache configuration file
|
||||
for your application. In this example we are telling `mod_wsgi` to
|
||||
execute the application under a different user for security reasons:
|
||||
|
||||
For example, if you created a ``hello`` user and group, you should
|
||||
install your virtualenv and application as that user, then tell
|
||||
mod_wsgi to drop to that user after starting.
|
||||
.. sourcecode:: apache
|
||||
|
||||
.. code-block:: text
|
||||
<VirtualHost *>
|
||||
ServerName example.com
|
||||
|
||||
$ sudo /home/hello/.venv/bin/mod_wsgi-express start-server \
|
||||
/home/hello/wsgi.py \
|
||||
--user hello --group hello --port 80 --processes 4
|
||||
WSGIDaemonProcess yourapplication user=user1 group=group1 threads=5
|
||||
WSGIScriptAlias / /var/www/yourapplication/yourapplication.wsgi
|
||||
|
||||
<Directory /var/www/yourapplication>
|
||||
WSGIProcessGroup yourapplication
|
||||
WSGIApplicationGroup %{GLOBAL}
|
||||
Order deny,allow
|
||||
Allow from all
|
||||
</Directory>
|
||||
</VirtualHost>
|
||||
|
||||
For more information consult the `mod_wsgi wiki`_.
|
||||
|
||||
.. _mod_wsgi: http://code.google.com/p/modwsgi/
|
||||
.. _installation instructions: http://code.google.com/p/modwsgi/wiki/QuickInstallationGuide
|
||||
.. _virtual python: http://pypi.python.org/pypi/virtualenv
|
||||
.. _mod_wsgi wiki: http://code.google.com/p/modwsgi/wiki/
|
||||
|
||||
Troubleshooting
|
||||
---------------
|
||||
|
||||
If your application does not run, follow this guide to troubleshoot:
|
||||
|
||||
**Problem:** application does not run, errorlog shows SystemExit ignored
|
||||
You have a ``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 `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))
|
||||
|
||||
This sets up the load paths according to the settings of the virtual
|
||||
environment. Keep in mind that the path has to be absolute.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
102
docs/deploying/others.rst
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
.. _deploying-other-servers:
|
||||
|
||||
Other Servers
|
||||
=============
|
||||
|
||||
There are popular servers written in Python that allow the execution of WSGI
|
||||
applications as well. These servers stand alone when they run; you can proxy
|
||||
to them from your web server.
|
||||
|
||||
Tornado
|
||||
--------
|
||||
|
||||
`Tornado`_ is an open source version of the scalable, non-blocking web
|
||||
server and tools that power `FriendFeed`_. Because it is non-blocking and
|
||||
uses epoll, it can handle thousands of simultaneous standing connections,
|
||||
which means it is ideal for real-time web services. Integrating this
|
||||
service with Flask is a trivial task::
|
||||
|
||||
from tornado.wsgi import WSGIContainer
|
||||
from tornado.httpserver import HTTPServer
|
||||
from tornado.ioloop import IOLoop
|
||||
from yourapplication import app
|
||||
|
||||
http_server = HTTPServer(WSGIContainer(app))
|
||||
http_server.listen(5000)
|
||||
IOLoop.instance().start()
|
||||
|
||||
|
||||
.. _Tornado: http://www.tornadoweb.org/
|
||||
.. _FriendFeed: http://friendfeed.com/
|
||||
|
||||
Gevent
|
||||
-------
|
||||
|
||||
`Gevent`_ is a coroutine-based Python networking library that uses
|
||||
`greenlet`_ to provide a high-level synchronous API on top of `libevent`_
|
||||
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: http://codespeak.net/py/0.9.2/greenlet.html
|
||||
.. _libevent: http://monkey.org/~provos/libevent/
|
||||
|
||||
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: http://codespeak.net/py/0.9.2/greenlet.html
|
||||
|
||||
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`. Werkzeug ships a fixer that
|
||||
will solve some common setups, but you might want to write your own WSGI
|
||||
middleware for specific setups.
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
|
@ -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.
|
||||
|
|
@ -1,143 +1,68 @@
|
|||
.. _deploying-uwsgi:
|
||||
|
||||
uWSGI
|
||||
=====
|
||||
|
||||
`uWSGI`_ is a fast, compiled server suite with extensive configuration
|
||||
and capabilities beyond a basic server.
|
||||
uWSGI is a deployment option on servers like `nginx`_, `lighttpd`_, and
|
||||
`cherokee`_; see :ref:`deploying-fastcgi` and
|
||||
:ref:`deploying-other-servers` 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.
|
||||
* It is complex to configure beyond the basic application, and has so
|
||||
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.
|
||||
The most popular uWSGI server is `uwsgi`_, which we will use for this
|
||||
guide. Make sure to have it installed to follow along.
|
||||
|
||||
This page outlines the basics of running uWSGI. Be sure to read its
|
||||
documentation to understand what features are available.
|
||||
.. admonition:: Watch Out
|
||||
|
||||
.. _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
|
||||
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.
|
||||
Given a flask application in myapp.py, use the following command:
|
||||
|
||||
Create a virtualenv, install your application, then install ``pyuwsgi``.
|
||||
.. sourcecode:: text
|
||||
|
||||
.. code-block:: text
|
||||
$ uwsgi -s /tmp/uwsgi.sock --module myapp --callable app
|
||||
|
||||
$ cd hello-app
|
||||
$ python -m venv .venv
|
||||
$ . .venv/bin/activate
|
||||
$ pip install . # install your application
|
||||
$ pip install pyuwsgi
|
||||
Or, if you prefer:
|
||||
|
||||
If you have a compiler available, you can install the ``uwsgi`` package
|
||||
instead. Or install the ``pyuwsgi`` package from sdist instead of wheel.
|
||||
Either method will include SSL support.
|
||||
.. sourcecode:: text
|
||||
|
||||
.. code-block:: text
|
||||
$ uwsgi -s /tmp/uwsgi.sock -w myapp:app
|
||||
|
||||
$ 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
|
||||
Configuring nginx
|
||||
-----------------
|
||||
|
||||
The default sync worker is appropriate for most use cases. If you need numerous,
|
||||
long running, concurrent connections, uWSGI provides an asynchronous worker
|
||||
using `gevent`_. This is not the same as Python's ``async/await``, or the ASGI
|
||||
server spec. See :doc:`/gevent` for more information about enabling it in your
|
||||
application.
|
||||
A basic flask uWSGI configuration for nginx looks like this::
|
||||
|
||||
.. _gevent: https://www.gevent.org/
|
||||
location = /yourapplication { rewrite ^ /yourapplication/; }
|
||||
location /yourapplication { try_files $uri @yourapplication; }
|
||||
location @yourapplication {
|
||||
include uwsgi_params;
|
||||
uwsgi_param SCRIPT_NAME /yourapplication;
|
||||
uwsgi_modifier1 30;
|
||||
uwsgi_pass unix:/tmp/uwsgi.sock;
|
||||
}
|
||||
|
||||
When using gevent, greenlet>=1.0 is required. When using PyPy, PyPy>=7.3.7 is
|
||||
required.
|
||||
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 tell
|
||||
it the WSGI `SCRIPT_NAME` or set the uwsgi modifier to make use of it::
|
||||
|
||||
.. code-block:: text
|
||||
location / { try_files $uri @yourapplication; }
|
||||
location @yourapplication {
|
||||
include uwsgi_params;
|
||||
uwsgi_pass unix:/tmp/uwsgi.sock;
|
||||
}
|
||||
|
||||
$ uwsgi --http 127.0.0.1:8000 --master --gevent 100 -w wsgi:app
|
||||
|
||||
*** Starting uWSGI 2.0.20 (64bit) on [x] ***
|
||||
*** Operational MODE: async ***
|
||||
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] ***
|
||||
.. _nginx: http://nginx.org/
|
||||
.. _lighttpd: http://www.lighttpd.net/
|
||||
.. _cherokee: http://www.cherokee-project.com/
|
||||
.. _uwsgi: http://projects.unbit.it/uwsgi/
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
116
docs/design.rst
|
|
@ -1,3 +1,5 @@
|
|||
.. _design:
|
||||
|
||||
Design Decisions in Flask
|
||||
=========================
|
||||
|
||||
|
|
@ -39,14 +41,14 @@ the time. There are ways to fake multiple applications with a single
|
|||
application object, like maintaining a stack of applications, but this
|
||||
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
|
||||
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
|
||||
specific behavior. When the application object is deleted everything it
|
||||
allocated will be freed again.
|
||||
|
||||
Another thing that becomes possible when you have an explicit object lying
|
||||
around in your code is that you can subclass the base class
|
||||
(:class:`~flask.Flask`) to alter specific behavior. This would not be
|
||||
(:class:`~flask.Flask`) to alter specific behaviour. This would not be
|
||||
possible without hacks if the object were created ahead of time for you
|
||||
based on a class that is not exposed to you.
|
||||
|
||||
|
|
@ -74,13 +76,13 @@ there are better ways to do that so that you do not lose the reference
|
|||
to the application object :meth:`~flask.Flask.wsgi_app`).
|
||||
|
||||
Furthermore this design makes it possible to use a factory function to
|
||||
create the application which is very helpful for unit testing and similar
|
||||
things (:doc:`/patterns/appfactories`).
|
||||
create the application which is very helpful for unittesting and similar
|
||||
things (:ref:`app-factories`).
|
||||
|
||||
The Routing System
|
||||
------------------
|
||||
|
||||
Flask uses the Werkzeug routing system which was designed to
|
||||
Flask uses the Werkzeug routing system which has was designed to
|
||||
automatically order routes by complexity. This means that you can declare
|
||||
routes in arbitrary order and they will still work as expected. This is a
|
||||
requirement if you want to properly implement decorator based routing
|
||||
|
|
@ -88,18 +90,18 @@ since decorators could be fired in undefined order when the application is
|
|||
split into multiple modules.
|
||||
|
||||
Another design decision with the Werkzeug routing system is that routes
|
||||
in Werkzeug try to ensure that URLs are unique. Werkzeug will go quite far
|
||||
with that in that it will automatically redirect to a canonical URL if a route
|
||||
is ambiguous.
|
||||
in Werkzeug try to ensure that there is that URLs are unique. Werkzeug
|
||||
will go quite far with that in that it will automatically redirect to a
|
||||
canonical URL if a route is ambiguous.
|
||||
|
||||
|
||||
One Template Engine
|
||||
-------------------
|
||||
|
||||
Flask decides on one template engine: Jinja. Why doesn't Flask have a
|
||||
Flask decides on one template engine: Jinja2. Why doesn't Flask have a
|
||||
pluggable template engine interface? You can obviously use a different
|
||||
template engine, but Flask will still configure Jinja for you. While
|
||||
that limitation that Jinja is *always* configured will probably go away,
|
||||
template engine, but Flask will still configure Jinja2 for you. While
|
||||
that limitation that Jinja2 is *always* configured will probably go away,
|
||||
the decision to bundle one template engine and use that will not.
|
||||
|
||||
Template engines are like programming languages and each of those engines
|
||||
|
|
@ -107,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
|
||||
of variables and take the return value as string.
|
||||
|
||||
But that's about where similarities end. Jinja for example has an
|
||||
extensive filter system, a certain way to do template inheritance,
|
||||
support for reusable blocks (macros) that can be used from inside
|
||||
templates and also from Python code, supports iterative template
|
||||
rendering, configurable syntax and more. On the other hand an engine
|
||||
like Genshi is based on XML stream evaluation, template inheritance by
|
||||
taking the availability of XPath into account and more. Mako on the
|
||||
other hand treats templates similar to Python modules.
|
||||
But that's about where similarities end. Jinja2 for example has an
|
||||
extensive filter system, a certain way to do template inheritance, support
|
||||
for reusable blocks (macros) that can be used from inside templates and
|
||||
also from Python code, uses Unicode for all operations, supports
|
||||
iterative template rendering, configurable syntax and more. On the other
|
||||
hand an engine like Genshi is based on XML stream evaluation, template
|
||||
inheritance by taking the availability of XPath into account and more.
|
||||
Mako on the other hand treats templates similar to Python modules.
|
||||
|
||||
When it comes to connecting a template engine with an application or
|
||||
framework there is more than just rendering templates. For instance,
|
||||
Flask uses Jinja's extensive autoescaping support. Also it provides
|
||||
ways to access macros from Jinja templates.
|
||||
Flask uses Jinja2's extensive autoescaping support. Also it provides
|
||||
ways to access macros from Jinja2 templates.
|
||||
|
||||
A template abstraction layer that would not take the unique features of
|
||||
the template engines away is a science on its own and a too large
|
||||
|
|
@ -130,27 +132,11 @@ being present. You can easily use your own templating language, but an
|
|||
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
|
||||
libraries (namely Werkzeug and Jinja). Why shouldn't it? If we look
|
||||
libraries (namely Werkzeug and Jinja2). Why shouldn't it? If we look
|
||||
over to the Ruby side of web development there we have a protocol very
|
||||
similar to WSGI. Just that it's called Rack there, but besides that it
|
||||
looks very much like a WSGI rendition for Ruby. But nearly all
|
||||
|
|
@ -169,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.
|
||||
|
||||
|
||||
Context Locals
|
||||
--------------
|
||||
Thread Locals
|
||||
-------------
|
||||
|
||||
Flask uses special context locals and proxies to provide access to the
|
||||
current app and request data to any code running during a request, CLI command,
|
||||
etc. Context locals are specific to the worker handling the activity, such as a
|
||||
thread, process, coroutine, or greenlet.
|
||||
Flask uses thread local objects (context local objects in fact, they
|
||||
support greenlet contexts as well) for request, session and an extra
|
||||
object you can put your own things on (:data:`~flask.g`). Why is that and
|
||||
isn't that a bad idea?
|
||||
|
||||
The context and proxies help solve two development issues: circular imports, and
|
||||
passing around global data. :data:`.current_app` can be used to access the
|
||||
application object without needing to import the app object directly, avoiding
|
||||
circular import issues. :data:`.request`, :data:`.session`, and :data:`.g` can
|
||||
be imported to access the current data for the request, rather than needing to
|
||||
pass them as arguments through every single function in your project.
|
||||
Yes it is usually not such a bright idea to use thread locals. They cause
|
||||
troubles for servers that are not based on the concept of threads and make
|
||||
large applications harder to maintain. However Flask is just not designed
|
||||
for large applications or asynchronous servers. Flask wants to make it
|
||||
quick and easy to write a traditional web application.
|
||||
|
||||
|
||||
Async/await and ASGI support
|
||||
----------------------------
|
||||
|
||||
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.
|
||||
Also see the :ref:`becomingbig` section of the documentation for some
|
||||
inspiration for larger applications based on Flask.
|
||||
|
||||
|
||||
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
|
||||
or anything else in that direction. Flask itself just bridges to Werkzeug
|
||||
to implement a proper WSGI application and to Jinja to handle templating.
|
||||
to implement a proper WSGI application and to Jinja2 to handle templating.
|
||||
It also binds to a few common standard library packages such as logging.
|
||||
Everything else is up for extensions.
|
||||
|
||||
|
|
@ -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
|
||||
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.
|
||||
Everything else is up to you or extensions.
|
||||
|
|
|
|||
|
|
@ -1,523 +1,237 @@
|
|||
.. _application-errors:
|
||||
|
||||
Handling Application Errors
|
||||
===========================
|
||||
|
||||
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
|
||||
.. 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 some situations where perfectly fine code can lead to server
|
||||
errors:
|
||||
|
||||
- the client terminated the request early and the application was still
|
||||
reading from the incoming data
|
||||
- the database server was overloaded and could not handle the query
|
||||
reading from the incoming data.
|
||||
- the database server was overloaded and could not handle the query.
|
||||
- a filesystem is full
|
||||
- a harddrive crashed
|
||||
- a backend server overloaded
|
||||
- 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
|
||||
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
|
||||
page for you and log the exception to the :attr:`~flask.Flask.logger`.
|
||||
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
|
||||
production mode, Flask will display a very simple page for you and log the
|
||||
exception to the :attr:`~flask.Flask.logger`.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
.. _error-logging-tools:
|
||||
|
||||
Error Logging Tools
|
||||
-------------------
|
||||
|
||||
Sending error mails, even if just for critical ones, can become
|
||||
overwhelming if enough users are hitting the error and log files are
|
||||
typically never looked at. This is why we recommend using `Sentry
|
||||
<https://sentry.io/>`_ for dealing with application errors. It's
|
||||
available as a source-available project `on GitHub
|
||||
<https://github.com/getsentry/sentry>`_ and is also available as a `hosted version
|
||||
<https://sentry.io/signup/>`_ which you can try for free. Sentry
|
||||
aggregates duplicate errors, captures the full stack trace and local
|
||||
variables for debugging, and sends you mails based on new errors or
|
||||
frequency thresholds.
|
||||
|
||||
To use Sentry you need to install the ``sentry-sdk`` client with extra
|
||||
``flask`` dependencies.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ pip install sentry-sdk[flask]
|
||||
|
||||
And then add this to your Flask app:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import sentry_sdk
|
||||
from sentry_sdk.integrations.flask import FlaskIntegration
|
||||
|
||||
sentry_sdk.init('YOUR_DSN_HERE', integrations=[FlaskIntegration()])
|
||||
|
||||
The ``YOUR_DSN_HERE`` value needs to be replaced with the DSN value you
|
||||
get from your Sentry installation.
|
||||
|
||||
After installation, failures leading to an Internal Server Error
|
||||
are automatically reported to Sentry and from there you can
|
||||
receive error notifications.
|
||||
|
||||
See also:
|
||||
|
||||
- Sentry also supports catching errors from a worker queue
|
||||
(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.
|
||||
This can be done by registering 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`.
|
||||
|
||||
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
|
||||
```````````
|
||||
|
||||
Register handlers by decorating a function with
|
||||
:meth:`~flask.Flask.errorhandler`. Or use
|
||||
:meth:`~flask.Flask.register_error_handler` to register the function later.
|
||||
Remember to set the error code when returning the response.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@app.errorhandler(werkzeug.exceptions.BadRequest)
|
||||
def handle_bad_request(e):
|
||||
return 'bad request!', 400
|
||||
|
||||
# or, without the decorator
|
||||
app.register_error_handler(400, handle_bad_request)
|
||||
|
||||
:exc:`werkzeug.exceptions.HTTPException` subclasses like
|
||||
:exc:`~werkzeug.exceptions.BadRequest` and their HTTP codes are interchangeable
|
||||
when registering handlers. (``BadRequest.code == 400``)
|
||||
|
||||
Non-standard HTTP codes cannot be registered by code because they are not known
|
||||
by Werkzeug. Instead, define a subclass of
|
||||
:class:`~werkzeug.exceptions.HTTPException` with the appropriate code and
|
||||
register and raise that exception class.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class InsufficientStorage(werkzeug.exceptions.HTTPException):
|
||||
code = 507
|
||||
description = 'Not enough storage space.'
|
||||
|
||||
app.register_error_handler(InsufficientStorage, handle_507)
|
||||
|
||||
raise InsufficientStorage()
|
||||
|
||||
Handlers can be registered for any exception class, not just
|
||||
:exc:`~werkzeug.exceptions.HTTPException` subclasses or HTTP status
|
||||
codes. Handlers can be registered for a specific class, or for all subclasses
|
||||
of a parent class.
|
||||
|
||||
|
||||
Handling
|
||||
````````
|
||||
|
||||
When building a Flask application you *will* run into exceptions. If some part
|
||||
of your code breaks while handling a request (and you have no error handlers
|
||||
registered), a "500 Internal Server Error"
|
||||
(:exc:`~werkzeug.exceptions.InternalServerError`) will be returned by default.
|
||||
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 "500 Internal Server Error".
|
||||
|
||||
For example, if an instance of :exc:`ConnectionRefusedError` is raised,
|
||||
and a handler is registered for :exc:`ConnectionError` and
|
||||
:exc:`ConnectionRefusedError`, the more specific :exc:`ConnectionRefusedError`
|
||||
handler is called with the exception instance to generate the response.
|
||||
|
||||
Handlers registered on the blueprint take precedence over those registered
|
||||
globally on the application, assuming a blueprint is handling the request that
|
||||
raises the exception. However, the blueprint cannot handle 404 routing errors
|
||||
because the 404 occurs at the routing level before the blueprint can be
|
||||
determined.
|
||||
|
||||
|
||||
Generic Exception Handlers
|
||||
``````````````````````````
|
||||
|
||||
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
|
||||
-------
|
||||
|
||||
See :doc:`/logging` for information about how to log exceptions, such as
|
||||
by emailing them to admins.
|
||||
|
||||
|
||||
Debugging
|
||||
---------
|
||||
|
||||
See :doc:`/debugging` for information about how to debug errors in
|
||||
development and production.
|
||||
with errors.
|
||||
|
||||
Error Mails
|
||||
-----------
|
||||
|
||||
If the application runs in production mode (which it will do on your
|
||||
server) you won't see any log messages by default. Why is that? Flask
|
||||
tries to be a zero-configuration framework. Where should it drop the logs
|
||||
for you if there is no configuration? Guessing is not a good idea because
|
||||
chances are, the place it guessed is not the place where the user has
|
||||
permission to create a logfile. Also, for most small applications nobody
|
||||
will look at the logs anyways.
|
||||
|
||||
In fact, I promise you right now that if you configure a logfile for the
|
||||
application errors you will never look at it except for debugging an issue
|
||||
when a user reported it for you. What you want instead is a mail the
|
||||
second the exception happened. Then you get an alert and you can do
|
||||
something about it.
|
||||
|
||||
Flask uses the Python builtin logging system, and it can actually send
|
||||
you mails for errors which is probably what you want. Here is how you can
|
||||
configure the Flask logger to send you mails for exceptions::
|
||||
|
||||
ADMINS = ['yourname@example.com']
|
||||
if not app.debug:
|
||||
import logging
|
||||
from logging.handlers import SMTPHandler
|
||||
mail_handler = SMTPHandler('127.0.0.1',
|
||||
'server-error@example.com',
|
||||
ADMINS, 'YourApplication Failed')
|
||||
mail_handler.setLevel(logging.ERROR)
|
||||
app.logger.addHandler(mail_handler)
|
||||
|
||||
So what just happened? We created a new
|
||||
:class:`~logging.handlers.SMTPHandler` that will send mails with the mail
|
||||
server listening on ``127.0.0.1`` to all the `ADMINS` from the address
|
||||
*server-error@example.com* with the subject "YourApplication Failed". If
|
||||
your mail server requires credentials, these can also be provided. For
|
||||
that check out the documentation for the
|
||||
:class:`~logging.handlers.SMTPHandler`.
|
||||
|
||||
We also tell the handler to only send errors and more critical messages.
|
||||
Because we certainly don't want to get a mail for warnings or other
|
||||
useless logs that might happen during request handling.
|
||||
|
||||
Before you run that in production, please also look at :ref:`logformat` to
|
||||
put more information into that error mail. That will save you from a lot
|
||||
of frustration.
|
||||
|
||||
|
||||
Logging to a File
|
||||
-----------------
|
||||
|
||||
Even if you get mails, you probably also want to log warnings. It's a
|
||||
good idea to keep as much information around that might be required to
|
||||
debug a problem. Please note that Flask itself will not issue any
|
||||
warnings in the core system, so it's your responsibility to warn in the
|
||||
code if something seems odd.
|
||||
|
||||
There are a couple of handlers provided by the logging system out of the
|
||||
box but not all of them are useful for basic error logging. The most
|
||||
interesting are probably the following:
|
||||
|
||||
- :class:`~logging.FileHandler` - logs messages to a file on the
|
||||
filesystem.
|
||||
- :class:`~logging.handlers.RotatingFileHandler` - logs messages to a file
|
||||
on the filesystem and will rotate after a certain number of messages.
|
||||
- :class:`~logging.handlers.NTEventLogHandler` - will log to the system
|
||||
event log of a Windows system. If you are deploying on a Windows box,
|
||||
this is what you want to use.
|
||||
- :class:`~logging.handlers.SysLogHandler` - sends logs to a UNIX
|
||||
syslog.
|
||||
|
||||
Once you picked your log handler, do like you did with the SMTP handler
|
||||
above, just make sure to use a lower setting (I would recommend
|
||||
`WARNING`)::
|
||||
|
||||
if not app.debug:
|
||||
import logging
|
||||
from themodule import TheHandlerYouWant
|
||||
file_handler = TheHandlerYouWant(...)
|
||||
file_handler.setLevel(logging.WARNING)
|
||||
app.logger.addHandler(file_handler)
|
||||
|
||||
.. _logformat:
|
||||
|
||||
Controlling the Log Format
|
||||
--------------------------
|
||||
|
||||
By default a handler will only write the message string into a file or
|
||||
send you that message as mail. A log record stores more information,
|
||||
and it makes a lot of sense to configure your logger to also contain that
|
||||
information so that you have a better idea of why that error happened, and
|
||||
more importantly, where it did.
|
||||
|
||||
A formatter can be instantiated with a format string. Note that
|
||||
tracebacks are appended to the log entry automatically. You don't have to
|
||||
do that in the log formatter format string.
|
||||
|
||||
Here some example setups:
|
||||
|
||||
Email
|
||||
`````
|
||||
|
||||
::
|
||||
|
||||
from logging import Formatter
|
||||
mail_handler.setFormatter(Formatter('''
|
||||
Message type: %(levelname)s
|
||||
Location: %(pathname)s:%(lineno)d
|
||||
Module: %(module)s
|
||||
Function: %(funcName)s
|
||||
Time: %(asctime)s
|
||||
|
||||
Message:
|
||||
|
||||
%(message)s
|
||||
'''))
|
||||
|
||||
File logging
|
||||
````````````
|
||||
|
||||
::
|
||||
|
||||
from logging import Formatter
|
||||
file_handler.setFormatter(Formatter(
|
||||
'%(asctime)s %(levelname)s: %(message)s '
|
||||
'[in %(pathname)s:%(lineno)d]'
|
||||
))
|
||||
|
||||
|
||||
Complex Log Formatting
|
||||
``````````````````````
|
||||
|
||||
Here is a list of useful formatting variables for the format string. Note
|
||||
that this list is not complete, consult the official documentation of the
|
||||
:mod:`logging` package for a full list.
|
||||
|
||||
.. tabularcolumns:: |p{3cm}|p{12cm}|
|
||||
|
||||
+------------------+----------------------------------------------------+
|
||||
| Format | Description |
|
||||
+==================+====================================================+
|
||||
| ``%(levelname)s``| Text logging level for the message |
|
||||
| | (``'DEBUG'``, ``'INFO'``, ``'WARNING'``, |
|
||||
| | ``'ERROR'``, ``'CRITICAL'``). |
|
||||
+------------------+----------------------------------------------------+
|
||||
| ``%(pathname)s`` | Full pathname of the source file where the |
|
||||
| | logging call was issued (if available). |
|
||||
+------------------+----------------------------------------------------+
|
||||
| ``%(filename)s`` | Filename portion of pathname. |
|
||||
+------------------+----------------------------------------------------+
|
||||
| ``%(module)s`` | Module (name portion of filename). |
|
||||
+------------------+----------------------------------------------------+
|
||||
| ``%(funcName)s`` | Name of function containing the logging call. |
|
||||
+------------------+----------------------------------------------------+
|
||||
| ``%(lineno)d`` | Source line number where the logging call was |
|
||||
| | issued (if available). |
|
||||
+------------------+----------------------------------------------------+
|
||||
| ``%(asctime)s`` | Human-readable time when the LogRecord` was |
|
||||
| | created. By default this is of the form |
|
||||
| | ``"2003-07-08 16:49:45,896"`` (the numbers after |
|
||||
| | the comma are millisecond portion of the time). |
|
||||
| | This can be changed by subclassing the formatter |
|
||||
| | and overriding the |
|
||||
| | :meth:`~logging.Formatter.formatTime` method. |
|
||||
+------------------+----------------------------------------------------+
|
||||
| ``%(message)s`` | The logged message, computed as ``msg % args`` |
|
||||
+------------------+----------------------------------------------------+
|
||||
|
||||
If you want to further customize the formatting, you can subclass the
|
||||
formatter. The formatter has three interesting methods:
|
||||
|
||||
:meth:`~logging.Formatter.format`:
|
||||
handles the actual formatting. It is passed a
|
||||
:class:`~logging.LogRecord` object and has to return the formatted
|
||||
string.
|
||||
:meth:`~logging.Formatter.formatTime`:
|
||||
called for `asctime` formatting. If you want a different time format
|
||||
you can override this method.
|
||||
:meth:`~logging.Formatter.formatException`
|
||||
called for exception formatting. It is passed an :attr:`~sys.exc_info`
|
||||
tuple and has to return a string. The default is usually fine, you
|
||||
don't have to override it.
|
||||
|
||||
For more information, head over to the official documentation.
|
||||
|
||||
|
||||
Other Libraries
|
||||
---------------
|
||||
|
||||
So far we only configured the logger your application created itself.
|
||||
Other libraries might log themselves as well. For example, SQLAlchemy uses
|
||||
logging heavily in its core. While there is a method to configure all
|
||||
loggers at once in the :mod:`logging` package, I would not recommend using
|
||||
it. There might be a situation in which you want to have multiple
|
||||
separate applications running side by side in the same Python interpreter
|
||||
and then it becomes impossible to have different logging setups for those.
|
||||
|
||||
Instead, I would recommend figuring out which loggers you are interested
|
||||
in, getting the loggers with the :func:`~logging.getLogger` function and
|
||||
iterating over them to attach handlers::
|
||||
|
||||
from logging import getLogger
|
||||
loggers = [app.logger, getLogger('sqlalchemy'),
|
||||
getLogger('otherlibrary')]
|
||||
for logger in loggers:
|
||||
logger.addHandler(mail_handler)
|
||||
logger.addHandler(file_handler)
|
||||
|
|
|
|||
|
|
@ -1,305 +1,387 @@
|
|||
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
|
||||
application. While `PyPI`_ contains many Flask extensions, you may not
|
||||
find one that fits your need. If this is the case, you can create your
|
||||
own, and publish it for others to use as well.
|
||||
If you want to create your own Flask extension for something that does not
|
||||
exist yet, this guide to extension development will help you get your
|
||||
extension running in no time and to feel like users would expect your
|
||||
extension to behave.
|
||||
|
||||
This guide will show how to create a Flask extension, and some of the
|
||||
common patterns and requirements involved. Since extensions can do
|
||||
anything, this guide won't be able to cover every possibility.
|
||||
.. _Flask Extension Registry: http://flask.pocoo.org/extensions/
|
||||
|
||||
The best ways to learn about extensions are to look at how other
|
||||
extensions you use are written, and discuss with others. Discuss your
|
||||
design ideas with others on our `Discord Chat`_ or
|
||||
`GitHub Discussions`_.
|
||||
Anatomy of an Extension
|
||||
-----------------------
|
||||
|
||||
The best extensions share common patterns, so that anyone familiar with
|
||||
using one extension won't feel completely lost with another. This can
|
||||
only work if collaboration happens early.
|
||||
Extensions are all located in a package called ``flask_something``
|
||||
where "something" is the name of the library you want to bridge. So for
|
||||
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 `setup.py` files.
|
||||
|
||||
Flask sets up a redirect package called :data:`flask.ext` where users
|
||||
should import the extensions from. If you for instance have a package
|
||||
called ``flask_something`` users would import it as
|
||||
``flask.ext.something``. This is done to transition from the old
|
||||
namespace packages. See :ref:`ext-import-transition` for more details.
|
||||
|
||||
But how 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 behaviour.
|
||||
|
||||
Most importantly the extension must be shipped with a `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 as BSD or MIT or a more liberal license
|
||||
to be enlisted on 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 `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
|
||||
suffix. If it wraps another library, it should include the library name
|
||||
as well. This makes it easy to search for extensions, and makes their
|
||||
purpose clearer.
|
||||
That's a lot of code but you can really just copy/paste that from existing
|
||||
extensions and adapt.
|
||||
|
||||
A general Python packaging recommendation is that the install name from
|
||||
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.
|
||||
flask_sqlite3.py
|
||||
````````````````
|
||||
|
||||
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``
|
||||
- ``flask-name-lower`` imported as ``flask_name_lower``
|
||||
- ``Flask-ComboName`` imported as ``flask_comboname``
|
||||
- ``Name-Flask`` imported as ``name_flask``
|
||||
Initializing Extensions
|
||||
-----------------------
|
||||
|
||||
Many extensions will need some kind of initialization step. For example,
|
||||
consider your application is currently connecting to SQLite like the
|
||||
documentation suggests (:ref:`sqlite3`) you will need to provide a few
|
||||
functions and before / after request handlers. So how does the extension
|
||||
know the name of the application object?
|
||||
|
||||
The Extension Class and Initialization
|
||||
--------------------------------------
|
||||
Quite simple: you pass it to it.
|
||||
|
||||
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.
|
||||
There are two recommended ways for an extension to initialize:
|
||||
|
||||
.. code-block:: python
|
||||
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 behaviour. 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 a
|
||||
manager object that handles opening and closing database connections.
|
||||
|
||||
The Extension Code
|
||||
------------------
|
||||
|
||||
Here's the contents of the `flask_sqlite3.py` for copy/paste::
|
||||
|
||||
from __future__ import absolute_import
|
||||
import sqlite3
|
||||
|
||||
from flask import _request_ctx_stack
|
||||
|
||||
class SQLite3(object):
|
||||
|
||||
def __init__(self, app):
|
||||
self.app = app
|
||||
self.app.config.setdefault('SQLITE3_DATABASE', ':memory:')
|
||||
self.app.teardown_request(self.teardown_request)
|
||||
self.app.before_request(self.before_request)
|
||||
|
||||
def connect(self):
|
||||
return sqlite3.connect(self.app.config['SQLITE3_DATABASE'])
|
||||
|
||||
def before_request(self):
|
||||
ctx = _request_ctx_stack.top
|
||||
ctx.sqlite3_db = self.connect()
|
||||
|
||||
def teardown_request(self, exception):
|
||||
ctx = _request_ctx_stack.top
|
||||
ctx.sqlite3_db.close()
|
||||
|
||||
def get_db(self):
|
||||
ctx = _request_ctx_stack.top
|
||||
if ctx is not None:
|
||||
return ctx.sqlite3_db
|
||||
|
||||
So here's what these lines of code do:
|
||||
|
||||
1. The ``__future__`` import is necessary to activate absolute imports.
|
||||
Otherwise we could not call our module `sqlite3.py` and import the
|
||||
top-level `sqlite3` module which actually implements the connection to
|
||||
SQLite.
|
||||
2. We create a class for our extension that requires a supplied `app` object,
|
||||
sets a configuration for the database if it's not there
|
||||
(:meth:`dict.setdefault`), and attaches `before_request` and
|
||||
`teardown_request` handlers.
|
||||
3. Next, we define a `connect` function that opens a database connection.
|
||||
4. Then we set up the request handlers we bound to the app above. Note here
|
||||
that we're attaching our database connection to the top request context via
|
||||
`_request_ctx_stack.top`. Extensions should use the top context and not the
|
||||
`g` object to store things like database connections.
|
||||
5. Finally, we add a `get_db` function that simplifies access to the context's
|
||||
database.
|
||||
|
||||
So why did we decide on a class based approach here? Because using our
|
||||
extension looks something like this::
|
||||
|
||||
from flask import Flask
|
||||
from flask_sqlite3 import SQLite3
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config.from_pyfile('the-config.cfg')
|
||||
manager = SQLite3(app)
|
||||
db = manager.get_db()
|
||||
|
||||
You can then use the database from views like this::
|
||||
|
||||
@app.route('/')
|
||||
def show_all():
|
||||
cur = db.cursor()
|
||||
cur.execute(...)
|
||||
|
||||
Opening a database connection from outside a view function is simple.
|
||||
|
||||
>>> from yourapplication import db
|
||||
>>> cur = db.cursor()
|
||||
>>> cur.execute(...)
|
||||
|
||||
Adding an `init_app` Function
|
||||
-----------------------------
|
||||
|
||||
In practice, you'll almost always want to permit users to initialize your
|
||||
extension and provide an app object after the fact. This can help avoid
|
||||
circular import problems when a user is breaking their app into multiple files.
|
||||
Our extension could add an `init_app` function as follows::
|
||||
|
||||
class SQLite3(object):
|
||||
|
||||
class HelloExtension:
|
||||
def __init__(self, app=None):
|
||||
if app is not None:
|
||||
self.init_app(app)
|
||||
self.app = app
|
||||
self.init_app(self.app)
|
||||
else:
|
||||
self.app = None
|
||||
|
||||
def init_app(self, app):
|
||||
app.before_request(...)
|
||||
self.app = app
|
||||
self.app.config.setdefault('SQLITE3_DATABASE', ':memory:')
|
||||
self.app.teardown_request(self.teardown_request)
|
||||
self.app.before_request(self.before_request)
|
||||
|
||||
It is important that the app is not stored on the extension, don't do
|
||||
``self.app = app``. The only time the extension should have direct
|
||||
access to an app is during ``init_app``, otherwise it should use
|
||||
:data:`.current_app`.
|
||||
def connect(self):
|
||||
return sqlite3.connect(app.config['SQLITE3_DATABASE'])
|
||||
|
||||
This allows the extension to support the application factory pattern,
|
||||
avoids circular import issues when importing the extension instance
|
||||
elsewhere in a user's code, and makes testing with different
|
||||
configurations easier.
|
||||
def before_request(self):
|
||||
ctx = _request_ctx_stack.top
|
||||
ctx.sqlite3_db = self.connect()
|
||||
|
||||
.. code-block:: python
|
||||
def teardown_request(self, exception):
|
||||
ctx = _request_ctx_stack.top
|
||||
ctx.sqlite3_db.close()
|
||||
|
||||
hello = HelloExtension()
|
||||
def get_db(self):
|
||||
ctx = _request_ctx_stack.top
|
||||
if ctx is not None:
|
||||
return ctx.sqlite3_db
|
||||
|
||||
def create_app():
|
||||
app = Flask(__name__)
|
||||
hello.init_app(app)
|
||||
return app
|
||||
The user could then initialize the extension in one file::
|
||||
|
||||
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.
|
||||
manager = SQLite3()
|
||||
|
||||
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.
|
||||
and bind their app to the extension in another file::
|
||||
|
||||
manager.init_app(app)
|
||||
|
||||
End-Of-Request Behavior
|
||||
-----------------------
|
||||
|
||||
Due to the change in Flask 0.7 regarding functions that are run at the end
|
||||
of the request your extension will have to be extra careful there if it
|
||||
wants to continue to support older versions of Flask. The following
|
||||
pattern is a good way to support both::
|
||||
|
||||
def close_connection(response):
|
||||
ctx = _request_ctx_stack.top
|
||||
ctx.sqlite3_db.close()
|
||||
return response
|
||||
|
||||
if hasattr(app, 'teardown_request'):
|
||||
app.teardown_request(close_connection)
|
||||
else:
|
||||
app.after_request(close_connection)
|
||||
|
||||
Strictly speaking the above code is wrong, because teardown functions are
|
||||
passed the exception and typically don't return anything. However because
|
||||
the return value is discarded this will just work assuming that the code
|
||||
in between does not touch the passed parameter.
|
||||
|
||||
Learn from Others
|
||||
-----------------
|
||||
|
||||
This documentation only touches the bare minimum for extension
|
||||
development. If you want to learn more, it's a very good idea to check
|
||||
out existing extensions on the `Flask Extension Registry`_. If you feel
|
||||
lost there is still the `mailinglist`_ and the `IRC channel`_ to get some
|
||||
ideas for nice looking APIs. Especially if you do something nobody before
|
||||
you did, it might be a very good idea to get some more input. This not
|
||||
only to get an idea about what people might want to have from an
|
||||
extension, but also to avoid having multiple developers working on pretty
|
||||
much the same side by side.
|
||||
|
||||
Remember: good API design is hard, so introduce your project on the
|
||||
mailinglist, and let other developers give you a helping hand with
|
||||
designing the API.
|
||||
|
||||
The best Flask extensions are extensions that share common idioms for the
|
||||
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:
|
||||
|
||||
1. An approved Flask extension must provide exactly one package or module
|
||||
named ``flask_extensionname``. They might also reside inside a
|
||||
``flaskext`` namespace packages though this is discouraged now.
|
||||
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, in case of ``python setup.py test``
|
||||
dependencies for tests alone can be specified in the `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
|
||||
`setup.py` file unless a dependency cannot be met because it is not
|
||||
available on PyPI.
|
||||
7. The extension must have documentation that uses one of the two Flask
|
||||
themes for Sphinx documentation.
|
||||
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 2.5, 2.6 as well as
|
||||
Python 2.7
|
||||
|
||||
|
||||
Adding Behavior
|
||||
---------------
|
||||
.. _ext-import-transition:
|
||||
|
||||
There are many ways that an extension can add behavior. Any setup
|
||||
methods that are available on the :class:`Flask` object can be used
|
||||
during an extension's ``init_app`` method.
|
||||
Extension Import Transition
|
||||
---------------------------
|
||||
|
||||
A common pattern is to use :meth:`~Flask.before_request` to initialize
|
||||
some data or a connection at the beginning of each request, then
|
||||
:meth:`~Flask.teardown_request` to clean it up at the end. This can be
|
||||
stored on :data:`.g`, discussed more below.
|
||||
For a while we recommended using namespace packages for Flask extensions.
|
||||
This turned out to be problematic in practice because many different
|
||||
competing namespace package systems exist and pip would automatically
|
||||
switch between different systems and this caused a lot of problems for
|
||||
users.
|
||||
|
||||
A more lazy approach is to provide a method that initializes and caches
|
||||
the data or connection. For example, a ``ext.get_db`` method could
|
||||
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.
|
||||
Instead we now recommend naming packages ``flask_foo`` instead of the now
|
||||
deprecated ``flaskext.foo``. Flask 0.8 introduces a redirect import
|
||||
system that lets uses import from ``flask.ext.foo`` and it will try
|
||||
``flask_foo`` first and if that fails ``flaskext.foo``.
|
||||
|
||||
Besides doing something before and after every view, your extension
|
||||
might want to add some specific views as well. In this case, you could
|
||||
define a :class:`Blueprint`, then call :meth:`~Flask.register_blueprint`
|
||||
during ``init_app`` to add the blueprint to the app.
|
||||
Flask extensions should urge users to import from ``flask.ext.foo``
|
||||
instead of ``flask_foo`` or ``flaskext_foo`` so that extensions can
|
||||
transition to the new package name without affecting users.
|
||||
|
||||
|
||||
Configuration Techniques
|
||||
------------------------
|
||||
|
||||
There can be multiple levels and sources of configuration for an
|
||||
extension. You should consider what parts of your extension fall into
|
||||
each one.
|
||||
|
||||
- Configuration per application instance, through ``app.config``
|
||||
values. This is configuration that could reasonably change for each
|
||||
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.
|
||||
|
||||
It's up to you to decide what configuration is appropriate for your
|
||||
extension, based on what you need and what you want to support.
|
||||
|
||||
Configuration should not be changed after the application setup phase is
|
||||
complete and the server begins handling requests. Configuration is
|
||||
global, any changes to it are not guaranteed to be visible to other
|
||||
workers.
|
||||
|
||||
|
||||
Data During a Request
|
||||
---------------------
|
||||
|
||||
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)
|
||||
blog.init_app(app)
|
||||
|
||||
Another technique could be to use an attribute on the extension, such as
|
||||
``self.post_model`` from above. Add the extension to ``app.extensions``
|
||||
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
|
||||
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
|
||||
perfect solution here, only different strategies and tradeoffs depending
|
||||
on your needs and how much customization you want to offer. Luckily,
|
||||
this sort of resource dependency is not a common need for most
|
||||
extensions. Remember, if you need help with design, ask on our
|
||||
`Discord Chat`_ or `GitHub Discussions`_.
|
||||
|
||||
|
||||
Recommended Extension Guidelines
|
||||
--------------------------------
|
||||
|
||||
Flask previously had the concept of "approved extensions", where the
|
||||
Flask maintainers evaluated the quality, support, and compatibility of
|
||||
the extensions before listing them. While the list became too difficult
|
||||
to maintain over time, the guidelines are still relevant to all
|
||||
extensions maintained and developed today, as they help the Flask
|
||||
ecosystem remain consistent and compatible.
|
||||
|
||||
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
|
||||
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
|
||||
must be installable in editable mode with ``pip install -e .``.
|
||||
6. It must ship tests that can be invoked with a common tool like
|
||||
``tox -e py``, ``nox -s test`` or ``pytest``. If not using ``tox``,
|
||||
the test dependencies should be specified in a requirements file.
|
||||
The tests must be part of the sdist distribution.
|
||||
7. A link to the documentation or project website must be in the PyPI
|
||||
metadata or the readme. The documentation should use the Flask theme
|
||||
from the `Official Pallets Themes`_.
|
||||
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
|
||||
.. _Discord Chat: https://discord.gg/pallets
|
||||
.. _GitHub Discussions: https://github.com/pallets/flask/discussions
|
||||
.. _Official Pallets Themes: https://pypi.org/project/Pallets-Sphinx-Themes/
|
||||
.. _Pallets-Eco: https://github.com/pallets-eco
|
||||
.. _EOL calendar: https://devguide.python.org/versions/
|
||||
.. _OAuth extension: http://packages.python.org/Flask-OAuth/
|
||||
.. _mailinglist: http://flask.pocoo.org/mailinglist/
|
||||
.. _IRC channel: http://flask.pocoo.org/community/irc/
|
||||
|
|
|
|||
|
|
@ -1,48 +1,48 @@
|
|||
Extensions
|
||||
==========
|
||||
|
||||
Extensions are extra packages that add functionality to a Flask
|
||||
application. For example, an extension might add support for sending
|
||||
email or connecting to a database. Some extensions add entire new
|
||||
frameworks to help build certain types of applications, like a REST API.
|
||||
Flask Extensions
|
||||
================
|
||||
|
||||
Flask extensions extend the functionality of Flask in various different
|
||||
ways. For instance they add support for databases and other common tasks.
|
||||
|
||||
Finding Extensions
|
||||
------------------
|
||||
|
||||
Flask extensions are usually named "Flask-Foo" or "Foo-Flask". You can
|
||||
search PyPI for packages tagged with `Framework :: Flask <pypi_>`_.
|
||||
|
||||
Flask extensions are listed on the `Flask Extension Registry`_ and can be
|
||||
downloaded with ``easy_install`` or ``pip``. If you add a Flask extension
|
||||
as dependency to your ``requirements.rst`` or ``setup.py`` file they are
|
||||
usually installed with a simple command or when your application installs.
|
||||
|
||||
Using Extensions
|
||||
----------------
|
||||
|
||||
Consult each extension's documentation for installation, configuration,
|
||||
and usage instructions. Generally, extensions pull their own
|
||||
configuration from :attr:`app.config <flask.Flask.config>` and are
|
||||
passed an application instance during initialization. For example,
|
||||
an extension called "Flask-Foo" might be used like this::
|
||||
Extensions typically have documentation that goes along that shows how to
|
||||
use it. There are no general rules in how extensions are supposed to
|
||||
behave but they are imported from common locations. If you have an
|
||||
extension called ``Flask-Foo`` or ``Foo-Flask`` it will be always
|
||||
importable from ``flask.ext.foo``::
|
||||
|
||||
from flask_foo import Foo
|
||||
from flask.ext import foo
|
||||
|
||||
foo = Foo()
|
||||
Flask Before 0.8
|
||||
----------------
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config.update(
|
||||
FOO_BAR='baz',
|
||||
FOO_SPAM='eggs',
|
||||
)
|
||||
If you are using Flask 0.7 or earlier the :data:`flask.ext` package will not
|
||||
exist, instead you have to import from ``flaskext.foo`` or ``flask_foo``
|
||||
depending on how the extension is distributed. If you want to develop an
|
||||
application that supports Flask 0.7 or earlier you should still import
|
||||
from the :data:`flask.ext` package. We provide you with a compatibility
|
||||
module that provides this package for older versions of Flask. You can
|
||||
download it from github: `flaskext_compat.py`_
|
||||
|
||||
foo.init_app(app)
|
||||
And here is how you can use it::
|
||||
|
||||
import flaskext_compat
|
||||
flaskext_compat.activate()
|
||||
|
||||
Building Extensions
|
||||
-------------------
|
||||
from flask.ext import foo
|
||||
|
||||
While `PyPI <pypi_>`_ contains many Flask extensions, you may not find
|
||||
an extension that fits your need. If this is the case, you can create
|
||||
your own, and publish it for others to use as well. Read
|
||||
:doc:`extensiondev` to develop your own Flask extension.
|
||||
Once the ``flaskext_compat`` module is activated the :data:`flask.ext` will
|
||||
exist and you can start importing from there.
|
||||
|
||||
|
||||
.. _pypi: https://pypi.org/search/?c=Framework+%3A%3A+Flask
|
||||
.. _Flask Extension Registry: http://flask.pocoo.org/extensions/
|
||||
.. _flaskext_compat.py: https://github.com/mitsuhiko/flask/raw/master/scripts/flaskext_compat.py
|
||||
|
|
|
|||
16
docs/flaskdocext.py
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import re
|
||||
import inspect
|
||||
|
||||
|
||||
_internal_mark_re = re.compile(r'^\s*:internal:\s*$(?m)')
|
||||
|
||||
|
||||
def skip_member(app, what, name, obj, skip, options):
|
||||
docstring = inspect.getdoc(obj)
|
||||
if skip:
|
||||
return True
|
||||
return _internal_mark_re.search(docstring or '') is not None
|
||||
|
||||
|
||||
def setup(app):
|
||||
app.connect('autodoc-skip-member', skip_member)
|
||||
86
docs/flaskext.py
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
# flasky extensions. flasky pygments style based on tango style
|
||||
from pygments.style import Style
|
||||
from pygments.token import Keyword, Name, Comment, String, Error, \
|
||||
Number, Operator, Generic, Whitespace, Punctuation, Other, Literal
|
||||
|
||||
|
||||
class FlaskyStyle(Style):
|
||||
background_color = "#f8f8f8"
|
||||
default_style = ""
|
||||
|
||||
styles = {
|
||||
# No corresponding class for the following:
|
||||
#Text: "", # class: ''
|
||||
Whitespace: "underline #f8f8f8", # class: 'w'
|
||||
Error: "#a40000 border:#ef2929", # class: 'err'
|
||||
Other: "#000000", # class 'x'
|
||||
|
||||
Comment: "italic #8f5902", # class: 'c'
|
||||
Comment.Preproc: "noitalic", # class: 'cp'
|
||||
|
||||
Keyword: "bold #004461", # class: 'k'
|
||||
Keyword.Constant: "bold #004461", # class: 'kc'
|
||||
Keyword.Declaration: "bold #004461", # class: 'kd'
|
||||
Keyword.Namespace: "bold #004461", # class: 'kn'
|
||||
Keyword.Pseudo: "bold #004461", # class: 'kp'
|
||||
Keyword.Reserved: "bold #004461", # class: 'kr'
|
||||
Keyword.Type: "bold #004461", # class: 'kt'
|
||||
|
||||
Operator: "#582800", # class: 'o'
|
||||
Operator.Word: "bold #004461", # class: 'ow' - like keywords
|
||||
|
||||
Punctuation: "bold #000000", # class: 'p'
|
||||
|
||||
# because special names such as Name.Class, Name.Function, etc.
|
||||
# are not recognized as such later in the parsing, we choose them
|
||||
# to look the same as ordinary variables.
|
||||
Name: "#000000", # class: 'n'
|
||||
Name.Attribute: "#c4a000", # class: 'na' - to be revised
|
||||
Name.Builtin: "#004461", # class: 'nb'
|
||||
Name.Builtin.Pseudo: "#3465a4", # class: 'bp'
|
||||
Name.Class: "#000000", # class: 'nc' - to be revised
|
||||
Name.Constant: "#000000", # class: 'no' - to be revised
|
||||
Name.Decorator: "#888", # class: 'nd' - to be revised
|
||||
Name.Entity: "#ce5c00", # class: 'ni'
|
||||
Name.Exception: "bold #cc0000", # class: 'ne'
|
||||
Name.Function: "#000000", # class: 'nf'
|
||||
Name.Property: "#000000", # class: 'py'
|
||||
Name.Label: "#f57900", # class: 'nl'
|
||||
Name.Namespace: "#000000", # class: 'nn' - to be revised
|
||||
Name.Other: "#000000", # class: 'nx'
|
||||
Name.Tag: "bold #004461", # class: 'nt' - like a keyword
|
||||
Name.Variable: "#000000", # class: 'nv' - to be revised
|
||||
Name.Variable.Class: "#000000", # class: 'vc' - to be revised
|
||||
Name.Variable.Global: "#000000", # class: 'vg' - to be revised
|
||||
Name.Variable.Instance: "#000000", # class: 'vi' - to be revised
|
||||
|
||||
Number: "#990000", # class: 'm'
|
||||
|
||||
Literal: "#000000", # class: 'l'
|
||||
Literal.Date: "#000000", # class: 'ld'
|
||||
|
||||
String: "#4e9a06", # class: 's'
|
||||
String.Backtick: "#4e9a06", # class: 'sb'
|
||||
String.Char: "#4e9a06", # class: 'sc'
|
||||
String.Doc: "italic #8f5902", # class: 'sd' - like a comment
|
||||
String.Double: "#4e9a06", # class: 's2'
|
||||
String.Escape: "#4e9a06", # class: 'se'
|
||||
String.Heredoc: "#4e9a06", # class: 'sh'
|
||||
String.Interpol: "#4e9a06", # class: 'si'
|
||||
String.Other: "#4e9a06", # class: 'sx'
|
||||
String.Regex: "#4e9a06", # class: 'sr'
|
||||
String.Single: "#4e9a06", # class: 's1'
|
||||
String.Symbol: "#4e9a06", # class: 'ss'
|
||||
|
||||
Generic: "#000000", # class: 'g'
|
||||
Generic.Deleted: "#a40000", # class: 'gd'
|
||||
Generic.Emph: "italic #000000", # class: 'ge'
|
||||
Generic.Error: "#ef2929", # class: 'gr'
|
||||
Generic.Heading: "bold #000080", # class: 'gh'
|
||||
Generic.Inserted: "#00A000", # class: 'gi'
|
||||
Generic.Output: "#888", # class: 'go'
|
||||
Generic.Prompt: "#745334", # class: 'gp'
|
||||
Generic.Strong: "bold #000000", # class: 'gs'
|
||||
Generic.Subheading: "bold #800080", # class: 'gu'
|
||||
Generic.Traceback: "bold #a40000", # class: 'gt'
|
||||
}
|
||||
118
docs/flaskstyle.sty
Normal 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
|
||||
100
docs/foreword.rst
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
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?
|
||||
-----------------------
|
||||
|
||||
To me, the "micro" in microframework refers not only to the simplicity and
|
||||
small size of the framework, but also the fact that it does not make much
|
||||
decisions for you. While Flask does pick a templating engine for you, we
|
||||
won't make such decisions for your datastore or other parts.
|
||||
|
||||
For us however the term “micro” does not mean that the whole implementation
|
||||
has to fit into a single Python file.
|
||||
|
||||
One of the design decisions with Flask was that simple tasks should be
|
||||
simple and not take up a lot of code and yet not limit yourself. Because
|
||||
of that we took a few design choices that some people might find
|
||||
surprising or unorthodox. For example, Flask uses thread-local objects
|
||||
internally so that you don't have to pass objects around from function to
|
||||
function within a request in order to stay threadsafe. While this is a
|
||||
really easy approach and saves you a lot of time, it might also cause some
|
||||
troubles for very large applications because changes on these thread-local
|
||||
objects can happen anywhere in the same thread. In order to solve these
|
||||
problems we don't hide the thread locals for you but instead embrace them
|
||||
and provide you with a lot of tools to make it as pleasant as possible to
|
||||
work with them.
|
||||
|
||||
Flask is also based on convention over configuration, which means that
|
||||
many things are preconfigured. For example, by convention, templates and
|
||||
static files are in subdirectories within the Python source tree of the
|
||||
application. While this can be changed you usually don't have to.
|
||||
|
||||
The main reason however why Flask is called a "microframework" is the idea
|
||||
to keep the core simple but extensible. There is no database abstraction
|
||||
layer, no form validation or anything else where different libraries
|
||||
already exist that can handle that. However Flask knows the concept of
|
||||
extensions that can add this functionality into your application as if it
|
||||
was implemented in Flask itself. There are currently extensions for
|
||||
object relational mappers, form validation, upload handling, various open
|
||||
authentication technologies and more.
|
||||
|
||||
Since Flask is based on a very solid foundation there is not a lot of code
|
||||
in Flask itself. As such it's easy to adapt even for lage applications
|
||||
and we are making sure that you can either configure it as much as
|
||||
possible by subclassing things or by forking the entire codebase. 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`.
|
||||
|
||||
Web Development is Dangerous
|
||||
----------------------------
|
||||
|
||||
I'm not joking. Well, maybe a little. 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.
|
||||
|
||||
So always keep security in mind when doing web development.
|
||||
|
||||
The Status of Python 3
|
||||
----------------------
|
||||
|
||||
Currently the Python community is in the process of improving libraries to
|
||||
support the new iteration of the Python programming language. While the
|
||||
situation is greatly improving there are still some issues that make it
|
||||
hard for us to switch over to Python 3 just now. These problems are
|
||||
partially caused by changes in the language that went unreviewed for too
|
||||
long, partially also because we have not quite worked out how the lower
|
||||
level API should change for the unicode differences in Python3.
|
||||
|
||||
Werkzeug and Flask will be ported to Python 3 as soon as a solution for
|
||||
the changes is found, and we will provide helpful tips how to upgrade
|
||||
existing applications to Python 3. Until then, we strongly recommend
|
||||
using Python 2.6 and 2.7 with activated Python 3 warnings during
|
||||
development. If you plan on upgrading to Python 3 in the near future we
|
||||
strongly recommend that you read `How to write forwards compatible
|
||||
Python code <http://lucumr.pocoo.org/2011/1/22/forwards-compatible-python/>`_.
|
||||
125
docs/gevent.rst
|
|
@ -1,125 +0,0 @@
|
|||
Async with Gevent
|
||||
=================
|
||||
|
||||
`Gevent`_ patches Python's standard library to run within special async workers
|
||||
called `greenlets`_. Gevent has existed since long before Python's native
|
||||
asyncio was available, and Flask has always worked with it.
|
||||
|
||||
.. _gevent: https://www.gevent.org
|
||||
.. _greenlets: https://greenlet.readthedocs.io
|
||||
|
||||
Gevent is a reliable way to handle numerous, long lived, concurrent connections,
|
||||
and to achieve similar capabilities to ASGI and asyncio. This works without
|
||||
needing to write ``async def`` or ``await`` anywhere, but relies on gevent and
|
||||
greenlet's low level manipulation of the Python interpreter.
|
||||
|
||||
Deciding whether you should use gevent with Flask, or `Quart`_, or something
|
||||
else, is ultimately up to understanding the specific needs of your project.
|
||||
|
||||
.. _quart: https://quart.palletsprojects.com
|
||||
|
||||
|
||||
Enabling gevent
|
||||
---------------
|
||||
|
||||
You need to apply gevent's patching as early as possible in your code. This
|
||||
enables gevent's underlying event loop and converts many Python internals to run
|
||||
inside it. Add the following at the top of your project's module or top
|
||||
``__init__.py``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import gevent.monkey
|
||||
gevent.monkey.patch_all()
|
||||
|
||||
When deploying in production, use :doc:`/deploying/gunicorn` or
|
||||
:doc:`/deploying/uwsgi` with a gevent worker, as described on those pages.
|
||||
|
||||
To run concurrent tasks within your own code, such as views, use
|
||||
|gevent.spawn|_:
|
||||
|
||||
.. |gevent.spawn| replace:: ``gevent.spawn()``
|
||||
.. _gevent.spawn: https://www.gevent.org/api/gevent.html#gevent.spawn
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@app.post("/send")
|
||||
def send_email():
|
||||
gevent.spawn(email.send, to="example@example.example", text="example")
|
||||
return "Email is being sent."
|
||||
|
||||
If you need to access :data:`request` or other Flask context globals within the
|
||||
spawned function, decorate the function with :func:`.stream_with_context` or
|
||||
:func:`.copy_current_request_context`. Prefer passing the exact data you need
|
||||
when spawning the function, rather than using the decorators.
|
||||
|
||||
.. note::
|
||||
|
||||
When using gevent, greenlet>=1.0 is required. When using PyPy, PyPy>=7.3.7
|
||||
is required.
|
||||
|
||||
|
||||
.. _gevent-asyncio:
|
||||
|
||||
Combining with ``async``/``await``
|
||||
----------------------------------
|
||||
|
||||
Gevent's patching does not interact well with Flask's built-in asyncio support.
|
||||
If you want to use Gevent and asyncio in the same app, you'll need to override
|
||||
:meth:`flask.Flask.async_to_sync` to run async functions inside gevent.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import gevent.monkey
|
||||
gevent.monkey.patch_all()
|
||||
|
||||
import asyncio
|
||||
from flask import Flask, request
|
||||
|
||||
loop = asyncio.EventLoop()
|
||||
gevent.spawn(loop.run_forever)
|
||||
|
||||
class GeventFlask(Flask):
|
||||
def async_to_sync(self, func):
|
||||
def run(*args, **kwargs):
|
||||
coro = func(*args, **kwargs)
|
||||
future = asyncio.run_coroutine_threadsafe(coro, loop)
|
||||
return future.result()
|
||||
|
||||
return run
|
||||
|
||||
app = GeventFlask(__name__)
|
||||
|
||||
@app.get("/")
|
||||
async def greet():
|
||||
await asyncio.sleep(1)
|
||||
return f"Hello, {request.args.get("name", "World")}!"
|
||||
|
||||
This starts an asyncio event loop in a gevent worker. Async functions are
|
||||
scheduled on that event loop. This may still have limitations, and may need to
|
||||
be modified further when using other asyncio implementations.
|
||||
|
||||
|
||||
libuv
|
||||
~~~~~
|
||||
|
||||
`libuv`_ is another event loop implementation that `gevent supports`_. There's
|
||||
also a project called `uvloop`_ that enables libuv in asyncio. If you want to
|
||||
use libuv, use gevent's support, not uvloop. It may be possible to further
|
||||
modify the ``async_to_sync`` code from the previous section to work with uvloop,
|
||||
but that's not currently known.
|
||||
|
||||
.. _libuv: https://libuv.org/
|
||||
.. _gevent supports: https://www.gevent.org/loop_impls.html
|
||||
.. _uvloop: https://uvloop.readthedocs.io/
|
||||
|
||||
To enable gevent's libuv support, add the following at the *very* top of your
|
||||
code, before ``gevent.monkey.patch_all()``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import gevent
|
||||
gevent.config.loop = "libuv"
|
||||
|
||||
import gevent.monkey
|
||||
gevent.monkey.patch_all()
|
||||
207
docs/htmlfaq.rst
Normal 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 `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 behaviour 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://www.diveintohtml5.org/
|
||||
|
||||
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.
|
||||
101
docs/index.rst
|
|
@ -1,89 +1,30 @@
|
|||
.. rst-class:: hide-header
|
||||
:orphan:
|
||||
|
||||
Welcome to Flask
|
||||
================
|
||||
|
||||
.. image:: _static/flask-name.svg
|
||||
:align: center
|
||||
:height: 200px
|
||||
.. image:: _static/logo-full.png
|
||||
:alt: Flask: web development, one drop at a time
|
||||
:class: floatingflask
|
||||
|
||||
Welcome to Flask's documentation. Flask is a lightweight WSGI web application framework.
|
||||
It is designed to make getting started quick and easy, with the ability to scale up to
|
||||
complex applications.
|
||||
Welcome to Flask's documentation. This documentation is divided into
|
||||
different parts. I recommend that you get started with
|
||||
:ref:`installation` and then head over to the :ref:`quickstart`.
|
||||
Besides the quickstart there is also a more detailed :ref:`tutorial` that
|
||||
shows how to create a complete (albeit small) application with Flask. If
|
||||
you'd rather dive into the internals of Flask, check out
|
||||
the :ref:`api` documentation. Common patterns are described in the
|
||||
:ref:`patterns` section.
|
||||
|
||||
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
|
||||
:doc:`patterns/index` section. The rest of the docs describe each
|
||||
component of Flask in detail, with a full reference in the :doc:`api`
|
||||
section.
|
||||
Flask depends on two external libraries: the `Jinja2`_ template
|
||||
engine and the `Werkzeug`_ WSGI toolkit. These libraries are not documented
|
||||
here. If you want to dive into their documentation check out the
|
||||
following links:
|
||||
|
||||
Flask depends on the `Werkzeug`_ WSGI toolkit, the `Jinja`_ template engine, and the
|
||||
`Click`_ CLI toolkit. Be sure to check their documentation as well as Flask's when
|
||||
looking for information.
|
||||
- `Jinja2 Documentation <http://jinja.pocoo.org/2/documentation/>`_
|
||||
- `Werkzeug Documentation <http://werkzeug.pocoo.org/documentation/>`_
|
||||
|
||||
.. _Werkzeug: https://werkzeug.palletsprojects.com
|
||||
.. _Jinja: https://jinja.palletsprojects.com
|
||||
.. _Click: https://click.palletsprojects.com
|
||||
.. _Jinja2: http://jinja.pocoo.org/2/
|
||||
.. _Werkzeug: http://werkzeug.pocoo.org/
|
||||
|
||||
|
||||
User's Guide
|
||||
------------
|
||||
|
||||
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
|
||||
.. include:: contents.rst.inc
|
||||
|
|
|
|||
|
|
@ -1,143 +1,175 @@
|
|||
.. _installation:
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
Flask depends on two external libraries, `Werkzeug
|
||||
<http://werkzeug.pocoo.org/>`_ and `Jinja2 <http://jinja.pocoo.org/2/>`_.
|
||||
Werkzeug is a toolkit for WSGI, the standard Python interface between web
|
||||
applications and a variety of servers for both development and deployment.
|
||||
Jinja2 renders templates.
|
||||
|
||||
Python Version
|
||||
--------------
|
||||
So how do you get all that on your computer quickly? There are many ways
|
||||
which this section will explain, but the most kick-ass method is
|
||||
virtualenv, so let's look at that first.
|
||||
|
||||
We recommend using the latest version of Python. Flask supports Python 3.10 and newer.
|
||||
Either way, you will need Python 2.5 or higher to get started, so be sure
|
||||
to have an up to date Python 2.x installation. At the time of writing,
|
||||
the WSGI specification is not yet finalized for Python 3, so Flask cannot
|
||||
support the 3.x series of Python.
|
||||
|
||||
.. _virtualenv:
|
||||
|
||||
virtualenv
|
||||
----------
|
||||
|
||||
Virtualenv is probably what you want to use during development, and in
|
||||
production too if you have shell access there.
|
||||
|
||||
What problem does virtualenv solve? If you like Python as I do,
|
||||
chances are you want to use it for other projects besides Flask-based
|
||||
web applications. But the more projects you have, the more likely it is
|
||||
that you will be working with different versions of Python itself, or at
|
||||
least different versions of Python libraries. Let's face it; quite often
|
||||
libraries break backwards compatibility, and it's unlikely that any serious
|
||||
application will have zero dependencies. So what do you do if two or more
|
||||
of your projects have conflicting dependencies?
|
||||
|
||||
Virtualenv to the rescue! It basically enables multiple side-by-side
|
||||
installations of Python, one for each project. It doesn't actually
|
||||
install separate copies of Python, but it does provide a clever way
|
||||
to keep different project environments isolated.
|
||||
|
||||
So let's see how virtualenv works!
|
||||
|
||||
If you are on Mac OS X or Linux, chances are that one of the following two
|
||||
commands will work for you::
|
||||
|
||||
$ sudo easy_install virtualenv
|
||||
|
||||
or even better::
|
||||
|
||||
$ sudo pip install virtualenv
|
||||
|
||||
One of these will probably install virtualenv on your system. Maybe it's
|
||||
even in your package manager. If you use Ubuntu, try::
|
||||
|
||||
$ sudo apt-get install python-virtualenv
|
||||
|
||||
If you are on Windows and don't have the `easy_install` command, you must
|
||||
install it first. Check the :ref:`windows-easy-install` section for more
|
||||
information about how to do that. Once you have it installed, run the
|
||||
same commands as above, but without the `sudo` prefix.
|
||||
|
||||
Once you have virtualenv installed, just fire up a shell and create
|
||||
your own environment. I usually create a project folder and an `env`
|
||||
folder within::
|
||||
|
||||
$ mkdir myproject
|
||||
$ cd myproject
|
||||
$ virtualenv env
|
||||
New python executable in env/bin/python
|
||||
Installing setuptools............done.
|
||||
|
||||
Now, whenever you want to work on a project, you only have to activate
|
||||
the corresponding environment. On OS X and Linux, do the following::
|
||||
|
||||
$ . env/bin/activate
|
||||
|
||||
(Note the space between the dot and the script name. The dot means that
|
||||
this script should run in the context of the current shell. If this command
|
||||
does not work in your shell, try replacing the dot with ``source``)
|
||||
|
||||
If you are a Windows user, the following command is for you::
|
||||
|
||||
$ env\scripts\activate
|
||||
|
||||
Either way, you should now be using your virtualenv (see how the prompt of
|
||||
your shell has changed to show the virtualenv).
|
||||
|
||||
Now you can just enter the following command to get Flask activated in
|
||||
your virtualenv::
|
||||
|
||||
$ easy_install Flask
|
||||
|
||||
A few seconds later you are good to go.
|
||||
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
System Wide Installation
|
||||
------------------------
|
||||
|
||||
These distributions will be installed automatically when installing Flask.
|
||||
This is possible as well, but I do not recommend it. Just run
|
||||
`easy_install` with root rights::
|
||||
|
||||
* `Werkzeug`_ implements WSGI, the standard Python interface between
|
||||
applications and servers.
|
||||
* `Jinja`_ is a template language that renders the pages your application
|
||||
serves.
|
||||
* `MarkupSafe`_ comes with Jinja. It escapes untrusted input when rendering
|
||||
templates to avoid injection attacks.
|
||||
* `ItsDangerous`_ securely signs data to ensure its integrity. This is used
|
||||
to protect Flask's session cookie.
|
||||
* `Click`_ is a framework for writing command line applications. It provides
|
||||
the ``flask`` command and allows adding custom management commands.
|
||||
* `Blinker`_ provides support for :doc:`signals`.
|
||||
$ sudo easy_install Flask
|
||||
|
||||
.. _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/
|
||||
(Run it in an Admin shell on Windows systems and without `sudo`).
|
||||
|
||||
|
||||
Optional dependencies
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
Living on the Edge
|
||||
------------------
|
||||
|
||||
These distributions will not be installed automatically. Flask will detect and
|
||||
use them if you install them.
|
||||
If you want to work with the latest version of Flask, there are two ways: you
|
||||
can either let `easy_install` pull in the development version, or tell it
|
||||
to operate on a git checkout. Either way, virtualenv is recommended.
|
||||
|
||||
* `python-dotenv`_ enables support for :ref:`dotenv` when running ``flask``
|
||||
commands.
|
||||
* `Watchdog`_ provides a faster, more efficient reloader for the development
|
||||
server.
|
||||
Get the git checkout in a new virtualenv and run in development mode::
|
||||
|
||||
.. _python-dotenv: https://github.com/theskumar/python-dotenv#readme
|
||||
.. _watchdog: https://pythonhosted.org/watchdog/
|
||||
$ git clone http://github.com/mitsuhiko/flask.git
|
||||
Initialized empty Git repository in ~/dev/flask/.git/
|
||||
$ cd flask
|
||||
$ virtualenv env
|
||||
$ . env/bin/activate
|
||||
New python executable in env/bin/python
|
||||
Installing setuptools............done.
|
||||
$ python setup.py develop
|
||||
...
|
||||
Finished processing dependencies for Flask
|
||||
|
||||
This will pull in the dependencies and activate the git head as the current
|
||||
version inside the virtualenv. Then you just have to ``git pull origin``
|
||||
to get the latest version.
|
||||
|
||||
To just get the development version without git, do this instead::
|
||||
|
||||
$ mkdir flask
|
||||
$ cd flask
|
||||
$ virtualenv env
|
||||
$ . env/bin/activate
|
||||
New python executable in env/bin/python
|
||||
Installing setuptools............done.
|
||||
$ easy_install Flask==dev
|
||||
...
|
||||
Finished processing dependencies for Flask==dev
|
||||
|
||||
.. _windows-easy-install:
|
||||
|
||||
`easy_install` on Windows
|
||||
-------------------------
|
||||
|
||||
On Windows, installation of `easy_install` is a little bit trickier because
|
||||
slightly different rules apply on Windows than on Unix-like systems, but
|
||||
it's not difficult. The easiest way to do it is to download the
|
||||
`ez_setup.py`_ file and run it. The easiest way to run the file is to
|
||||
open your downloads folder and double-click on the file.
|
||||
|
||||
Next, add the `easy_install` command and other Python scripts to the
|
||||
command search path, by adding your Python installation's Scripts folder
|
||||
to the `PATH` environment variable. To do that, right-click on the
|
||||
"Computer" icon on the Desktop or in the Start menu, and choose
|
||||
"Properties". Then, on Windows Vista and Windows 7 click on "Advanced System
|
||||
settings"; on Windows XP, click on the "Advanced" tab instead. Then click
|
||||
on the "Environment variables" button and double click on the "Path"
|
||||
variable in the "System variables" section. There append the path of your
|
||||
Python interpreter's Scripts folder; make sure you delimit it from
|
||||
existing values with a semicolon. Assuming you are using Python 2.6 on
|
||||
the default path, add the following value::
|
||||
|
||||
;C:\Python26\Scripts
|
||||
|
||||
Then you are done. To check that it worked, open the Command Prompt and
|
||||
execute ``easy_install``. If you have User Account Control enabled on
|
||||
Windows Vista or Windows 7, it should prompt you for admin privileges.
|
||||
|
||||
|
||||
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
|
||||
--------------------
|
||||
|
||||
Use a virtual environment to manage the dependencies for your project, both in
|
||||
development and in production.
|
||||
|
||||
What problem does a virtual environment solve? The more Python projects you
|
||||
have, the more likely it is that you need to work with different versions of
|
||||
Python libraries, or even Python itself. Newer versions of libraries for one
|
||||
project can break compatibility in another project.
|
||||
|
||||
Virtual environments are independent groups of Python libraries, one for each
|
||||
project. Packages installed for one project will not affect other projects or
|
||||
the operating system's packages.
|
||||
|
||||
Python comes bundled with the :mod:`venv` module to create virtual
|
||||
environments.
|
||||
|
||||
|
||||
.. _install-create-env:
|
||||
|
||||
Create an environment
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Create a project folder and a :file:`.venv` folder within:
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. group-tab:: macOS/Linux
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ mkdir myproject
|
||||
$ cd myproject
|
||||
$ python3 -m venv .venv
|
||||
|
||||
.. group-tab:: Windows
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
> mkdir myproject
|
||||
> cd myproject
|
||||
> py -3 -m venv .venv
|
||||
|
||||
|
||||
.. _install-activate-env:
|
||||
|
||||
Activate the environment
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Before you work on your project, activate the corresponding environment:
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. group-tab:: macOS/Linux
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ . .venv/bin/activate
|
||||
|
||||
.. group-tab:: Windows
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
> .venv\Scripts\activate
|
||||
|
||||
Your shell prompt will change to show the name of the activated
|
||||
environment.
|
||||
|
||||
|
||||
Install Flask
|
||||
-------------
|
||||
|
||||
Within the activated environment, use the following command to install
|
||||
Flask:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
$ pip install Flask
|
||||
|
||||
Flask is now installed. Check out the :doc:`/quickstart` or go to the
|
||||
:doc:`Documentation Overview </index>`.
|
||||
.. _ez_setup.py: http://peak.telecommunity.com/dist/ez_setup.py
|
||||
|
|
|
|||
6
docs/latexindex.rst
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
:orphan:
|
||||
|
||||
Flask Documentation
|
||||
===================
|
||||
|
||||
.. include:: contents.rst.inc
|
||||
|
|
@ -1,5 +1,48 @@
|
|||
BSD-3-Clause License
|
||||
====================
|
||||
License
|
||||
=======
|
||||
|
||||
.. literalinclude:: ../LICENSE.txt
|
||||
:language: text
|
||||
Flask is licensed under a three clause BSD License. It basically means:
|
||||
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 sourcecode 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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
183
docs/logging.rst
|
|
@ -1,183 +0,0 @@
|
|||
Logging
|
||||
=======
|
||||
|
||||
Flask uses standard Python :mod:`logging`. Messages about your Flask
|
||||
application are logged with :meth:`app.logger <flask.Flask.logger>`,
|
||||
which takes the same name as :attr:`app.name <flask.Flask.name>`. This
|
||||
logger can also be used to log your own messages.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@app.route('/login', methods=['POST'])
|
||||
def login():
|
||||
user = get_user(request.form['username'])
|
||||
|
||||
if user.check_password(request.form['password']):
|
||||
login_user(user)
|
||||
app.logger.info('%s logged in successfully', user.username)
|
||||
return redirect(url_for('index'))
|
||||
else:
|
||||
app.logger.info('%s failed to log in', user.username)
|
||||
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
|
||||
-------------------
|
||||
|
||||
When you want to configure logging for your project, you should do it as soon
|
||||
as possible when the program starts. If :meth:`app.logger <flask.Flask.logger>`
|
||||
is accessed before logging is configured, it will add a default handler. If
|
||||
possible, configure logging before creating the application object.
|
||||
|
||||
This example uses :func:`~logging.config.dictConfig` to create a logging
|
||||
configuration similar to Flask's default, except for all logs::
|
||||
|
||||
from logging.config import dictConfig
|
||||
|
||||
dictConfig({
|
||||
'version': 1,
|
||||
'formatters': {'default': {
|
||||
'format': '[%(asctime)s] %(levelname)s in %(module)s: %(message)s',
|
||||
}},
|
||||
'handlers': {'wsgi': {
|
||||
'class': 'logging.StreamHandler',
|
||||
'stream': 'ext://flask.logging.wsgi_errors_stream',
|
||||
'formatter': 'default'
|
||||
}},
|
||||
'root': {
|
||||
'level': 'INFO',
|
||||
'handlers': ['wsgi']
|
||||
}
|
||||
})
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
Default Configuration
|
||||
`````````````````````
|
||||
|
||||
If you do not configure logging yourself, Flask will add a
|
||||
:class:`~logging.StreamHandler` to :meth:`app.logger <flask.Flask.logger>`
|
||||
automatically. During requests, it will write to the stream specified by the
|
||||
WSGI server in ``environ['wsgi.errors']`` (which is usually
|
||||
:data:`sys.stderr`). Outside a request, it will log to :data:`sys.stderr`.
|
||||
|
||||
|
||||
Removing the Default Handler
|
||||
````````````````````````````
|
||||
|
||||
If you configured logging after accessing
|
||||
:meth:`app.logger <flask.Flask.logger>`, and need to remove the default
|
||||
handler, you can import and remove it::
|
||||
|
||||
from flask.logging import default_handler
|
||||
|
||||
app.logger.removeHandler(default_handler)
|
||||
|
||||
|
||||
Email Errors to Admins
|
||||
----------------------
|
||||
|
||||
When running the application on a remote server for production, you probably
|
||||
won't be looking at the log messages very often. The WSGI server will probably
|
||||
send log messages to a file, and you'll only check that file if a user tells
|
||||
you something went wrong.
|
||||
|
||||
To be proactive about discovering and fixing bugs, you can configure a
|
||||
:class:`logging.handlers.SMTPHandler` to send an email when errors and higher
|
||||
are logged. ::
|
||||
|
||||
import logging
|
||||
from logging.handlers import SMTPHandler
|
||||
|
||||
mail_handler = SMTPHandler(
|
||||
mailhost='127.0.0.1',
|
||||
fromaddr='server-error@example.com',
|
||||
toaddrs=['admin@example.com'],
|
||||
subject='Application Error'
|
||||
)
|
||||
mail_handler.setLevel(logging.ERROR)
|
||||
mail_handler.setFormatter(logging.Formatter(
|
||||
'[%(asctime)s] %(levelname)s in %(module)s: %(message)s'
|
||||
))
|
||||
|
||||
if not app.debug:
|
||||
app.logger.addHandler(mail_handler)
|
||||
|
||||
This requires that you have an SMTP server set up on the same server. See the
|
||||
Python docs for more information about configuring the handler.
|
||||
|
||||
|
||||
Injecting Request Information
|
||||
-----------------------------
|
||||
|
||||
Seeing more information about the request, such as the IP address, may help
|
||||
debugging some errors. You can subclass :class:`logging.Formatter` to inject
|
||||
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
|
||||
handler. ::
|
||||
|
||||
from flask import has_request_context, request
|
||||
from flask.logging import default_handler
|
||||
|
||||
class RequestFormatter(logging.Formatter):
|
||||
def format(self, record):
|
||||
if has_request_context():
|
||||
record.url = request.url
|
||||
record.remote_addr = request.remote_addr
|
||||
else:
|
||||
record.url = None
|
||||
record.remote_addr = None
|
||||
|
||||
return super().format(record)
|
||||
|
||||
formatter = RequestFormatter(
|
||||
'[%(asctime)s] %(remote_addr)s requested %(url)s\n'
|
||||
'%(levelname)s in %(module)s: %(message)s'
|
||||
)
|
||||
default_handler.setFormatter(formatter)
|
||||
mail_handler.setFormatter(formatter)
|
||||
|
||||
|
||||
Other Libraries
|
||||
---------------
|
||||
|
||||
Other libraries may use logging extensively, and you want to see relevant
|
||||
messages from those logs too. The simplest way to do this is to add handlers
|
||||
to the root logger instead of only the app logger. ::
|
||||
|
||||
from flask.logging import default_handler
|
||||
|
||||
root = logging.getLogger()
|
||||
root.addHandler(default_handler)
|
||||
root.addHandler(mail_handler)
|
||||
|
||||
Depending on your project, it may be more useful to configure each logger you
|
||||
care about separately, instead of configuring only the root logger. ::
|
||||
|
||||
for logger in (
|
||||
logging.getLogger(app.name),
|
||||
logging.getLogger('sqlalchemy'),
|
||||
logging.getLogger('other_package'),
|
||||
):
|
||||
logger.addHandler(default_handler)
|
||||
logger.addHandler(mail_handler)
|
||||
|
||||
|
||||
Werkzeug
|
||||
````````
|
||||
|
||||
Werkzeug logs basic request/response information to the ``'werkzeug'`` logger.
|
||||
If the root logger has no handlers configured, Werkzeug adds a
|
||||
:class:`~logging.StreamHandler` to its logger.
|
||||
|
||||
|
||||
Flask Extensions
|
||||
````````````````
|
||||
|
||||
Depending on the situation, an extension may choose to log to
|
||||
:meth:`app.logger <flask.Flask.logger>` or its own named logger. Consult each
|
||||
extension's documentation for details.
|
||||
BIN
docs/logo.pdf
Normal file
142
docs/make.bat
|
|
@ -1,35 +1,139 @@
|
|||
@ECHO OFF
|
||||
|
||||
pushd %~dp0
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set SOURCEDIR=.
|
||||
set BUILDDIR=_build
|
||||
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
|
||||
if NOT "%PAPER%" == "" (
|
||||
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
|
||||
)
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
%SPHINXBUILD% >NUL 2>NUL
|
||||
if errorlevel 9009 (
|
||||
echo.
|
||||
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||
echo.may add the Sphinx directory to PATH.
|
||||
echo.
|
||||
echo.If you don't have Sphinx installed, grab it from
|
||||
echo.http://sphinx-doc.org/
|
||||
exit /b 1
|
||||
if "%1" == "help" (
|
||||
:help
|
||||
echo.Please use `make ^<target^>` where ^<target^> is one of
|
||||
echo. html to make standalone HTML files
|
||||
echo. dirhtml to make HTML files named index.html in directories
|
||||
echo. singlehtml to make a single large HTML file
|
||||
echo. pickle to make pickle files
|
||||
echo. json to make JSON files
|
||||
echo. htmlhelp to make HTML files and a HTML help project
|
||||
echo. qthelp to make HTML files and a qthelp project
|
||||
echo. devhelp to make HTML files and a Devhelp project
|
||||
echo. epub to make an epub
|
||||
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
|
||||
echo. changes to make an overview over all changed/added/deprecated items
|
||||
echo. linkcheck to check all external links for integrity
|
||||
echo. doctest to run all doctests embedded in the documentation if enabled
|
||||
goto end
|
||||
)
|
||||
|
||||
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
goto end
|
||||
if "%1" == "clean" (
|
||||
for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
|
||||
del /q /s %BUILDDIR%\*
|
||||
goto end
|
||||
)
|
||||
|
||||
:help
|
||||
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
if "%1" == "html" (
|
||||
%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/html.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "dirhtml" (
|
||||
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "singlehtml" (
|
||||
%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "pickle" (
|
||||
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
|
||||
echo.
|
||||
echo.Build finished; now you can process the pickle files.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "json" (
|
||||
%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
|
||||
echo.
|
||||
echo.Build finished; now you can process the JSON files.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "htmlhelp" (
|
||||
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
|
||||
echo.
|
||||
echo.Build finished; now you can run HTML Help Workshop with the ^
|
||||
.hhp project file in %BUILDDIR%/htmlhelp.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "qthelp" (
|
||||
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
|
||||
echo.
|
||||
echo.Build finished; now you can run "qcollectiongenerator" with the ^
|
||||
.qhcp project file in %BUILDDIR%/qthelp, like this:
|
||||
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Flask.qhcp
|
||||
echo.To view the help file:
|
||||
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Flask.ghc
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "devhelp" (
|
||||
%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% _build/devhelp
|
||||
echo.
|
||||
echo.Build finished.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "epub" (
|
||||
%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
|
||||
echo.
|
||||
echo.Build finished. The epub file is in %BUILDDIR%/epub.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "latex" (
|
||||
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
|
||||
echo.
|
||||
echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "changes" (
|
||||
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
|
||||
echo.
|
||||
echo.The overview file is in %BUILDDIR%/changes.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "linkcheck" (
|
||||
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
|
||||
echo.
|
||||
echo.Link check complete; look for any errors in the above output ^
|
||||
or in %BUILDDIR%/linkcheck/output.txt.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "doctest" (
|
||||
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
|
||||
echo.
|
||||
echo.Testing of doctests in the sources finished, look at the ^
|
||||
results in %BUILDDIR%/doctest/output.txt.
|
||||
goto end
|
||||
)
|
||||
|
||||
:end
|
||||
popd
|
||||
|
|
|
|||
|
|
@ -1,36 +1,34 @@
|
|||
.. _app-dispatch:
|
||||
|
||||
Application Dispatching
|
||||
=======================
|
||||
|
||||
Application dispatching is the process of combining multiple Flask
|
||||
applications on the WSGI level. You can combine not only Flask
|
||||
applications but any WSGI application. This would allow you to run a
|
||||
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
|
||||
internally.
|
||||
applications on the WSGI level. You can not only combine Flask
|
||||
applications into something larger but any WSGI application. This would
|
||||
even allow you to run a 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 internally.
|
||||
|
||||
The fundamental difference from :doc:`packages` is that in this case you
|
||||
are running the same or different Flask applications that are entirely
|
||||
isolated from each other. They run different configurations and are
|
||||
dispatched on the WSGI level.
|
||||
The fundamental difference from the :ref:`module approach
|
||||
<larger-applications>` is that in this case you are running the same or
|
||||
different Flask applications that are entirely isolated from each other.
|
||||
They run different configurations and are dispatched on the WSGI level.
|
||||
|
||||
|
||||
Working with this Document
|
||||
--------------------------
|
||||
|
||||
Each of the techniques and examples below results in an ``application``
|
||||
object that can be run with any WSGI server. For development, use the
|
||||
``flask run`` command to start a development server. For production, see
|
||||
:doc:`/deploying/index`.
|
||||
Each of the techniques and examples below results in an ``application`` object
|
||||
that can be run with any WSGI server. For production, see :ref:`deployment`.
|
||||
For development, Werkzeug provides a builtin server for development available
|
||||
at :func:`werkzeug.serving.run_simple`::
|
||||
|
||||
.. code-block:: python
|
||||
from werkzeug.serving import run_simple
|
||||
run_simple('localhost', 5000, application, use_reloader=True)
|
||||
|
||||
from flask import Flask
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.route('/')
|
||||
def hello_world():
|
||||
return 'Hello World!'
|
||||
Note that :func:`run_simple <werkzeug.serving.run_simple>` is not intended for
|
||||
use in production. Use a :ref:`full-blown WSGI server <deployment>`.
|
||||
|
||||
|
||||
Combining Applications
|
||||
|
|
@ -40,20 +38,18 @@ If you have entirely separated applications and you want them to work next
|
|||
to each other in the same Python interpreter process you can take
|
||||
advantage of the :class:`werkzeug.wsgi.DispatcherMiddleware`. The idea
|
||||
here is that each Flask application is a valid WSGI application and they
|
||||
are combined by the dispatcher middleware into a larger one that is
|
||||
are combined by the dispatcher middleware into a larger one that
|
||||
dispatched based on prefix.
|
||||
|
||||
For example you could have your main application run on ``/`` and your
|
||||
backend interface on ``/backend``.
|
||||
For example you could have your main application run on `/` and your
|
||||
backend interface on `/backend`::
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from werkzeug.middleware.dispatcher import DispatcherMiddleware
|
||||
from werkzeug.wsgi import DispatcherMiddleware
|
||||
from frontend_app import application as frontend
|
||||
from backend_app import application as backend
|
||||
|
||||
application = DispatcherMiddleware(frontend, {
|
||||
'/backend': backend
|
||||
'/backend': backend
|
||||
})
|
||||
|
||||
|
||||
|
|
@ -62,10 +58,10 @@ Dispatch by Subdomain
|
|||
|
||||
Sometimes you might want to use multiple instances of the same application
|
||||
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 instanciate it, that is
|
||||
really easy to implement. In order to develop your application to support
|
||||
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
|
||||
instance you configure your webserver to dispatch all requests for all
|
||||
|
|
@ -76,14 +72,12 @@ the dynamic application creation.
|
|||
|
||||
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
|
||||
delegates it to your Flask application. If that application does not
|
||||
exist yet, it is dynamically created and remembered.
|
||||
|
||||
.. code-block:: python
|
||||
and delegates it to your Flask application. If that application does not
|
||||
exist yet, it is dynamically created and remembered::
|
||||
|
||||
from threading import Lock
|
||||
|
||||
class SubdomainDispatcher:
|
||||
class SubdomainDispatcher(object):
|
||||
|
||||
def __init__(self, domain, create_app):
|
||||
self.domain = domain
|
||||
|
|
@ -107,9 +101,7 @@ exist yet, it is dynamically created and remembered.
|
|||
return app(environ, start_response)
|
||||
|
||||
|
||||
This dispatcher can then be used like this:
|
||||
|
||||
.. code-block:: python
|
||||
This dispatcher can then be used like this::
|
||||
|
||||
from myapplication import create_app, get_user_for_subdomain
|
||||
from werkzeug.exceptions import NotFound
|
||||
|
|
@ -134,15 +126,13 @@ Dispatch by Path
|
|||
----------------
|
||||
|
||||
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
|
||||
request path up to the first slash.
|
||||
|
||||
.. code-block:: python
|
||||
the `Host` header to figure out the subdomain one simply looks at the
|
||||
request path up to the first slash::
|
||||
|
||||
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):
|
||||
self.default_app = default_app
|
||||
|
|
@ -160,24 +150,15 @@ request path up to the first slash.
|
|||
return app
|
||||
|
||||
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:
|
||||
shift_path_info(environ)
|
||||
pop_path_info(environ)
|
||||
else:
|
||||
app = self.default_app
|
||||
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
|
||||
falls back to another application if the creator function returns ``None``.
|
||||
|
||||
.. code-block:: python
|
||||
falls back to another application if the creator function returns `None`::
|
||||
|
||||
from myapplication import create_app, default_app, get_user_for_prefix
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
.. _app-factories:
|
||||
|
||||
Application Factories
|
||||
=====================
|
||||
|
||||
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 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.
|
||||
the blueprint is imported. But if you move the creation of this object,
|
||||
into a function, you can then create multiple instances of this and later.
|
||||
|
||||
So why would you want to do this?
|
||||
|
||||
|
|
@ -28,9 +30,6 @@ The idea is to set up the application in a function. Like this::
|
|||
app = Flask(__name__)
|
||||
app.config.from_pyfile(config_filename)
|
||||
|
||||
from yourapplication.model import db
|
||||
db.init_app(app)
|
||||
|
||||
from yourapplication.views.admin import admin
|
||||
from yourapplication.views.frontend import frontend
|
||||
app.register_blueprint(admin)
|
||||
|
|
@ -52,67 +51,25 @@ get access to the application with the config? Use
|
|||
|
||||
Here we look up the name of a template in the config.
|
||||
|
||||
Factories & Extensions
|
||||
----------------------
|
||||
|
||||
It's preferable to create your extensions and app factories so that the
|
||||
extension object does not initially get bound to the application.
|
||||
|
||||
Using `Flask-SQLAlchemy <https://flask-sqlalchemy.palletsprojects.com/>`_,
|
||||
as an example, you should not do something along those lines::
|
||||
|
||||
def create_app(config_filename):
|
||||
app = Flask(__name__)
|
||||
app.config.from_pyfile(config_filename)
|
||||
|
||||
db = SQLAlchemy(app)
|
||||
|
||||
But, rather, in model.py (or equivalent)::
|
||||
|
||||
db = SQLAlchemy()
|
||||
|
||||
and in your application.py (or equivalent)::
|
||||
|
||||
def create_app(config_filename):
|
||||
app = Flask(__name__)
|
||||
app.config.from_pyfile(config_filename)
|
||||
|
||||
from yourapplication.model import db
|
||||
db.init_app(app)
|
||||
|
||||
Using this design pattern, no application-specific state is stored on the
|
||||
extension object, so one extension object can be used for multiple apps.
|
||||
For more information about the design of extensions refer to :doc:`/extensiondev`.
|
||||
|
||||
Using Applications
|
||||
------------------
|
||||
|
||||
To run such an application, you can use the :command:`flask` command:
|
||||
So to use such an application you then have to create the application
|
||||
first. Here an example `run.py` file that runs such an application::
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ flask --app hello run
|
||||
|
||||
Flask will automatically detect the factory if it is named
|
||||
``create_app`` or ``make_app`` in ``hello``. You can also pass arguments
|
||||
to the factory like this:
|
||||
|
||||
.. 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.
|
||||
from yourapplication import create_app
|
||||
app = create_app('/path/to/config.cfg')
|
||||
app.run()
|
||||
|
||||
Factory Improvements
|
||||
--------------------
|
||||
|
||||
The factory function above is not very clever, but you can improve it.
|
||||
The following changes are straightforward to implement:
|
||||
The factory function from above is not very clever so far, you can improve
|
||||
it. The following changes are straightforward and possible:
|
||||
|
||||
1. Make it possible to pass in configuration values for unit tests so that
|
||||
you don't have to create config files on the filesystem.
|
||||
2. Call a function from a blueprint when the application is setting up so
|
||||
1. make it possible to pass in configuration values for unittests so that
|
||||
you don't have to create config files on the filesystem
|
||||
2. call a function from a blueprint when the application is setting up so
|
||||
that you have a place to modify attributes of the application (like
|
||||
hooking in before/after request handlers etc.)
|
||||
3. Add in WSGI middlewares when the application is being created if necessary.
|
||||
hooking in before / after request handlers etc.)
|
||||
3. Add in WSGI middlewares when the application is creating if necessary.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
.. _caching-pattern:
|
||||
|
||||
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
|
||||
time.
|
||||
|
||||
Flask itself does not provide caching for you, but `Flask-Caching`_, an
|
||||
extension for Flask does. Flask-Caching supports various backends, and it is
|
||||
even possible to develop your own caching backend.
|
||||
Flask itself does not provide caching for you, but Werkzeug, one of the
|
||||
libraries it is based on, has some very basic cache support. It supports
|
||||
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 <http://pypi.python.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
|
||||
|
|
|
|||
|
|
@ -1,242 +0,0 @@
|
|||
Background Tasks with Celery
|
||||
============================
|
||||
|
||||
If your application has a long running task, such as processing some uploaded data or
|
||||
sending email, you don't want to wait for it to finish during a request. Instead, use a
|
||||
task queue to send the necessary data to another 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.
|
||||
|
||||
|
||||
Install
|
||||
-------
|
||||
|
||||
Install Celery from PyPI, for example using pip:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ pip install celery
|
||||
|
||||
|
||||
Integrate Celery with Flask
|
||||
---------------------------
|
||||
|
||||
You can use Celery without any integration with Flask, but it's convenient to configure
|
||||
it through Flask's config, and to let tasks access the Flask application.
|
||||
|
||||
Celery uses similar ideas to Flask, with a ``Celery`` app object that has configuration
|
||||
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, Task
|
||||
|
||||
def celery_init_app(app: Flask) -> Celery:
|
||||
class FlaskTask(Task):
|
||||
def __call__(self, *args: object, **kwargs: object) -> object:
|
||||
with app.app_context():
|
||||
return self.run(*args, **kwargs)
|
||||
|
||||
celery_app = Celery(app.name, task_cls=FlaskTask)
|
||||
celery_app.config_from_object(app.config["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 ``CELERY`` key in the Flask configuration. The Celery app is set as the default, so
|
||||
that it is seen during each request. The ``Task`` subclass automatically runs task
|
||||
functions with a Flask app context active, so that services like your database
|
||||
connections are available.
|
||||
|
||||
.. _configuration: https://celery.readthedocs.io/en/stable/userguide/configuration.html
|
||||
|
||||
Here's a basic ``example.py`` that configures Celery to use Redis for communication. We
|
||||
enable a result backend, but ignore results by default. This allows us to store results
|
||||
only for tasks where we care about the result.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import Flask
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config.from_mapping(
|
||||
CELERY=dict(
|
||||
broker_url="redis://localhost",
|
||||
result_backend="redis://localhost",
|
||||
task_ignore_result=True,
|
||||
),
|
||||
)
|
||||
celery_app = celery_init_app(app)
|
||||
|
||||
Point the ``celery worker`` command at this and it will find the ``celery_app`` object.
|
||||
|
||||
.. 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
|
||||
|
||||
Earlier, we configured Celery to ignore task results by default. Since we want to know
|
||||
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.
|
||||
|
||||
|
||||
Calling Tasks
|
||||
-------------
|
||||
|
||||
The decorated function becomes a task object with methods to call it in the background.
|
||||
The simplest way is to use the ``delay(*args, **kwargs)`` method. See Celery's docs for
|
||||
more methods.
|
||||
|
||||
A Celery worker must be running to run the task. Starting a worker is shown in the
|
||||
previous sections.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import request
|
||||
|
||||
@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)
|
||||
|
|
@ -1,44 +1,73 @@
|
|||
.. _deferred-callbacks:
|
||||
|
||||
Deferred Request Callbacks
|
||||
==========================
|
||||
|
||||
One of the design principles of Flask is that response objects are created and
|
||||
passed down a chain of potential callbacks that can modify them or replace
|
||||
them. When the request handling starts, there is no response object yet. It is
|
||||
created as necessary either by a view function or by some other component in
|
||||
the system.
|
||||
One of the design principles of Flask is that response objects are created
|
||||
and passed down a chain of potential callbacks that can modify them or
|
||||
replace them. When the request handling starts, there is no response
|
||||
object yet. It is created as necessary either by a view function or by
|
||||
some other component in the system.
|
||||
|
||||
What happens if you want to modify the response at a point where the response
|
||||
does not exist yet? A common example for that would be a
|
||||
:meth:`~flask.Flask.before_request` callback that wants to set a cookie on the
|
||||
response object.
|
||||
But what happens if you want to modify the response at a point where the
|
||||
response does not exist yet? A common example for that would be a
|
||||
before-request function that wants to set a cookie on the response object.
|
||||
|
||||
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`
|
||||
callback instead. However, sometimes moving code there makes it
|
||||
more complicated or awkward to reason about.
|
||||
One way is to avoid the situation. Very often that is possible. For
|
||||
instance you can try to move that logic into an after-request callback
|
||||
instead. Sometimes however moving that code there is just not a very
|
||||
pleasant experience or makes code look very awkward.
|
||||
|
||||
As an alternative, you can use :func:`~flask.after_this_request` to register
|
||||
callbacks that will execute after only the current request. This way you can
|
||||
defer code execution from anywhere in the application, based on the current
|
||||
request.
|
||||
As an alternative possibility you can attach a bunch of callback functions
|
||||
to the :data:`~flask.g` object and call then at the end of the request.
|
||||
This way you can defer code execution from anywhere in the application.
|
||||
|
||||
At any time during a request, we can register a function to be called at the
|
||||
end of the request. For example you can remember the current language of the
|
||||
user in a cookie in a :meth:`~flask.Flask.before_request` callback::
|
||||
|
||||
from flask import request, after_this_request
|
||||
The Decorator
|
||||
-------------
|
||||
|
||||
The following decorator is the key. It registers a function on a list on
|
||||
the :data:`~flask.g` object::
|
||||
|
||||
from flask import g
|
||||
|
||||
def after_this_request(f):
|
||||
if not hasattr(g, 'after_request_callbacks'):
|
||||
g.after_request_callbacks = []
|
||||
g.after_request_callbacks.append(f)
|
||||
return f
|
||||
|
||||
|
||||
Calling the Deferred
|
||||
--------------------
|
||||
|
||||
Now you can use the `after_this_request` decorator to mark a function to
|
||||
be called at the end of the request. But we still need to call them. For
|
||||
this the following function needs to be registered as
|
||||
:meth:`~flask.Flask.after_request` callback::
|
||||
|
||||
@app.after_request
|
||||
def call_after_request_callbacks(response):
|
||||
for callback in getattr(g, 'after_request_callbacks', ()):
|
||||
response = callback(response)
|
||||
return response
|
||||
|
||||
|
||||
A Practical Example
|
||||
-------------------
|
||||
|
||||
Now we can easily at any point in time register a function to be called at
|
||||
the end of this particular request. For example you can remember the
|
||||
current language of the user in a cookie in the before-request function::
|
||||
|
||||
from flask import request
|
||||
|
||||
@app.before_request
|
||||
def detect_user_language():
|
||||
language = request.cookies.get('user_lang')
|
||||
|
||||
if language is None:
|
||||
language = guess_language_from_request()
|
||||
|
||||
# when the response exists, set a cookie with the language
|
||||
@after_this_request
|
||||
def remember_language(response):
|
||||
response.set_cookie('user_lang', language)
|
||||
return response
|
||||
|
||||
g.language = language
|
||||
|
|
|
|||
166
docs/patterns/distribute.rst
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
.. _distribute-deployment:
|
||||
|
||||
Deploying with Distribute
|
||||
=========================
|
||||
|
||||
`distribute`_, formerly setuptools, is an extension library that is
|
||||
commonly used to (like the name says) 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" another package can hook into to extend the
|
||||
other package.
|
||||
- **installation manager**: `easy_install`, which comes with distribute
|
||||
can install other libraries for you. You can also use `pip`_ which
|
||||
sooner or later will replace `easy_install` which does more than just
|
||||
installing packages for you.
|
||||
|
||||
Flask itself, and all the libraries you can find on the cheeseshop
|
||||
are distributed with either distribute, the older setuptools or distutils.
|
||||
|
||||
In this case we assume your application is called
|
||||
`yourapplication.py` and you are not using a module, but a :ref:`package
|
||||
<larger-applications>`. Distributing resources with standard modules is
|
||||
not supported by `distribute`_ so we will not bother with it. 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 distribute 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 running, you either have setuptools or distribute
|
||||
available on your system anyways. If you do not, fear not, there is a
|
||||
script to install it for you: `distribute_setup.py`_. Just download and
|
||||
run with your Python interpreter.
|
||||
|
||||
Standard disclaimer applies: :ref:`you better use a virtualenv
|
||||
<virtualenv>`.
|
||||
|
||||
Your setup code always goes into a file named `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.
|
||||
|
||||
Yes, even if you are using `distribute`, you are importing from a package
|
||||
called `setuptools`. `distribute` is fully backwards compatible with
|
||||
`setuptools`, so it also uses the same import name.
|
||||
|
||||
A basic `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 distribute 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 distribute to look for a `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.
|
||||
|
||||
|
||||
.. _distributing-resources:
|
||||
|
||||
Distributing Resources
|
||||
----------------------
|
||||
|
||||
If you try to install the package you just created, you will notice that
|
||||
folders like `static` or `templates` are not installed for you. The
|
||||
reason for this is that distribute does not know which files to add for
|
||||
you. What you should do, is to create a `MANIFEST.in` file next to your
|
||||
`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 `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 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'
|
||||
]
|
||||
|
||||
I mentioned earlier that 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 still do as if there was a PyPI entry for it and provide a list of
|
||||
alternative locations where distribute 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 distribute will find the files. If you have an internal company
|
||||
server that contains the packages, provide the URL to that server there.
|
||||
|
||||
|
||||
Installing / Developing
|
||||
-----------------------
|
||||
|
||||
To install your application (ideally into a virtualenv) just run the
|
||||
`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.
|
||||
|
||||
|
||||
.. _distribute: http://pypi.python.org/pypi/distribute
|
||||
.. _pip: http://pypi.python.org/pypi/pip
|
||||
.. _distribute_setup.py: http://python-distribute.org/distribute_setup.py
|
||||
77
docs/patterns/errorpages.rst
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
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 terrible good idea 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, just like a view function, but it is
|
||||
called when an error happens and is passed that error. The error is most
|
||||
likely a :exc:`~werkzeug.exceptions.HTTPException`, but in one case it
|
||||
can be a different error: a handler for internal server errors will be
|
||||
passed other exception instances as well if they are uncaught.
|
||||
|
||||
An error handler is registered with the :meth:`~flask.Flask.errorhandler`
|
||||
decorator and the error code of the exception. Keep in mind that Flask
|
||||
will *not* set the error code for you, so make sure to also provide the
|
||||
HTTP status code when returning a response.
|
||||
|
||||
Here an example implementation for a "404 Page Not Found" exception::
|
||||
|
||||
from flask import render_template
|
||||
|
||||
@app.errorhandler(404)
|
||||
def page_not_found(e):
|
||||
return render_template('404.html'), 404
|
||||
|
||||
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 %}
|
||||
196
docs/patterns/fabric.rst
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
.. _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
|
||||
`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 `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
|
||||
sourcecode 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():
|
||||
# create a new source distribution as tarball
|
||||
local('python setup.py sdist --formats=gztar', capture=False)
|
||||
|
||||
def deploy():
|
||||
# figure out the release name and version
|
||||
dist = local('python setup.py --fullname', capture=True).strip()
|
||||
# upload the source tarball to the temporary folder on the server
|
||||
put('dist/%s.tar.gz' % dist, '/tmp/yourapplication.tar.gz')
|
||||
# create a place where we can unzip the tarball, then enter
|
||||
# that directory and unzip it
|
||||
run('mkdir /tmp/yourapplication')
|
||||
with cd('/tmp/yourapplication'):
|
||||
run('tar xzf /tmp/yourapplication.tar.gz')
|
||||
# now setup the package with our virtual environment's
|
||||
# python interpreter
|
||||
run('/var/www/yourapplication/env/bin/python setup.py install')
|
||||
# now that all is set up, delete the folder again
|
||||
run('rm -rf /tmp/yourapplication /tmp/yourapplication.tar.gz')
|
||||
# and finally touch the .wsgi file so that mod_wsgi triggers
|
||||
# a reload of the application
|
||||
run('touch /var/www/yourapplication.wsgi')
|
||||
|
||||
The example above is well documented and should be straightforward. Here
|
||||
a recap of the most common commands fabric provides:
|
||||
|
||||
- `run` - executes a command on a remote server
|
||||
- `local` - executes a command on the local machine
|
||||
- `put` - uploads a file to the remote server
|
||||
- `cd` - changes the directory on the serverside. This has to be used
|
||||
in combination with the `with` statement.
|
||||
|
||||
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
|
||||
``/var/www/yourapplication`` folder created and
|
||||
``/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 ``/var/www``::
|
||||
|
||||
$ mkdir /var/www/yourapplication
|
||||
$ cd /var/www/yourapplication
|
||||
$ virtualenv --distribute env
|
||||
|
||||
2. Upload a new `application.wsgi` file to the server and the
|
||||
configuration file for the application (eg: `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 `application.wsgi` and
|
||||
`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: ``/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 `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 testsuite.
|
||||
|
||||
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://fabfile.org/
|
||||
|
|
@ -4,7 +4,7 @@ Adding a favicon
|
|||
A "favicon" is an icon used by browsers for tabs and bookmarks. This helps
|
||||
to distinguish your website and to give it a unique brand.
|
||||
|
||||
A common question is how to add a favicon to a Flask application. First, of
|
||||
A common question is how to add a favicon to a flask application. First, of
|
||||
course, you need an icon. It should be 16 × 16 pixels and in the ICO file
|
||||
format. This is not a requirement but a de-facto standard supported by all
|
||||
relevant browsers. Put the icon in your static directory as
|
||||
|
|
@ -20,15 +20,12 @@ tag in your HTML. So, for example:
|
|||
That's all you need for most browsers, however some really old ones do not
|
||||
support this standard. The old de-facto standard is to serve this file,
|
||||
with this name, at the website root. If your application is not mounted at
|
||||
the root path of the domain you either need to configure the web server to
|
||||
the root path of the domain you either need to configure the webserver to
|
||||
serve the icon at the root or if you can't do that you're out of luck. If
|
||||
however your application is the root you can simply route a redirect::
|
||||
|
||||
app.add_url_rule(
|
||||
"/favicon.ico",
|
||||
endpoint="favicon",
|
||||
redirect_to=url_for("static", filename="favicon.ico"),
|
||||
)
|
||||
app.add_url_rule('/favicon.ico',
|
||||
redirect_to=url_for('static', filename='favicon.ico'))
|
||||
|
||||
If you want to save the extra redirect request you can also write a view
|
||||
using :func:`~flask.send_from_directory`::
|
||||
|
|
@ -47,10 +44,10 @@ same.
|
|||
|
||||
The above will serve the icon via your application and if possible it's
|
||||
better to configure your dedicated web server to serve it; refer to the
|
||||
web server's documentation.
|
||||
webserver's documentation.
|
||||
|
||||
See also
|
||||
--------
|
||||
|
||||
* The `Favicon <https://en.wikipedia.org/wiki/Favicon>`_ article on
|
||||
* The `Favicon <http://en.wikipedia.org/wiki/Favicon>`_ article on
|
||||
Wikipedia
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
.. _uploading-files:
|
||||
|
||||
Uploading Files
|
||||
===============
|
||||
|
||||
|
|
@ -19,58 +21,54 @@ specific upload folder and displays a file to the user. Let's look at the
|
|||
bootstrapping code for our application::
|
||||
|
||||
import os
|
||||
from flask import Flask, flash, request, redirect, url_for
|
||||
from werkzeug.utils import secure_filename
|
||||
from flask import Flask, request, redirect, url_for
|
||||
from werkzeug import secure_filename
|
||||
|
||||
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.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
|
||||
|
||||
So first we need a couple of imports. Most should be straightforward, the
|
||||
:func:`werkzeug.secure_filename` is explained a little bit later. The
|
||||
``UPLOAD_FOLDER`` is where we will store the uploaded files and the
|
||||
``ALLOWED_EXTENSIONS`` is the set of allowed file extensions.
|
||||
`UPLOAD_FOLDER` is where we will store the uploaded files and the
|
||||
`ALLOWED_EXTENSIONS` is the set of allowed file extensions. Then we add a
|
||||
URL rule by hand to the application. Now usually we're not doing that, so
|
||||
why here? The reasons is that we want the webserver (or our development
|
||||
server) to serve these files for us and so we only need a rule to generate
|
||||
the URL to these files.
|
||||
|
||||
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
|
||||
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
|
||||
:ref:`security-xss`). Also make sure to disallow ``.php`` files if the server
|
||||
executes them, but who has PHP installed on their server, right? :)
|
||||
:ref:`xss`). Also make sure to disallow `.php` files if the server
|
||||
executes them, but who has PHP installed on his server, right? :)
|
||||
|
||||
Next the functions that check if an extension is valid and that uploads
|
||||
the file and redirects the user to the URL for the uploaded file::
|
||||
|
||||
def allowed_file(filename):
|
||||
return '.' in filename and \
|
||||
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
|
||||
filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS
|
||||
|
||||
@app.route('/', methods=['GET', 'POST'])
|
||||
def upload_file():
|
||||
if request.method == 'POST':
|
||||
# check if the post request has the file part
|
||||
if 'file' not in request.files:
|
||||
flash('No file part')
|
||||
return redirect(request.url)
|
||||
file = request.files['file']
|
||||
# If the user does not select a file, the browser submits an
|
||||
# empty file without a filename.
|
||||
if file.filename == '':
|
||||
flash('No selected file')
|
||||
return redirect(request.url)
|
||||
if file and allowed_file(file.filename):
|
||||
filename = secure_filename(file.filename)
|
||||
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
|
||||
return redirect(url_for('download_file', name=filename))
|
||||
return redirect(url_for('uploaded_file',
|
||||
filename=filename))
|
||||
return '''
|
||||
<!doctype html>
|
||||
<title>Upload new File</title>
|
||||
<h1>Upload new File</h1>
|
||||
<form method=post enctype=multipart/form-data>
|
||||
<input type=file name=file>
|
||||
<input type=submit value=Upload>
|
||||
<form action="" method=post enctype=multipart/form-data>
|
||||
<p><input type=file name=file>
|
||||
<input type=submit value=Upload>
|
||||
</form>
|
||||
'''
|
||||
|
||||
|
|
@ -91,7 +89,7 @@ before storing it directly on the filesystem.
|
|||
filename = "../../../../home/username/.bashrc"
|
||||
|
||||
Assuming the number of ``../`` is correct and you would join this with
|
||||
the ``UPLOAD_FOLDER`` the user might have the ability to modify a file on
|
||||
the `UPLOAD_FOLDER` the user might have the ability to modify a file on
|
||||
the server's filesystem he or she should not modify. This does require some
|
||||
knowledge about how the application looks like, but trust me, hackers
|
||||
are patient :)
|
||||
|
|
@ -101,28 +99,28 @@ before storing it directly on the filesystem.
|
|||
>>> secure_filename('../../../../home/username/.bashrc')
|
||||
'home_username_.bashrc'
|
||||
|
||||
We want to be able to serve the uploaded files so they can be downloaded
|
||||
by users. We'll define a ``download_file`` view to serve files in the
|
||||
upload folder by name. ``url_for("download_file", name=name)`` generates
|
||||
download URLs.
|
||||
|
||||
.. code-block:: python
|
||||
Now one last thing is missing: the serving of the uploaded files. As of
|
||||
Flask 0.5 we can use a function that does that for us::
|
||||
|
||||
from flask import send_from_directory
|
||||
|
||||
@app.route('/uploads/<name>')
|
||||
def download_file(name):
|
||||
return send_from_directory(app.config["UPLOAD_FOLDER"], name)
|
||||
@app.route('/uploads/<filename>')
|
||||
def uploaded_file(filename):
|
||||
return send_from_directory(app.config['UPLOAD_FOLDER'],
|
||||
filename)
|
||||
|
||||
If you're using middleware or the HTTP server to serve files, you can
|
||||
register the ``download_file`` endpoint as ``build_only`` so ``url_for``
|
||||
will work without a view function.
|
||||
Alternatively you can register `uploaded_file` as `build_only` rule and
|
||||
use the :class:`~werkzeug.wsgi.SharedDataMiddleware`. This also works with
|
||||
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(
|
||||
"/uploads/<name>", endpoint="download_file", build_only=True
|
||||
)
|
||||
If you now run the application everything should work as expected.
|
||||
|
||||
|
||||
Improving Uploads
|
||||
|
|
@ -131,28 +129,22 @@ Improving Uploads
|
|||
.. versionadded:: 0.6
|
||||
|
||||
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
|
||||
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``
|
||||
config key::
|
||||
|
||||
from flask import Flask, Request
|
||||
|
||||
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.
|
||||
If a larger file is transmitted, Flask will raise a
|
||||
The code above will limited the maximum allowed payload to 16 megabytes.
|
||||
If a larger file is transmitted, Flask will raise an
|
||||
:exc:`~werkzeug.exceptions.RequestEntityTooLarge` exception.
|
||||
|
||||
.. admonition:: Connection Reset Issue
|
||||
|
||||
When using the local development server, you may get a connection
|
||||
reset error instead of a 413 response. You will get the correct
|
||||
status response when running the app with a production WSGI server.
|
||||
|
||||
This feature was added in Flask 0.6 but can be achieved in older versions
|
||||
as well by subclassing the request object. For more information on that
|
||||
consult the Werkzeug documentation on file handling.
|
||||
|
|
@ -163,20 +155,27 @@ Upload Progress Bars
|
|||
|
||||
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
|
||||
poll the progress with JavaScript from the client. The client asks the
|
||||
server every 5 seconds how much it has transmitted, but this is
|
||||
something it should already know.
|
||||
poll the progress with JavaScript from the client. Long story short: the
|
||||
client asks the server every 5 seconds how much it has transmitted
|
||||
already. Do you realize the irony? The client is asking for something it
|
||||
should already know.
|
||||
|
||||
Now there are better solutions to that work faster and more reliable. The
|
||||
web changed a lot lately and you can use HTML5, Java, Silverlight or Flash
|
||||
to get a nicer uploading experience on the client side. Look at the
|
||||
following libraries for some nice examples how to do that:
|
||||
|
||||
- `Plupload <http://www.plupload.com/>`_ - HTML5, Java, Flash
|
||||
- `SWFUpload <http://www.swfupload.org/>`_ - Flash
|
||||
- `JumpLoader <http://jumploader.com/>`_ - Java
|
||||
|
||||
|
||||
An Easier Solution
|
||||
------------------
|
||||
|
||||
Now there are better solutions that work faster and are more reliable. There
|
||||
are JavaScript libraries like jQuery_ that have form plugins to ease the
|
||||
construction of progress bar.
|
||||
|
||||
Because the common pattern for file uploads exists almost unchanged in all
|
||||
applications dealing with uploads, there are also some Flask extensions that
|
||||
implement a full fledged upload mechanism that allows controlling which
|
||||
file extensions are allowed to be uploaded.
|
||||
applications dealing with uploads, there is a Flask extension called
|
||||
`Flask-Uploads`_ that implements a full fledged upload mechanism with
|
||||
white and blacklisting of extensions and more.
|
||||
|
||||
.. _jQuery: https://jquery.com/
|
||||
.. _Flask-Uploads: http://packages.python.org/Flask-Uploads/
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
.. _message-flashing-pattern:
|
||||
|
||||
Message Flashing
|
||||
================
|
||||
|
||||
|
|
@ -7,20 +9,14 @@ application. Flask provides a really simple way to give feedback to a
|
|||
user with the flashing system. The flashing system basically makes it
|
||||
possible to record a message at the end of a request and access it next
|
||||
request and only next request. This is usually combined with a layout
|
||||
template that does this. Note that browsers and sometimes web servers enforce
|
||||
a limit on cookie sizes. This means that flashing messages that are too
|
||||
large for session cookies causes message flashing to fail silently.
|
||||
template that does this.
|
||||
|
||||
Simple Flashing
|
||||
---------------
|
||||
|
||||
So here is a full example::
|
||||
|
||||
from flask import Flask, flash, redirect, render_template, \
|
||||
request, url_for
|
||||
|
||||
app = Flask(__name__)
|
||||
app.secret_key = b'_5#y2L"F4Q8z\n\xec]/'
|
||||
from flask import flash, redirect, url_for, render_template
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
|
|
@ -31,14 +27,14 @@ So here is a full example::
|
|||
error = None
|
||||
if request.method == 'POST':
|
||||
if request.form['username'] != 'admin' or \
|
||||
request.form['password'] != 'secret':
|
||||
request.form['password'] != 'secret':
|
||||
error = 'Invalid credentials'
|
||||
else:
|
||||
flash('You were successfully logged in')
|
||||
return redirect(url_for('index'))
|
||||
return render_template('login.html', error=error)
|
||||
|
||||
And here is the :file:`layout.html` template which does the magic:
|
||||
And here the ``layout.html`` template which does the magic:
|
||||
|
||||
.. sourcecode:: html+jinja
|
||||
|
||||
|
|
@ -55,7 +51,7 @@ And here is the :file:`layout.html` template which does the magic:
|
|||
{% endwith %}
|
||||
{% block body %}{% endblock %}
|
||||
|
||||
Here is the :file:`index.html` template which inherits from :file:`layout.html`:
|
||||
And here the index.html template:
|
||||
|
||||
.. sourcecode:: html+jinja
|
||||
|
||||
|
|
@ -65,8 +61,7 @@ Here is the :file:`index.html` template which inherits from :file:`layout.html`:
|
|||
<p>Do you want to <a href="{{ url_for('login') }}">log in?</a>
|
||||
{% endblock %}
|
||||
|
||||
And here is the :file:`login.html` template which also inherits from
|
||||
:file:`layout.html`:
|
||||
And of course the login template:
|
||||
|
||||
.. sourcecode:: html+jinja
|
||||
|
||||
|
|
@ -76,7 +71,7 @@ And here is the :file:`login.html` template which also inherits from
|
|||
{% if error %}
|
||||
<p class=error><strong>Error:</strong> {{ error }}
|
||||
{% endif %}
|
||||
<form method=post>
|
||||
<form action="" method=post>
|
||||
<dl>
|
||||
<dt>Username:
|
||||
<dd><input type=text name=username value="{{
|
||||
|
|
@ -101,7 +96,7 @@ error messages could be displayed with a red background.
|
|||
To flash a message with a different category, just use the second argument
|
||||
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
|
||||
:func:`~flask.get_flashed_messages` function to also return the
|
||||
|
|
@ -122,27 +117,3 @@ categories. The loop looks slightly different in that situation then:
|
|||
This is just one example of how to render these flashed messages. One
|
||||
might also use the category to add a prefix such as
|
||||
``<strong>Error:</strong>`` to the message.
|
||||
|
||||
Filtering Flash Messages
|
||||
------------------------
|
||||
|
||||
.. versionadded:: 0.9
|
||||
|
||||
Optionally you can pass a list of categories which filters the results of
|
||||
:func:`~flask.get_flashed_messages`. This is useful if you wish to
|
||||
render each category in a separate block.
|
||||
|
||||
.. sourcecode:: html+jinja
|
||||
|
||||
{% with errors = get_flashed_messages(category_filter=["error"]) %}
|
||||
{% if errors %}
|
||||
<div class="alert-message block-message error">
|
||||
<a class="close" href="#">×</a>
|
||||
<ul>
|
||||
{%- for msg in errors %}
|
||||
<li>{{ msg }}</li>
|
||||
{% endfor -%}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,17 @@
|
|||
.. _patterns:
|
||||
|
||||
Patterns for Flask
|
||||
==================
|
||||
|
||||
Certain features and interactions are common enough that you will find
|
||||
them in most web applications. For example, many applications use a
|
||||
relational database and user authentication. They will open a database
|
||||
connection at the beginning of the request and get the information for
|
||||
the logged in user. At the end of the request, the database connection
|
||||
is closed.
|
||||
Certain things are common enough that the chances are high you will find
|
||||
them in most web applications. For example quite a lot of applications
|
||||
are using relational databases and user authentication. In that case,
|
||||
chances are they will open a database connection at the beginning of the
|
||||
request and get the information of the currently logged in user. At the
|
||||
end of the request, the database connection is closed again.
|
||||
|
||||
These types of patterns may be a bit outside the scope of Flask itself,
|
||||
but Flask makes it easy to implement them. Some common patterns are
|
||||
collected in the following pages.
|
||||
There are more user contributed snippets and patterns in the `Flask
|
||||
Snippet Archives <http://flask.pocoo.org/snippets/>`_.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
|
@ -19,6 +20,8 @@ collected in the following pages.
|
|||
appfactories
|
||||
appdispatch
|
||||
urlprocessors
|
||||
distribute
|
||||
fabric
|
||||
sqlite3
|
||||
sqlalchemy
|
||||
fileuploads
|
||||
|
|
@ -27,14 +30,10 @@ collected in the following pages.
|
|||
wtforms
|
||||
templateinheritance
|
||||
flashing
|
||||
javascript
|
||||
jquery
|
||||
errorpages
|
||||
lazyloading
|
||||
mongoengine
|
||||
mongokit
|
||||
favicon
|
||||
streaming
|
||||
deferredcallbacks
|
||||
methodoverrides
|
||||
requestchecksum
|
||||
celery
|
||||
subclassing
|
||||
singlepageapplications
|
||||
|
|
|
|||