Browse Source

Merge remote-tracking branch 'remotes/origin/master' into json-mixin

pull/2358/head
David Lord 8 years ago
parent
commit
dbc70c9274
No known key found for this signature in database
GPG Key ID: 7A1C87E3F5BC42A8
  1. 11
      .coveragerc
  2. 1
      .gitattributes
  3. 35
      .github/ISSUE_TEMPLATE.rst
  4. 16
      .github/PULL_REQUEST_TEMPLATE.rst
  5. 6
      .gitignore
  6. 3
      .gitmodules
  7. 59
      .travis.yml
  8. 1
      AUTHORS
  9. 131
      CHANGES
  10. 173
      CONTRIBUTING.rst
  11. 33
      Makefile
  12. 4
      README
  13. BIN
      docs/_static/pycharm-runconfig.png
  14. 6
      docs/_templates/sidebarintro.html
  15. 1
      docs/_themes
  16. 84
      docs/api.rst
  17. 30
      docs/appcontext.rst
  18. 2
      docs/becomingbig.rst
  19. 18
      docs/blueprints.rst
  20. 63
      docs/cli.rst
  21. 34
      docs/conf.py
  22. 424
      docs/config.rst
  23. 1
      docs/contents.rst.inc
  24. 1
      docs/contributing.rst
  25. 8
      docs/deploying/fastcgi.rst
  26. 4
      docs/deploying/index.rst
  27. 12
      docs/deploying/mod_wsgi.rst
  28. 4
      docs/deploying/uwsgi.rst
  29. 16
      docs/deploying/wsgi-standalone.rst
  30. 82
      docs/errorhandling.rst
  31. 28
      docs/extensiondev.rst
  32. 86
      docs/flaskext.py
  33. 230
      docs/installation.rst
  34. 4
      docs/patterns/appfactories.rst
  35. 80
      docs/patterns/celery.rst
  36. 80
      docs/patterns/deferredcallbacks.rst
  37. 2
      docs/patterns/distribute.rst
  38. 40
      docs/patterns/errorpages.rst
  39. 2
      docs/patterns/favicon.rst
  40. 8
      docs/patterns/fileuploads.rst
  41. 52
      docs/patterns/packages.rst
  42. 10
      docs/patterns/sqlalchemy.rst
  43. 2
      docs/patterns/sqlite3.rst
  44. 2
      docs/patterns/wtforms.rst
  45. 259
      docs/quickstart.rst
  46. 13
      docs/reqcontext.rst
  47. 104
      docs/security.rst
  48. 2
      docs/styleguide.rst
  49. 192
      docs/testing.rst
  50. 2
      docs/tutorial/css.rst
  51. 5
      docs/tutorial/dbcon.rst
  52. 33
      docs/tutorial/dbinit.rst
  53. 16
      docs/tutorial/folders.rst
  54. 20
      docs/tutorial/index.rst
  55. 4
      docs/tutorial/introduction.rst
  56. 63
      docs/tutorial/packaging.rst
  57. 68
      docs/tutorial/setup.rst
  58. 11
      docs/tutorial/templates.rst
  59. 44
      docs/tutorial/testing.rst
  60. 5
      docs/tutorial/views.rst
  61. 44
      docs/upgrading.rst
  62. 8
      examples/flaskr/README
  63. 0
      examples/flaskr/flaskr/blueprints/__init__.py
  64. 55
      examples/flaskr/flaskr/blueprints/flaskr.py
  65. 64
      examples/flaskr/flaskr/factory.py
  66. 4
      examples/flaskr/flaskr/templates/layout.html
  67. 2
      examples/flaskr/flaskr/templates/login.html
  68. 2
      examples/flaskr/flaskr/templates/show_entries.html
  69. 15
      examples/flaskr/setup.py
  70. 6
      examples/flaskr/tests/context.py
  71. 55
      examples/flaskr/tests/test_flaskr.py
  72. 2
      examples/minitwit/.gitignore
  73. 3
      examples/minitwit/MANIFEST.in
  74. 12
      examples/minitwit/README
  75. 1
      examples/minitwit/minitwit/__init__.py
  76. 4
      examples/minitwit/minitwit/minitwit.py
  77. 0
      examples/minitwit/minitwit/schema.sql
  78. 0
      examples/minitwit/minitwit/static/style.css
  79. 0
      examples/minitwit/minitwit/templates/layout.html
  80. 0
      examples/minitwit/minitwit/templates/login.html
  81. 0
      examples/minitwit/minitwit/templates/register.html
  82. 0
      examples/minitwit/minitwit/templates/timeline.html
  83. 2
      examples/minitwit/setup.cfg
  84. 16
      examples/minitwit/setup.py
  85. 10
      examples/minitwit/tests/test_minitwit.py
  86. 10
      examples/patterns/largerapp/setup.py
  87. 12
      examples/patterns/largerapp/tests/test_largerapp.py
  88. 4
      examples/patterns/largerapp/yourapplication/__init__.py
  89. 0
      examples/patterns/largerapp/yourapplication/static/style.css
  90. 0
      examples/patterns/largerapp/yourapplication/templates/index.html
  91. 0
      examples/patterns/largerapp/yourapplication/templates/layout.html
  92. 0
      examples/patterns/largerapp/yourapplication/templates/login.html
  93. 5
      examples/patterns/largerapp/yourapplication/views.py
  94. 4
      flask/__init__.py
  95. 2
      flask/_compat.py
  96. 482
      flask/app.py
  97. 7
      flask/blueprints.py
  98. 220
      flask/cli.py
  99. 2
      flask/config.py
  100. 17
      flask/ctx.py
  101. Some files were not shown because too many files have changed in this diff Show More

11
.coveragerc

@ -0,0 +1,11 @@
[run]
branch = True
source =
flask
tests
[paths]
source =
flask
.tox/*/lib/python*/site-packages/flask
.tox/pypy/site-packages/flask

1
.gitattributes vendored

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

35
.github/ISSUE_TEMPLATE.rst

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

16
.github/PULL_REQUEST_TEMPLATE.rst

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

6
.gitignore vendored

@ -11,3 +11,9 @@ _mailinglist
.tox .tox
.cache/ .cache/
.idea/ .idea/
# Coverage reports
htmlcov
.coverage
.coverage.*
*,cover

3
.gitmodules vendored

@ -1,3 +0,0 @@
[submodule "docs/_themes"]
path = docs/_themes
url = https://github.com/mitsuhiko/flask-sphinx-themes.git

59
.travis.yml

@ -1,48 +1,37 @@
sudo: false sudo: false
language: python language: python
python:
- "2.6"
- "2.7"
- "pypy"
- "3.3"
- "3.4"
- "3.5"
env:
- REQUIREMENTS=lowest
- REQUIREMENTS=lowest-simplejson
- REQUIREMENTS=release
- REQUIREMENTS=release-simplejson
- REQUIREMENTS=devel
- REQUIREMENTS=devel-simplejson
matrix: matrix:
exclude: include:
# Python 3 support currently does not work with lowest requirements - python: 3.6
- python: "3.3" env: TOXENV=py,codecov
env: REQUIREMENTS=lowest - python: 3.5
- python: "3.3" env: TOXENV=py,codecov
env: REQUIREMENTS=lowest-simplejson - python: 3.4
- python: "3.4" env: TOXENV=py,codecov
env: REQUIREMENTS=lowest - python: 3.3
- python: "3.4" env: TOXENV=py,codecov
env: REQUIREMENTS=lowest-simplejson - python: 2.7
- python: "3.5" env: TOXENV=py,codecov
env: REQUIREMENTS=lowest - python: 2.6
- python: "3.5" env: TOXENV=py,codecov
env: REQUIREMENTS=lowest-simplejson - python: pypy
env: TOXENV=py,codecov
- python: nightly
env: TOXENV=py
- python: 3.6
env: TOXENV=docs-html
- python: 3.6
env: TOXENV=py-simplejson,codecov
install: install:
- pip install tox - pip install tox
script: script:
- tox -e py-$REQUIREMENTS - tox
branches: cache:
except: - pip
- website
notifications: notifications:
email: false email: false

1
AUTHORS

@ -22,6 +22,7 @@ Patches and Suggestions
- Florent Xicluna - Florent Xicluna
- Georg Brandl - Georg Brandl
- Jeff Widman @jeffwidman - Jeff Widman @jeffwidman
- Joshua Bronson @jab
- Justin Quick - Justin Quick
- Kenneth Reitz - Kenneth Reitz
- Keyan Pishdadian - Keyan Pishdadian

131
CHANGES

@ -3,12 +3,127 @@ Flask Changelog
Here you can see the full list of changes between each Flask release. Here you can see the full list of changes between each Flask release.
Version 0.13
------------
Major release, unreleased
- Minimum Werkzeug version bumped to 0.9, but please use the latest version.
- Minimum Click version bumped to 4, but please use the latest version.
- Make ``app.run()`` into a noop if a Flask application is run from the
development server on the command line. This avoids some behavior that
was confusing to debug for newcomers.
- Change default configuration ``JSONIFY_PRETTYPRINT_REGULAR=False``.
``jsonify()`` method returns compressed response by default, and pretty
response in debug mode. (`#2193`_)
- Change ``Flask.__init__`` to accept two new keyword arguments,
``host_matching`` and ``static_host``. This enables ``host_matching`` to be
set properly by the time the constructor adds the static route, and enables
the static route to be properly associated with the required host.
(``#1559``)
- ``send_file`` supports Unicode in ``attachment_filename``. (`#2223`_)
- Pass ``_scheme`` argument from ``url_for`` to ``handle_build_error``.
(`#2017`_)
- Add support for ``provide_automatic_options`` in ``add_url_rule`` to disable
adding OPTIONS method when the ``view_func`` argument is not a class.
(`#1489`_).
- ``MethodView`` can inherit method handlers from base classes. (`#1936`_)
- Errors caused while opening the session at the beginning of the request are
handled by the app's error handlers. (`#2254`_)
- Blueprints gained ``json_encoder`` and ``json_decoder`` attributes to
override the app's encoder and decoder. (`#1898`_)
- ``Flask.make_response`` raises ``TypeError`` instead of ``ValueError`` for
bad response types. The error messages have been improved to describe why the
type is invalid. (`#2256`_)
- Add ``routes`` CLI command to output routes registered on the application.
(`#2259`_)
- Show warning when session cookie domain is a bare hostname or an IP
address, as these may not behave properly in some browsers, such as Chrome.
(`#2282`_)
- Allow IP address as exact session cookie domain. (`#2282`_)
- ``SESSION_COOKIE_DOMAIN`` is set if it is detected through ``SERVER_NAME``.
(`#2282`_)
- Auto-detect zero-argument app factory called ``create_app`` or ``make_app``
from ``FLASK_APP``. (`#2297`_)
- Factory functions are not required to take a ``script_info`` parameter to
work with the ``flask`` command. If they take a single parameter or a
parameter named ``script_info``, the ``ScriptInfo`` object will be passed.
(`#2319`_)
- FLASK_APP=myproject.app:create_app('dev') support.
- ``FLASK_APP`` can be set to an app factory, with arguments if needed, for
example ``FLASK_APP=myproject.app:create_app('dev')``. (`#2326`_)
- ``View.provide_automatic_options = True`` is set on the view function from
``View.as_view``, to be detected in ``app.add_url_rule``. (`#2316`_)
- Error handling will try handlers registered for ``blueprint, code``,
``app, code``, ``blueprint, exception``, ``app, exception``. (`#2314`_)
- ``Cookie`` is added to the response's ``Vary`` header if the session is
accessed at all during the request (and it wasn't deleted). (`#2288`_)
- ``app.test_request_context()`` take ``subdomain`` and ``url_scheme``
parameters for use when building base URL. (`#1621`_)
- Set ``APPLICATION_ROOT = '/'`` by default. This was already the implicit
default when it was set to ``None``.
- ``TRAP_BAD_REQUEST_ERRORS`` is enabled by default in debug mode.
``BadRequestKeyError`` has a message with the bad key in debug mode instead
of the generic bad request message. (`#2348`_)
- Allow registering new tags with ``TaggedJSONSerializer`` to support
storing other types in the session cookie. (`#2352`_)
- Only open the session if the request has not been pushed onto the context
stack yet. This allows ``stream_with_context`` generators to access the same
session that the containing view uses. (`#2354`_)
.. _#1489: https://github.com/pallets/flask/pull/1489
.. _#1621: https://github.com/pallets/flask/pull/1621
.. _#1898: https://github.com/pallets/flask/pull/1898
.. _#1936: https://github.com/pallets/flask/pull/1936
.. _#2017: https://github.com/pallets/flask/pull/2017
.. _#2193: https://github.com/pallets/flask/pull/2193
.. _#2223: https://github.com/pallets/flask/pull/2223
.. _#2254: https://github.com/pallets/flask/pull/2254
.. _#2256: https://github.com/pallets/flask/pull/2256
.. _#2259: https://github.com/pallets/flask/pull/2259
.. _#2282: https://github.com/pallets/flask/pull/2282
.. _#2288: https://github.com/pallets/flask/pull/2288
.. _#2297: https://github.com/pallets/flask/pull/2297
.. _#2314: https://github.com/pallets/flask/pull/2314
.. _#2316: https://github.com/pallets/flask/pull/2316
.. _#2319: https://github.com/pallets/flask/pull/2319
.. _#2326: https://github.com/pallets/flask/pull/2326
.. _#2348: https://github.com/pallets/flask/pull/2348
.. _#2352: https://github.com/pallets/flask/pull/2352
.. _#2354: https://github.com/pallets/flask/pull/2354
Version 0.12.2
--------------
Released on May 16 2017
- Fix a bug in `safe_join` on Windows.
Version 0.12.1
--------------
Bugfix release, released on March 31st 2017
- Prevent `flask run` from showing a NoAppException when an ImportError occurs
within the imported application module.
- Fix encoding behavior of ``app.config.from_pyfile`` for Python 3. Fix
``#2118``.
- Use the ``SERVER_NAME`` config if it is present as default values for
``app.run``. ``#2109``, ``#2152``
- Call `ctx.auto_pop` with the exception object instead of `None`, in the
event that a `BaseException` such as `KeyboardInterrupt` is raised in a
request handler.
Version 0.12 Version 0.12
------------ ------------
Released on December 21st 2016, codename Punsch.
- the cli command now responds to `--version`. - the cli command now responds to `--version`.
- Mimetype guessing for ``send_file`` has been removed, as per issue ``#104``. - Mimetype guessing and ETag generation for file-like objects in ``send_file``
See pull request ``#1849``. has been removed, as per issue ``#104``. See pull request ``#1849``.
- Mimetype guessing in ``send_file`` now fails loudly and doesn't fall back to
``application/octet-stream``. See pull request ``#1988``.
- Make ``flask.safe_join`` able to join multiple paths like ``os.path.join`` - Make ``flask.safe_join`` able to join multiple paths like ``os.path.join``
(pull request ``#1730``). (pull request ``#1730``).
- Added `json` keyword argument to :meth:`flask.testing.FlaskClient.open` - Added `json` keyword argument to :meth:`flask.testing.FlaskClient.open`
@ -16,6 +131,14 @@ Version 0.12
send JSON requests from the test client. send JSON requests from the test client.
- Added ``is_json`` and ``get_json`` to :class:``flask.wrappers.Response`` - Added ``is_json`` and ``get_json`` to :class:``flask.wrappers.Response``
in order to make it easier to build assertions when testing JSON responses. in order to make it easier to build assertions when testing JSON responses.
- Revert a behavior change that made the dev server crash instead of returning
a Internal Server Error (pull request ``#2006``).
- Correctly invoke response handlers for both regular request dispatching as
well as error handlers.
- Disable logger propagation by default for the app logger.
- Add support for range requests in ``send_file``.
- ``app.test_client`` includes preset default environment, which can now be
directly set, instead of per ``client.get``.
Version 0.11.2 Version 0.11.2
-------------- --------------
@ -99,6 +222,8 @@ Released on May 29th 2016, codename Absinthe.
- Don't leak exception info of already catched exceptions to context teardown - Don't leak exception info of already catched exceptions to context teardown
handlers (pull request ``#1393``). handlers (pull request ``#1393``).
- Allow custom Jinja environment subclasses (pull request ``#1422``). - Allow custom Jinja environment subclasses (pull request ``#1422``).
- Updated extension dev guidelines.
- ``flask.g`` now has ``pop()`` and ``setdefault`` methods. - ``flask.g`` now has ``pop()`` and ``setdefault`` methods.
- Turn on autoescape for ``flask.templating.render_template_string`` by default - Turn on autoescape for ``flask.templating.render_template_string`` by default
(pull request ``#1515``). (pull request ``#1515``).
@ -330,7 +455,7 @@ Released on September 29th 2011, codename Rakija
- Applications now not only have a root path where the resources and modules - Applications now not only have a root path where the resources and modules
are located but also an instance path which is the designated place to are located but also an instance path which is the designated place to
drop files that are modified at runtime (uploads etc.). Also this is drop files that are modified at runtime (uploads etc.). Also this is
conceptionally only instance depending and outside version control so it's conceptually only instance depending and outside version control so it's
the perfect place to put configuration files etc. For more information the perfect place to put configuration files etc. For more information
see :ref:`instance-folders`. see :ref:`instance-folders`.
- Added the ``APPLICATION_ROOT`` configuration variable. - Added the ``APPLICATION_ROOT`` configuration variable.

173
CONTRIBUTING.rst

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

33
Makefile

@ -1,23 +1,35 @@
.PHONY: clean-pyc ext-test test tox-test test-with-mem upload-docs docs audit .PHONY: all install-dev test coverage cov test-all tox docs audit release clean-pyc upload-docs ebook
all: clean-pyc test all: test
test: install-dev:
pip install -r test-requirements.txt -q pip install -q -e .[dev]
FLASK_DEBUG= py.test tests examples
tox-test: test: clean-pyc install-dev
pytest
coverage: clean-pyc install-dev
pip install -q -e .[test]
coverage run -m pytest
coverage report
coverage html
cov: coverage
test-all: install-dev
tox tox
tox: test-all
docs: clean-pyc install-dev
$(MAKE) -C docs html
audit: audit:
python setup.py audit python setup.py audit
release: release:
python scripts/make-release.py python scripts/make-release.py
ext-test:
python tests/flaskext_test.py --browse
clean-pyc: clean-pyc:
find . -name '*.pyc' -exec rm -f {} + find . -name '*.pyc' -exec rm -f {} +
find . -name '*.pyo' -exec rm -f {} + find . -name '*.pyo' -exec rm -f {} +
@ -39,6 +51,3 @@ ebook:
@echo 'Requires X-forwarding for Qt features used in conversion (ssh -X).' @echo 'Requires X-forwarding for Qt features used in conversion (ssh -X).'
@echo 'Do not mind "Invalid value for ..." CSS errors if .mobi renders.' @echo 'Do not mind "Invalid value for ..." CSS errors if .mobi renders.'
ssh -X pocoo.org ebook-convert /var/www/flask.pocoo.org/docs/flask-docs.epub /var/www/flask.pocoo.org/docs/flask-docs.mobi --cover http://flask.pocoo.org/docs/_images/logo-full.png --authors 'Armin Ronacher' ssh -X pocoo.org ebook-convert /var/www/flask.pocoo.org/docs/flask-docs.epub /var/www/flask.pocoo.org/docs/flask-docs.mobi --cover http://flask.pocoo.org/docs/_images/logo-full.png --authors 'Armin Ronacher'
docs:
$(MAKE) -C docs html

4
README

@ -33,9 +33,9 @@
Good that you're asking. The tests are in the Good that you're asking. The tests are in the
tests/ folder. To run the tests use the tests/ folder. To run the tests use the
`py.test` testing tool: `pytest` testing tool:
$ py.test $ pytest
Details on contributing can be found in CONTRIBUTING.rst Details on contributing can be found in CONTRIBUTING.rst

BIN
docs/_static/pycharm-runconfig.png vendored

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

6
docs/_templates/sidebarintro.html vendored

@ -16,7 +16,7 @@
<h3>Useful Links</h3> <h3>Useful Links</h3>
<ul> <ul>
<li><a href="http://flask.pocoo.org/">The Flask Website</a></li> <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="https://pypi.python.org/pypi/Flask">Flask @ PyPI</a></li>
<li><a href="http://github.com/pallets/flask">Flask @ GitHub</a></li> <li><a href="https://github.com/pallets/flask">Flask @ GitHub</a></li>
<li><a href="http://github.com/pallets/flask/issues">Issue Tracker</a></li> <li><a href="https://github.com/pallets/flask/issues">Issue Tracker</a></li>
</ul> </ul>

1
docs/_themes

@ -1 +0,0 @@
Subproject commit 3d964b660442e23faedf801caed6e3c7bd42d5c9

84
docs/api.rst

@ -29,62 +29,13 @@ Incoming Request Data
--------------------- ---------------------
.. autoclass:: Request .. autoclass:: Request
:members: is_json, get_json :members:
:inherited-members:
.. attribute:: form
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.
.. attribute:: args
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 .. attribute:: environ
The underlying WSGI environment. The underlying WSGI environment.
.. attribute:: method
The current request method (``POST``, ``GET`` etc.)
.. attribute:: path .. attribute:: path
.. attribute:: full_path .. attribute:: full_path
.. attribute:: script_root .. attribute:: script_root
@ -114,15 +65,8 @@ Incoming Request Data
`url_root` ``u'http://www.example.com/myapplication/'`` `url_root` ``u'http://www.example.com/myapplication/'``
============= ====================================================== ============= ======================================================
.. attribute:: is_xhr
``True`` if the request was triggered via a JavaScript .. attribute:: request
`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` To access incoming request data, you can use the global `request`
object. Flask parses incoming request data for you and gives you object. Flask parses incoming request data for you and gives you
@ -227,18 +171,6 @@ implementation that Flask is using.
.. autoclass:: SessionMixin .. autoclass:: SessionMixin
:members: :members:
.. autodata:: session_json_serializer
This object provides dumping and loading methods similar to simplejson
but it also tags certain builtin Python objects that commonly appear in
sessions. Currently the following extended values are supported in
the JSON it dumps:
- :class:`~markupsafe.Markup` objects
- :class:`~uuid.UUID` objects
- :class:`~datetime.datetime` objects
- :class:`tuple`\s
.. admonition:: Notice .. admonition:: Notice
The ``PERMANENT_SESSION_LIFETIME`` config key can also be an integer The ``PERMANENT_SESSION_LIFETIME`` config key can also be an integer
@ -316,13 +248,7 @@ Useful Functions and Classes
.. autofunction:: url_for .. autofunction:: url_for
.. function:: abort(code) .. autofunction:: abort
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:: redirect
@ -416,6 +342,8 @@ you are using Flask 0.10 which implies that:
.. autoclass:: JSONDecoder .. autoclass:: JSONDecoder
:members: :members:
.. automodule:: flask.json.tag
Template Rendering Template Rendering
------------------ ------------------

30
docs/appcontext.rst

@ -5,12 +5,15 @@ The Application Context
.. versionadded:: 0.9 .. versionadded:: 0.9
One of the design ideas behind Flask is that there are two different One of the design ideas behind Flask is that there are at least two
“states” in which code is executed. The application setup state in which different “states” in which code is executed:
the application implicitly is on the module level. It starts when the
:class:`Flask` object is instantiated, and it implicitly ends when the 1. The application setup state, in which the application implicitly is
first request comes in. While the application is in this state a few on the module level.
assumptions are true:
This state starts when the :class:`Flask` object is instantiated, and
it implicitly ends when the first request comes in. While the
application is in this state, a few assumptions are true:
- the programmer can modify the application object safely. - the programmer can modify the application object safely.
- no request handling happened so far - no request handling happened so far
@ -18,18 +21,21 @@ assumptions are true:
modify it, there is no magic proxy that can give you a reference to modify it, there is no magic proxy that can give you a reference to
the application object you're currently creating or modifying. the application object you're currently creating or modifying.
In contrast, during request handling, a couple of other rules exist: 2. In contrast, in the request handling state, a couple of other rules
exist:
- while a request is active, the context local objects - while a request is active, the context local objects
(:data:`flask.request` and others) point to the current request. (:data:`flask.request` and others) point to the current request.
- any code can get hold of these objects at any time. - any code can get hold of these objects at any time.
There is a third state which is sitting in between a little bit. 3. There is also a third state somewhere in between 'module-level' and
'request-handling':
Sometimes you are dealing with an application in a way that is similar to Sometimes you are dealing with an application in a way that is similar to
how you interact with applications during request handling; just that there how you interact with applications during request handling, but without
is no request active. Consider, for instance, that you're sitting in an there being an active request. Consider, for instance, that you're
interactive Python shell and interacting with the application, or a sitting in an interactive Python shell and interacting with the
command line application. application, or a command line application.
The application context is what powers the :data:`~flask.current_app` The application context is what powers the :data:`~flask.current_app`
context local. context local.

2
docs/becomingbig.rst

@ -12,7 +12,7 @@ Flask started in part to demonstrate how to build your own framework on top of
existing well-used tools Werkzeug (WSGI) and Jinja (templating), and as it existing well-used tools Werkzeug (WSGI) and Jinja (templating), and as it
developed, it became useful to a wide audience. As you grow your codebase, developed, it became useful to a wide audience. As you grow your codebase,
don't just use Flask -- understand it. Read the source. Flask's code is don't just use Flask -- understand it. Read the source. Flask's code is
written to be read; it's documentation is published so you can use its internal written to be read; its documentation is published so you can use its internal
APIs. Flask sticks to documented APIs in upstream libraries, and documents its APIs. Flask sticks to documented APIs in upstream libraries, and documents its
internal utilities so that you can find the hook points needed for your internal utilities so that you can find the hook points needed for your
project. project.

18
docs/blueprints.rst

@ -245,4 +245,22 @@ Here is an example for a "404 Page Not Found" exception::
def page_not_found(e): def page_not_found(e):
return render_template('pages/404.html') 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 errorhandler 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(ex)
else:
return ex
More information on error handling see :ref:`errorpages`. More information on error handling see :ref:`errorpages`.

63
docs/cli.rst

@ -56,14 +56,24 @@ If you are constantly working with a virtualenv you can also put the
bottom of the file. That way every time you activate your virtualenv you bottom of the file. That way every time you activate your virtualenv you
automatically also activate the correct application name. automatically also activate the correct application name.
Edit the activate script for the shell you use. For example:
Unix Bash: ``venv/bin/activate``::
FLASK_APP=hello
export FLASK_APP
Windows CMD.exe: ``venv\Scripts\activate.bat``::
set "FLASK_APP=hello"
:END
Debug Flag Debug Flag
---------- ----------
The :command:`flask` script can also be instructed to enable the debug The :command:`flask` script can also be instructed to enable the debug
mode of the application automatically by exporting ``FLASK_DEBUG``. If mode of the application automatically by exporting ``FLASK_DEBUG``. If
set to ``1`` debug is enabled or ``0`` disables it. set to ``1`` debug is enabled or ``0`` disables it::
Or with a filename::
export FLASK_DEBUG=1 export FLASK_DEBUG=1
@ -141,8 +151,8 @@ This could be a file named :file:`autoapp.py` with these contents::
from yourapplication import create_app from yourapplication import create_app
app = create_app(os.environ['YOURAPPLICATION_CONFIG']) app = create_app(os.environ['YOURAPPLICATION_CONFIG'])
Once this has happened you can make the flask command automatically pick Once this has happened you can make the :command:`flask` command automatically
it up:: pick it up::
export YOURAPPLICATION_CONFIG=/path/to/config.cfg export YOURAPPLICATION_CONFIG=/path/to/config.cfg
export FLASK_APP=/path/to/autoapp.py export FLASK_APP=/path/to/autoapp.py
@ -248,3 +258,46 @@ Inside :file:`mypackage/commands.py` you can then export a Click object::
Once that package is installed in the same virtualenv as Flask itself you Once that package is installed in the same virtualenv as Flask itself you
can run ``flask my-command`` to invoke your command. This is useful to can run ``flask my-command`` to invoke your command. This is useful to
provide extra functionality that Flask itself cannot ship. provide extra functionality that Flask itself cannot ship.
PyCharm Integration
-------------------
The new Flask CLI features aren’t yet fully integrated into the PyCharm IDE,
so we have to do a few tweaks to get them working smoothly.
In your PyCharm application, with your project open, click on *Run*
from the menu bar and go to *Edit Configurations*. You’ll be greeted by a
screen similar to this:
.. image:: _static/pycharm-runconfig.png
:align: center
:class: screenshot
:alt: screenshot of pycharm's run configuration settings
There’s quite a few options to change, but don’t worry— once we’ve done it
for one command, we can easily copy the entire configuration and make a
single tweak to give us access to other flask cli commands, including
any custom ones you may implement yourself.
For the *Script* input (**A**), we want to navigate to the virtual environment
we’re using for our project and within that folder we want to pick the ``flask``
file which will reside in the ``bin`` folder, or in the ``Scripts`` folder if
you're on Windows.
The *Script Parameter* field (**B**) is set to the cli command you wish to
execute, in this example we use ``run`` which will run our development server.
We need to add an environment variable (**C**) to identify our application.
Click on the browse button and add an entry with ``FLASK_APP`` on the
left and the name of the python file, or package on the right
(``app.py`` for example).
Next we need to set the working directory (**D**) to be the same folder where
our application file or package resides.
Finally, untick the *PYTHONPATH* options (**E**) and give the configuration a
good descriptive name, such as “Run Flask Server” and click *Apply*.
Now that we have on run configuration which implements ``flask run`` from within
PyCharm, we can simply copy that configuration and alter the script argument
to run a different cli command, e.g. ``flask shell``.

34
docs/conf.py

@ -11,15 +11,17 @@
# All configuration values have a default; values that are commented out # All configuration values have a default; values that are commented out
# serve to show the default. # serve to show the default.
from __future__ import print_function from __future__ import print_function
from datetime import datetime
import os import os
import sys import sys
import pkg_resources import pkg_resources
import time
import datetime
BUILD_DATE = datetime.datetime.utcfromtimestamp(int(os.environ.get('SOURCE_DATE_EPOCH', time.time())))
# If extensions (or modules to document with autodoc) are in another directory, # 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 # 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. # documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.append(os.path.join(os.path.dirname(__file__), '_themes'))
sys.path.append(os.path.dirname(__file__)) sys.path.append(os.path.dirname(__file__))
# -- General configuration ----------------------------------------------------- # -- General configuration -----------------------------------------------------
@ -35,6 +37,14 @@ extensions = [
'flaskdocext' 'flaskdocext'
] ]
try:
__import__('sphinxcontrib.log_cabinet')
except ImportError:
print('sphinxcontrib-log-cabinet is not installed.')
print('Changelog directives will not be re-organized.')
else:
extensions.append('sphinxcontrib.log_cabinet')
# Add any paths that contain templates here, relative to this directory. # Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates'] templates_path = ['_templates']
@ -49,7 +59,7 @@ master_doc = 'index'
# General information about the project. # General information about the project.
project = u'Flask' project = u'Flask'
copyright = u'2010 - {0}, Armin Ronacher'.format(datetime.utcnow().year) copyright = u'2010 - {0}, Armin Ronacher'.format(BUILD_DATE.year)
# The version info for the project you're documenting, acts as replacement for # The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the # |version| and |release|, also used in various other places throughout the
@ -110,7 +120,7 @@ exclude_patterns = ['_build']
# html_theme_options = {} # html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory. # Add any paths that contain custom themes here, relative to this directory.
html_theme_path = ['_themes'] # html_theme_path = ['_themes']
# The name for this set of Sphinx documents. If None, it defaults to # The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation". # "<project> v<release> documentation".
@ -231,7 +241,7 @@ latex_additional_files = ['flaskstyle.sty', 'logo.pdf']
# The scheme of the identifier. Typical schemes are ISBN or URL. # The scheme of the identifier. Typical schemes are ISBN or URL.
#epub_scheme = '' #epub_scheme = ''
# The unique identifier of the text. This can be a ISBN number # The unique identifier of the text. This can be an ISBN number
# or the project homepage. # or the project homepage.
#epub_identifier = '' #epub_identifier = ''
@ -257,26 +267,14 @@ intersphinx_mapping = {
'werkzeug': ('http://werkzeug.pocoo.org/docs/', None), 'werkzeug': ('http://werkzeug.pocoo.org/docs/', None),
'click': ('http://click.pocoo.org/', None), 'click': ('http://click.pocoo.org/', None),
'jinja': ('http://jinja.pocoo.org/docs/', None), 'jinja': ('http://jinja.pocoo.org/docs/', None),
'sqlalchemy': ('http://docs.sqlalchemy.org/en/latest/', None), 'sqlalchemy': ('https://docs.sqlalchemy.org/en/latest/', None),
'wtforms': ('https://wtforms.readthedocs.io/en/latest/', None), 'wtforms': ('https://wtforms.readthedocs.io/en/latest/', None),
'blinker': ('https://pythonhosted.org/blinker/', None) 'blinker': ('https://pythonhosted.org/blinker/', None)
} }
try:
__import__('flask_theme_support')
pygments_style = 'flask_theme_support.FlaskyStyle'
html_theme = 'flask'
html_theme_options = { html_theme_options = {
'touch_icon': 'touch-icon.png' 'touch_icon': 'touch-icon.png'
} }
except ImportError:
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)
# unwrap decorators # unwrap decorators
def unwrap_decorators(): def unwrap_decorators():

424
docs/config.rst

@ -3,8 +3,6 @@
Configuration Handling Configuration Handling
====================== ======================
.. versionadded:: 0.3
Applications need some kind of configuration. There are different settings Applications need some kind of configuration. There are different settings
you might want to change depending on the application environment like you might want to change depending on the application environment like
toggling the debug mode, setting the secret key, and other such toggling the debug mode, setting the secret key, and other such
@ -44,178 +42,263 @@ method::
SECRET_KEY='...' SECRET_KEY='...'
) )
.. admonition:: Debug Mode with the ``flask`` Script
If you use the :command:`flask` script to start a local development
server, to enable the debug mode, you need to export the ``FLASK_DEBUG``
environment variable before running the server::
$ export FLASK_DEBUG=1
$ flask run
(On Windows you need to use ``set`` instead of ``export``).
``app.debug`` and ``app.config['DEBUG']`` are not compatible with
  the :command:`flask` script. They only worked when using ``Flask.run()``
method.
Builtin Configuration Values Builtin Configuration Values
---------------------------- ----------------------------
The following configuration values are used internally by Flask: The following configuration values are used internally by Flask:
.. tabularcolumns:: |p{6.5cm}|p{8.5cm}| .. py:data:: DEBUG
================================= ========================================= Enable debug mode. When using the development server with ``flask run`` or
``DEBUG`` enable/disable debug mode ``app.run``, an interactive debugger will be shown for unhanlded
``TESTING`` enable/disable testing mode exceptions, and the server will be reloaded when code changes.
``PROPAGATE_EXCEPTIONS`` explicitly enable or disable the
propagation of exceptions. If not set or **Do not enable debug mode in production.**
explicitly set to ``None`` this is
implicitly true if either ``TESTING`` or Default: ``False``
``DEBUG`` is true.
``PRESERVE_CONTEXT_ON_EXCEPTION`` By default if the application is in .. py:data:: TESTING
debug mode the request context is not
popped on exceptions to enable debuggers Enable testing mode. Exceptions are propagated rather than handled by the
to introspect the data. This can be the app's error handlers. Extensions may also change their behavior to
disabled by this key. You can also use facilitate easier testing. You should enable this in your own tests.
this setting to force-enable it for non
debug execution which might be useful to Default: ``False``
debug production applications (but also
very risky). .. py:data:: PROPAGATE_EXCEPTIONS
``SECRET_KEY`` the secret key
``SESSION_COOKIE_NAME`` the name of the session cookie Exceptions are re-raised rather than being handled by the app's error
``SESSION_COOKIE_DOMAIN`` the domain for the session cookie. If handlers. If not set, this is implicitly true if ``TESTING`` or ``DEBUG``
this is not set, the cookie will be is enabled.
valid for all subdomains of
``SERVER_NAME``. Default: ``None``
``SESSION_COOKIE_PATH`` the path for the session cookie. If
this is not set the cookie will be valid .. py:data:: PRESERVE_CONTEXT_ON_EXCEPTION
for all of ``APPLICATION_ROOT`` or if
that is not set for ``'/'``. Don't pop the request context when an exception occurs. If not set, this
``SESSION_COOKIE_HTTPONLY`` controls if the cookie should be set is true if ``DEBUG`` is true. This allows debuggers to introspect the
with the httponly flag. Defaults to request data on errors, and should normally not need to be set directly.
``True``.
``SESSION_COOKIE_SECURE`` controls if the cookie should be set Default: ``None``
with the secure flag. Defaults to
``False``. .. py:data:: TRAP_HTTP_EXCEPTIONS
``PERMANENT_SESSION_LIFETIME`` the lifetime of a permanent session as
:class:`datetime.timedelta` object. If there is no handler for an ``HTTPException``-type exception, re-raise it
Starting with Flask 0.8 this can also be to be handled by the interactive debugger instead of returning it as a
an integer representing seconds. simple error response.
``SESSION_REFRESH_EACH_REQUEST`` this flag controls how permanent
sessions are refreshed. If set to ``True`` Default: ``False``
(which is the default) then the cookie
is refreshed each request which .. py:data:: TRAP_BAD_REQUEST_ERRORS``
automatically bumps the lifetime. If
set to ``False`` a `set-cookie` header is Trying to access a key that doesn't exist from request dicts like ``args``
only sent if the session is modified. and ``form`` will return a 400 Bad Request error page. Enable this to treat
Non permanent sessions are not affected the error as an unhandled exception instead so that you get the interactive
by this. debugger. This is a more specific version of ``TRAP_HTTP_EXCEPTIONS``. If
``USE_X_SENDFILE`` enable/disable x-sendfile unset, it is enabled in debug mode.
``LOGGER_NAME`` the name of the logger
``LOGGER_HANDLER_POLICY`` the policy of the default logging Default: ``None``
handler. The default is ``'always'``
which means that the default logging .. py:data:: SECRET_KEY
handler is always active. ``'debug'``
will only activate logging in debug A secret key that will be used for securely signing the session cookie
mode, ``'production'`` will only log in and can be used for any other security related needs by extensions or your
production and ``'never'`` disables it application. It should be a long random string of bytes, although unicode
entirely. is accepted too. For example, copy the output of this to your config::
``SERVER_NAME`` the name and port number of the server.
Required for subdomain support (e.g.: python -c 'import os; print(os.urandom(32))'
``'myapp.dev:5000'``) Note that
localhost does not support subdomains so **Do not reveal the secret key when posting questions or committing code.**
setting this to “localhost” does not
help. Setting a ``SERVER_NAME`` also Default: ``None``
by default enables URL generation
without a request context but with an .. py:data:: SESSION_COOKIE_NAME
application context.
``APPLICATION_ROOT`` If the application does not occupy The name of the session cookie. Can be changed in case you already have a
a whole domain or subdomain this can cookie with the same name.
be set to the path where the application
is configured to live. This is for Default: ``'session'``
session cookie as path value. If
domains are used, this should be .. py:data:: SESSION_COOKIE_DOMAIN
``None``.
``MAX_CONTENT_LENGTH`` If set to a value in bytes, Flask will The domain match rule that the session cookie will be valid for. If not
reject incoming requests with a set, the cookie will be valid for all subdomains of ``SERVER_NAME``. If
content length greater than this by ``False``, the cookie's domain will not be set.
returning a 413 status code.
``SEND_FILE_MAX_AGE_DEFAULT`` Default cache control max age to use with Default: ``None``
:meth:`~flask.Flask.send_static_file` (the
default static file handler) and .. py:data:: SESSION_COOKIE_PATH
:func:`~flask.send_file`, as
:class:`datetime.timedelta` or as seconds. The path that the session cookie will be valid for. If not set, the cookie
Override this value on a per-file will be valid underneath ``APPLICATION_ROOT`` or ``/`` if that is not set.
basis using the
:meth:`~flask.Flask.get_send_file_max_age` Default: ``None``
hook on :class:`~flask.Flask` or
:class:`~flask.Blueprint`, .. py:data:: SESSION_COOKIE_HTTPONLY
respectively. Defaults to 43200 (12 hours).
``TRAP_HTTP_EXCEPTIONS`` If this is set to ``True`` Flask will Browsers will not allow JavaScript access to cookies marked as "HTTP only"
not execute the error handlers of HTTP for security.
exceptions but instead treat the
exception like any other and bubble it Default: ``True``
through the exception stack. This is
helpful for hairy debugging situations .. py:data:: SESSION_COOKIE_SECURE
where you have to find out where an HTTP
exception is coming from. Browsers will only send cookies with requests over HTTPS if the cookie is
``TRAP_BAD_REQUEST_ERRORS`` Werkzeug's internal data structures that marked "secure". The application must be served over HTTPS for this to make
deal with request specific data will sense.
raise special key errors that are also
bad request exceptions. Likewise many Default: ``False``
operations can implicitly fail with a
BadRequest exception for consistency. .. py:data:: PERMANENT_SESSION_LIFETIME
Since it's nice for debugging to know
why exactly it failed this flag can be If ``session.permanent`` is true, the cookie's max age will be set to this
used to debug those situations. If this number of seconds. Can either be a :class:`datetime.timedelta` or an
config is set to ``True`` you will get ``int``.
a regular traceback instead.
``PREFERRED_URL_SCHEME`` The URL scheme that should be used for Default: ``timedelta(days=31)`` (``2678400`` seconds)
URL generation if no URL scheme is
available. This defaults to ``http``. .. py:data:: SESSION_REFRESH_EACH_REQUEST
``JSON_AS_ASCII`` By default Flask serialize object to
ascii-encoded JSON. If this is set to Control whether the cookie is sent with every response when
``False`` Flask will not encode to ASCII ``session.permanent`` is true. Sending the cookie every time (the default)
and output strings as-is and return can more reliably keep the session from expiring, but uses more bandwidth.
unicode strings. ``jsonify`` will Non-permanent sessions are not affected.
automatically encode it in ``utf-8``
then for transport for instance. Default: ``True``
``JSON_SORT_KEYS`` By default Flask will serialize JSON
objects in a way that the keys are .. py:data:: USE_X_SENDFILE
ordered. This is done in order to
ensure that independent of the hash seed When serving files, set the ``X-Sendfile`` header instead of serving the
of the dictionary the return value will data with Flask. Some web servers, such as Apache, recognize this and serve
be consistent to not trash external HTTP the data more efficiently. This only makes sense when using such a server.
caches. You can override the default
behavior by changing this variable. Default: ``False``
This is not recommended but might give
you a performance improvement on the .. py:data:: SEND_FILE_MAX_AGE_DEFAULT
cost of cachability.
``JSONIFY_PRETTYPRINT_REGULAR`` If this is set to ``True`` (the default) When serving files, set the cache control max age to this number of
jsonify responses will be pretty printed seconds. Can either be a :class:`datetime.timedelta` or an ``int``.
if they are not requested by an Override this value on a per-file basis using
XMLHttpRequest object (controlled by :meth:`~flask.Flask.get_send_file_max_age` on the application or blueprint.
the ``X-Requested-With`` header)
``JSONIFY_MIMETYPE`` MIME type used for jsonify responses. Default: ``timedelta(hours=12)`` (``43200`` seconds)
``TEMPLATES_AUTO_RELOAD`` Whether to check for modifications of
the template source and reload it .. py:data:: LOGGER_NAME
automatically. By default the value is
``None`` which means that Flask checks The name of the logger that the Flask application sets up. If not set,
original file only in debug mode. it will take the import name passed to ``Flask.__init__``.
``EXPLAIN_TEMPLATE_LOADING`` If this is enabled then every attempt to
load a template will write an info Default: ``None``
message to the logger explaining the
attempts to locate the template. This .. py:data:: LOGGER_HANDLER_POLICY
can be useful to figure out why
templates cannot be found or wrong When to activate the application's logger handler. ``'always'`` always
templates appear to be loaded. enables it, ``'debug'`` only activates it in debug mode, ``'production'``
================================= ========================================= only activates it when not in debug mode, and ``'never'`` never enables it.
.. admonition:: More on ``SERVER_NAME`` Default: ``'always'``
The ``SERVER_NAME`` key is used for the subdomain support. Because .. py:data:: SERVER_NAME
Flask cannot guess the subdomain part without the knowledge of the
actual server name, this is required if you want to work with Inform the application what host and port it is bound to. Required for
subdomains. This is also used for the session cookie. subdomain route matching support.
Please keep in mind that not only Flask has the problem of not knowing If set, will be used for the session cookie domain if
what subdomains are, your web browser does as well. Most modern web ``SESSION_COOKIE_DOMAIN`` is not set. Modern web browsers will not allow
browsers will not allow cross-subdomain cookies to be set on a setting cookies for domains without a dot. To use a domain locally,
server name without dots in it. So if your server name is add any names that should route to the app to your ``hosts`` file. ::
``'localhost'`` you will not be able to set a cookie for
``'localhost'`` and every subdomain of it. Please choose a different 127.0.0.1 localhost.dev
server name in that case, like ``'myapplication.local'`` and add
this name + the subdomains you want to use into your host config If set, ``url_for`` can generate external URLs with only an application
or setup a local `bind`_. context instead of a request context.
.. _bind: https://www.isc.org/downloads/bind/ Default: ``None``
.. py:data:: APPLICATION_ROOT
Inform the application what path it is mounted under by the application /
web server.
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
Don't read more than this many bytes from the incoming request data. If not
set and the request does not specify a ``CONTENT_LENGTH``, no data will be
read for security.
Default: ``None``
.. py:data:: JSON_AS_ASCII
Serialize objects to ASCII-encoded JSON. If this is disabled, the JSON
will be returned as a Unicode string, or encoded as ``UTF-8`` by
``jsonify``. This has security implications when rendering the JSON in
to JavaScript in templates, and should typically remain enabled.
Default: ``True``
.. py:data:: JSON_SORT_KEYS
Sort the keys of JSON objects alphabetically. This is useful for caching
because it ensures the data is serialized the same way no matter what
Python's hash seed is. While not recommended, you can disable this for a
possible performance improvement at the cost of caching.
Default: ``True``
.. py:data:: JSONIFY_PRETTYPRINT_REGULAR
``jsonify`` responses will be output with newlines, spaces, and indentation
for easier reading by humans. Always enabled in debug mode.
Default: ``False``
.. py:data:: JSONIFY_MIMETYPE
The mimetype of ``jsonify`` responses.
Default: ``'application/json'``
.. 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``
.. versionadded:: 0.4 .. versionadded:: 0.4
``LOGGER_NAME`` ``LOGGER_NAME``
@ -262,7 +345,7 @@ So a common pattern is this::
This first loads the configuration from the This first loads the configuration from the
`yourapplication.default_settings` module and then overrides the values `yourapplication.default_settings` module and then overrides the values
with the contents of the file the :envvar:``YOURAPPLICATION_SETTINGS`` with the contents of the file the :envvar:`YOURAPPLICATION_SETTINGS`
environment variable points to. This environment variable can be set on 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 Linux or OS X with the export command in the shell before starting the
server:: server::
@ -463,3 +546,4 @@ Example usage for both::
# or via open_instance_resource: # or via open_instance_resource:
with app.open_instance_resource('application.cfg') as f: with app.open_instance_resource('application.cfg') as f:
config = f.read() config = f.read()

1
docs/contents.rst.inc

@ -59,3 +59,4 @@ Design notes, legal information and changelog are here for the interested.
upgrading upgrading
changelog changelog
license license
contributing

1
docs/contributing.rst

@ -0,0 +1 @@
.. include:: ../CONTRIBUTING.rst

8
docs/deploying/fastcgi.rst

@ -144,7 +144,7 @@ A basic FastCGI configuration for lighttpd looks like that::
) )
alias.url = ( alias.url = (
"/static/" => "/path/to/your/static" "/static/" => "/path/to/your/static/"
) )
url.rewrite-once = ( url.rewrite-once = (
@ -159,7 +159,7 @@ work in the URL root you have to work around a lighttpd bug with the
Make sure to apply it only if you are mounting the application the URL 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 root. Also, see the Lighty docs for more information on `FastCGI and Python
<http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModFastCGI>`_ (note that <https://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModFastCGI>`_ (note that
explicitly passing a socket to run() is no longer necessary). explicitly passing a socket to run() is no longer necessary).
Configuring nginx Configuring nginx
@ -234,7 +234,7 @@ python path. Common problems are:
web server. web server.
- Different python interpreters being used. - Different python interpreters being used.
.. _nginx: http://nginx.org/ .. _nginx: https://nginx.org/
.. _lighttpd: http://www.lighttpd.net/ .. _lighttpd: https://www.lighttpd.net/
.. _cherokee: http://cherokee-project.com/ .. _cherokee: http://cherokee-project.com/
.. _flup: https://pypi.python.org/pypi/flup .. _flup: https://pypi.python.org/pypi/flup

4
docs/deploying/index.rst

@ -21,8 +21,10 @@ Hosted options
- `Deploying Flask on OpenShift <https://developers.openshift.com/en/python-flask.html>`_ - `Deploying Flask on OpenShift <https://developers.openshift.com/en/python-flask.html>`_
- `Deploying Flask on Webfaction <http://flask.pocoo.org/snippets/65/>`_ - `Deploying Flask on Webfaction <http://flask.pocoo.org/snippets/65/>`_
- `Deploying Flask on Google App Engine <https://github.com/kamalgill/flask-appengine-template>`_ - `Deploying Flask on Google App Engine <https://github.com/kamalgill/flask-appengine-template>`_
- `Deploying Flask on AWS Elastic Beanstalk <https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/create-deploy-python-flask.html>`_
- `Sharing your Localhost Server with Localtunnel <http://flask.pocoo.org/snippets/89/>`_ - `Sharing your Localhost Server with Localtunnel <http://flask.pocoo.org/snippets/89/>`_
- `Deploying on Azure (IIS) <https://azure.microsoft.com/documentation/articles/web-sites-python-configure/>`_ - `Deploying on Azure (IIS) <https://azure.microsoft.com/documentation/articles/web-sites-python-configure/>`_
- `Deploying on PythonAnywhere <https://help.pythonanywhere.com/pages/Flask/>`_
Self-hosted options Self-hosted options
------------------- -------------------
@ -30,8 +32,8 @@ Self-hosted options
.. toctree:: .. toctree::
:maxdepth: 2 :maxdepth: 2
mod_wsgi
wsgi-standalone wsgi-standalone
uwsgi uwsgi
mod_wsgi
fastcgi fastcgi
cgi cgi

12
docs/deploying/mod_wsgi.rst

@ -13,7 +13,7 @@ If you are using the `Apache`_ webserver, consider using `mod_wsgi`_.
not called because this will always start a local WSGI server which not called because this will always start a local WSGI server which
we do not want if we deploy that application to mod_wsgi. we do not want if we deploy that application to mod_wsgi.
.. _Apache: http://httpd.apache.org/ .. _Apache: https://httpd.apache.org/
Installing `mod_wsgi` Installing `mod_wsgi`
--------------------- ---------------------
@ -114,7 +114,7 @@ refuse to run with the above configuration. On a Windows system, eliminate those
Note: There have been some changes in access control configuration for `Apache 2.4`_. Note: There have been some changes in access control configuration for `Apache 2.4`_.
.. _Apache 2.4: http://httpd.apache.org/docs/trunk/upgrading.html .. _Apache 2.4: https://httpd.apache.org/docs/trunk/upgrading.html
Most notably, the syntax for directory permissions has changed from httpd 2.2 Most notably, the syntax for directory permissions has changed from httpd 2.2
@ -130,12 +130,12 @@ to httpd 2.4 syntax
Require all granted Require all granted
For more information consult the `mod_wsgi wiki`_. For more information consult the `mod_wsgi documentation`_.
.. _mod_wsgi: http://code.google.com/p/modwsgi/ .. _mod_wsgi: https://github.com/GrahamDumpleton/mod_wsgi
.. _installation instructions: http://code.google.com/p/modwsgi/wiki/QuickInstallationGuide .. _installation instructions: https://modwsgi.readthedocs.io/en/develop/installation.html
.. _virtual python: https://pypi.python.org/pypi/virtualenv .. _virtual python: https://pypi.python.org/pypi/virtualenv
.. _mod_wsgi wiki: http://code.google.com/p/modwsgi/w/list .. _mod_wsgi documentation: https://modwsgi.readthedocs.io/en/develop/index.html
Troubleshooting Troubleshooting
--------------- ---------------

4
docs/deploying/uwsgi.rst

@ -66,7 +66,7 @@ to have it in the URL root its a bit simpler::
uwsgi_pass unix:/tmp/yourapplication.sock; uwsgi_pass unix:/tmp/yourapplication.sock;
} }
.. _nginx: http://nginx.org/ .. _nginx: https://nginx.org/
.. _lighttpd: http://www.lighttpd.net/ .. _lighttpd: https://www.lighttpd.net/
.. _cherokee: http://cherokee-project.com/ .. _cherokee: http://cherokee-project.com/
.. _uwsgi: http://projects.unbit.it/uwsgi/ .. _uwsgi: http://projects.unbit.it/uwsgi/

16
docs/deploying/wsgi-standalone.rst

@ -27,6 +27,22 @@ For example, to run a Flask application with 4 worker processes (``-w
.. _eventlet: http://eventlet.net/ .. _eventlet: http://eventlet.net/
.. _greenlet: https://greenlet.readthedocs.io/en/latest/ .. _greenlet: https://greenlet.readthedocs.io/en/latest/
uWSGI
--------
`uWSGI`_ is a fast application server written in C. It is very configurable
which makes it more complicated to setup than gunicorn.
Running `uWSGI HTTP Router`_::
uwsgi --http 127.0.0.1:5000 --module myproject:app
For a more optimized setup, see `configuring uWSGI and NGINX`_.
.. _uWSGI: http://uwsgi-docs.readthedocs.io/en/latest/
.. _uWSGI HTTP Router: http://uwsgi-docs.readthedocs.io/en/latest/HTTP.html#the-uwsgi-http-https-router
.. _configuring uWSGI and NGINX: uwsgi.html#starting-your-app-with-uwsgi
Gevent Gevent
------- -------

82
docs/errorhandling.rst

@ -34,7 +34,7 @@ Error Logging Tools
Sending error mails, even if just for critical ones, can become Sending error mails, even if just for critical ones, can become
overwhelming if enough users are hitting the error and log files are overwhelming if enough users are hitting the error and log files are
typically never looked at. This is why we recommend using `Sentry typically never looked at. This is why we recommend using `Sentry
<http://www.getsentry.com/>`_ for dealing with application errors. It's <https://www.getsentry.com/>`_ for dealing with application errors. It's
available as an Open Source project `on GitHub available as an Open Source project `on GitHub
<https://github.com/getsentry/sentry>`__ and is also available as a `hosted version <https://github.com/getsentry/sentry>`__ and is also available as a `hosted version
<https://getsentry.com/signup/>`_ which you can try for free. Sentry <https://getsentry.com/signup/>`_ which you can try for free. Sentry
@ -51,7 +51,7 @@ And then add this to your Flask app::
from raven.contrib.flask import Sentry from raven.contrib.flask import Sentry
sentry = Sentry(app, dsn='YOUR_DSN_HERE') sentry = Sentry(app, dsn='YOUR_DSN_HERE')
Of if you are using factories you can also init it later:: Or if you are using factories you can also init it later::
from raven.contrib.flask import Sentry from raven.contrib.flask import Sentry
sentry = Sentry(dsn='YOUR_DSN_HERE') sentry = Sentry(dsn='YOUR_DSN_HERE')
@ -76,49 +76,72 @@ Error handlers
You might want to show custom error pages to the user when an error occurs. You might want to show custom error pages to the user when an error occurs.
This can be done by registering error handlers. This can be done by registering error handlers.
Error handlers are normal :ref:`views` but instead of being registered for An error handler is a normal view function that return a response, but instead
routes they are registered for exceptions that are rised while trying to of being registered for a route, it is registered for an exception or HTTP
do something else. status code that would is raised while trying to handle a request.
Registering Registering
``````````` ```````````
Register error handlers using :meth:`~flask.Flask.errorhandler` or Register handlers by decorating a function with
:meth:`~flask.Flask.register_error_handler`:: :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. ::
@app.errorhandler(werkzeug.exceptions.BadRequest) @app.errorhandler(werkzeug.exceptions.BadRequest)
def handle_bad_request(e): def handle_bad_request(e):
return 'bad request!' return 'bad request!', 400
app.register_error_handler(400, lambda e: 'bad request!') # or, without the decorator
app.register_error_handler(400, handle_bad_request)
Those two ways are equivalent, but the first one is more clear and leaves
you with a function to call on your whim (and in tests). Note that
:exc:`werkzeug.exceptions.HTTPException` subclasses like :exc:`werkzeug.exceptions.HTTPException` subclasses like
:exc:`~werkzeug.exceptions.BadRequest` from the example and their HTTP codes :exc:`~werkzeug.exceptions.BadRequest` and their HTTP codes are interchangeable
are interchangeable when handed to the registration methods or decorator when registering handlers. (``BadRequest.code == 400``)
(``BadRequest.code == 400``).
You are however not limited to :exc:`~werkzeug.exceptions.HTTPException` Non-standard HTTP codes cannot be registered by code because they are not known
or HTTP status codes but can register a handler for every exception class you by Werkzeug. Instead, define a subclass of
like. :class:`~werkzeug.exceptions.HTTPException` with the appropriate code and
register and raise that exception class. ::
.. versionchanged:: 0.11 class InsufficientStorage(werkzeug.exceptions.HTTPException):
code = 507
description = 'Not enough storage space.'
app.register_error_handler(InsuffcientStorage, handle_507)
Errorhandlers are now prioritized by specificity of the exception classes raise InsufficientStorage()
they are registered for instead of the order they are registered in.
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 Handling
```````` ````````
Once an exception instance is raised, its class hierarchy is traversed, When an exception is caught by Flask while handling a request, it is first
and searched for in the exception classes for which handlers are registered. looked up by code. If no handler is registered for the code, it is looked up
The most specific handler is selected. 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.
E.g. if an instance of :exc:`ConnectionRefusedError` is raised, and a handler For example, if an instance of :exc:`ConnectionRefusedError` is raised, and a handler
is registered for :exc:`ConnectionError` and :exc:`ConnectionRefusedError`, is registered for :exc:`ConnectionError` and :exc:`ConnectionRefusedError`,
the more specific :exc:`ConnectionRefusedError` handler is called on the the more specific :exc:`ConnectionRefusedError` handler is called with the
exception instance, and its response is shown to the user. 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.
.. versionchanged:: 0.11
Handlers are prioritized by specificity of the exception classes they are
registered for instead of the order they are registered in.
Error Mails Error Mails
----------- -----------
@ -216,7 +239,7 @@ A formatter can be instantiated with a format string. Note that
tracebacks are appended to the log entry automatically. You don't have to tracebacks are appended to the log entry automatically. You don't have to
do that in the log formatter format string. do that in the log formatter format string.
Here some example setups: Here are some example setups:
Email Email
````` `````
@ -276,8 +299,9 @@ that this list is not complete, consult the official documentation of the
| ``%(lineno)d`` | Source line number where the logging call was | | ``%(lineno)d`` | Source line number where the logging call was |
| | issued (if available). | | | issued (if available). |
+------------------+----------------------------------------------------+ +------------------+----------------------------------------------------+
| ``%(asctime)s`` | Human-readable time when the LogRecord` was | | ``%(asctime)s`` | Human-readable time when the |
| | created. By default this is of the form | | | :class:`~logging.LogRecord` was created. |
| | By default this is of the form |
| | ``"2003-07-08 16:49:45,896"`` (the numbers after | | | ``"2003-07-08 16:49:45,896"`` (the numbers after |
| | the comma are millisecond portion of the time). | | | the comma are millisecond portion of the time). |
| | This can be changed by subclassing the formatter | | | This can be changed by subclassing the formatter |

28
docs/extensiondev.rst

@ -29,12 +29,6 @@ be something like "Flask-SimpleXML". Make sure to include the name
This is how users can then register dependencies to your extension in This is how users can then register dependencies to your extension in
their :file:`setup.py` files. their :file:`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 what do extensions look like themselves? An extension has to ensure But what do extensions look like themselves? An extension has to ensure
that it works with multiple Flask application instances at once. This is that it works with multiple Flask application instances at once. This is
a requirement because many people will use patterns like the a requirement because many people will use patterns like the
@ -48,7 +42,7 @@ that people can easily install the development version into their
virtualenv without having to download the library by hand. virtualenv without having to download the library by hand.
Flask extensions must be licensed under a BSD, MIT or more liberal license Flask extensions must be licensed under a BSD, MIT or more liberal license
to be able to be enlisted in the Flask Extension Registry. Keep in mind in order to be listed in the Flask Extension Registry. Keep in mind
that the Flask Extension Registry is a moderated place and libraries will that the Flask Extension Registry is a moderated place and libraries will
be reviewed upfront if they behave as required. be reviewed upfront if they behave as required.
@ -154,10 +148,10 @@ What to use depends on what you have in mind. For the SQLite 3 extension
we will use the class-based approach because it will provide users with an we will use the class-based approach because it will provide users with an
object that handles opening and closing database connections. object that handles opening and closing database connections.
What's important about classes is that they encourage to be shared around When designing your classes, it's important to make them easily reusable
on module level. In that case, the object itself must not under any at the module level. This means the object itself must not under any
circumstances store any application specific state and must be shareable circumstances store any application specific state and must be shareable
between different application. between different applications.
The Extension Code The Extension Code
------------------ ------------------
@ -334,10 +328,10 @@ 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 out existing extensions on the `Flask Extension Registry`_. If you feel
lost there is still the `mailinglist`_ and the `IRC channel`_ to get some 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 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 you did, it might be a very good idea to get some more input. This not only
only to get an idea about what people might want to have from an generates useful feedback on what people might want from an extension, but
extension, but also to avoid having multiple developers working on pretty also avoids having multiple developers working in isolation on pretty much the
much the same side by side. same problem.
Remember: good API design is hard, so introduce your project on the Remember: good API design is hard, so introduce your project on the
mailinglist, and let other developers give you a helping hand with mailinglist, and let other developers give you a helping hand with
@ -389,11 +383,9 @@ extension to be approved you have to follow these guidelines:
(``PackageName==dev``). (``PackageName==dev``).
9. The ``zip_safe`` flag in the setup script must be set to ``False``, 9. The ``zip_safe`` flag in the setup script must be set to ``False``,
even if the extension would be safe for zipping. even if the extension would be safe for zipping.
10. An extension currently has to support Python 2.6 as well as 10. An extension currently has to support Python 2.7, Python 3.3 and higher.
Python 2.7
.. _ext-import-transition:
Extension Import Transition Extension Import Transition
--------------------------- ---------------------------
@ -413,6 +405,6 @@ schema. The ``flask.ext.foo`` compatibility alias is still in Flask 0.11 but is
now deprecated -- you should use ``flask_foo``. now deprecated -- you should use ``flask_foo``.
.. _OAuth extension: http://pythonhosted.org/Flask-OAuth/ .. _OAuth extension: https://pythonhosted.org/Flask-OAuth/
.. _mailinglist: http://flask.pocoo.org/mailinglist/ .. _mailinglist: http://flask.pocoo.org/mailinglist/
.. _IRC channel: http://flask.pocoo.org/community/irc/ .. _IRC channel: http://flask.pocoo.org/community/irc/

86
docs/flaskext.py

@ -1,86 +0,0 @@
# 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'
}

230
docs/installation.rst

@ -3,163 +3,173 @@
Installation Installation
============ ============
Flask depends on some external libraries, like `Werkzeug Python Version
<http://werkzeug.pocoo.org/>`_ and `Jinja2 <http://jinja.pocoo.org/>`_. --------------
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.
So how do you get all that on your computer quickly? There are many ways you We recommend using the latest version of Python 3. Flask supports Python 3.3
could do that, but the most kick-ass method is virtualenv, so let's have a look and newer, Python 2.6 and newer, and PyPy.
at that first.
You will need Python 2.6 or newer to get started, so be sure to have an Dependencies
up-to-date Python 2.x installation. For using Flask with Python 3 have a ------------
look at :ref:`python3-support`.
.. _virtualenv: These distributions will be installed automatically when installing Flask.
virtualenv * `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.
Virtualenv is probably what you want to use during development, and if you have .. _Werkzeug: http://werkzeug.pocoo.org/
shell access to your production machines, you'll probably want to use it there, .. _Jinja: http://jinja.pocoo.org/
too. .. _MarkupSafe: https://pypi.python.org/pypi/MarkupSafe
.. _ItsDangerous: https://pythonhosted.org/itsdangerous/
.. _Click: http://click.pocoo.org/
What problem does virtualenv solve? If you like Python as much as I do, Optional dependencies
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! Virtualenv enables multiple side-by-side These distributions will not be installed automatically. Flask will detect and
installations of Python, one for each project. It doesn't actually install use them if you install them.
separate copies of Python, but it does provide a clever way to keep different
project environments isolated. Let's see how virtualenv works.
If you are on Mac OS X or Linux, chances are that the following * `Blinker`_ provides support for :ref:`signals`.
command will work for you:: * `SimpleJSON`_ is a fast JSON implementation that is compatible with
Python's ``json`` module. It is preferred for JSON operations if it is
installed.
$ sudo pip install virtualenv .. _Blinker: https://pythonhosted.org/blinker/
.. _SimpleJSON: https://simplejson.readthedocs.io/
It will probably install virtualenv on your system. Maybe it's even Virtual environments
in your package manager. If you use Ubuntu, try:: --------------------
$ sudo apt-get install python-virtualenv Use a virtual environment to manage the dependencies for your project, both in
development and in production.
If you are on Windows and don't have the ``easy_install`` command, you must What problem does a virtual environment solve? The more Python projects you
install it first. Check the :ref:`windows-easy-install` section for more have, the more likely it is that you need to work with different versions of
information about how to do that. Once you have it installed, run the same Python libraries, or even Python itself. Newer versions of libraries for one
commands as above, but without the ``sudo`` prefix. project can break compatibility in another project.
Once you have virtualenv installed, just fire up a shell and create Virtual environments are independent groups of Python libraries, one for each
your own environment. I usually create a project folder and a :file:`venv` project. Packages installed for one project will not affect other projects or
folder within:: the operating system's packages.
$ mkdir myproject Python 3 comes bundled with the :mod:`venv` module to create virtual
$ cd myproject environments. If you're using a modern version of Python, you can continue on
$ virtualenv venv to the next section.
New python executable in venv/bin/python
Installing setuptools, pip............done.
Now, whenever you want to work on a project, you only have to activate the If you're using Python 2, see :ref:`install-install-virtualenv` first.
corresponding environment. On OS X and Linux, do the following::
$ . venv/bin/activate .. _install-create-env:
If you are a Windows user, the following command is for you:: Create an environment
~~~~~~~~~~~~~~~~~~~~~
$ venv\scripts\activate Create a project folder and a :file:`venv` folder within:
Either way, you should now be using your virtualenv (notice how the prompt of .. code-block:: sh
your shell has changed to show the active environment).
And if you want to go back to the real world, use the following command:: mkdir myproject
cd myproject
python3 -m venv venv
$ deactivate On Windows:
After doing this, the prompt of your shell should be as familiar as before. .. code-block:: bat
Now, let's move on. Enter the following command to get Flask activated in your py -3 -m venv venv
virtualenv::
$ pip install Flask If you needed to install virtualenv because you are on an older version of
Python, use the following command instead:
A few seconds later and you are good to go. .. code-block:: sh
virtualenv venv
System-Wide Installation On Windows:
------------------------
This is possible as well, though I do not recommend it. Just run .. code-block:: bat
``pip`` with root privileges::
$ sudo pip install Flask \Python27\Scripts\virtualenv.exe venv
(On Windows systems, run it in a command-prompt window with administrator Activate the environment
privileges, and leave out ``sudo``.) ~~~~~~~~~~~~~~~~~~~~~~~~
Before you work on your project, activate the corresponding environment:
Living on the Edge .. code-block:: sh
------------------
. venv/bin/activate
On Windows:
.. code-block:: bat
venv\Scripts\activate
Your shell prompt will change to show the name of the activated environment.
Install Flask
-------------
If you want to work with the latest version of Flask, there are two ways: you Within the activated environment, use the following command to install Flask:
can either let ``pip`` pull in the development version, or you can tell
it to operate on a git checkout. Either way, virtualenv is recommended.
Get the git checkout in a new virtualenv and run in development mode:: .. code-block:: sh
$ git clone http://github.com/pallets/flask.git pip install Flask
Initialized empty Git repository in ~/dev/flask/.git/
$ cd flask Living on the edge
$ virtualenv venv ~~~~~~~~~~~~~~~~~~
New python executable in venv/bin/python
Installing setuptools, pip............done. If you want to work with the latest Flask code before it's released, install or
$ . venv/bin/activate update the code from the master branch:
$ python setup.py develop
... .. code-block:: sh
Finished processing dependencies for Flask
pip install -U https://github.com/pallets/flask/archive/master.tar.gz
.. _install-install-virtualenv:
Install virtualenv
------------------
This will pull in the dependencies and activate the git head as the current If you are using Python 2, the venv module is not available. Instead,
version inside the virtualenv. Then all you have to do is run ``git pull install `virtualenv`_.
origin`` to update to the latest version.
.. _windows-easy-install: On Linux, virtualenv is provided by your package manager:
`pip` and `setuptools` on Windows .. code-block:: sh
---------------------------------
Sometimes getting the standard "Python packaging tools" like ``pip``, ``setuptools`` # Debian, Ubuntu
and ``virtualenv`` can be a little trickier, but nothing very hard. The crucial sudo apt-get install python-virtualenv
package you will need is pip - this will let you install
anything else (like virtualenv). Fortunately there is a "bootstrap script"
you can run to install.
If you don't currently have ``pip``, then `get-pip.py` will install it for you. # CentOS, Fedora
sudo yum install python-virtualenv
`get-pip.py`_ # Arch
sudo pacman -S python-virtualenv
It should be double-clickable once you download it. If you already have ``pip``, If you are on Mac OS X or Windows, download `get-pip.py`_, then:
you can upgrade them by running::
> pip install --upgrade pip setuptools .. code-block:: sh
Most often, once you pull up a command prompt you want to be able to type ``pip`` sudo python2 Downloads/get-pip.py
and ``python`` which will run those things, but this might not automatically happen sudo python2 -m pip install virtualenv
on Windows, because it doesn't know where those executables are (give either a try!).
To fix this, you should be able to navigate to your Python install directory On Windows, as an administrator:
(e.g :file:`C:\Python27`), then go to :file:`Tools`, then :file:`Scripts`, then find the
:file:`win_add2path.py` file and run that. Open a **new** Command Prompt and
check that you can now just type ``python`` to bring up the interpreter.
Finally, to install `virtualenv`_, you can simply run:: .. code-block:: bat
> pip install virtualenv \Python27\python.exe Downloads\get-pip.py
\Python27\python.exe -m pip install virtualenv
Then you can be off on your way following the installation instructions above. Now you can continue to :ref:`install-create-env`.
.. _virtualenv: https://virtualenv.pypa.io/
.. _get-pip.py: https://bootstrap.pypa.io/get-pip.py .. _get-pip.py: https://bootstrap.pypa.io/get-pip.py

4
docs/patterns/appfactories.rst

@ -6,7 +6,7 @@ Application Factories
If you are already using packages and blueprints for your application If you are already using packages and blueprints for your application
(:ref:`blueprints`) there are a couple of really nice ways to further improve (:ref:`blueprints`) there are a couple of really nice ways to further improve
the experience. A common pattern is creating the application object when the experience. A common pattern is creating the application object when
the blueprint is imported. But if you move the creation of this object, the blueprint is imported. But if you move the creation of this object
into a function, you can then create multiple instances of this app later. into a function, you can then create multiple instances of this app later.
So why would you want to do this? So why would you want to do this?
@ -60,7 +60,7 @@ Factories & Extensions
It's preferable to create your extensions and app factories so that the It's preferable to create your extensions and app factories so that the
extension object does not initially get bound to the application. extension object does not initially get bound to the application.
Using `Flask-SQLAlchemy <http://pythonhosted.org/Flask-SQLAlchemy/>`_, Using `Flask-SQLAlchemy <http://flask-sqlalchemy.pocoo.org/>`_,
as an example, you should not do something along those lines:: as an example, you should not do something along those lines::
def create_app(config_filename): def create_app(config_filename):

80
docs/patterns/celery.rst

@ -1,24 +1,27 @@
Celery Based Background Tasks Celery Background Tasks
============================= =======================
Celery is a task queue for Python with batteries included. It used to If your application has a long running task, such as processing some uploaded
have a Flask integration but it became unnecessary after some data or sending email, you don't want to wait for it to finish during a
restructuring of the internals of Celery with Version 3. This guide fills request. Instead, use a task queue to send the necessary data to another
in the blanks in how to properly use Celery with Flask but assumes that process that will run the task in the background while the request returns
you generally already read the `First Steps with Celery immediately.
<http://docs.celeryproject.org/en/latest/getting-started/first-steps-with-celery.html>`_
guide in the official Celery documentation.
Installing Celery Celery is a powerful task queue that can be used for simple background tasks
----------------- as well as complex multi-stage programs and schedules. This guide will show you
how to configure Celery using Flask, but assumes you've already read the
`First Steps with Celery <http://docs.celeryproject.org/en/latest/getting-started/first-steps-with-celery.html>`_
guide in the Celery documentation.
Celery is on the Python Package Index (PyPI), so it can be installed with Install
standard Python tools like :command:`pip` or :command:`easy_install`:: -------
Celery is a separate Python package. Install it from PyPI using pip::
$ pip install celery $ pip install celery
Configuring Celery Configure
------------------ ---------
The first thing you need is a Celery instance, this is called the celery The first thing you need is a Celery instance, this is called the celery
application. It serves the same purpose as the :class:`~flask.Flask` application. It serves the same purpose as the :class:`~flask.Flask`
@ -36,15 +39,18 @@ This is all that is necessary to properly integrate Celery with Flask::
from celery import Celery from celery import Celery
def make_celery(app): def make_celery(app):
celery = Celery(app.import_name, backend=app.config['CELERY_BACKEND'], celery = Celery(
broker=app.config['CELERY_BROKER_URL']) app.import_name,
backend=app.config['CELERY_RESULT_BACKEND'],
broker=app.config['CELERY_BROKER_URL']
)
celery.conf.update(app.config) celery.conf.update(app.config)
TaskBase = celery.Task
class ContextTask(TaskBase): class ContextTask(celery.Task):
abstract = True
def __call__(self, *args, **kwargs): def __call__(self, *args, **kwargs):
with app.app_context(): with app.app_context():
return TaskBase.__call__(self, *args, **kwargs) return self.run(*args, **kwargs)
celery.Task = ContextTask celery.Task = ContextTask
return celery return celery
@ -53,11 +59,12 @@ from the application config, updates the rest of the Celery config from
the Flask config and then creates a subclass of the task that wraps the the Flask config and then creates a subclass of the task that wraps the
task execution in an application context. task execution in an application context.
Minimal Example An example task
--------------- ---------------
With what we have above this is the minimal example of using Celery with Let's write a task that adds two numbers together and returns the result. We
Flask:: configure Celery's broker and backend to use Redis, create a ``celery``
application using the factor from above, and then use it to define the task. ::
from flask import Flask from flask import Flask
@ -68,26 +75,27 @@ Flask::
) )
celery = make_celery(flask_app) celery = make_celery(flask_app)
@celery.task() @celery.task()
def add_together(a, b): def add_together(a, b):
return a + b return a + b
This task can now be called in the background: This task can now be called in the background::
>>> result = add_together.delay(23, 42) result = add_together.delay(23, 42)
>>> result.wait() result.wait() # 65
65
Running the Celery Worker Run a worker
------------------------- ------------
Now if you jumped in and already executed the above code you will be If you jumped in and already executed the above code you will be
disappointed to learn that your ``.wait()`` will never actually return. disappointed to learn that ``.wait()`` will never actually return.
That's because you also need to run celery. You can do that by running That's because you also need to run a Celery worker to receive and execute the
celery as a worker:: task. ::
$ celery -A your_application.celery worker $ celery -A your_application.celery worker
The ``your_application`` string has to point to your application's package The ``your_application`` string has to point to your application's package
or module that creates the `celery` object. or module that creates the ``celery`` object.
Now that the worker is running, ``wait`` will return the result once the task
is finished.

80
docs/patterns/deferredcallbacks.rst

@ -3,71 +3,43 @@
Deferred Request Callbacks Deferred Request Callbacks
========================== ==========================
One of the design principles of Flask is that response objects are created One of the design principles of Flask is that response objects are created and
and passed down a chain of potential callbacks that can modify them or passed down a chain of potential callbacks that can modify them or replace
replace them. When the request handling starts, there is no response them. When the request handling starts, there is no response object yet. It is
object yet. It is created as necessary either by a view function or by created as necessary either by a view function or by some other component in
some other component in the system. the system.
But what happens if you want to modify the response at a point where the What happens if you want to modify the response at a point where the response
response does not exist yet? A common example for that would be a 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. :meth:`~flask.Flask.before_request` callback 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 an after-request callback One way is to avoid the situation. Very often that is possible. For instance
instead. Sometimes however moving that code there is just not a very you can try to move that logic into a :meth:`~flask.Flask.after_request`
pleasant experience or makes code look very awkward. callback instead. However, sometimes moving code there makes it more
more complicated or awkward to reason about.
As an alternative possibility you can attach a bunch of callback functions
to the :data:`~flask.g` object and call them at the end of the request. As an alternative, you can use :func:`~flask.after_this_request` to register
This way you can defer code execution from anywhere in the application. 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.
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', ()):
callback(response)
return response
A Practical Example
-------------------
At any time during a request, we can register a function to be called at the 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 end of the request. For example you can remember the current language of the
user in a cookie in the before-request function:: user in a cookie in a :meth:`~flask.Flask.before_request` callback::
from flask import request from flask import request, after_this_request
@app.before_request @app.before_request
def detect_user_language(): def detect_user_language():
language = request.cookies.get('user_lang') language = request.cookies.get('user_lang')
if language is None: if language is None:
language = guess_language_from_request() language = guess_language_from_request()
# when the response exists, set a cookie with the language
@after_this_request @after_this_request
def remember_language(response): def remember_language(response):
response.set_cookie('user_lang', language) response.set_cookie('user_lang', language)
g.language = language g.language = language

2
docs/patterns/distribute.rst

@ -174,4 +174,4 @@ the code without having to run ``install`` again after each change.
.. _pip: https://pypi.python.org/pypi/pip .. _pip: https://pypi.python.org/pypi/pip
.. _Setuptools: https://pythonhosted.org/setuptools .. _Setuptools: https://pypi.python.org/pypi/setuptools

40
docs/patterns/errorpages.rst

@ -47,28 +47,45 @@ even if the application behaves correctly:
Error Handlers Error Handlers
-------------- --------------
An error handler is a function, just like a view function, but it is An error handler is a function that returns a response when a type of error is
called when an error happens and is passed that error. The error is most raised, similar to how a view is a function that returns a response when a
likely a :exc:`~werkzeug.exceptions.HTTPException`, but in one case it request URL is matched. It is passed the instance of the error being handled,
can be a different error: a handler for internal server errors will be which is most likely a :exc:`~werkzeug.exceptions.HTTPException`. An error
passed other exception instances as well if they are uncaught. handler for "500 Internal Server Error" will be passed uncaught exceptions in
addition to explicit 500 errors.
An error handler is registered with the :meth:`~flask.Flask.errorhandler` An error handler is registered with the :meth:`~flask.Flask.errorhandler`
decorator and the error code of the exception. Keep in mind that Flask decorator or the :meth:`~flask.Flask.register_error_handler` method. A handler
will *not* set the error code for you, so make sure to also provide the can be registered for a status code, like 404, or for an exception class.
HTTP status code when returning a response.
Please note that if you add an error handler for "500 Internal Server The status code of the response will not be set to the handler's code. Make
Error", Flask will not trigger it if it's running in Debug mode. sure to provide the appropriate HTTP status code when returning a response from
a handler.
Here an example implementation for a "404 Page Not Found" exception:: A handler for "500 Internal Server Error" will not be used when running in
debug mode. Instead, the interactive debugger will be shown.
Here is an example implementation for a "404 Page Not Found" exception::
from flask import render_template from flask import render_template
@app.errorhandler(404) @app.errorhandler(404)
def page_not_found(e):
# note that we set the 404 status explicitly
return render_template('404.html'), 404
When using the :ref:`application factory pattern <app-factories>`::
from flask import Flask, render_template
def page_not_found(e): def page_not_found(e):
return render_template('404.html'), 404 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: An example template might be this:
.. sourcecode:: html+jinja .. sourcecode:: html+jinja
@ -80,4 +97,3 @@ An example template might be this:
<p>What you were looking for is just not there. <p>What you were looking for is just not there.
<p><a href="{{ url_for('index') }}">go somewhere nice</a> <p><a href="{{ url_for('index') }}">go somewhere nice</a>
{% endblock %} {% endblock %}

2
docs/patterns/favicon.rst

@ -49,5 +49,5 @@ web server's documentation.
See also See also
-------- --------
* The `Favicon <http://en.wikipedia.org/wiki/Favicon>`_ article on * The `Favicon <https://en.wikipedia.org/wiki/Favicon>`_ article on
Wikipedia Wikipedia

8
docs/patterns/fileuploads.rst

@ -21,7 +21,7 @@ specific upload folder and displays a file to the user. Let's look at the
bootstrapping code for our application:: bootstrapping code for our application::
import os import os
from flask import Flask, request, redirect, url_for from flask import Flask, flash, request, redirect, url_for
from werkzeug.utils import secure_filename from werkzeug.utils import secure_filename
UPLOAD_FOLDER = '/path/to/the/uploads' UPLOAD_FOLDER = '/path/to/the/uploads'
@ -58,7 +58,7 @@ the file and redirects the user to the URL for the uploaded file::
return redirect(request.url) return redirect(request.url)
file = request.files['file'] file = request.files['file']
# if user does not select file, browser also # if user does not select file, browser also
# submit a empty part without filename # submit an empty part without filename
if file.filename == '': if file.filename == '':
flash('No selected file') flash('No selected file')
return redirect(request.url) return redirect(request.url)
@ -72,7 +72,7 @@ the file and redirects the user to the URL for the uploaded file::
<title>Upload new File</title> <title>Upload new File</title>
<h1>Upload new File</h1> <h1>Upload new File</h1>
<form method=post enctype=multipart/form-data> <form method=post enctype=multipart/form-data>
<p><input type=file name=file> <input type=file name=file>
<input type=submit value=Upload> <input type=submit value=Upload>
</form> </form>
''' '''
@ -181,4 +181,4 @@ applications dealing with uploads, there is also a Flask extension called
blacklisting of extensions and more. blacklisting of extensions and more.
.. _jQuery: https://jquery.com/ .. _jQuery: https://jquery.com/
.. _Flask-Uploads: http://pythonhosted.org/Flask-Uploads/ .. _Flask-Uploads: https://pythonhosted.org/Flask-Uploads/

52
docs/patterns/packages.rst

@ -8,15 +8,19 @@ module. That is quite simple. Imagine a small application looks like
this:: this::
/yourapplication /yourapplication
/yourapplication.py yourapplication.py
/static /static
/style.css style.css
/templates /templates
layout.html layout.html
index.html index.html
login.html login.html
... ...
If you find yourself stuck on something, feel free
to take a look at the source code for this example.
You'll find `the full src for this example here`_.
Simple Packages Simple Packages
--------------- ---------------
@ -29,9 +33,9 @@ You should then end up with something like that::
/yourapplication /yourapplication
/yourapplication /yourapplication
/__init__.py __init__.py
/static /static
/style.css style.css
/templates /templates
layout.html layout.html
index.html index.html
@ -41,11 +45,36 @@ You should then end up with something like that::
But how do you run your application now? The naive ``python But how do you run your application now? The naive ``python
yourapplication/__init__.py`` will not work. Let's just say that Python yourapplication/__init__.py`` will not work. Let's just say that Python
does not want modules in packages to be the startup file. But that is not does not want modules in packages to be the startup file. But that is not
a big problem, just add a new file called :file:`runserver.py` next to the inner a big problem, just add a new file called :file:`setup.py` next to the inner
:file:`yourapplication` folder with the following contents:: :file:`yourapplication` folder with the following contents::
from yourapplication import app from setuptools import setup
app.run(debug=True)
setup(
name='yourapplication',
packages=['yourapplication'],
include_package_data=True,
install_requires=[
'flask',
],
)
In order to run the application you need to export an environment variable
that tells Flask where to find the application instance::
export FLASK_APP=yourapplication
If you are outside of the project directory make sure to provide the exact
path to your application directory. Similarly you can turn on "debug
mode" with this environment variable::
export FLASK_DEBUG=true
In order to install and run the application you need to issue the following
commands::
pip install -e .
flask run
What did we gain from this? Now we can restructure the application a bit What did we gain from this? Now we can restructure the application a bit
into multiple modules. The only thing you have to remember is the into multiple modules. The only thing you have to remember is the
@ -77,12 +106,12 @@ And this is what :file:`views.py` would look like::
You should then end up with something like that:: You should then end up with something like that::
/yourapplication /yourapplication
/runserver.py setup.py
/yourapplication /yourapplication
/__init__.py __init__.py
/views.py views.py
/static /static
/style.css style.css
/templates /templates
layout.html layout.html
index.html index.html
@ -105,6 +134,7 @@ You should then end up with something like that::
.. _working-with-modules: .. _working-with-modules:
.. _the full src for this example here: https://github.com/pallets/flask/tree/master/examples/patterns/largerapp
Working with Blueprints Working with Blueprints
----------------------- -----------------------

10
docs/patterns/sqlalchemy.rst

@ -22,7 +22,7 @@ if you want to get started quickly.
You can download `Flask-SQLAlchemy`_ from `PyPI You can download `Flask-SQLAlchemy`_ from `PyPI
<https://pypi.python.org/pypi/Flask-SQLAlchemy>`_. <https://pypi.python.org/pypi/Flask-SQLAlchemy>`_.
.. _Flask-SQLAlchemy: http://pythonhosted.org/Flask-SQLAlchemy/ .. _Flask-SQLAlchemy: http://flask-sqlalchemy.pocoo.org/
Declarative Declarative
@ -108,9 +108,9 @@ Querying is simple as well:
>>> User.query.filter(User.name == 'admin').first() >>> User.query.filter(User.name == 'admin').first()
<User u'admin'> <User u'admin'>
.. _SQLAlchemy: http://www.sqlalchemy.org/ .. _SQLAlchemy: https://www.sqlalchemy.org/
.. _declarative: .. _declarative:
http://docs.sqlalchemy.org/en/latest/orm/extensions/declarative/ https://docs.sqlalchemy.org/en/latest/orm/extensions/declarative/
Manual Object Relational Mapping Manual Object Relational Mapping
-------------------------------- --------------------------------
@ -135,7 +135,7 @@ Here is an example :file:`database.py` module for your application::
def init_db(): def init_db():
metadata.create_all(bind=engine) metadata.create_all(bind=engine)
As for the declarative approach you need to close the session after As in the declarative approach, you need to close the session after
each request or application context shutdown. Put this into your each request or application context shutdown. Put this into your
application module:: application module::
@ -215,4 +215,4 @@ You can also pass strings of SQL statements to the
(1, u'admin', u'admin@localhost') (1, u'admin', u'admin@localhost')
For more information about SQLAlchemy, head over to the For more information about SQLAlchemy, head over to the
`website <http://www.sqlalchemy.org/>`_. `website <https://www.sqlalchemy.org/>`_.

2
docs/patterns/sqlite3.rst

@ -131,7 +131,7 @@ To pass variable parts to the SQL statement, use a question mark in the
statement and pass in the arguments as a list. Never directly add them to statement and pass in the arguments as a list. Never directly add them to
the SQL statement with string formatting because this makes it possible the SQL statement with string formatting because this makes it possible
to attack the application using `SQL Injections to attack the application using `SQL Injections
<http://en.wikipedia.org/wiki/SQL_injection>`_. <https://en.wikipedia.org/wiki/SQL_injection>`_.
Initial Schemas Initial Schemas
--------------- ---------------

2
docs/patterns/wtforms.rst

@ -19,7 +19,7 @@ forms.
fun. You can get it from `PyPI fun. You can get it from `PyPI
<https://pypi.python.org/pypi/Flask-WTF>`_. <https://pypi.python.org/pypi/Flask-WTF>`_.
.. _Flask-WTF: http://pythonhosted.org/Flask-WTF/ .. _Flask-WTF: https://flask-wtf.readthedocs.io/en/stable/
The Forms The Forms
--------- ---------

259
docs/quickstart.rst

@ -50,7 +50,14 @@ to tell your terminal the application to work with by exporting the
$ flask run $ flask run
* Running on http://127.0.0.1:5000/ * Running on http://127.0.0.1:5000/
If you are on Windows you need to use ``set`` instead of ``export``. If you are on Windows, the environment variable syntax depends on command line
interpreter. On Command Prompt::
C:\path\to\app>set FLASK_APP=hello.py
And on PowerShell::
PS C:\path\to\app> $env:FLASK_APP = "hello.py"
Alternatively you can use :command:`python -m flask`:: Alternatively you can use :command:`python -m flask`::
@ -102,9 +109,9 @@ docs to see the alternative method for running a server.
Invalid Import Name Invalid Import Name
``````````````````` ```````````````````
The ``-a`` argument to :command:`flask` is the name of the module to The ``FLASK_APP`` environment variable is the name of the module to import at
import. In case that module is incorrectly named you will get an import :command:`flask run`. In case that module is incorrectly named you will get an
error upon start (or if debug is enabled when you navigate to the import error upon start (or if debug is enabled when you navigate to the
application). It will tell you what it tried to import and why it failed. application). It will tell you what it tried to import and why it failed.
The most common reason is a typo or because you did not actually create an The most common reason is a typo or because you did not actually create an
@ -153,20 +160,22 @@ Screenshot of the debugger in action:
:class: screenshot :class: screenshot
:alt: screenshot of debugger in action :alt: screenshot of debugger in action
More information on using the debugger can be found in the `Werkzeug
documentation`_.
.. _Werkzeug documentation: http://werkzeug.pocoo.org/docs/debug/#using-the-debugger
Have another debugger in mind? See :ref:`working-with-debuggers`. Have another debugger in mind? See :ref:`working-with-debuggers`.
Routing Routing
------- -------
Modern web applications have beautiful URLs. This helps people remember Modern web applications use meaningful URLs to help users. Users are more
the URLs, which is especially handy for applications that are used from likely to like a page and come back if the page uses a meaningful URL they can
mobile devices with slower network connections. If the user can directly remember and use to directly visit a page.
go to the desired page without having to hit the index page it is more
likely they will like the page and come back next time.
As you have seen above, the :meth:`~flask.Flask.route` decorator is used to Use the :meth:`~flask.Flask.route` decorator to bind a function to a URL. ::
bind a function to a URL. Here are some basic examples::
@app.route('/') @app.route('/')
def index(): def index():
@ -176,16 +185,16 @@ bind a function to a URL. Here are some basic examples::
def hello(): def hello():
return 'Hello, World' return 'Hello, World'
But there is more to it! You can make certain parts of the URL dynamic and You can do more! You can make parts of the URL dynamic and attach multiple
attach multiple rules to a function. rules to a function.
Variable Rules Variable Rules
`````````````` ``````````````
To add variable parts to a URL you can mark these special sections as You can add variable sections to a URL by marking sections with
``<variable_name>``. Such a part is then passed as a keyword argument to your ``<variable_name>``. Your function then receives the ``<variable_name>``
function. Optionally a converter can be used by specifying a rule with as a keyword argument. Optionally, you can use a converter to specify the type
``<converter:variable_name>``. Here are some nice examples:: of the argument like ``<converter:variable_name>``. ::
@app.route('/user/<username>') @app.route('/user/<username>')
def show_user_profile(username): def show_user_profile(username):
@ -197,22 +206,23 @@ function. Optionally a converter can be used by specifying a rule with
# show the post with the given id, the id is an integer # show the post with the given id, the id is an integer
return 'Post %d' % post_id return 'Post %d' % post_id
The following converters exist: @app.route('/path/<path:subpath>')
def show_subpath(subpath):
# show the subpath after /path/
return 'Subpath %s' % subpath
=========== =============================================== Converter types:
`string` 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
=========== ===============================================
.. admonition:: Unique URLs / Redirection Behavior ========== ==========================================
``string`` (default) accepts any text without a slash
``int`` accepts positive integers
``float`` accepts positive floating point values
``path`` like ``string`` but also accepts slashes
``uuid`` accepts UUID strings
========== ==========================================
Flask's URL rules are based on Werkzeug's routing module. The idea Unique URLs / Redirection Behavior
behind that module is to ensure beautiful and unique URLs based on ``````````````````````````````````
precedents laid down by Apache and earlier HTTP servers.
Take these two rules:: Take these two rules::
@ -224,84 +234,83 @@ The following converters exist:
def about(): def about():
return 'The about page' return 'The about page'
Though they look rather similar, they differ in their use of the trailing Though they look similar, they differ in their use of the trailing slash in
slash in the URL *definition*. In the first case, the canonical URL for the the URL. In the first case, the canonical URL for the ``projects`` endpoint
``projects`` endpoint has a trailing slash. In that sense, it is similar to uses a trailing slash. It's similar to a folder in a file system; if you
a folder on a filesystem. Accessing it without a trailing slash will cause access the URL without a trailing slash, Flask redirects you to the
Flask to redirect to the canonical URL with the trailing slash. canonical URL with the trailing slash.
In the second case, however, the URL is defined without a trailing slash, In the second case, however, the URL definition lacks a trailing slash,
rather like the pathname of a file on UNIX-like systems. Accessing the URL like the pathname of a file on UNIX-like systems. Accessing the URL with a
with a trailing slash will produce a 404 "Not Found" error. trailing slash produces a 404 “Not Found” error.
This behavior allows relative URLs to continue working even if the trailing This behavior allows relative URLs to continue working even if the trailing
slash is omitted, consistent with how Apache and other servers work. Also, slash is omitted, consistent with how Apache and other servers work. Also,
the URLs will stay unique, which helps search engines avoid indexing the the URLs will stay unique, which helps search engines avoid indexing the
same page twice. same page twice.
.. _url-building: .. _url-building:
URL Building URL Building
```````````` ````````````
If it can match URLs, can Flask also generate them? Of course it can. To To build a URL to a specific function, use the :func:`~flask.url_for` function.
build a URL to a specific function you can use the :func:`~flask.url_for` It accepts the name of the function as its first argument and any number of
function. It accepts the name of the function as first argument and a number keyword arguments, each corresponding to a variable part of the URL rule.
of keyword arguments, each corresponding to the variable part of the URL rule. Unknown variable parts are appended to the URL as query parameters.
Unknown variable parts are appended to the URL as query parameters. Here are
some examples::
>>> from flask import Flask, url_for
>>> app = Flask(__name__)
>>> @app.route('/')
... def index(): pass
...
>>> @app.route('/login')
... def login(): pass
...
>>> @app.route('/user/<username>')
... def profile(username): pass
...
>>> with app.test_request_context():
... print url_for('index')
... print url_for('login')
... print url_for('login', next='/')
... print url_for('profile', username='John Doe')
...
/
/login
/login?next=/
/user/John%20Doe
(This also uses the :meth:`~flask.Flask.test_request_context` method, explained
below. It tells Flask to behave as though it is handling a request, even
though we are interacting with it through a Python shell. Have a look at the
explanation below. :ref:`context-locals`).
Why would you want to build URLs using the URL reversing function Why would you want to build URLs using the URL reversing function
:func:`~flask.url_for` instead of hard-coding them into your templates? :func:`~flask.url_for` instead of hard-coding them into your templates?
There are three good reasons for this:
1. Reversing is often more descriptive than hard-coding the URLs. More 1. Reversing is often more descriptive than hard-coding the URLs.
importantly, it allows you to change URLs in one go, without having to 2. You can change your URLs in one go instead of needing to remember to
remember to change URLs all over the place. manually change hard-coded URLs.
2. URL building will handle escaping of special characters and Unicode 3. URL building handles escaping of special characters and Unicode data
data transparently for you, so you don't have to deal with them. transparently.
3. If your application is placed outside the URL root - say, in 4. If your application is placed outside the URL root, for example, in
``/myapplication`` instead of ``/`` - :func:`~flask.url_for` will handle ``/myapplication`` instead of ``/``, :func:`~flask.url_for` properly
that properly for you. handles that for you.
For example, here we use the :meth:`~flask.Flask.test_request_context` method
to try out :func:`~flask.url_for`. :meth:`~flask.Flask.test_request_context`
tells Flask to behave as though it's handling a request even while we use a
Python shell. See :ref:`context-locals`. ::
from flask import Flask, url_for
app = Flask(__name__)
@app.route('/')
def index():
return 'index'
@app.route('/login')
def login():
return 'login'
@app.route('/user/<username>')
def profile(username):
return '{}'s profile'.format(username)
with app.test_request_context():
print(url_for('index'))
print(url_for('login'))
print(url_for('login', next='/'))
print(url_for('profile', username='John Doe'))
/
/login
/login?next=/
/user/John%20Doe
HTTP Methods HTTP Methods
```````````` ````````````
HTTP (the protocol web applications are speaking) knows different methods for Web applications use different HTTP methods when accessing URLs. You should
accessing URLs. By default, a route only answers to ``GET`` requests, but that familiarize yourself with the HTTP methods as you work with Flask. By default,
can be changed by providing the ``methods`` argument to the a route only answers to ``GET`` requests. You can use the ``methods`` argument
:meth:`~flask.Flask.route` decorator. Here are some examples:: of the :meth:`~flask.Flask.route` decorator to handle different HTTP methods.
::
from flask import request
@app.route('/login', methods=['GET', 'POST']) @app.route('/login', methods=['GET', 'POST'])
def login(): def login():
@ -310,64 +319,11 @@ can be changed by providing the ``methods`` argument to the
else: else:
show_the_login_form() show_the_login_form()
If ``GET`` is present, ``HEAD`` will be added automatically for you. You If ``GET`` is present, Flask automatically adds support for the ``HEAD`` method
don't have to deal with that. It will also make sure that ``HEAD`` requests and handles ``HEAD`` requests according to the the `HTTP RFC`_. Likewise,
are handled as the `HTTP RFC`_ (the document describing the HTTP ``OPTIONS`` is automatically implemented for you.
protocol) demands, so you can completely ignore that part of the HTTP
specification. Likewise, as of Flask 0.6, ``OPTIONS`` is implemented for you .. _HTTP RFC: https://www.ietf.org/rfc/rfc2068.txt
automatically as well.
You have no idea what an HTTP method is? Worry not, here is a quick
introduction to HTTP methods and why they matter:
The HTTP method (also often called "the verb") tells the server what the
client wants to *do* with the requested page. The following methods are
very common:
``GET``
The browser tells the server to just *get* the information stored on
that page and send it. This is probably the most common method.
``HEAD``
The browser tells the server to get the information, but it is only
interested in the *headers*, not the content of the page. An
application is supposed to handle that as if a ``GET`` request was
received but to not deliver the actual content. In Flask you don't
have to deal with that at all, the underlying Werkzeug library handles
that for you.
``POST``
The browser tells the server that it wants to *post* some new
information to that URL and that the server must ensure the data is
stored and only stored once. This is how HTML forms usually
transmit data to the server.
``PUT``
Similar to ``POST`` but the server might trigger the store procedure
multiple times by overwriting the old values more than once. Now you
might be asking why this is useful, but there are some good reasons
to do it this way. Consider that the connection is lost during
transmission: in this situation a system between the browser and the
server might receive the request safely a second time without breaking
things. With ``POST`` that would not be possible because it must only
be triggered once.
``DELETE``
Remove the information at the given location.
``OPTIONS``
Provides a quick way for a client to figure out which methods are
supported by this URL. Starting with Flask 0.6, this is implemented
for you automatically.
Now the interesting part is that in HTML4 and XHTML1, the only methods a
form can submit to the server are ``GET`` and ``POST``. But with JavaScript
and future HTML standards you can use the other methods as well. Furthermore
HTTP has become quite popular lately and browsers are no longer the only
clients that are using HTTP. For instance, many revision control systems
use it.
.. _HTTP RFC: http://www.ietf.org/rfc/rfc2068.txt
Static Files Static Files
------------ ------------
@ -538,16 +494,16 @@ The Request Object
`````````````````` ``````````````````
The request object is documented in the API section and we will not cover The request object is documented in the API section and we will not cover
it here in detail (see :class:`~flask.request`). Here is a broad overview of it here in detail (see :class:`~flask.Request`). Here is a broad overview of
some of the most common operations. First of all you have to import it from some of the most common operations. First of all you have to import it from
the ``flask`` module:: the ``flask`` module::
from flask import request from flask import request
The current request method is available by using the The current request method is available by using the
:attr:`~flask.request.method` attribute. To access form data (data :attr:`~flask.Request.method` attribute. To access form data (data
transmitted in a ``POST`` or ``PUT`` request) you can use the transmitted in a ``POST`` or ``PUT`` request) you can use the
:attr:`~flask.request.form` attribute. Here is a full example of the two :attr:`~flask.Request.form` attribute. Here is a full example of the two
attributes mentioned above:: attributes mentioned above::
@app.route('/login', methods=['POST', 'GET']) @app.route('/login', methods=['POST', 'GET'])
@ -570,7 +526,7 @@ error page is shown instead. So for many situations you don't have to
deal with that problem. deal with that problem.
To access parameters submitted in the URL (``?key=value``) you can use the To access parameters submitted in the URL (``?key=value``) you can use the
:attr:`~flask.request.args` attribute:: :attr:`~flask.Request.args` attribute::
searchword = request.args.get('key', '') searchword = request.args.get('key', '')
@ -579,7 +535,7 @@ We recommend accessing URL parameters with `get` or by catching the
bad request page in that case is not user friendly. bad request page in that case is not user friendly.
For a full list of methods and attributes of the request object, head over For a full list of methods and attributes of the request object, head over
to the :class:`~flask.request` documentation. to the :class:`~flask.Request` documentation.
File Uploads File Uploads
@ -817,6 +773,9 @@ values do not persist across requests, cookies are indeed enabled, and you are
not getting a clear error message, check the size of the cookie in your page not getting a clear error message, check the size of the cookie in your page
responses compared to the size supported by web browsers. responses compared to the size supported by web browsers.
Besides the default client-side based sessions, if you want to handle
sessions on the server-side instead, there are several
Flask extensions that support this.
Message Flashing Message Flashing
---------------- ----------------

13
docs/reqcontext.rst

@ -119,8 +119,8 @@ understand what is actually happening. The new behavior is quite simple:
not executed yet or at all (for example in test environments sometimes not executed yet or at all (for example in test environments sometimes
you might want to not execute before-request callbacks). you might want to not execute before-request callbacks).
Now what happens on errors? In production mode if an exception is not Now what happens on errors? If you are not in debug mode and an exception is not
caught, the 500 internal server handler is called. In development mode caught, the 500 internal server handler is called. In debug mode
however the exception is not further processed and bubbles up to the WSGI however the exception is not further processed and bubbles up to the WSGI
server. That way things like the interactive debugger can provide helpful server. That way things like the interactive debugger can provide helpful
debug information. debug information.
@ -214,10 +214,11 @@ provide you with important information.
Starting with Flask 0.7 you have finer control over that behavior by Starting with Flask 0.7 you have finer control over that behavior by
setting the ``PRESERVE_CONTEXT_ON_EXCEPTION`` configuration variable. By setting the ``PRESERVE_CONTEXT_ON_EXCEPTION`` configuration variable. By
default it's linked to the setting of ``DEBUG``. If the application is in default it's linked to the setting of ``DEBUG``. If the application is in
debug mode the context is preserved, in production mode it's not. debug mode the context is preserved. If debug mode is set to off, the context
is not preserved.
Do not force activate ``PRESERVE_CONTEXT_ON_EXCEPTION`` in production mode Do not force activate ``PRESERVE_CONTEXT_ON_EXCEPTION`` if debug mode is set to off
as it will cause your application to leak memory on exceptions. However as it will cause your application to leak memory on exceptions. However,
it can be useful during development to get the same error preserving it can be useful during development to get the same error preserving
behavior as in development mode when attempting to debug an error that behavior as debug mode when attempting to debug an error that
only occurs under production settings. only occurs under production settings.

104
docs/security.rst

@ -15,7 +15,7 @@ it JavaScript) into the context of a website. To remedy this, developers
have to properly escape text so that it cannot include arbitrary HTML have to properly escape text so that it cannot include arbitrary HTML
tags. For more information on that have a look at the Wikipedia article tags. For more information on that have a look at the Wikipedia article
on `Cross-Site Scripting on `Cross-Site Scripting
<http://en.wikipedia.org/wiki/Cross-site_scripting>`_. <https://en.wikipedia.org/wiki/Cross-site_scripting>`_.
Flask configures Jinja2 to automatically escape all values unless Flask configures Jinja2 to automatically escape all values unless
explicitly told otherwise. This should rule out all XSS problems caused explicitly told otherwise. This should rule out all XSS problems caused
@ -104,3 +104,105 @@ vulnerabilities
<https://github.com/pallets/flask/issues/248#issuecomment-59934857>`_, so <https://github.com/pallets/flask/issues/248#issuecomment-59934857>`_, so
this behavior was changed and :func:`~flask.jsonify` now supports serializing this behavior was changed and :func:`~flask.jsonify` now supports serializing
arrays. arrays.
Security Headers
----------------
Browsers recognize various response headers in order to control security. We
recommend reviewing each of the headers below for use in your application.
The `Flask-Talisman`_ extension can be used to manage HTTPS and the security
headers for you.
.. _Flask-Talisman: https://github.com/GoogleCloudPlatform/flask-talisman
HTTP Strict Transport Security (HSTS)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Tells the browser to convert all HTTP requests to HTTPS, preventing
man-in-the-middle (MITM) attacks. ::
response.haders['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains'
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security
Content Security Policy (CSP)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Tell the browser where it can load various types of resource from. This header
should be used whenever possible, but requires some work to define the correct
policy for your site. A very strict policy would be::
response.headers['Content-Security-Policy'] = "default-src: 'self'"
- https://csp.withgoogle.com/docs/index.html
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
X-Content-Type-Options
~~~~~~~~~~~~~~~~~~~~~~
Forces the browser to honor the response content type instead of trying to
detect it, which can be abused to generate a cross-site scripting (XSS)
attack. ::
response.headers['X-Content-Type-Options'] = 'nosniff'
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options
X-Frame-Options
~~~~~~~~~~~~~~~
Prevents external sites from embedding your site in an ``iframe``. This
prevents a class of attacks where clicks in the outer frame can be translated
invisibly to clicks on your page's elements. This is also known as
"clickjacking". ::
response.headers['X-Frame-Options'] = 'SAMEORIGIN'
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
X-XSS-Protection
~~~~~~~~~~~~~~~~
The browser will try to prevent reflected XSS attacks by not loading the page
if the request contains something that looks like JavaScript and the response
contains the same data. ::
response.headers['X-XSS-Protection'] = '1; mode=block'
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection
Set-Cookie options
~~~~~~~~~~~~~~~~~~
These options can be added to a ``Set-Cookie`` header to improve their
security. Flask has configuration options to set these on the session cookie.
They can be set on other cookies too.
- ``Secure`` limits cookies to HTTPS traffic only.
- ``HttpOnly`` protects the contents of cookies from being read with
JavaScript.
- ``SameSite`` ensures that cookies can only be requested from the same
domain that created them. It is not supported by Flask yet.
::
app.config.update(
SESSION_COOKIE_SECURE=True,
SESSION_COOKIE_HTTPONLY=True,
)
response.set_cookie('username', 'flask', secure=True, httponly=True)
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#Secure_and_HttpOnly_cookies
HTTP Public Key Pinning (HPKP)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This tells the browser to authenticate with the server using only the specific
certificate key to prevent MITM attacks.
.. warning::
Be careful when enabling this, as it is very difficult to undo if you set up
or upgrade your key incorrectly.
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Public_Key_Pinning

2
docs/styleguide.rst

@ -167,7 +167,7 @@ Docstring conventions:
""" """
Module header: Module header:
The module header consists of an utf-8 encoding declaration (if non The module header consists of a utf-8 encoding declaration (if non
ASCII letters are used, but it is recommended all the time) and a ASCII letters are used, but it is recommended all the time) and a
standard docstring:: standard docstring::

192
docs/testing.rst

@ -5,23 +5,30 @@ Testing Flask Applications
**Something that is untested is broken.** **Something that is untested is broken.**
The origin of this quote is unknown and while it is not entirely correct, it is also The origin of this quote is unknown and while it is not entirely correct, it
not far from the truth. Untested applications make it hard to is also not far from the truth. Untested applications make it hard to
improve existing code and developers of untested applications tend to improve existing code and developers of untested applications tend to
become pretty paranoid. If an application has automated tests, you can become pretty paranoid. If an application has automated tests, you can
safely make changes and instantly know if anything breaks. safely make changes and instantly know if anything breaks.
Flask provides a way to test your application by exposing the Werkzeug Flask provides a way to test your application by exposing the Werkzeug
test :class:`~werkzeug.test.Client` and handling the context locals for you. test :class:`~werkzeug.test.Client` and handling the context locals for you.
You can then use that with your favourite testing solution. In this documentation You can then use that with your favourite testing solution.
we will use the :mod:`unittest` package that comes pre-installed with Python.
In this documentation we will use the `pytest`_ package as the base
framework for our tests. You can install it with ``pip``, like so::
pip install pytest
.. _pytest:
https://pytest.org
The Application The Application
--------------- ---------------
First, we need an application to test; we will use the application from First, we need an application to test; we will use the application from
the :ref:`tutorial`. If you don't have that application yet, get the the :ref:`tutorial`. If you don't have that application yet, get the
sources from `the examples`_. source code from `the examples`_.
.. _the examples: .. _the examples:
https://github.com/pallets/flask/tree/master/examples/flaskr/ https://github.com/pallets/flask/tree/master/examples/flaskr/
@ -29,90 +36,91 @@ sources from `the examples`_.
The Testing Skeleton The Testing Skeleton
-------------------- --------------------
In order to test the application, we add a second module We begin by adding a tests directory under the application root. Then
(:file:`flaskr_tests.py`) and create a unittest skeleton there:: create a Python file to store our tests (:file:`test_flaskr.py`). When we
format the filename like ``test_*.py``, it will be auto-discoverable by
pytest.
Next, we create a `pytest fixture`_ called
:func:`client` that configures
the application for testing and initializes a new database.::
import os import os
import flaskr
import unittest
import tempfile import tempfile
class FlaskrTestCase(unittest.TestCase): import pytest
def setUp(self): from flaskr import flaskr
self.db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp()
@pytest.fixture
def client():
db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp()
flaskr.app.config['TESTING'] = True flaskr.app.config['TESTING'] = True
self.app = flaskr.app.test_client() client = flaskr.app.test_client()
with flaskr.app.app_context(): with flaskr.app.app_context():
flaskr.init_db() flaskr.init_db()
def tearDown(self): yield client
os.close(self.db_fd)
os.unlink(flaskr.app.config['DATABASE'])
if __name__ == '__main__': os.close(db_fd)
unittest.main() os.unlink(flaskr.app.config['DATABASE'])
The code in the :meth:`~unittest.TestCase.setUp` method creates a new test This client fixture will be called by each individual test. It gives us a
client and initializes a new database. This function is called before simple interface to the application, where we can trigger test requests to the
each individual test function is run. To delete the database after the application. The client will also keep track of cookies for us.
test, we close the file and remove it from the filesystem in the
:meth:`~unittest.TestCase.tearDown` method. Additionally during setup the
``TESTING`` config flag is activated. What it does is disable the error
catching during request handling so that you get better error reports when
performing test requests against the application.
This test client will give us a simple interface to the application. We can During setup, the ``TESTING`` config flag is activated. What
trigger test requests to the application, and the client will also keep track this does is disable error catching during request handling, so that
of cookies for us. you get better error reports when performing test requests against the
application.
Because SQLite3 is filesystem-based we can easily use the tempfile module Because SQLite3 is filesystem-based, we can easily use the :mod:`tempfile` module
to create a temporary database and initialize it. The to create a temporary database and initialize it. The
:func:`~tempfile.mkstemp` function does two things for us: it returns a :func:`~tempfile.mkstemp` function does two things for us: it returns a
low-level file handle and a random file name, the latter we use as low-level file handle and a random file name, the latter we use as
database name. We just have to keep the `db_fd` around so that we can use database name. We just have to keep the `db_fd` around so that we can use
the :func:`os.close` function to close the file. the :func:`os.close` function to close the file.
To delete the database after the test, the fixture closes the file and removes
it from the filesystem.
If we now run the test suite, we should see the following output:: If we now run the test suite, we should see the following output::
$ python flaskr_tests.py $ pytest
---------------------------------------------------------------------- ================ test session starts ================
Ran 0 tests in 0.000s rootdir: ./flask/examples/flaskr, inifile: setup.cfg
collected 0 items
OK =========== no tests ran in 0.07 seconds ============
Even though it did not run any actual tests, we already know that our flaskr Even though it did not run any actual tests, we already know that our ``flaskr``
application is syntactically valid, otherwise the import would have died application is syntactically valid, otherwise the import would have died
with an exception. with an exception.
.. _pytest fixture:
https://docs.pytest.org/en/latest/fixture.html
The First Test The First Test
-------------- --------------
Now it's time to start testing the functionality of the application. Now it's time to start testing the functionality of the application.
Let's check that the application shows "No entries here so far" if we Let's check that the application shows "No entries here so far" if we
access the root of the application (``/``). To do this, we add a new access the root of the application (``/``). To do this, we add a new
test method to our class, like this:: test function to :file:`test_flaskr.py`, like this::
class FlaskrTestCase(unittest.TestCase):
def setUp(self): def test_empty_db(client):
self.db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp() """Start with a blank database."""
self.app = flaskr.app.test_client()
flaskr.init_db()
def tearDown(self):
os.close(self.db_fd)
os.unlink(flaskr.app.config['DATABASE'])
def test_empty_db(self): rv = client.get('/')
rv = self.app.get('/')
assert b'No entries here so far' in rv.data assert b'No entries here so far' in rv.data
Notice that our test functions begin with the word `test`; this allows Notice that our test functions begin with the word `test`; this allows
:mod:`unittest` to automatically identify the method as a test to run. `pytest`_ to automatically identify the function as a test to run.
By using `self.app.get` we can send an HTTP ``GET`` request to the application with By using ``client.get`` we can send an HTTP ``GET`` request to the application with
the given path. The return value will be a :class:`~flask.Flask.response_class` object. the given path. The return value will be a :class:`~flask.Flask.response_class` object.
We can now use the :attr:`~werkzeug.wrappers.BaseResponse.data` attribute to inspect We can now use the :attr:`~werkzeug.wrappers.BaseResponse.data` attribute to inspect
the return value (as string) from the application. In this case, we ensure that the return value (as string) from the application. In this case, we ensure that
@ -120,12 +128,15 @@ the return value (as string) from the application. In this case, we ensure that
Run it again and you should see one passing test:: Run it again and you should see one passing test::
$ python flaskr_tests.py $ pytest -v
.
----------------------------------------------------------------------
Ran 1 test in 0.034s
OK ================ test session starts ================
rootdir: ./flask/examples/flaskr, inifile: setup.cfg
collected 1 items
tests/test_flaskr.py::test_empty_db PASSED
============= 1 passed in 0.10 seconds ==============
Logging In and Out Logging In and Out
------------------ ------------------
@ -136,67 +147,78 @@ of the application. To do this, we fire some requests to the login and logout
pages with the required form data (username and password). And because the pages with the required form data (username and password). And because the
login and logout pages redirect, we tell the client to `follow_redirects`. login and logout pages redirect, we tell the client to `follow_redirects`.
Add the following two methods to your `FlaskrTestCase` class:: Add the following two functions to your :file:`test_flaskr.py` file::
def login(self, username, password): def login(client, username, password):
return self.app.post('/login', data=dict( return client.post('/login', data=dict(
username=username, username=username,
password=password password=password
), follow_redirects=True) ), follow_redirects=True)
def logout(self):
return self.app.get('/logout', follow_redirects=True) def logout(client):
return client.get('/logout', follow_redirects=True)
Now we can easily test that logging in and out works and that it fails with Now we can easily test that logging in and out works and that it fails with
invalid credentials. Add this new test to the class:: invalid credentials. Add this new test function::
def test_login_logout(self): def test_login_logout(client):
rv = self.login('admin', 'default') """Make sure login and logout works."""
assert 'You were logged in' in rv.data
rv = self.logout() rv = login(client, flaskr.app.config['USERNAME'], flaskr.app.config['PASSWORD'])
assert 'You were logged out' in rv.data assert b'You were logged in' in rv.data
rv = self.login('adminx', 'default')
assert 'Invalid username' in rv.data rv = logout(client)
rv = self.login('admin', 'defaultx') assert b'You were logged out' in rv.data
assert 'Invalid password' in rv.data
rv = login(client, flaskr.app.config['USERNAME'] + 'x', flaskr.app.config['PASSWORD'])
assert b'Invalid username' in rv.data
rv = login(client, flaskr.app.config['USERNAME'], flaskr.app.config['PASSWORD'] + 'x')
assert b'Invalid password' in rv.data
Test Adding Messages Test Adding Messages
-------------------- --------------------
We should also test that adding messages works. Add a new test method We should also test that adding messages works. Add a new test function
like this:: like this::
def test_messages(self): def test_messages(client):
self.login('admin', 'default') """Test that messages work."""
rv = self.app.post('/add', data=dict(
login(client, flaskr.app.config['USERNAME'], flaskr.app.config['PASSWORD'])
rv = client.post('/add', data=dict(
title='<Hello>', title='<Hello>',
text='<strong>HTML</strong> allowed here' text='<strong>HTML</strong> allowed here'
), follow_redirects=True) ), follow_redirects=True)
assert 'No entries here so far' not in rv.data assert b'No entries here so far' not in rv.data
assert '&lt;Hello&gt;' in rv.data assert b'&lt;Hello&gt;' in rv.data
assert '<strong>HTML</strong> allowed here' in rv.data assert b'<strong>HTML</strong> allowed here' in rv.data
Here we check that HTML is allowed in the text but not in the title, Here we check that HTML is allowed in the text but not in the title,
which is the intended behavior. which is the intended behavior.
Running that should now give us three passing tests:: Running that should now give us three passing tests::
$ python flaskr_tests.py $ pytest -v
...
---------------------------------------------------------------------- ================ test session starts ================
Ran 3 tests in 0.332s rootdir: ./flask/examples/flaskr, inifile: setup.cfg
collected 3 items
OK tests/test_flaskr.py::test_empty_db PASSED
tests/test_flaskr.py::test_login_logout PASSED
tests/test_flaskr.py::test_messages PASSED
============= 3 passed in 0.23 seconds ==============
For more complex tests with headers and status codes, check out the For more complex tests with headers and status codes, check out the
`MiniTwit Example`_ from the sources which contains a larger test `MiniTwit Example`_ from the sources which contains a larger test
suite. suite.
.. _MiniTwit Example: .. _MiniTwit Example:
https://github.com/pallets/flask/tree/master/examples/minitwit/ https://github.com/pallets/flask/tree/master/examples/minitwit/
Other Testing Tricks Other Testing Tricks
-------------------- --------------------

2
docs/tutorial/css.rst

@ -1,6 +1,6 @@
.. _tutorial-css: .. _tutorial-css:
Step 9: Adding Style Step 8: Adding Style
==================== ====================
Now that everything else works, it's time to add some style to the Now that everything else works, it's time to add some style to the

5
docs/tutorial/dbcon.rst

@ -3,7 +3,10 @@
Step 4: Database Connections Step 4: Database Connections
---------------------------- ----------------------------
You now have a function for establishing a database connection with Let's continue building our code in the ``flaskr.py`` file.
(Scroll to the end of the page for more about project layout.)
You currently have a function for establishing a database connection with
`connect_db`, but by itself, it is not particularly useful. Creating and `connect_db`, but by itself, it is not particularly useful. Creating and
closing database connections all the time is very inefficient, so you will closing database connections all the time is very inefficient, so you will
need to keep it around for longer. Because database connections need to keep it around for longer. Because database connections

33
docs/tutorial/dbinit.rst

@ -9,33 +9,39 @@ systems need a schema that tells them how to store that information.
Before starting the server for the first time, it's important to create Before starting the server for the first time, it's important to create
that schema. that schema.
Such a schema can be created by piping the ``schema.sql`` file into the Such a schema could be created by piping the ``schema.sql`` file into the
`sqlite3` command as follows:: ``sqlite3`` command as follows::
sqlite3 /tmp/flaskr.db < schema.sql sqlite3 /tmp/flaskr.db < schema.sql
The downside of this is that it requires the ``sqlite3`` command to be However, the downside of this is that it requires the ``sqlite3`` command
installed, which is not necessarily the case on every system. This also to be installed, which is not necessarily the case on every system. This
requires that you provide the path to the database, which can introduce also requires that you provide the path to the database, which can introduce
errors. It's a good idea to add a function that initializes the database errors.
for you, to the application.
To do this, you can create a function and hook it into a :command:`flask` Instead of the ``sqlite3`` command above, it's a good idea to add a function
command that initializes the database. For now just take a look at the to our application that initializes the database for you. To do this, you
code segment below. A good place to add this function, and command, is can create a function and hook it into a :command:`flask` command that
just below the `connect_db` function in :file:`flaskr.py`:: initializes the database.
Take a look at the code segment below. A good place to add this function,
and command, is just below the ``connect_db`` function in :file:`flaskr.py`::
def init_db(): def init_db():
db = get_db() db = get_db()
with app.open_resource('schema.sql', mode='r') as f: with app.open_resource('schema.sql', mode='r') as f:
db.cursor().executescript(f.read()) db.cursor().executescript(f.read())
db.commit() db.commit()
@app.cli.command('initdb') @app.cli.command('initdb')
def initdb_command(): def initdb_command():
"""Initializes the database.""" """Initializes the database."""
init_db() init_db()
print 'Initialized the database.' print('Initialized the database.')
The ``app.cli.command()`` decorator registers a new command with the The ``app.cli.command()`` decorator registers a new command with the
:command:`flask` script. When the command executes, Flask will automatically :command:`flask` script. When the command executes, Flask will automatically
@ -59,7 +65,8 @@ On that cursor, there is a method to execute a complete script. Finally, you
only have to commit the changes. SQLite3 and other transactional only have to commit the changes. SQLite3 and other transactional
databases will not commit unless you explicitly tell it to. databases will not commit unless you explicitly tell it to.
Now, it is possible to create a database with the :command:`flask` script:: Now, in a terminal, from the application root directory :file:`flaskr/` it is
possible to create a database with the :command:`flask` script::
flask initdb flask initdb
Initialized the database. Initialized the database.

16
docs/tutorial/folders.rst

@ -3,8 +3,11 @@
Step 0: Creating The Folders Step 0: Creating The Folders
============================ ============================
Before getting started, you will need to create the folders needed for this It is recommended to install your Flask application within a virtualenv. Please
application:: read the :ref:`installation` section to set up your environment.
Now that you have installed Flask, you will need to create the folders required
for this tutorial. Your directory structure will look like this::
/flaskr /flaskr
/flaskr /flaskr
@ -13,13 +16,14 @@ application::
The application will be installed and run as Python package. This is the The application will be installed and run as Python package. This is the
recommended way to install and run Flask applications. You will see exactly recommended way to install and run Flask applications. You will see exactly
how to run ``flaskr`` later on in this tutorial. For now go ahead and create how to run ``flaskr`` later on in this tutorial.
the applications directory structure. In the next few steps you will be
creating the database schema as well as the main module. For now go ahead and create the applications directory structure. In the next
few steps you will be creating the database schema as well as the main module.
As a quick side note, the files inside of the :file:`static` folder are As a quick side note, the files inside of the :file:`static` folder are
available to users of the application via HTTP. This is the place where CSS and available to users of the application via HTTP. This is the place where CSS and
Javascript files go. Inside the :file:`templates` folder, Flask will look for JavaScript files go. Inside the :file:`templates` folder, Flask will look for
`Jinja2`_ templates. You will see examples of this later on. `Jinja2`_ templates. You will see examples of this later on.
For now you should continue with :ref:`tutorial-schema`. For now you should continue with :ref:`tutorial-schema`.

20
docs/tutorial/index.rst

@ -3,19 +3,19 @@
Tutorial Tutorial
======== ========
You want to develop an application with Python and Flask? Here you have Learn by example to develop an application with Python and Flask.
the chance to learn by example. In this tutorial, we will create a simple
microblogging application. It only supports one user that can create In this tutorial, we will create a simple blogging application. It only
text-only entries and there are no feeds or comments, but it still supports one user, only allows text entries, and has no feeds or comments.
features everything you need to get started. We will use Flask and SQLite
as a database (which comes out of the box with Python) so there is nothing While very simple, this example still features everything you need to get
else you need. started. In addition to Flask, we will use SQLite for the database, which is
built-in to Python, so there is nothing else you need.
If you want the full source code in advance or for comparison, check out If you want the full source code in advance or for comparison, check out
the `example source`_. the `example source`_.
.. _example source: .. _example source: https://github.com/pallets/flask/tree/master/examples/flaskr/
https://github.com/pallets/flask/tree/master/examples/flaskr/
.. toctree:: .. toctree::
:maxdepth: 2 :maxdepth: 2
@ -24,7 +24,7 @@ the `example source`_.
folders folders
schema schema
setup setup
setuptools packaging
dbcon dbcon
dbinit dbinit
views views

4
docs/tutorial/introduction.rst

@ -22,7 +22,7 @@ connections in a more intelligent way, allowing you to target different
relational databases at once and more. You might also want to consider relational databases at once and more. You might also want to consider
one of the popular NoSQL databases if your data is more suited for those. one of the popular NoSQL databases if your data is more suited for those.
Here a screenshot of the final application: Here is a screenshot of the final application:
.. image:: ../_static/flaskr.png .. image:: ../_static/flaskr.png
:align: center :align: center
@ -31,4 +31,4 @@ Here a screenshot of the final application:
Continue with :ref:`tutorial-folders`. Continue with :ref:`tutorial-folders`.
.. _SQLAlchemy: http://www.sqlalchemy.org/ .. _SQLAlchemy: https://www.sqlalchemy.org/

63
docs/tutorial/setuptools.rst → docs/tutorial/packaging.rst

@ -1,7 +1,7 @@
.. _tutorial-setuptools: .. _tutorial-packaging:
Step 3: Installing flaskr with setuptools Step 3: Installing flaskr as a Package
========================================= ======================================
Flask is now shipped with built-in support for `Click`_. Click provides Flask is now shipped with built-in support for `Click`_. Click provides
Flask with enhanced and extensible command line utilities. Later in this Flask with enhanced and extensible command line utilities. Later in this
@ -9,21 +9,23 @@ tutorial you will see exactly how to extend the ``flask`` command line
interface (CLI). interface (CLI).
A useful pattern to manage a Flask application is to install your app A useful pattern to manage a Flask application is to install your app
using `setuptools`_. This involves creating a :file:`setup.py` following the `Python Packaging Guide`_. Presently this involves
in the projects root directory. You also need to add an empty creating two new files; :file:`setup.py` and :file:`MANIFEST.in` in the
:file:`__init__.py` file to make the :file:`flaskr/flaskr` directory projects root directory. You also need to add an :file:`__init__.py`
a package. The code structure at this point should be:: file to make the :file:`flaskr/flaskr` directory a package. After these
changes, your code structure should be::
/flaskr /flaskr
/flaskr /flaskr
__init__.py __init__.py
/static /static
/templates /templates
flaskr.py
schema.sql
setup.py setup.py
MANIFEST.in
The content of the ``setup.py`` file for ``flaskr`` is: Create the ``setup.py`` file for ``flaskr`` with the following content::
.. sourcecode:: python
from setuptools import setup from setuptools import setup
@ -39,38 +41,53 @@ The content of the ``setup.py`` file for ``flaskr`` is:
When using setuptools, it is also necessary to specify any special files When using setuptools, it is also necessary to specify any special files
that should be included in your package (in the :file:`MANIFEST.in`). that should be included in your package (in the :file:`MANIFEST.in`).
In this case, the static and templates directories need to be included, In this case, the static and templates directories need to be included,
as well as the schema. Create the :file:`MANIFEST.in` and add the as well as the schema.
following lines::
Create the :file:`MANIFEST.in` and add the following lines::
graft flaskr/templates graft flaskr/templates
graft flaskr/static graft flaskr/static
include flaskr/schema.sql include flaskr/schema.sql
Next, to simplify locating the application, create the file,
:file:`flaskr/__init__.py` containing only the following import statement::
from .flaskr import app
This import statement brings the application instance into the top-level
of the application package. When it is time to run the application, the
Flask development server needs the location of the app instance. This
import statement simplifies the location process. Without the above
import statement, the export statement a few steps below would need to be
``export FLASK_APP=flaskr.flaskr``.
At this point you should be able to install the application. As usual, it At this point you should be able to install the application. As usual, it
is recommended to install your Flask application within a `virtualenv`_. is recommended to install your Flask application within a `virtualenv`_.
With that said, go ahead and install the application with:: With that said, from the ``flaskr/`` directory, go ahead and install the
application with::
pip install --editable . pip install --editable .
.. note:: The above installation command assumes that it is run within the The above installation command assumes that it is run within the projects
projects root directory, `flaskr/`. Also, the `editable` flag allows root directory, ``flaskr/``. The ``editable`` flag allows editing
editing source code without having to reinstall the Flask app each time source code without having to reinstall the Flask app each time you make
you make changes. changes. The flaskr app is now installed in your virtualenv (see output
of ``pip freeze``).
With that out of the way, you should be able to start up the application. With that out of the way, you should be able to start up the application.
Do this with the following commands:: Do this on Mac or Linux with the following commands in ``flaskr/``::
export FLASK_APP=flaskr.flaskr export FLASK_APP=flaskr
export FLASK_DEBUG=1 export FLASK_DEBUG=true
flask run flask run
(In case you are on Windows you need to use `set` instead of `export`). (In case you are on Windows you need to use ``set`` instead of ``export``).
The :envvar:`FLASK_DEBUG` flag enables or disables the interactive debugger. The :envvar:`FLASK_DEBUG` flag enables or disables the interactive debugger.
*Never leave debug mode activated in a production system*, because it will *Never leave debug mode activated in a production system*, because it will
allow users to execute code on the server! allow users to execute code on the server!
You will see a message telling you that server has started along with You will see a message telling you that server has started along with
the address at which you can access it. the address at which you can access it in a browser.
When you head over to the server in your browser, you will get a 404 error When you head over to the server in your browser, you will get a 404 error
because we don't have any views yet. That will be addressed a little later, because we don't have any views yet. That will be addressed a little later,
@ -85,5 +102,5 @@ but first, you should get the database working.
Continue with :ref:`tutorial-dbcon`. Continue with :ref:`tutorial-dbcon`.
.. _Click: http://click.pocoo.org .. _Click: http://click.pocoo.org
.. _setuptools: https://setuptools.readthedocs.io .. _Python Packaging Guide: https://packaging.python.org
.. _virtualenv: https://virtualenv.pypa.io .. _virtualenv: https://virtualenv.pypa.io

68
docs/tutorial/setup.rst

@ -3,27 +3,31 @@
Step 2: Application Setup Code Step 2: Application Setup Code
============================== ==============================
Now that the schema is in place, you can create the application module, Next, we will create the application module, :file:`flaskr.py`. Just like the
:file:`flaskr.py`. This file should be placed inside of the :file:`schema.sql` file you created in the previous step, this file should be
:file:`flaskr/flaskr` folder. The first several lines of code in the placed inside of the :file:`flaskr/flaskr` folder.
application module are the needed import statements. After that there will be a
few lines of configuration code. For small applications like ``flaskr``, it is For this tutorial, all the Python code we use will be put into this file
possible to drop the configuration directly into the module. However, a cleaner (except for one line in ``__init__.py``, and any testing or optional files you
solution is to create a separate ``.ini`` or ``.py`` file, load that, and decide to create).
import the values from there.
The first several lines of code in the application module are the needed import
statements. After that there will be a few lines of configuration code.
For small applications like ``flaskr``, it is possible to drop the configuration
directly into the module. However, a cleaner solution is to create a separate
``.py`` file, load that, and import the values from there.
Here are the import statements (in :file:`flaskr.py`):: Here are the import statements (in :file:`flaskr.py`)::
# all the imports
import os import os
import sqlite3 import sqlite3
from flask import Flask, request, session, g, redirect, url_for, abort, \
render_template, flash
The next couple lines will create the actual application instance and from flask import (Flask, request, session, g, redirect, url_for, abort,
initialize it with the config from the same file in :file:`flaskr.py`: render_template, flash)
.. sourcecode:: python The next couple lines will create the actual application instance and
initialize it with the config from the same file in :file:`flaskr.py`::
app = Flask(__name__) # create the application instance :) app = Flask(__name__) # create the application instance :)
app.config.from_object(__name__) # load config from this file , flaskr.py app.config.from_object(__name__) # load config from this file , flaskr.py
@ -37,8 +41,8 @@ initialize it with the config from the same file in :file:`flaskr.py`:
)) ))
app.config.from_envvar('FLASKR_SETTINGS', silent=True) app.config.from_envvar('FLASKR_SETTINGS', silent=True)
The :class:`~flask.Config` object works similarly to a dictionary, so it can be In the above code, the :class:`~flask.Config` object works similarly to a
updated with new values. dictionary, so it can be updated with new values.
.. admonition:: Database Path .. admonition:: Database Path
@ -58,15 +62,15 @@ updated with new values.
Usually, it is a good idea to load a separate, environment-specific Usually, it is a good idea to load a separate, environment-specific
configuration file. Flask allows you to import multiple configurations and it configuration file. Flask allows you to import multiple configurations and it
will use the setting defined in the last import. This enables robust will use the setting defined in the last import. This enables robust
configuration setups. :meth:`~flask.Config.from_envvar` can help achieve this. configuration setups. :meth:`~flask.Config.from_envvar` can help achieve
this. ::
.. sourcecode:: python
app.config.from_envvar('FLASKR_SETTINGS', silent=True) app.config.from_envvar('FLASKR_SETTINGS', silent=True)
Simply define the environment variable :envvar:`FLASKR_SETTINGS` that points to If you want to do this (not required for this tutorial) simply define the
a config file to be loaded. The silent switch just tells Flask to not complain environment variable :envvar:`FLASKR_SETTINGS` that points to a config file
if no such environment key is set. to be loaded. The silent switch just tells Flask to not complain if no such
environment key is set.
In addition to that, you can use the :meth:`~flask.Config.from_object` In addition to that, you can use the :meth:`~flask.Config.from_object`
method on the config object and provide it with an import name of a method on the config object and provide it with an import name of a
@ -76,22 +80,22 @@ that in all cases, only variable names that are uppercase are considered.
The ``SECRET_KEY`` is needed to keep the client-side sessions secure. The ``SECRET_KEY`` is needed to keep the client-side sessions secure.
Choose that key wisely and as hard to guess and complex as possible. Choose that key wisely and as hard to guess and complex as possible.
Lastly, you will add a method that allows for easy connections to the Lastly, add a method that allows for easy connections to the specified
specified database. This can be used to open a connection on request and database. ::
also from the interactive Python shell or a script. This will come in
handy later. You can create a simple database connection through SQLite and
then tell it to use the :class:`sqlite3.Row` object to represent rows.
This allows the rows to be treated as if they were dictionaries instead of
tuples.
.. sourcecode:: python
def connect_db(): def connect_db():
"""Connects to the specific database.""" """Connects to the specific database."""
rv = sqlite3.connect(app.config['DATABASE']) rv = sqlite3.connect(app.config['DATABASE'])
rv.row_factory = sqlite3.Row rv.row_factory = sqlite3.Row
return rv return rv
This can be used to open a connection on request and also from the
interactive Python shell or a script. This will come in handy later.
You can create a simple database connection through SQLite and then tell
it to use the :class:`sqlite3.Row` object to represent rows. This allows
the rows to be treated as if they were dictionaries instead of tuples.
In the next section you will see how to run the application. In the next section you will see how to run the application.
Continue with :ref:`tutorial-setuptools`. Continue with :ref:`tutorial-packaging`.

11
docs/tutorial/templates.rst

@ -1,6 +1,6 @@
.. _tutorial-templates: .. _tutorial-templates:
Step 8: The Templates Step 7: The Templates
===================== =====================
Now it is time to start working on the templates. As you may have Now it is time to start working on the templates. As you may have
@ -15,7 +15,8 @@ escaped with their XML equivalents.
We are also using template inheritance which makes it possible to reuse We are also using template inheritance which makes it possible to reuse
the layout of the website in all pages. the layout of the website in all pages.
Put the following templates into the :file:`templates` folder: Create the follwing three HTML files and place them in the
:file:`templates` folder:
.. _Jinja2: http://jinja.pocoo.org/docs/templates .. _Jinja2: http://jinja.pocoo.org/docs/templates
@ -59,7 +60,7 @@ show_entries.html
This template extends the :file:`layout.html` template from above to display the This template extends the :file:`layout.html` template from above to display the
messages. Note that the ``for`` loop iterates over the messages we passed messages. Note that the ``for`` loop iterates over the messages we passed
in with the :func:`~flask.render_template` function. Notice that the form is in with the :func:`~flask.render_template` function. Notice that the form is
configured to to submit to the `add_entry` view function and use ``POST`` as configured to submit to the `add_entry` view function and use ``POST`` as
HTTP method: HTTP method:
.. sourcecode:: html+jinja .. sourcecode:: html+jinja
@ -79,9 +80,9 @@ HTTP method:
{% endif %} {% endif %}
<ul class=entries> <ul class=entries>
{% for entry in entries %} {% for entry in entries %}
<li><h2>{{ entry.title }}</h2>{{ entry.text|safe }} <li><h2>{{ entry.title }}</h2>{{ entry.text|safe }}</li>
{% else %} {% else %}
<li><em>Unbelievable. No entries here so far</em> <li><em>Unbelievable. No entries here so far</em></li>
{% endfor %} {% endfor %}
</ul> </ul>
{% endblock %} {% endblock %}

44
docs/tutorial/testing.rst

@ -9,10 +9,10 @@ modifications in the future. The application above is used as a basic
example of how to perform unit testing in the :ref:`testing` section of the example of how to perform unit testing in the :ref:`testing` section of the
documentation. Go there to see how easy it is to test Flask applications. documentation. Go there to see how easy it is to test Flask applications.
Adding Tests to flaskr Adding tests to flaskr
====================== ----------------------
Assuming you have seen the testing section above and have either written Assuming you have seen the :ref:`testing` section and have either written
your own tests for ``flaskr`` or have followed along with the examples your own tests for ``flaskr`` or have followed along with the examples
provided, you might be wondering about ways to organize the project. provided, you might be wondering about ways to organize the project.
@ -24,30 +24,38 @@ One possible and recommended project structure is::
static/ static/
templates/ templates/
tests/ tests/
context.py
test_flaskr.py test_flaskr.py
setup.py setup.py
MANIFEST.in MANIFEST.in
For now go ahead a create the :file:`tests/` directory as well as the For now go ahead a create the :file:`tests/` directory as well as the
:file:`context.py` and :file:`test_flaskr.py` files, if you haven't :file:`test_flaskr.py` file.
already. The context file is used as an import helper. The contents
of that file are::
import sys, os Running the tests
-----------------
basedir = os.path.dirname(os.path.abspath(__file__)) At this point you can run the tests. Here ``pytest`` will be used.
sys.path.insert(0, basedir + '/../')
from flaskr import flaskr .. note:: Make sure that ``pytest`` is installed in the same virtualenv
as flaskr. Otherwise ``pytest`` test will not be able to import the
required components to test the application::
Testing + Setuptools pip install -e .
==================== pip install pytest
One way to handle testing is to integrate it with ``setuptools``. All it Run and watch the tests pass, within the top-level :file:`flaskr/`
requires is adding a couple of lines to the :file:`setup.py` file and directory as::
creating a new file :file:`setup.cfg`. Go ahead and update the
:file:`setup.py` to contain:: pytest
Testing + setuptools
--------------------
One way to handle testing is to integrate it with ``setuptools``. Here
that requires adding a couple of lines to the :file:`setup.py` file and
creating a new file :file:`setup.cfg`. One benefit of running the tests
this way is that you do not have to install ``pytest``. Go ahead and
update the :file:`setup.py` file to contain::
from setuptools import setup from setuptools import setup
@ -58,7 +66,6 @@ creating a new file :file:`setup.cfg`. Go ahead and update the
install_requires=[ install_requires=[
'flask', 'flask',
], ],
)
setup_requires=[ setup_requires=[
'pytest-runner', 'pytest-runner',
], ],
@ -66,6 +73,7 @@ creating a new file :file:`setup.cfg`. Go ahead and update the
'pytest', 'pytest',
], ],
) )
Now create :file:`setup.cfg` in the project root (alongside Now create :file:`setup.cfg` in the project root (alongside
:file:`setup.py`):: :file:`setup.py`)::

5
docs/tutorial/views.rst

@ -1,10 +1,11 @@
.. _tutorial-views: .. _tutorial-views:
Step 7: The View Functions Step 6: The View Functions
========================== ==========================
Now that the database connections are working, you can start writing the Now that the database connections are working, you can start writing the
view functions. You will need four of them: view functions. You will need four of them; Show Entries, Add New Entry,
Login and Logout. Add the following code snipets to :file:`flaskr.py`.
Show Entries Show Entries
------------ ------------

44
docs/upgrading.rst

@ -19,7 +19,45 @@ providing the ``--upgrade`` parameter::
$ pip install --upgrade Flask $ pip install --upgrade Flask
.. _upgrading-to-10: .. _upgrading-to-012:
Version 0.12
------------
Changes to send_file
````````````````````
The ``filename`` is no longer automatically inferred from file-like objects.
This means that the following code will no longer automatically have
``X-Sendfile`` support, etag generation or MIME-type guessing::
response = send_file(open('/path/to/file.txt'))
Any of the following is functionally equivalent::
fname = '/path/to/file.txt'
# Just pass the filepath directly
response = send_file(fname)
# Set the MIME-type and ETag explicitly
response = send_file(open(fname), mimetype='text/plain')
response.set_etag(...)
# Set `attachment_filename` for MIME-type guessing
# ETag still needs to be manually set
response = send_file(open(fname), attachment_filename=fname)
response.set_etag(...)
The reason for this is that some file-like objects have an invalid or even
misleading ``name`` attribute. Silently swallowing errors in such cases was not
a satisfying solution.
Additionally the default of falling back to ``application/octet-stream`` has
been restricted. If Flask can't guess one or the user didn't provide one, the
function fails if no filename information was provided.
.. _upgrading-to-011:
Version 0.11 Version 0.11
------------ ------------
@ -105,7 +143,7 @@ when there is no request context yet but an application context. The old
``flask.Flask.request_globals_class`` attribute was renamed to ``flask.Flask.request_globals_class`` attribute was renamed to
:attr:`flask.Flask.app_ctx_globals_class`. :attr:`flask.Flask.app_ctx_globals_class`.
.. _Flask-OldSessions: http://pythonhosted.org/Flask-OldSessions/ .. _Flask-OldSessions: https://pythonhosted.org/Flask-OldSessions/
Version 0.9 Version 0.9
----------- -----------
@ -160,7 +198,7 @@ applications with Flask. Because we want to make upgrading as easy as
possible we tried to counter the problems arising from these changes by possible we tried to counter the problems arising from these changes by
providing a script that can ease the transition. providing a script that can ease the transition.
The script scans your whole application and generates an unified diff with The script scans your whole application and generates a unified diff with
changes it assumes are safe to apply. However as this is an automated changes it assumes are safe to apply. However as this is an automated
tool it won't be able to find all use cases and it might miss some. We tool it won't be able to find all use cases and it might miss some. We
internally spread a lot of deprecation warnings all over the place to make internally spread a lot of deprecation warnings all over the place to make

8
examples/flaskr/README

@ -9,9 +9,11 @@
~ How do I use it? ~ How do I use it?
1. edit the configuration in the flaskr.py file or 1. edit the configuration in the factory.py file or
export an FLASKR_SETTINGS environment variable export an FLASKR_SETTINGS environment variable
pointing to a configuration file. pointing to a configuration file or pass in a
dictionary with config values using the create_app
function.
2. install the app from the root of the project directory 2. install the app from the root of the project directory
@ -19,7 +21,7 @@
3. Instruct flask to use the right application 3. Instruct flask to use the right application
export FLASK_APP=flaskr.flaskr export FLASK_APP=flaskr.factory:create_app()
4. initialize the database with this command: 4. initialize the database with this command:

0
examples/flaskr/flaskr/blueprints/__init__.py

55
examples/flaskr/flaskr/flaskr.py → examples/flaskr/flaskr/blueprints/flaskr.py

@ -10,29 +10,18 @@
:license: BSD, see LICENSE for more details. :license: BSD, see LICENSE for more details.
""" """
import os
from sqlite3 import dbapi2 as sqlite3 from sqlite3 import dbapi2 as sqlite3
from flask import Flask, request, session, g, redirect, url_for, abort, \ from flask import Blueprint, request, session, g, redirect, url_for, abort, \
render_template, flash render_template, flash, current_app
# create our little application :) # create our blueprint :)
app = Flask(__name__) bp = Blueprint('flaskr', __name__)
# Load default config and override config from an environment variable
app.config.update(dict(
DATABASE=os.path.join(app.root_path, 'flaskr.db'),
DEBUG=True,
SECRET_KEY='development key',
USERNAME='admin',
PASSWORD='default'
))
app.config.from_envvar('FLASKR_SETTINGS', silent=True)
def connect_db(): def connect_db():
"""Connects to the specific database.""" """Connects to the specific database."""
rv = sqlite3.connect(app.config['DATABASE']) rv = sqlite3.connect(current_app.config['DATABASE'])
rv.row_factory = sqlite3.Row rv.row_factory = sqlite3.Row
return rv return rv
@ -40,18 +29,11 @@ def connect_db():
def init_db(): def init_db():
"""Initializes the database.""" """Initializes the database."""
db = get_db() db = get_db()
with app.open_resource('schema.sql', mode='r') as f: with current_app.open_resource('schema.sql', mode='r') as f:
db.cursor().executescript(f.read()) db.cursor().executescript(f.read())
db.commit() db.commit()
@app.cli.command('initdb')
def initdb_command():
"""Creates the database tables."""
init_db()
print('Initialized the database.')
def get_db(): def get_db():
"""Opens a new database connection if there is none yet for the """Opens a new database connection if there is none yet for the
current application context. current application context.
@ -61,14 +43,7 @@ def get_db():
return g.sqlite_db return g.sqlite_db
@app.teardown_appcontext @bp.route('/')
def close_db(error):
"""Closes the database again at the end of the request."""
if hasattr(g, 'sqlite_db'):
g.sqlite_db.close()
@app.route('/')
def show_entries(): def show_entries():
db = get_db() db = get_db()
cur = db.execute('select title, text from entries order by id desc') cur = db.execute('select title, text from entries order by id desc')
@ -76,7 +51,7 @@ def show_entries():
return render_template('show_entries.html', entries=entries) return render_template('show_entries.html', entries=entries)
@app.route('/add', methods=['POST']) @bp.route('/add', methods=['POST'])
def add_entry(): def add_entry():
if not session.get('logged_in'): if not session.get('logged_in'):
abort(401) abort(401)
@ -85,26 +60,26 @@ def add_entry():
[request.form['title'], request.form['text']]) [request.form['title'], request.form['text']])
db.commit() db.commit()
flash('New entry was successfully posted') flash('New entry was successfully posted')
return redirect(url_for('show_entries')) return redirect(url_for('flaskr.show_entries'))
@app.route('/login', methods=['GET', 'POST']) @bp.route('/login', methods=['GET', 'POST'])
def login(): def login():
error = None error = None
if request.method == 'POST': if request.method == 'POST':
if request.form['username'] != app.config['USERNAME']: if request.form['username'] != current_app.config['USERNAME']:
error = 'Invalid username' error = 'Invalid username'
elif request.form['password'] != app.config['PASSWORD']: elif request.form['password'] != current_app.config['PASSWORD']:
error = 'Invalid password' error = 'Invalid password'
else: else:
session['logged_in'] = True session['logged_in'] = True
flash('You were logged in') flash('You were logged in')
return redirect(url_for('show_entries')) return redirect(url_for('flaskr.show_entries'))
return render_template('login.html', error=error) return render_template('login.html', error=error)
@app.route('/logout') @bp.route('/logout')
def logout(): def logout():
session.pop('logged_in', None) session.pop('logged_in', None)
flash('You were logged out') flash('You were logged out')
return redirect(url_for('show_entries')) return redirect(url_for('flaskr.show_entries'))

64
examples/flaskr/flaskr/factory.py

@ -0,0 +1,64 @@
# -*- coding: utf-8 -*-
"""
Flaskr
~~~~~~
A microblog example application written as Flask tutorial with
Flask and sqlite3.
:copyright: (c) 2015 by Armin Ronacher.
:license: BSD, see LICENSE for more details.
"""
import os
from flask import Flask, g
from werkzeug.utils import find_modules, import_string
from flaskr.blueprints.flaskr import init_db
def create_app(config=None):
app = Flask('flaskr')
app.config.update(dict(
DATABASE=os.path.join(app.root_path, 'flaskr.db'),
DEBUG=True,
SECRET_KEY='development key',
USERNAME='admin',
PASSWORD='default'
))
app.config.update(config or {})
app.config.from_envvar('FLASKR_SETTINGS', silent=True)
register_blueprints(app)
register_cli(app)
register_teardowns(app)
return app
def register_blueprints(app):
"""Register all blueprint modules
Reference: Armin Ronacher, "Flask for Fun and for Profit" PyBay 2016.
"""
for name in find_modules('flaskr.blueprints'):
mod = import_string(name)
if hasattr(mod, 'bp'):
app.register_blueprint(mod.bp)
return None
def register_cli(app):
@app.cli.command('initdb')
def initdb_command():
"""Creates the database tables."""
init_db()
print('Initialized the database.')
def register_teardowns(app):
@app.teardown_appcontext
def close_db(error):
"""Closes the database again at the end of the request."""
if hasattr(g, 'sqlite_db'):
g.sqlite_db.close()

4
examples/flaskr/flaskr/templates/layout.html

@ -5,9 +5,9 @@
<h1>Flaskr</h1> <h1>Flaskr</h1>
<div class="metanav"> <div class="metanav">
{% if not session.logged_in %} {% if not session.logged_in %}
<a href="{{ url_for('login') }}">log in</a> <a href="{{ url_for('flaskr.login') }}">log in</a>
{% else %} {% else %}
<a href="{{ url_for('logout') }}">log out</a> <a href="{{ url_for('flaskr.logout') }}">log out</a>
{% endif %} {% endif %}
</div> </div>
{% for message in get_flashed_messages() %} {% for message in get_flashed_messages() %}

2
examples/flaskr/flaskr/templates/login.html

@ -2,7 +2,7 @@
{% block body %} {% block body %}
<h2>Login</h2> <h2>Login</h2>
{% if error %}<p class="error"><strong>Error:</strong> {{ error }}{% endif %} {% if error %}<p class="error"><strong>Error:</strong> {{ error }}{% endif %}
<form action="{{ url_for('login') }}" method="post"> <form action="{{ url_for('flaskr.login') }}" method="post">
<dl> <dl>
<dt>Username: <dt>Username:
<dd><input type="text" name="username"> <dd><input type="text" name="username">

2
examples/flaskr/flaskr/templates/show_entries.html

@ -1,7 +1,7 @@
{% extends "layout.html" %} {% extends "layout.html" %}
{% block body %} {% block body %}
{% if session.logged_in %} {% if session.logged_in %}
<form action="{{ url_for('add_entry') }}" method="post" class="add-entry"> <form action="{{ url_for('flaskr.add_entry') }}" method="post" class="add-entry">
<dl> <dl>
<dt>Title: <dt>Title:
<dd><input type="text" size="30" name="title"> <dd><input type="text" size="30" name="title">

15
examples/flaskr/setup.py

@ -1,8 +1,19 @@
from setuptools import setup # -*- coding: utf-8 -*-
"""
Flaskr Tests
~~~~~~~~~~~~
Tests the Flaskr application.
:copyright: (c) 2015 by Armin Ronacher.
:license: BSD, see LICENSE for more details.
"""
from setuptools import setup, find_packages
setup( setup(
name='flaskr', name='flaskr',
packages=['flaskr'], packages=find_packages(),
include_package_data=True, include_package_data=True,
install_requires=[ install_requires=[
'flask', 'flask',

6
examples/flaskr/tests/context.py

@ -1,6 +0,0 @@
import sys, os
basedir = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, basedir + '/../')
from flaskr import flaskr

55
examples/flaskr/tests/test_flaskr.py

@ -9,23 +9,38 @@
:license: BSD, see LICENSE for more details. :license: BSD, see LICENSE for more details.
""" """
import pytest
import os import os
import tempfile import tempfile
import pytest
from flaskr.factory import create_app
from flaskr.blueprints.flaskr import init_db
from context import flaskr
@pytest.fixture @pytest.fixture
def client(request): def app(request):
db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp()
flaskr.app.config['TESTING'] = True db_fd, temp_db_location = tempfile.mkstemp()
client = flaskr.app.test_client() config = {
with flaskr.app.app_context(): 'DATABASE': temp_db_location,
flaskr.init_db() 'TESTING': True,
'DB_FD': db_fd
}
app = create_app(config=config)
with app.app_context():
init_db()
yield app
@pytest.fixture
def client(request, app):
client = app.test_client()
def teardown(): def teardown():
os.close(db_fd) os.close(app.config['DB_FD'])
os.unlink(flaskr.app.config['DATABASE']) os.unlink(app.config['DATABASE'])
request.addfinalizer(teardown) request.addfinalizer(teardown)
return client return client
@ -48,25 +63,25 @@ def test_empty_db(client):
assert b'No entries here so far' in rv.data assert b'No entries here so far' in rv.data
def test_login_logout(client): def test_login_logout(client, app):
"""Make sure login and logout works""" """Make sure login and logout works"""
rv = login(client, flaskr.app.config['USERNAME'], rv = login(client, app.config['USERNAME'],
flaskr.app.config['PASSWORD']) app.config['PASSWORD'])
assert b'You were logged in' in rv.data assert b'You were logged in' in rv.data
rv = logout(client) rv = logout(client)
assert b'You were logged out' in rv.data assert b'You were logged out' in rv.data
rv = login(client, flaskr.app.config['USERNAME'] + 'x', rv = login(client,app.config['USERNAME'] + 'x',
flaskr.app.config['PASSWORD']) app.config['PASSWORD'])
assert b'Invalid username' in rv.data assert b'Invalid username' in rv.data
rv = login(client, flaskr.app.config['USERNAME'], rv = login(client, app.config['USERNAME'],
flaskr.app.config['PASSWORD'] + 'x') app.config['PASSWORD'] + 'x')
assert b'Invalid password' in rv.data assert b'Invalid password' in rv.data
def test_messages(client): def test_messages(client, app):
"""Test that messages work""" """Test that messages work"""
login(client, flaskr.app.config['USERNAME'], login(client, app.config['USERNAME'],
flaskr.app.config['PASSWORD']) app.config['PASSWORD'])
rv = client.post('/add', data=dict( rv = client.post('/add', data=dict(
title='<Hello>', title='<Hello>',
text='<strong>HTML</strong> allowed here' text='<strong>HTML</strong> allowed here'

2
examples/minitwit/.gitignore vendored

@ -0,0 +1,2 @@
minitwit.db
.eggs/

3
examples/minitwit/MANIFEST.in

@ -0,0 +1,3 @@
graft minitwit/templates
graft minitwit/static
include minitwit/schema.sql

12
examples/minitwit/README

@ -14,15 +14,19 @@
export an MINITWIT_SETTINGS environment variable export an MINITWIT_SETTINGS environment variable
pointing to a configuration file. pointing to a configuration file.
2. tell flask about the right application: 2. install the app from the root of the project directory
pip install --editable .
3. tell flask about the right application:
export FLASK_APP=minitwit export FLASK_APP=minitwit
2. fire up a shell and run this: 4. fire up a shell and run this:
flask initdb flask initdb
3. now you can run minitwit: 5. now you can run minitwit:
flask run flask run
@ -31,5 +35,5 @@
~ Is it tested? ~ Is it tested?
You betcha. Run the `test_minitwit.py` file to You betcha. Run the `python setup.py test` file to
see the tests pass. see the tests pass.

1
examples/minitwit/minitwit/__init__.py

@ -0,0 +1 @@
from .minitwit import app

4
examples/minitwit/minitwit.py → examples/minitwit/minitwit/minitwit.py

@ -25,7 +25,7 @@ DEBUG = True
SECRET_KEY = 'development key' SECRET_KEY = 'development key'
# create our little application :) # create our little application :)
app = Flask(__name__) app = Flask('minitwit')
app.config.from_object(__name__) app.config.from_object(__name__)
app.config.from_envvar('MINITWIT_SETTINGS', silent=True) app.config.from_envvar('MINITWIT_SETTINGS', silent=True)
@ -85,7 +85,7 @@ def format_datetime(timestamp):
def gravatar_url(email, size=80): def gravatar_url(email, size=80):
"""Return the gravatar image for the given email address.""" """Return the gravatar image for the given email address."""
return 'http://www.gravatar.com/avatar/%s?d=identicon&s=%d' % \ return 'https://www.gravatar.com/avatar/%s?d=identicon&s=%d' % \
(md5(email.strip().lower().encode('utf-8')).hexdigest(), size) (md5(email.strip().lower().encode('utf-8')).hexdigest(), size)

0
examples/minitwit/schema.sql → examples/minitwit/minitwit/schema.sql

0
examples/minitwit/static/style.css → examples/minitwit/minitwit/static/style.css

0
examples/minitwit/templates/layout.html → examples/minitwit/minitwit/templates/layout.html

0
examples/minitwit/templates/login.html → examples/minitwit/minitwit/templates/login.html

0
examples/minitwit/templates/register.html → examples/minitwit/minitwit/templates/register.html

0
examples/minitwit/templates/timeline.html → examples/minitwit/minitwit/templates/timeline.html

2
examples/minitwit/setup.cfg

@ -0,0 +1,2 @@
[aliases]
test=pytest

16
examples/minitwit/setup.py

@ -0,0 +1,16 @@
from setuptools import setup
setup(
name='minitwit',
packages=['minitwit'],
include_package_data=True,
install_requires=[
'flask',
],
setup_requires=[
'pytest-runner',
],
tests_require=[
'pytest',
],
)

10
examples/minitwit/test_minitwit.py → examples/minitwit/tests/test_minitwit.py

@ -9,24 +9,22 @@
:license: BSD, see LICENSE for more details. :license: BSD, see LICENSE for more details.
""" """
import os import os
import minitwit
import tempfile import tempfile
import pytest import pytest
from minitwit import minitwit
@pytest.fixture @pytest.fixture
def client(request): def client():
db_fd, minitwit.app.config['DATABASE'] = tempfile.mkstemp() db_fd, minitwit.app.config['DATABASE'] = tempfile.mkstemp()
client = minitwit.app.test_client() client = minitwit.app.test_client()
with minitwit.app.app_context(): with minitwit.app.app_context():
minitwit.init_db() minitwit.init_db()
def teardown(): yield client
"""Get rid of the database again after each test."""
os.close(db_fd) os.close(db_fd)
os.unlink(minitwit.app.config['DATABASE']) os.unlink(minitwit.app.config['DATABASE'])
request.addfinalizer(teardown)
return client
def register(client, username, password, password2=None, email=None): def register(client, username, password, password2=None, email=None):

10
examples/patterns/largerapp/setup.py

@ -0,0 +1,10 @@
from setuptools import setup
setup(
name='yourapplication',
packages=['yourapplication'],
include_package_data=True,
install_requires=[
'flask',
],
)

12
examples/patterns/largerapp/tests/test_largerapp.py

@ -0,0 +1,12 @@
from yourapplication import app
import pytest
@pytest.fixture
def client():
app.config['TESTING'] = True
client = app.test_client()
return client
def test_index(client):
rv = client.get('/')
assert b"Hello World!" in rv.data

4
examples/patterns/largerapp/yourapplication/__init__.py

@ -0,0 +1,4 @@
from flask import Flask
app = Flask('yourapplication')
import yourapplication.views

0
examples/patterns/largerapp/yourapplication/static/style.css

0
examples/patterns/largerapp/yourapplication/templates/index.html

0
examples/patterns/largerapp/yourapplication/templates/layout.html

0
examples/patterns/largerapp/yourapplication/templates/login.html

5
examples/patterns/largerapp/yourapplication/views.py

@ -0,0 +1,5 @@
from yourapplication import app
@app.route('/')
def index():
return 'Hello World!'

4
flask/__init__.py

@ -10,7 +10,7 @@
:license: BSD, see LICENSE for more details. :license: BSD, see LICENSE for more details.
""" """
__version__ = '0.11.2-dev' __version__ = '0.13-dev'
# utilities we import from Werkzeug and Jinja2 that are unused # utilities we import from Werkzeug and Jinja2 that are unused
# in the module but are exported as public interface. # in the module but are exported as public interface.
@ -40,7 +40,7 @@ from .signals import signals_available, template_rendered, request_started, \
# it. # it.
from . import json from . import json
# This was the only thing that flask used to export at one point and it had # This was the only thing that Flask used to export at one point and it had
# a more generic name. # a more generic name.
jsonify = json.jsonify jsonify = json.jsonify

2
flask/_compat.py

@ -25,6 +25,7 @@ if not PY2:
itervalues = lambda d: iter(d.values()) itervalues = lambda d: iter(d.values())
iteritems = lambda d: iter(d.items()) iteritems = lambda d: iter(d.items())
from inspect import getfullargspec as getargspec
from io import StringIO from io import StringIO
def reraise(tp, value, tb=None): def reraise(tp, value, tb=None):
@ -43,6 +44,7 @@ else:
itervalues = lambda d: d.itervalues() itervalues = lambda d: d.itervalues()
iteritems = lambda d: d.iteritems() iteritems = lambda d: d.iteritems()
from inspect import getargspec
from cStringIO import StringIO from cStringIO import StringIO
exec('def reraise(tp, value, tb=None):\n raise tp, value, tb') exec('def reraise(tp, value, tb=None):\n raise tp, value, tb')

482
flask/app.py

@ -10,31 +10,32 @@
""" """
import os import os
import sys import sys
from threading import Lock import warnings
from datetime import timedelta from datetime import timedelta
from itertools import chain
from functools import update_wrapper from functools import update_wrapper
from collections import deque from itertools import chain
from threading import Lock
from werkzeug.datastructures import ImmutableDict
from werkzeug.routing import Map, Rule, RequestRedirect, BuildError
from werkzeug.exceptions import HTTPException, InternalServerError, \
MethodNotAllowed, BadRequest, default_exceptions
from .helpers import _PackageBoundObject, url_for, get_flashed_messages, \ from werkzeug.datastructures import ImmutableDict, Headers
locked_cached_property, _endpoint_from_view_func, find_package, \ from werkzeug.exceptions import BadRequest, HTTPException, \
get_debug_flag InternalServerError, MethodNotAllowed, default_exceptions, \
from . import json, cli BadRequestKeyError
from .wrappers import Request, Response from werkzeug.routing import BuildError, Map, RequestRedirect, Rule
from .config import ConfigAttribute, Config
from .ctx import RequestContext, AppContext, _AppCtxGlobals from . import cli, json
from .globals import _request_ctx_stack, request, session, g from ._compat import integer_types, reraise, string_types, text_type
from .config import Config, ConfigAttribute
from .ctx import AppContext, RequestContext, _AppCtxGlobals
from .globals import _request_ctx_stack, g, request, session
from .helpers import _PackageBoundObject, \
_endpoint_from_view_func, find_package, get_debug_flag, \
get_flashed_messages, locked_cached_property, url_for
from .sessions import SecureCookieSessionInterface from .sessions import SecureCookieSessionInterface
from .signals import appcontext_tearing_down, got_request_exception, \
request_finished, request_started, request_tearing_down
from .templating import DispatchingJinjaLoader, Environment, \ from .templating import DispatchingJinjaLoader, Environment, \
_default_template_ctx_processor _default_template_ctx_processor
from .signals import request_started, request_finished, got_request_exception, \ from .wrappers import Request, Response
request_tearing_down, appcontext_tearing_down
from ._compat import reraise, string_types, text_type, integer_types
# a lock used for logger initialization # a lock used for logger initialization
_logger_lock = Lock() _logger_lock = Lock()
@ -124,6 +125,9 @@ class Flask(_PackageBoundObject):
.. versionadded:: 0.11 .. versionadded:: 0.11
The `root_path` parameter was added. The `root_path` parameter was added.
.. versionadded:: 0.13
The `host_matching` and `static_host` parameters were added.
:param import_name: the name of the application package :param import_name: the name of the application package
:param static_url_path: can be used to specify a different path for the :param static_url_path: can be used to specify a different path for the
static files on the web. Defaults to the name static files on the web. Defaults to the name
@ -131,6 +135,11 @@ class Flask(_PackageBoundObject):
:param static_folder: the folder with static files that should be served :param static_folder: the folder with static files that should be served
at `static_url_path`. Defaults to the ``'static'`` at `static_url_path`. Defaults to the ``'static'``
folder in the root path of the application. folder in the root path of the application.
:param host_matching: sets the app's ``url_map.host_matching`` to the given
given value. Defaults to False.
:param static_host: the host to use when adding the static route. Defaults
to None. Required when using ``host_matching=True``
with a ``static_folder`` configured.
:param template_folder: the folder that contains the templates that should :param template_folder: the folder that contains the templates that should
be used by the application. Defaults to be used by the application. Defaults to
``'templates'`` folder in the root path of the ``'templates'`` folder in the root path of the
@ -213,7 +222,7 @@ class Flask(_PackageBoundObject):
#: The testing flag. Set this to ``True`` to enable the test mode of #: The testing flag. Set this to ``True`` to enable the test mode of
#: Flask extensions (and in the future probably also Flask itself). #: Flask extensions (and in the future probably also Flask itself).
#: For example this might activate unittest helpers that have an #: For example this might activate test helpers that have an
#: additional runtime cost which should not be enabled by default. #: additional runtime cost which should not be enabled by default.
#: #:
#: If this is enabled and PROPAGATE_EXCEPTIONS is not changed from the #: If this is enabled and PROPAGATE_EXCEPTIONS is not changed from the
@ -300,7 +309,7 @@ class Flask(_PackageBoundObject):
'LOGGER_NAME': None, 'LOGGER_NAME': None,
'LOGGER_HANDLER_POLICY': 'always', 'LOGGER_HANDLER_POLICY': 'always',
'SERVER_NAME': None, 'SERVER_NAME': None,
'APPLICATION_ROOT': None, 'APPLICATION_ROOT': '/',
'SESSION_COOKIE_NAME': 'session', 'SESSION_COOKIE_NAME': 'session',
'SESSION_COOKIE_DOMAIN': None, 'SESSION_COOKIE_DOMAIN': None,
'SESSION_COOKIE_PATH': None, 'SESSION_COOKIE_PATH': None,
@ -309,13 +318,13 @@ class Flask(_PackageBoundObject):
'SESSION_REFRESH_EACH_REQUEST': True, 'SESSION_REFRESH_EACH_REQUEST': True,
'MAX_CONTENT_LENGTH': None, 'MAX_CONTENT_LENGTH': None,
'SEND_FILE_MAX_AGE_DEFAULT': timedelta(hours=12), 'SEND_FILE_MAX_AGE_DEFAULT': timedelta(hours=12),
'TRAP_BAD_REQUEST_ERRORS': False, 'TRAP_BAD_REQUEST_ERRORS': None,
'TRAP_HTTP_EXCEPTIONS': False, 'TRAP_HTTP_EXCEPTIONS': False,
'EXPLAIN_TEMPLATE_LOADING': False, 'EXPLAIN_TEMPLATE_LOADING': False,
'PREFERRED_URL_SCHEME': 'http', 'PREFERRED_URL_SCHEME': 'http',
'JSON_AS_ASCII': True, 'JSON_AS_ASCII': True,
'JSON_SORT_KEYS': True, 'JSON_SORT_KEYS': True,
'JSONIFY_PRETTYPRINT_REGULAR': True, 'JSONIFY_PRETTYPRINT_REGULAR': False,
'JSONIFY_MIMETYPE': 'application/json', 'JSONIFY_MIMETYPE': 'application/json',
'TEMPLATES_AUTO_RELOAD': None, 'TEMPLATES_AUTO_RELOAD': None,
}) })
@ -338,7 +347,8 @@ class Flask(_PackageBoundObject):
session_interface = SecureCookieSessionInterface() session_interface = SecureCookieSessionInterface()
def __init__(self, import_name, static_path=None, static_url_path=None, def __init__(self, import_name, static_path=None, static_url_path=None,
static_folder='static', template_folder='templates', static_folder='static', static_host=None,
host_matching=False, template_folder='templates',
instance_path=None, instance_relative_config=False, instance_path=None, instance_relative_config=False,
root_path=None): root_path=None):
_PackageBoundObject.__init__(self, import_name, _PackageBoundObject.__init__(self, import_name,
@ -392,7 +402,7 @@ class Flask(_PackageBoundObject):
#: is the class for the instance check and the second the error handler #: is the class for the instance check and the second the error handler
#: function. #: function.
#: #:
#: To register a error handler, use the :meth:`errorhandler` #: To register an error handler, use the :meth:`errorhandler`
#: decorator. #: decorator.
self.error_handler_spec = {None: self._error_handlers} self.error_handler_spec = {None: self._error_handlers}
@ -405,17 +415,16 @@ class Flask(_PackageBoundObject):
#: .. versionadded:: 0.9 #: .. versionadded:: 0.9
self.url_build_error_handlers = [] self.url_build_error_handlers = []
#: A dictionary with lists of functions that should be called at the #: A dictionary with lists of functions that will be called at the
#: beginning of the request. The key of the dictionary is the name of #: beginning of each request. The key of the dictionary is the name of
#: the blueprint this function is active for, ``None`` for all requests. #: the blueprint this function is active for, or ``None`` for all
#: This can for example be used to open database connections or #: requests. To register a function, use the :meth:`before_request`
#: getting hold of the currently logged in user. To register a #: decorator.
#: function here, use the :meth:`before_request` decorator.
self.before_request_funcs = {} self.before_request_funcs = {}
#: A lists of functions that should be called at the beginning of the #: A list of functions that will be called at the beginning of the
#: first request to this instance. To register a function here, use #: first request to this instance. To register a function, use the
#: the :meth:`before_first_request` decorator. #: :meth:`before_first_request` decorator.
#: #:
#: .. versionadded:: 0.8 #: .. versionadded:: 0.8
self.before_first_request_funcs = [] self.before_first_request_funcs = []
@ -447,12 +456,11 @@ class Flask(_PackageBoundObject):
#: .. versionadded:: 0.9 #: .. versionadded:: 0.9
self.teardown_appcontext_funcs = [] self.teardown_appcontext_funcs = []
#: A dictionary with lists of functions that can be used as URL #: A dictionary with lists of functions that are called before the
#: value processor functions. Whenever a URL is built these functions #: :attr:`before_request_funcs` functions. The key of the dictionary is
#: are called to modify the dictionary of values in place. The key #: the name of the blueprint this function is active for, or ``None``
#: ``None`` here is used for application wide #: for all requests. To register a function, use
#: callbacks, otherwise the key is the name of the blueprint. #: :meth:`url_value_preprocessor`.
#: Each of these functions has the chance to modify the dictionary
#: #:
#: .. versionadded:: 0.7 #: .. versionadded:: 0.7
self.url_value_preprocessors = {} self.url_value_preprocessors = {}
@ -519,26 +527,29 @@ class Flask(_PackageBoundObject):
#: def to_python(self, value): #: def to_python(self, value):
#: return value.split(',') #: return value.split(',')
#: def to_url(self, values): #: def to_url(self, values):
#: return ','.join(BaseConverter.to_url(value) #: return ','.join(super(ListConverter, self).to_url(value)
#: for value in values) #: for value in values)
#: #:
#: app = Flask(__name__) #: app = Flask(__name__)
#: app.url_map.converters['list'] = ListConverter #: app.url_map.converters['list'] = ListConverter
self.url_map = Map() self.url_map = Map()
self.url_map.host_matching = host_matching
# tracks internally if the application already handled at least one # tracks internally if the application already handled at least one
# request. # request.
self._got_first_request = False self._got_first_request = False
self._before_request_lock = Lock() self._before_request_lock = Lock()
# register the static folder for the application. Do that even # Add a static route using the provided static_url_path, static_host,
# if the folder does not exist. First of all it might be created # and static_folder if there is a configured static_folder.
# while the server is running (usually happens during development) # Note we do this without checking if static_folder exists.
# but also because google appengine stores static files somewhere # For one, it might be created while the server is running (e.g. during
# else when mapped with the .yml file. # development). Also, Google App Engine stores static files somewhere
if self.has_static_folder: if self.has_static_folder:
assert bool(static_host) == host_matching, 'Invalid static_host/host_matching combination'
self.add_url_rule(self.static_url_path + '/<path:filename>', self.add_url_rule(self.static_url_path + '/<path:filename>',
endpoint='static', endpoint='static', host=static_host,
view_func=self.send_static_file) view_func=self.send_static_file)
#: The click command line context for this application. Commands #: The click command line context for this application. Commands
@ -814,7 +825,8 @@ class Flask(_PackageBoundObject):
:param host: the hostname to listen on. Set this to ``'0.0.0.0'`` to :param host: the hostname to listen on. Set this to ``'0.0.0.0'`` to
have the server available externally as well. Defaults to have the server available externally as well. Defaults to
``'127.0.0.1'``. ``'127.0.0.1'`` or the host in the ``SERVER_NAME`` config
variable if present.
:param port: the port of the webserver. Defaults to ``5000`` or the :param port: the port of the webserver. Defaults to ``5000`` or the
port defined in the ``SERVER_NAME`` config variable if port defined in the ``SERVER_NAME`` config variable if
present. present.
@ -825,25 +837,31 @@ class Flask(_PackageBoundObject):
:func:`werkzeug.serving.run_simple` for more :func:`werkzeug.serving.run_simple` for more
information. information.
""" """
# Change this into a no-op if the server is invoked from the
# command line. Have a look at cli.py for more information.
if os.environ.get('FLASK_RUN_FROM_CLI_SERVER') == '1':
from .debughelpers import explain_ignored_app_run
explain_ignored_app_run()
return
from werkzeug.serving import run_simple from werkzeug.serving import run_simple
if host is None: _host = '127.0.0.1'
host = '127.0.0.1' _port = 5000
if port is None: server_name = self.config.get("SERVER_NAME")
server_name = self.config['SERVER_NAME'] sn_host, sn_port = None, None
if server_name and ':' in server_name: if server_name:
port = int(server_name.rsplit(':', 1)[1]) sn_host, _, sn_port = server_name.partition(':')
else: host = host or sn_host or _host
port = 5000 port = int(port or sn_port or _port)
if debug is not None: if debug is not None:
self.debug = bool(debug) self.debug = bool(debug)
options.setdefault('use_reloader', self.debug) options.setdefault('use_reloader', self.debug)
options.setdefault('use_debugger', self.debug) options.setdefault('use_debugger', self.debug)
options.setdefault('passthrough_errors', True)
try: try:
run_simple(host, port, self, **options) run_simple(host, port, self, **options)
finally: finally:
# reset the first request information if the development server # reset the first request information if the development server
# resetted normally. This makes it possible to restart the server # reset normally. This makes it possible to restart the server
# without reloader and that stuff from an interactive shell. # without reloader and that stuff from an interactive shell.
self._got_first_request = False self._got_first_request = False
@ -877,9 +895,9 @@ class Flask(_PackageBoundObject):
from flask.testing import FlaskClient from flask.testing import FlaskClient
class CustomClient(FlaskClient): class CustomClient(FlaskClient):
def __init__(self, authentication=None, *args, **kwargs): def __init__(self, *args, **kwargs):
FlaskClient.__init__(*args, **kwargs) self._authentication = kwargs.pop("authentication")
self._authentication = authentication super(CustomClient,self).__init__( *args, **kwargs)
app.test_client_class = CustomClient app.test_client_class = CustomClient
client = app.test_client(authentication='Basic ....') client = app.test_client(authentication='Basic ....')
@ -909,8 +927,17 @@ class Flask(_PackageBoundObject):
:attr:`secret_key` is set. Instead of overriding this method :attr:`secret_key` is set. Instead of overriding this method
we recommend replacing the :class:`session_interface`. we recommend replacing the :class:`session_interface`.
.. deprecated: 1.0
Will be removed in 1.1. Use ``session_interface.open_session``
instead.
:param request: an instance of :attr:`request_class`. :param request: an instance of :attr:`request_class`.
""" """
warnings.warn(DeprecationWarning(
'"open_session" is deprecated and will be removed in 1.1. Use'
' "session_interface.open_session" instead.'
))
return self.session_interface.open_session(self, request) return self.session_interface.open_session(self, request)
def save_session(self, session, response): def save_session(self, session, response):
@ -918,19 +945,37 @@ class Flask(_PackageBoundObject):
implementation, check :meth:`open_session`. Instead of overriding this implementation, check :meth:`open_session`. Instead of overriding this
method we recommend replacing the :class:`session_interface`. method we recommend replacing the :class:`session_interface`.
.. deprecated: 1.0
Will be removed in 1.1. Use ``session_interface.save_session``
instead.
:param session: the session to be saved (a :param session: the session to be saved (a
:class:`~werkzeug.contrib.securecookie.SecureCookie` :class:`~werkzeug.contrib.securecookie.SecureCookie`
object) object)
:param response: an instance of :attr:`response_class` :param response: an instance of :attr:`response_class`
""" """
warnings.warn(DeprecationWarning(
'"save_session" is deprecated and will be removed in 1.1. Use'
' "session_interface.save_session" instead.'
))
return self.session_interface.save_session(self, session, response) return self.session_interface.save_session(self, session, response)
def make_null_session(self): def make_null_session(self):
"""Creates a new instance of a missing session. Instead of overriding """Creates a new instance of a missing session. Instead of overriding
this method we recommend replacing the :class:`session_interface`. this method we recommend replacing the :class:`session_interface`.
.. deprecated: 1.0
Will be removed in 1.1. Use ``session_interface.make_null_session``
instead.
.. versionadded:: 0.7 .. versionadded:: 0.7
""" """
warnings.warn(DeprecationWarning(
'"make_null_session" is deprecated and will be removed in 1.1. Use'
' "session_interface.make_null_session" instead.'
))
return self.session_interface.make_null_session(self) return self.session_interface.make_null_session(self)
@setupmethod @setupmethod
@ -960,7 +1005,7 @@ class Flask(_PackageBoundObject):
return iter(self._blueprint_order) return iter(self._blueprint_order)
@setupmethod @setupmethod
def add_url_rule(self, rule, endpoint=None, view_func=None, **options): def add_url_rule(self, rule, endpoint=None, view_func=None, provide_automatic_options=None, **options):
"""Connects a URL rule. Works exactly like the :meth:`route` """Connects a URL rule. Works exactly like the :meth:`route`
decorator. If a view_func is provided it will be registered with the decorator. If a view_func is provided it will be registered with the
endpoint. endpoint.
@ -1000,6 +1045,10 @@ class Flask(_PackageBoundObject):
endpoint endpoint
:param view_func: the function to call when serving a request to the :param view_func: the function to call when serving a request to the
provided endpoint provided endpoint
:param provide_automatic_options: controls whether the ``OPTIONS``
method should be added automatically. This can also be controlled
by setting the ``view_func.provide_automatic_options = False``
before adding the rule.
:param options: the options to be forwarded to the underlying :param options: the options to be forwarded to the underlying
:class:`~werkzeug.routing.Rule` object. A change :class:`~werkzeug.routing.Rule` object. A change
to Werkzeug is handling of method options. methods to Werkzeug is handling of method options. methods
@ -1029,6 +1078,7 @@ class Flask(_PackageBoundObject):
# starting with Flask 0.8 the view_func object can disable and # starting with Flask 0.8 the view_func object can disable and
# force-enable the automatic options handling. # force-enable the automatic options handling.
if provide_automatic_options is None:
provide_automatic_options = getattr(view_func, provide_automatic_options = getattr(view_func,
'provide_automatic_options', None) 'provide_automatic_options', None)
@ -1116,7 +1166,9 @@ class Flask(_PackageBoundObject):
@setupmethod @setupmethod
def errorhandler(self, code_or_exception): def errorhandler(self, code_or_exception):
"""A decorator that is used to register a function give a given """Register a function to handle errors by code or exception class.
A decorator that is used to register a function given an
error code. Example:: error code. Example::
@app.errorhandler(404) @app.errorhandler(404)
@ -1129,21 +1181,6 @@ class Flask(_PackageBoundObject):
def special_exception_handler(error): def special_exception_handler(error):
return 'Database connection failed', 500 return 'Database connection failed', 500
You can also register a function as error handler without using
the :meth:`errorhandler` decorator. The following example is
equivalent to the one above::
def page_not_found(error):
return 'This page does not exist', 404
app.error_handler_spec[None][404] = page_not_found
Setting error handlers via assignments to :attr:`error_handler_spec`
however is discouraged as it requires fiddling with nested dictionaries
and the special case for arbitrary exception types.
The first ``None`` refers to the active blueprint. If the error
handler should be application wide ``None`` shall be used.
.. versionadded:: 0.7 .. versionadded:: 0.7
Use :meth:`register_error_handler` instead of modifying Use :meth:`register_error_handler` instead of modifying
:attr:`error_handler_spec` directly, for application wide error :attr:`error_handler_spec` directly, for application wide error
@ -1154,13 +1191,15 @@ class Flask(_PackageBoundObject):
that do not necessarily have to be a subclass of the that do not necessarily have to be a subclass of the
:class:`~werkzeug.exceptions.HTTPException` class. :class:`~werkzeug.exceptions.HTTPException` class.
:param code: the code as integer for the handler :param code_or_exception: the code as integer for the handler, or
an arbitrary exception
""" """
def decorator(f): def decorator(f):
self._register_error_handler(None, code_or_exception, f) self._register_error_handler(None, code_or_exception, f)
return f return f
return decorator return decorator
@setupmethod
def register_error_handler(self, code_or_exception, f): def register_error_handler(self, code_or_exception, f):
"""Alternative error attach function to the :meth:`errorhandler` """Alternative error attach function to the :meth:`errorhandler`
decorator that is more straightforward to use for non decorator decorator that is more straightforward to use for non decorator
@ -1180,10 +1219,17 @@ class Flask(_PackageBoundObject):
if isinstance(code_or_exception, HTTPException): # old broken behavior if isinstance(code_or_exception, HTTPException): # old broken behavior
raise ValueError( raise ValueError(
'Tried to register a handler for an exception instance {0!r}.' 'Tried to register a handler for an exception instance {0!r}.'
'Handlers can only be registered for exception classes or HTTP error codes.' ' Handlers can only be registered for exception classes or'
.format(code_or_exception)) ' HTTP error codes.'.format(code_or_exception)
)
try:
exc_class, code = self._get_exc_class_and_code(code_or_exception) exc_class, code = self._get_exc_class_and_code(code_or_exception)
except KeyError:
raise KeyError(
"'{0}' is not a recognized HTTP error code. Use a subclass of"
" HTTPException with that code instead.".format(code_or_exception)
)
handlers = self.error_handler_spec.setdefault(key, {}).setdefault(code, {}) handlers = self.error_handler_spec.setdefault(key, {}).setdefault(code, {})
handlers[exc_class] = f handlers[exc_class] = f
@ -1289,10 +1335,12 @@ class Flask(_PackageBoundObject):
def before_request(self, f): def before_request(self, f):
"""Registers a function to run before each request. """Registers a function to run before each request.
The function will be called without any arguments. For example, this can be used to open a database connection, or to load
If the function returns a non-None value, it's handled as the logged in user from the session.
if it was the return value from the view and further
request handling is stopped. The function will be called without any arguments. If it returns a
non-None value, the value is handled as if it was the return value from
the view, and further request handling is stopped.
""" """
self.before_request_funcs.setdefault(None, []).append(f) self.before_request_funcs.setdefault(None, []).append(f)
return f return f
@ -1348,7 +1396,7 @@ class Flask(_PackageBoundObject):
will have to surround the execution of these code by try/except will have to surround the execution of these code by try/except
statements and log occurring errors. statements and log occurring errors.
When a teardown function was called because of a exception it will When a teardown function was called because of an exception it will
be passed an error object. be passed an error object.
The return values of teardown functions are ignored. The return values of teardown functions are ignored.
@ -1411,9 +1459,17 @@ class Flask(_PackageBoundObject):
@setupmethod @setupmethod
def url_value_preprocessor(self, f): def url_value_preprocessor(self, f):
"""Registers a function as URL value preprocessor for all view """Register a URL value preprocessor function for all view
functions of the application. It's called before the view functions functions in the application. These functions will be called before the
are called and can modify the url values provided. :meth:`before_request` functions.
The function can modify the values captured from the matched url before
they are passed to the view. For example, this can be used to pop a
common language code value and place it in ``g`` rather than pass it to
every view.
The function is passed the endpoint name and values dict. The return
value is ignored.
""" """
self.url_value_preprocessors.setdefault(None, []).append(f) self.url_value_preprocessors.setdefault(None, []).append(f)
return f return f
@ -1428,43 +1484,32 @@ class Flask(_PackageBoundObject):
return f return f
def _find_error_handler(self, e): def _find_error_handler(self, e):
"""Finds a registered error handler for the request’s blueprint. """Find a registered error handler for a request in this order:
Otherwise falls back to the app, returns None if not a suitable blueprint handler for a specific code, app handler for a specific code,
handler is found. blueprint generic HTTPException handler, app generic HTTPException handler,
and returns None if a suitable handler is not found.
""" """
exc_class, code = self._get_exc_class_and_code(type(e)) exc_class, code = self._get_exc_class_and_code(type(e))
def find_handler(handler_map): def find_handler(handler_map):
if not handler_map: if not handler_map:
return return
queue = deque(exc_class.__mro__)
# Protect from geniuses who might create circular references in for cls in exc_class.__mro__:
# __mro__
done = set()
while queue:
cls = queue.popleft()
if cls in done:
continue
done.add(cls)
handler = handler_map.get(cls) handler = handler_map.get(cls)
if handler is not None: if handler is not None:
# cache for next time exc_class is raised # cache for next time exc_class is raised
handler_map[exc_class] = handler handler_map[exc_class] = handler
return handler return handler
queue.extend(cls.__mro__) # check for any in blueprint or app
for name, c in ((request.blueprint, code), (None, code),
(request.blueprint, None), (None, None)):
handler = find_handler(self.error_handler_spec.get(name, {}).get(c))
# try blueprint handlers if handler:
handler = find_handler(self.error_handler_spec
.get(request.blueprint, {})
.get(code))
if handler is not None:
return handler return handler
# fall back to app handlers
return find_handler(self.error_handler_spec[None].get(code))
def handle_http_exception(self, e): def handle_http_exception(self, e):
"""Handles an HTTP exception. By default this will invoke the """Handles an HTTP exception. By default this will invoke the
registered error handlers and fall back to returning the registered error handlers and fall back to returning the
@ -1494,12 +1539,20 @@ class Flask(_PackageBoundObject):
traceback. This is helpful for debugging implicitly raised HTTP traceback. This is helpful for debugging implicitly raised HTTP
exceptions. exceptions.
.. versionchanged:: 1.0
Bad request errors are not trapped by default in debug mode.
.. versionadded:: 0.8 .. versionadded:: 0.8
""" """
if self.config['TRAP_HTTP_EXCEPTIONS']: if self.config['TRAP_HTTP_EXCEPTIONS']:
return True return True
if self.config['TRAP_BAD_REQUEST_ERRORS']:
trap_bad_request = self.config['TRAP_BAD_REQUEST_ERRORS']
# if unset, trap based on debug mode
if (trap_bad_request is None and self.debug) or trap_bad_request:
return isinstance(e, BadRequest) return isinstance(e, BadRequest)
return False return False
def handle_user_exception(self, e): def handle_user_exception(self, e):
@ -1510,16 +1563,30 @@ class Flask(_PackageBoundObject):
function will either return a response value or reraise the function will either return a response value or reraise the
exception with the same traceback. exception with the same traceback.
.. versionchanged:: 1.0
Key errors raised from request data like ``form`` show the the bad
key in debug mode rather than a generic bad request message.
.. versionadded:: 0.7 .. versionadded:: 0.7
""" """
exc_type, exc_value, tb = sys.exc_info() exc_type, exc_value, tb = sys.exc_info()
assert exc_value is e assert exc_value is e
# ensure not to trash sys.exc_info() at that point in case someone # ensure not to trash sys.exc_info() at that point in case someone
# wants the traceback preserved in handle_http_exception. Of course # wants the traceback preserved in handle_http_exception. Of course
# we cannot prevent users from trashing it themselves in a custom # we cannot prevent users from trashing it themselves in a custom
# trap_http_exception method so that's their fault then. # trap_http_exception method so that's their fault then.
# MultiDict passes the key to the exception, but that's ignored
# when generating the response message. Set an informative
# description for key errors in debug mode or when trapping errors.
if (
(self.debug or self.config['TRAP_BAD_REQUEST_ERRORS'])
and isinstance(e, BadRequestKeyError)
# only set it if it's still the default description
and e.description is BadRequestKeyError.description
):
e.description = "KeyError: '{0}'".format(*e.args)
if isinstance(e, HTTPException) and not self.trap_http_exception(e): if isinstance(e, HTTPException) and not self.trap_http_exception(e):
return self.handle_http_exception(e) return self.handle_http_exception(e)
@ -1556,7 +1623,7 @@ class Flask(_PackageBoundObject):
self.log_exception((exc_type, exc_value, tb)) self.log_exception((exc_type, exc_value, tb))
if handler is None: if handler is None:
return InternalServerError() return InternalServerError()
return handler(e) return self.finalize_request(handler(e), from_error_handler=True)
def log_exception(self, exc_info): def log_exception(self, exc_info):
"""Logs an exception. This is called by :meth:`handle_exception` """Logs an exception. This is called by :meth:`handle_exception`
@ -1624,9 +1691,30 @@ class Flask(_PackageBoundObject):
rv = self.dispatch_request() rv = self.dispatch_request()
except Exception as e: except Exception as e:
rv = self.handle_user_exception(e) rv = self.handle_user_exception(e)
return self.finalize_request(rv)
def finalize_request(self, rv, from_error_handler=False):
"""Given the return value from a view function this finalizes
the request by converting it into a response and invoking the
postprocessing functions. This is invoked for both normal
request dispatching as well as error handlers.
Because this means that it might be called as a result of a
failure a special safe mode is available which can be enabled
with the `from_error_handler` flag. If enabled, failures in
response processing will be logged and otherwise ignored.
:internal:
"""
response = self.make_response(rv) response = self.make_response(rv)
try:
response = self.process_response(response) response = self.process_response(response)
request_finished.send(self, response=response) request_finished.send(self, response=response)
except Exception:
if not from_error_handler:
raise
self.logger.exception('Request finalizing failed with an '
'error while handling an error')
return response return response
def try_trigger_before_first_request_functions(self): def try_trigger_before_first_request_functions(self):
@ -1679,62 +1767,106 @@ class Flask(_PackageBoundObject):
return False return False
def make_response(self, rv): def make_response(self, rv):
"""Converts the return value from a view function to a real """Convert the return value from a view function to an instance of
response object that is an instance of :attr:`response_class`. :attr:`response_class`.
The following types are allowed for `rv`: :param rv: the return value from the view function. The view function
must return a response. Returning ``None``, or the view ending
.. tabularcolumns:: |p{3.5cm}|p{9.5cm}| without returning, is not allowed. The following types are allowed
for ``view_rv``:
======================= ===========================================
:attr:`response_class` the object is returned unchanged ``str`` (``unicode`` in Python 2)
:class:`str` a response object is created with the A response object is created with the string encoded to UTF-8
string as body as the body.
:class:`unicode` a response object is created with the
string encoded to utf-8 as body ``bytes`` (``str`` in Python 2)
a WSGI function the function is called as WSGI application A response object is created with the bytes as the body.
and buffered as response object
:class:`tuple` A tuple in the form ``(response, status, ``tuple``
headers)`` or ``(response, headers)`` Either ``(body, status, headers)``, ``(body, status)``, or
where `response` is any of the ``(body, headers)``, where ``body`` is any of the other types
types defined here, `status` is a string allowed here, ``status`` is a string or an integer, and
or an integer and `headers` is a list or ``headers`` is a dictionary or a list of ``(key, value)``
a dictionary with header values. tuples. If ``body`` is a :attr:`response_class` instance,
======================= =========================================== ``status`` overwrites the exiting value and ``headers`` are
extended.
:param rv: the return value from the view function
:attr:`response_class`
The object is returned unchanged.
other :class:`~werkzeug.wrappers.Response` class
The object is coerced to :attr:`response_class`.
:func:`callable`
The function is called as a WSGI application. The result is
used to create a response object.
.. versionchanged:: 0.9 .. versionchanged:: 0.9
Previously a tuple was interpreted as the arguments for the Previously a tuple was interpreted as the arguments for the
response object. response object.
""" """
status_or_headers = headers = None
if isinstance(rv, tuple):
rv, status_or_headers, headers = rv + (None,) * (3 - len(rv))
if rv is None: status = headers = None
raise ValueError('View function did not return a response')
# unpack tuple returns
if isinstance(rv, (tuple, list)):
len_rv = len(rv)
# a 3-tuple is unpacked directly
if len_rv == 3:
rv, status, headers = rv
# decide if a 2-tuple has status or headers
elif len_rv == 2:
if isinstance(rv[1], (Headers, dict, tuple, list)):
rv, headers = rv
else:
rv, status = rv
# other sized tuples are not allowed
else:
raise TypeError(
'The view function did not return a valid response tuple.'
' The tuple must have the form (body, status, headers),'
' (body, status), or (body, headers).'
)
if isinstance(status_or_headers, (dict, list)): # the body must not be None
headers, status_or_headers = status_or_headers, None if rv is None:
raise TypeError(
'The view function did not return a valid response. The'
' function either returned None or ended without a return'
' statement.'
)
# make sure the body is an instance of the response class
if not isinstance(rv, self.response_class): if not isinstance(rv, self.response_class):
# When we create a response object directly, we let the constructor
# set the headers and status. We do this because there can be
# some extra logic involved when creating these objects with
# specific values (like default content type selection).
if isinstance(rv, (text_type, bytes, bytearray)): if isinstance(rv, (text_type, bytes, bytearray)):
rv = self.response_class(rv, headers=headers, # let the response class set the status and headers instead of
status=status_or_headers) # waiting to do it manually, so that the class can handle any
headers = status_or_headers = None # special logic
rv = self.response_class(rv, status=status, headers=headers)
status = headers = None
else: else:
# evaluate a WSGI callable, or coerce a different response
# class to the correct type
try:
rv = self.response_class.force_type(rv, request.environ) rv = self.response_class.force_type(rv, request.environ)
except TypeError as e:
new_error = TypeError(
'{e}\nThe view function did not return a valid'
' response. The return type must be a string, tuple,'
' Response instance, or WSGI callable, but it was a'
' {rv.__class__.__name__}.'.format(e=e, rv=rv)
)
reraise(TypeError, new_error, sys.exc_info()[2])
if status_or_headers is not None: # prefer the status if it was provided
if isinstance(status_or_headers, string_types): if status is not None:
rv.status = status_or_headers if isinstance(status, (text_type, bytes, bytearray)):
rv.status = status
else: else:
rv.status_code = status_or_headers rv.status_code = status
# extend existing headers with provided headers
if headers: if headers:
rv.headers.extend(headers) rv.headers.extend(headers)
@ -1759,7 +1891,7 @@ class Flask(_PackageBoundObject):
if self.config['SERVER_NAME'] is not None: if self.config['SERVER_NAME'] is not None:
return self.url_map.bind( return self.url_map.bind(
self.config['SERVER_NAME'], self.config['SERVER_NAME'],
script_name=self.config['APPLICATION_ROOT'] or '/', script_name=self.config['APPLICATION_ROOT'],
url_scheme=self.config['PREFERRED_URL_SCHEME']) url_scheme=self.config['PREFERRED_URL_SCHEME'])
def inject_url_defaults(self, endpoint, values): def inject_url_defaults(self, endpoint, values):
@ -1797,16 +1929,16 @@ class Flask(_PackageBoundObject):
raise error raise error
def preprocess_request(self): def preprocess_request(self):
"""Called before the actual request dispatching and will """Called before the request is dispatched. Calls
call each :meth:`before_request` decorated function, passing no :attr:`url_value_preprocessors` registered with the app and the
arguments. current blueprint (if any). Then calls :attr:`before_request_funcs`
If any of these functions returns a value, it's handled as registered with the app and the blueprint.
if it was the return value from the view and further
request handling is stopped.
This also triggers the :meth:`url_value_preprocessor` functions before If any :meth:`before_request` handler returns a non-None value, the
the actual :meth:`before_request` functions are called. value is handled as if it was the return value from the view, and
further request handling is stopped.
""" """
bp = _request_ctx_stack.top.request.blueprint bp = _request_ctx_stack.top.request.blueprint
funcs = self.url_value_preprocessors.get(None, ()) funcs = self.url_value_preprocessors.get(None, ())
@ -1846,7 +1978,7 @@ class Flask(_PackageBoundObject):
for handler in funcs: for handler in funcs:
response = handler(response) response = handler(response)
if not self.session_interface.is_null_session(ctx.session): if not self.session_interface.is_null_session(ctx.session):
self.save_session(ctx.session, response) self.session_interface.save_session(self, ctx.session, response)
return response return response
def do_teardown_request(self, exc=_sentinel): def do_teardown_request(self, exc=_sentinel):
@ -1931,10 +2063,19 @@ class Flask(_PackageBoundObject):
def test_request_context(self, *args, **kwargs): def test_request_context(self, *args, **kwargs):
"""Creates a WSGI environment from the given values (see """Creates a WSGI environment from the given values (see
:class:`werkzeug.test.EnvironBuilder` for more information, this :class:`werkzeug.test.EnvironBuilder` for more information, this
function accepts the same arguments). function accepts the same arguments plus two additional).
Additional arguments (only if ``base_url`` is not specified):
:param subdomain: subdomain to use for route matching
:param url_scheme: scheme for the request, default
``PREFERRED_URL_SCHEME`` or ``http``.
""" """
from flask.testing import make_test_environ_builder from flask.testing import make_test_environ_builder
builder = make_test_environ_builder(self, *args, **kwargs) builder = make_test_environ_builder(self, *args, **kwargs)
try: try:
return self.request_context(builder.get_environ()) return self.request_context(builder.get_environ())
finally: finally:
@ -1966,14 +2107,17 @@ class Flask(_PackageBoundObject):
exception context to start the response exception context to start the response
""" """
ctx = self.request_context(environ) ctx = self.request_context(environ)
ctx.push()
error = None error = None
try: try:
try: try:
ctx.push()
response = self.full_dispatch_request() response = self.full_dispatch_request()
except Exception as e: except Exception as e:
error = e error = e
response = self.make_response(self.handle_exception(e)) response = self.handle_exception(e)
except:
error = sys.exc_info()[1]
raise
return response(environ, start_response) return response(environ, start_response)
finally: finally:
if self.should_ignore_error(error): if self.should_ignore_error(error):

7
flask/blueprints.py

@ -89,6 +89,13 @@ class Blueprint(_PackageBoundObject):
warn_on_modifications = False warn_on_modifications = False
_got_registered_once = False _got_registered_once = False
#: Blueprint local JSON decoder class to use.
#: Set to ``None`` to use the app's :class:`~flask.app.Flask.json_encoder`.
json_encoder = None
#: Blueprint local JSON decoder class to use.
#: Set to ``None`` to use the app's :class:`~flask.app.Flask.json_decoder`.
json_decoder = None
def __init__(self, name, import_name, static_folder=None, def __init__(self, name, import_name, static_folder=None,
static_url_path=None, template_folder=None, static_url_path=None, template_folder=None,
url_prefix=None, subdomain=None, url_defaults=None, url_prefix=None, subdomain=None, url_defaults=None,

220
flask/cli.py

@ -9,43 +9,142 @@
:license: BSD, see LICENSE for more details. :license: BSD, see LICENSE for more details.
""" """
import ast
import inspect
import os import os
import re
import sys import sys
from threading import Lock, Thread import traceback
from functools import update_wrapper from functools import update_wrapper
from operator import attrgetter
from threading import Lock, Thread
import click import click
from . import __version__
from ._compat import iteritems, reraise from ._compat import iteritems, reraise
from .globals import current_app
from .helpers import get_debug_flag from .helpers import get_debug_flag
from . import __version__ from ._compat import getargspec
class NoAppException(click.UsageError): class NoAppException(click.UsageError):
"""Raised if an application cannot be found or loaded.""" """Raised if an application cannot be found or loaded."""
def find_best_app(module): def find_best_app(script_info, module):
"""Given a module instance this tries to find the best possible """Given a module instance this tries to find the best possible
application in the module or raises an exception. application in the module or raises an exception.
""" """
from . import Flask from . import Flask
# Search for the most common names first. # Search for the most common names first.
for attr_name in 'app', 'application': for attr_name in ('app', 'application'):
app = getattr(module, attr_name, None) app = getattr(module, attr_name, None)
if app is not None and isinstance(app, Flask): if isinstance(app, Flask):
return app return app
# Otherwise find the only object that is a Flask instance. # Otherwise find the only object that is a Flask instance.
matches = [v for k, v in iteritems(module.__dict__) matches = [
if isinstance(v, Flask)] v for k, v in iteritems(module.__dict__) if isinstance(v, Flask)
]
if len(matches) == 1: if len(matches) == 1:
return matches[0] return matches[0]
raise NoAppException('Failed to find application in module "%s". Are ' elif len(matches) > 1:
'you sure it contains a Flask application? Maybe ' raise NoAppException(
'you wrapped it in a WSGI middleware or you are ' 'Auto-detected multiple Flask applications in module "{module}".'
'using a factory function.' % module.__name__) ' Use "FLASK_APP={module}:name" to specify the correct'
' one.'.format(module=module.__name__)
)
# Search for app factory functions.
for attr_name in ('create_app', 'make_app'):
app_factory = getattr(module, attr_name, None)
if inspect.isfunction(app_factory):
try:
app = call_factory(app_factory, script_info)
if isinstance(app, Flask):
return app
except TypeError:
raise NoAppException(
'Auto-detected "{function}()" in module "{module}", but '
'could not call it without specifying arguments.'.format(
function=attr_name, module=module.__name__
)
)
raise NoAppException(
'Failed to find application in module "{module}". Are you sure '
'it contains a Flask application? Maybe you wrapped it in a WSGI '
'middleware.'.format(module=module.__name__)
)
def call_factory(app_factory, script_info, arguments=()):
"""Takes an app factory, a ``script_info` object and optionally a tuple
of arguments. Checks for the existence of a script_info argument and calls
the app_factory depending on that and the arguments provided.
"""
args_spec = getargspec(app_factory)
arg_names = args_spec.args
arg_defaults = args_spec.defaults
if 'script_info' in arg_names:
return app_factory(*arguments, script_info=script_info)
elif arguments:
return app_factory(*arguments)
elif not arguments and len(arg_names) == 1 and arg_defaults is None:
return app_factory(script_info)
return app_factory()
def find_app_by_string(string, script_info, module):
"""Checks if the given string is a variable name or a function. If it is
a function, it checks for specified arguments and whether it takes
a ``script_info`` argument and calls the function with the appropriate
arguments."""
from . import Flask
function_regex = r'^(?P<name>\w+)(?:\((?P<args>.*)\))?$'
match = re.match(function_regex, string)
if match:
name, args = match.groups()
try:
if args is not None:
args = args.rstrip(' ,')
if args:
args = ast.literal_eval(
"({args}, )".format(args=args))
else:
args = ()
app_factory = getattr(module, name, None)
app = call_factory(app_factory, script_info, args)
else:
attr = getattr(module, name, None)
if inspect.isfunction(attr):
app = call_factory(attr, script_info)
else:
app = attr
if isinstance(app, Flask):
return app
else:
raise RuntimeError('Failed to find application in module '
'"{name}"'.format(name=module))
except TypeError as e:
new_error = NoAppException(
'{e}\nThe app factory "{factory}" in module "{module}" could'
' not be called with the specified arguments (and a'
' script_info argument automatically added if applicable).'
' Did you make sure to use the right number of arguments as'
' well as not using keyword arguments or'
' non-literals?'.format(e=e, factory=string, module=module))
reraise(NoAppException, new_error, sys.exc_info()[2])
else:
raise NoAppException(
'The provided string "{string}" is not a valid variable name'
'or function expression.'.format(string=string))
def prepare_exec_for_file(filename): def prepare_exec_for_file(filename):
@ -77,7 +176,7 @@ def prepare_exec_for_file(filename):
return '.'.join(module[::-1]) return '.'.join(module[::-1])
def locate_app(app_id): def locate_app(script_info, app_id):
"""Attempts to locate the application.""" """Attempts to locate the application."""
__traceback_hide__ = True __traceback_hide__ = True
if ':' in app_id: if ':' in app_id:
@ -89,20 +188,23 @@ def locate_app(app_id):
try: try:
__import__(module) __import__(module)
except ImportError: except ImportError:
raise NoAppException('The file/path provided (%s) does not appear to ' # Reraise the ImportError if it occurred within the imported module.
'exist. Please verify the path is correct. If ' # Determine this by checking whether the trace has a depth > 1.
'app is not on PYTHONPATH, ensure the extension ' if sys.exc_info()[-1].tb_next:
'is .py' % module) stack_trace = traceback.format_exc()
raise NoAppException('There was an error trying to import'
' the app (%s):\n%s' % (module, stack_trace))
else:
raise NoAppException('The file/path provided (%s) does not appear'
' to exist. Please verify the path is '
'correct. If app is not on PYTHONPATH, '
'ensure the extension is .py' % module)
mod = sys.modules[module] mod = sys.modules[module]
if app_obj is None: if app_obj is None:
app = find_best_app(mod) return find_best_app(script_info, mod)
else: else:
app = getattr(mod, app_obj, None) return find_app_by_string(app_obj, script_info, mod)
if app is None:
raise RuntimeError('Failed to find application in module "%s"'
% module)
return app
def find_default_import_path(): def find_default_import_path():
@ -131,9 +233,9 @@ version_option = click.Option(['--version'],
is_flag=True, is_eager=True) is_flag=True, is_eager=True)
class DispatchingApp(object): class DispatchingApp(object):
"""Special application that dispatches to a flask application which """Special application that dispatches to a Flask application which
is imported by name in a background thread. If an error happens is imported by name in a background thread. If an error happens
it is is recorded and shows as part of the WSGI handling which in case it is recorded and shown as part of the WSGI handling which in case
of the Werkzeug debugger means that it shows up in the browser. of the Werkzeug debugger means that it shows up in the browser.
""" """
@ -220,7 +322,7 @@ class ScriptInfo(object):
if self._loaded_app is not None: if self._loaded_app is not None:
return self._loaded_app return self._loaded_app
if self.create_app is not None: if self.create_app is not None:
rv = self.create_app(self) rv = call_factory(self.create_app, self)
else: else:
if not self.app_import_path: if not self.app_import_path:
raise NoAppException( raise NoAppException(
@ -228,7 +330,7 @@ class ScriptInfo(object):
'the FLASK_APP environment variable.\n\nFor more ' 'the FLASK_APP environment variable.\n\nFor more '
'information see ' 'information see '
'http://flask.pocoo.org/docs/latest/quickstart/') 'http://flask.pocoo.org/docs/latest/quickstart/')
rv = locate_app(self.app_import_path) rv = locate_app(self, self.app_import_path)
debug = get_debug_flag() debug = get_debug_flag()
if debug is not None: if debug is not None:
rv.debug = debug rv.debug = debug
@ -310,6 +412,7 @@ class FlaskGroup(AppGroup):
if add_default_commands: if add_default_commands:
self.add_command(run_command) self.add_command(run_command)
self.add_command(shell_command) self.add_command(shell_command)
self.add_command(routes_command)
self._loaded_plugin_commands = False self._loaded_plugin_commands = False
@ -362,7 +465,9 @@ class FlaskGroup(AppGroup):
# want the help page to break if the app does not exist. # want the help page to break if the app does not exist.
# If someone attempts to use the command we try to create # If someone attempts to use the command we try to create
# the app again and this will give us the error. # the app again and this will give us the error.
pass # However, we will not do so silently because that would confuse
# users.
traceback.print_exc()
return sorted(rv) return sorted(rv)
def main(self, *args, **kwargs): def main(self, *args, **kwargs):
@ -406,6 +511,13 @@ def run_command(info, host, port, reload, debugger, eager_loading,
""" """
from werkzeug.serving import run_simple from werkzeug.serving import run_simple
# Set a global flag that indicates that we were invoked from the
# command line interface provided server command. This is detected
# by Flask.run to make the call into a no-op. This is necessary to
# avoid ugly errors when the script that is loaded here also attempts
# to start a server.
os.environ['FLASK_RUN_FROM_CLI_SERVER'] = '1'
debug = get_debug_flag() debug = get_debug_flag()
if reload is None: if reload is None:
reload = bool(debug) reload = bool(debug)
@ -429,8 +541,7 @@ def run_command(info, host, port, reload, debugger, eager_loading,
print(' * Forcing debug mode %s' % (debug and 'on' or 'off')) print(' * Forcing debug mode %s' % (debug and 'on' or 'off'))
run_simple(host, port, app, use_reloader=reload, run_simple(host, port, app, use_reloader=reload,
use_debugger=debugger, threaded=with_threads, use_debugger=debugger, threaded=with_threads)
passthrough_errors=True)
@click.command('shell', short_help='Runs a shell in the app context.') @click.command('shell', short_help='Runs a shell in the app context.')
@ -467,10 +578,57 @@ def shell_command():
code.interact(banner=banner, local=ctx) code.interact(banner=banner, local=ctx)
@click.command('routes', short_help='Show the routes for the app.')
@click.option(
'--sort', '-s',
type=click.Choice(('endpoint', 'methods', 'rule', 'match')),
default='endpoint',
help=(
'Method to sort routes by. "match" is the order that Flask will match '
'routes when dispatching a request.'
)
)
@click.option(
'--all-methods',
is_flag=True,
help="Show HEAD and OPTIONS methods."
)
@with_appcontext
def routes_command(sort, all_methods):
"""Show all registered routes with endpoints and methods."""
rules = list(current_app.url_map.iter_rules())
ignored_methods = set(() if all_methods else ('HEAD', 'OPTIONS'))
if sort in ('endpoint', 'rule'):
rules = sorted(rules, key=attrgetter(sort))
elif sort == 'methods':
rules = sorted(rules, key=lambda rule: sorted(rule.methods))
rule_methods = [
', '.join(sorted(rule.methods - ignored_methods)) for rule in rules
]
headers = ('Endpoint', 'Methods', 'Rule')
widths = (
max(len(rule.endpoint) for rule in rules),
max(len(methods) for methods in rule_methods),
max(len(rule.rule) for rule in rules),
)
widths = [max(len(h), w) for h, w in zip(headers, widths)]
row = '{{0:<{0}}} {{1:<{1}}} {{2:<{2}}}'.format(*widths)
click.echo(row.format(*headers).strip())
click.echo(row.format(*('-' * width for width in widths)))
for rule, methods in zip(rules, rule_methods):
click.echo(row.format(rule.endpoint, methods, rule.rule).rstrip())
cli = FlaskGroup(help="""\ cli = FlaskGroup(help="""\
This shell command acts as general utility script for Flask applications. This shell command acts as general utility script for Flask applications.
It loads the application configured (either through the FLASK_APP environment It loads the application configured (through the FLASK_APP environment
variable) and then provides commands either provided by the application or variable) and then provides commands either provided by the application or
Flask itself. Flask itself.

2
flask/config.py

@ -126,7 +126,7 @@ class Config(dict):
d = types.ModuleType('config') d = types.ModuleType('config')
d.__file__ = filename d.__file__ = filename
try: try:
with open(filename) as config_file: with open(filename, mode='rb') as config_file:
exec(compile(config_file.read(), filename, 'exec'), d.__dict__) exec(compile(config_file.read(), filename, 'exec'), d.__dict__)
except IOError as e: except IOError as e:
if silent and e.errno in (errno.ENOENT, errno.EISDIR): if silent and e.errno in (errno.ENOENT, errno.EISDIR):

17
flask/ctx.py

@ -325,13 +325,18 @@ class RequestContext(object):
_request_ctx_stack.push(self) _request_ctx_stack.push(self)
# Open the session at the moment that the request context is # Open the session at the moment that the request context is available.
# available. This allows a custom open_session method to use the # This allows a custom open_session method to use the request context.
# request context (e.g. code that access database information # Only open a new session if this is the first time the request was
# stored on `g` instead of the appcontext). # pushed, otherwise stream_with_context loses the session.
self.session = self.app.open_session(self.request)
if self.session is None: if self.session is None:
self.session = self.app.make_null_session() session_interface = self.app.session_interface
self.session = session_interface.open_session(
self.app, self.request
)
if self.session is None:
self.session = session_interface.make_null_session(self.app)
def pop(self, exc=_sentinel): def pop(self, exc=_sentinel):
"""Pops the request context and unbinds it by doing that. This will """Pops the request context and unbinds it by doing that. This will

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

Loading…
Cancel
Save