Merge branch 'master' into makecov
This commit is contained in:
commit
4cd0299aff
126 changed files with 2799 additions and 1474 deletions
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
CHANGES merge=union
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
|
|
@ -11,5 +11,9 @@ _mailinglist
|
|||
.tox
|
||||
.cache/
|
||||
.idea/
|
||||
.coverage
|
||||
|
||||
# Coverage reports
|
||||
htmlcov
|
||||
.coverage
|
||||
.coverage.*
|
||||
*,cover
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ python:
|
|||
- "3.3"
|
||||
- "3.4"
|
||||
- "3.5"
|
||||
- "3.6"
|
||||
|
||||
env:
|
||||
- REQUIREMENTS=lowest
|
||||
|
|
@ -32,7 +33,10 @@ matrix:
|
|||
env: REQUIREMENTS=lowest
|
||||
- python: "3.5"
|
||||
env: REQUIREMENTS=lowest-simplejson
|
||||
|
||||
- python: "3.6"
|
||||
env: REQUIREMENTS=lowest
|
||||
- python: "3.6"
|
||||
env: REQUIREMENTS=lowest-simplejson
|
||||
|
||||
install:
|
||||
- pip install tox
|
||||
|
|
|
|||
1
AUTHORS
1
AUTHORS
|
|
@ -21,6 +21,7 @@ Patches and Suggestions
|
|||
- Florent Xicluna
|
||||
- Georg Brandl
|
||||
- Jeff Widman @jeffwidman
|
||||
- Joshua Bronson @jab
|
||||
- Justin Quick
|
||||
- Kenneth Reitz
|
||||
- Keyan Pishdadian
|
||||
|
|
|
|||
95
CHANGES
95
CHANGES
|
|
@ -3,14 +3,103 @@ Flask Changelog
|
|||
|
||||
Here you can see the full list of changes between each Flask release.
|
||||
|
||||
Version 0.13
|
||||
------------
|
||||
|
||||
Major release, unreleased
|
||||
|
||||
- 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.
|
||||
- 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`_)
|
||||
|
||||
.. _#1489: https://github.com/pallets/flask/pull/1489
|
||||
.. _#1898: https://github.com/pallets/flask/pull/1898
|
||||
.. _#1936: https://github.com/pallets/flask/pull/1936
|
||||
.. _#2017: https://github.com/pallets/flask/pull/2017
|
||||
.. _#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
|
||||
|
||||
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
|
||||
------------
|
||||
|
||||
Released on December 21st 2016, codename Punsch.
|
||||
|
||||
- the cli command now responds to `--version`.
|
||||
- Mimetype guessing for ``send_file`` has been removed, as per issue ``#104``.
|
||||
See pull request ``#1849``.
|
||||
- Mimetype guessing and ETag generation for file-like objects in ``send_file``
|
||||
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``
|
||||
(pull request ``#1730``).
|
||||
- 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
|
||||
--------------
|
||||
|
||||
Bugfix release, unreleased
|
||||
|
||||
- Fix crash when running under PyPy3, see pull request ``#1814``.
|
||||
|
||||
Version 0.11.1
|
||||
--------------
|
||||
|
|
@ -318,7 +407,7 @@ Released on September 29th 2011, codename Rakija
|
|||
- 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
|
||||
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
|
||||
see :ref:`instance-folders`.
|
||||
- Added the ``APPLICATION_ROOT`` configuration variable.
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ Submitting patches
|
|||
clearly under which circumstances the bug happens. Make sure the test fails
|
||||
without your patch.
|
||||
|
||||
- Try to follow `PEP8 <http://legacy.python.org/dev/peps/pep-0008/>`_, but you
|
||||
- Try to follow `PEP8 <https://www.python.org/dev/peps/pep-0008/>`_, but you
|
||||
may ignore the line-length-limit if following it would make the code uglier.
|
||||
|
||||
Getting Started
|
||||
|
|
@ -37,6 +37,12 @@ Getting Started
|
|||
You probably want to set up a `virtualenv
|
||||
<https://virtualenv.readthedocs.io/en/latest/index.html>`_.
|
||||
|
||||
|
||||
The minimal requirement for running the testsuite is ``pytest``. You can
|
||||
install it with::
|
||||
|
||||
pip install pytest
|
||||
|
||||
Clone this repository::
|
||||
|
||||
git clone https://github.com/pallets/flask.git
|
||||
|
|
@ -56,7 +62,7 @@ install it with::
|
|||
|
||||
Then you can run the testsuite with::
|
||||
|
||||
py.test
|
||||
pytest tests/
|
||||
|
||||
**Shortcut**: ``make test`` will ensure ``pytest`` is installed, and run it.
|
||||
|
||||
|
|
@ -85,12 +91,39 @@ plugin. This assumes you have already run the testsuite (see previous section):
|
|||
|
||||
After this has been installed, you can output a report to the command line using this command::
|
||||
|
||||
py.test --cov=flask tests/
|
||||
pytest --cov=flask tests/
|
||||
|
||||
Generate a HTML report can be done using this command::
|
||||
|
||||
py.test --cov-report html --cov=flask tests/
|
||||
pytest --cov-report html --cov=flask tests/
|
||||
|
||||
Full docs on ``coverage.py`` are here: https://coverage.readthedocs.io
|
||||
|
||||
**Shortcut**: ``make cov`` will ensure ``pytest-cov`` is installed, run it, display the results, *and* save the HTML report.
|
||||
|
||||
|
||||
Caution
|
||||
=======
|
||||
pushing
|
||||
-------
|
||||
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.
|
||||
|
||||
|
||||
cloning
|
||||
-------
|
||||
The zero-padded file modes files above can cause issues while cloning, too. If you have
|
||||
|
||||
::
|
||||
|
||||
[fetch]
|
||||
fsckobjects = true
|
||||
|
||||
or
|
||||
|
||||
::
|
||||
|
||||
[receive]
|
||||
fsckObjects = true
|
||||
|
||||
|
||||
set in your git configuration file, cloning this repository will fail. The only solution is to set both of the above settings to false while cloning, and then setting them back to true after the cloning is finished.
|
||||
|
|
|
|||
8
Makefile
8
Makefile
|
|
@ -3,12 +3,8 @@
|
|||
all: clean-pyc test
|
||||
|
||||
test:
|
||||
pip install -r test-requirements.txt -q
|
||||
FLASK_DEBUG= py.test tests examples
|
||||
|
||||
tox-test:
|
||||
pip install -r test-requirements.txt -q
|
||||
tox
|
||||
pip install -r test-requirements.txt
|
||||
tox -e py-release
|
||||
|
||||
cov:
|
||||
pip install -r test-requirements.txt -q
|
||||
|
|
|
|||
4
README
4
README
|
|
@ -33,9 +33,9 @@
|
|||
|
||||
Good that you're asking. The tests are in 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
|
||||
|
||||
|
|
|
|||
6
docs/_templates/sidebarintro.html
vendored
6
docs/_templates/sidebarintro.html
vendored
|
|
@ -16,7 +16,7 @@
|
|||
<h3>Useful Links</h3>
|
||||
<ul>
|
||||
<li><a href="http://flask.pocoo.org/">The Flask Website</a></li>
|
||||
<li><a href="http://pypi.python.org/pypi/Flask">Flask @ PyPI</a></li>
|
||||
<li><a href="http://github.com/pallets/flask">Flask @ GitHub</a></li>
|
||||
<li><a href="http://github.com/pallets/flask/issues">Issue Tracker</a></li>
|
||||
<li><a href="https://pypi.python.org/pypi/Flask">Flask @ PyPI</a></li>
|
||||
<li><a href="https://github.com/pallets/flask">Flask @ GitHub</a></li>
|
||||
<li><a href="https://github.com/pallets/flask/issues">Issue Tracker</a></li>
|
||||
</ul>
|
||||
|
|
|
|||
68
docs/api.rst
68
docs/api.rst
|
|
@ -30,61 +30,12 @@ Incoming Request Data
|
|||
|
||||
.. autoclass:: Request
|
||||
: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.
|
||||
:inherited-members:
|
||||
|
||||
.. attribute:: environ
|
||||
|
||||
The underlying WSGI environment.
|
||||
|
||||
.. attribute:: method
|
||||
|
||||
The current request method (``POST``, ``GET`` etc.)
|
||||
|
||||
.. attribute:: path
|
||||
.. attribute:: full_path
|
||||
.. attribute:: script_root
|
||||
|
|
@ -114,15 +65,8 @@ Incoming Request Data
|
|||
`url_root` ``u'http://www.example.com/myapplication/'``
|
||||
============= ======================================================
|
||||
|
||||
.. attribute:: is_xhr
|
||||
|
||||
``True`` if the request was triggered via a JavaScript
|
||||
`XMLHttpRequest`. This only works with libraries that support the
|
||||
``X-Requested-With`` header and set it to `XMLHttpRequest`.
|
||||
Libraries that do that are prototype, jQuery and Mochikit and
|
||||
probably some more.
|
||||
|
||||
.. class:: request
|
||||
.. attribute:: request
|
||||
|
||||
To access incoming request data, you can use the global `request`
|
||||
object. Flask parses incoming request data for you and gives you
|
||||
|
|
@ -316,13 +260,7 @@ Useful Functions and Classes
|
|||
|
||||
.. autofunction:: url_for
|
||||
|
||||
.. function:: abort(code)
|
||||
|
||||
Raises an :exc:`~werkzeug.exceptions.HTTPException` for the given
|
||||
status code. For example to abort request handling with a page not
|
||||
found exception, you would call ``abort(404)``.
|
||||
|
||||
:param code: the HTTP error code.
|
||||
.. autofunction:: abort
|
||||
|
||||
.. autofunction:: redirect
|
||||
|
||||
|
|
|
|||
|
|
@ -74,9 +74,9 @@ The application context is also used by the :func:`~flask.url_for`
|
|||
function in case a ``SERVER_NAME`` was configured. This allows you to
|
||||
generate URLs even in the absence of a request.
|
||||
|
||||
If no request context has been pushed and an application context has
|
||||
not been explicitly set, a ``RuntimeError`` will be raised.
|
||||
::
|
||||
If no request context has been pushed and an application context has
|
||||
not been explicitly set, a ``RuntimeError`` will be raised. ::
|
||||
|
||||
RuntimeError: Working outside of application context.
|
||||
|
||||
Locality of the Context
|
||||
|
|
|
|||
|
|
@ -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
|
||||
developed, it became useful to a wide audience. As you grow your codebase,
|
||||
don't just use Flask -- understand it. Read the source. Flask's code is
|
||||
written to be read; 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
|
||||
internal utilities so that you can find the hook points needed for your
|
||||
project.
|
||||
|
|
|
|||
|
|
@ -177,11 +177,11 @@ the `template_folder` parameter to the :class:`Blueprint` constructor::
|
|||
admin = Blueprint('admin', __name__, template_folder='templates')
|
||||
|
||||
For static files, the path can be absolute or relative to the blueprint
|
||||
resource folder.
|
||||
resource folder.
|
||||
|
||||
The template folder is added to the search path of templates but with a lower
|
||||
priority than the actual application's template folder. That way you can
|
||||
easily override templates that a blueprint provides in the actual application.
|
||||
The template folder is added to the search path of templates but with a lower
|
||||
priority than the actual application's template folder. That way you can
|
||||
easily override templates that a blueprint provides in the actual application.
|
||||
This also means that if you don't want a blueprint template to be accidentally
|
||||
overridden, make sure that no other blueprint or actual application template
|
||||
has the same relative path. When multiple blueprints provide the same relative
|
||||
|
|
@ -194,7 +194,7 @@ want to render the template ``'admin/index.html'`` and you have provided
|
|||
this: :file:`yourapplication/admin/templates/admin/index.html`. The reason
|
||||
for the extra ``admin`` folder is to avoid getting our template overridden
|
||||
by a template named ``index.html`` in the actual application template
|
||||
folder.
|
||||
folder.
|
||||
|
||||
To further reiterate this: if you have a blueprint named ``admin`` and you
|
||||
want to render a template called :file:`index.html` which is specific to this
|
||||
|
|
@ -245,4 +245,22 @@ Here is an example for a "404 Page Not Found" exception::
|
|||
def page_not_found(e):
|
||||
return render_template('pages/404.html')
|
||||
|
||||
Most errorhandlers will simply work as expected; however, there is a caveat
|
||||
concerning handlers for 404 and 405 exceptions. These errorhandlers are only
|
||||
invoked from an appropriate ``raise`` statement or a call to ``abort`` in another
|
||||
of the blueprint's view functions; they are not invoked by, e.g., an invalid URL
|
||||
access. This is because the blueprint does not "own" a certain URL space, so
|
||||
the application instance has no way of knowing which blueprint 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`.
|
||||
|
|
|
|||
32
docs/cli.rst
32
docs/cli.rst
|
|
@ -32,7 +32,7 @@ Python module that contains a Flask application.
|
|||
|
||||
In that imported file the name of the app needs to be called ``app`` or
|
||||
optionally be specified after a colon. For instance
|
||||
`mymodule:application` would tell it to use the `application` object in
|
||||
``mymodule:application`` would tell it to use the `application` object in
|
||||
the :file:`mymodule.py` file.
|
||||
|
||||
Given a :file:`hello.py` file with the application in it named ``app``
|
||||
|
|
@ -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
|
||||
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
|
||||
----------
|
||||
|
||||
The :command:`flask` script can also be instructed to enable the debug
|
||||
mode of the application automatically by exporting ``FLASK_DEBUG``. If
|
||||
set to ``1`` debug is enabled or ``0`` disables it.
|
||||
|
||||
Or with a filename::
|
||||
set to ``1`` debug is enabled or ``0`` disables it::
|
||||
|
||||
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
|
||||
app = create_app(os.environ['YOURAPPLICATION_CONFIG'])
|
||||
|
||||
Once this has happened you can make the flask command automatically pick
|
||||
it up::
|
||||
Once this has happened you can make the :command:`flask` command automatically
|
||||
pick it up::
|
||||
|
||||
export YOURAPPLICATION_CONFIG=/path/to/config.cfg
|
||||
export FLASK_APP=/path/to/autoapp.py
|
||||
|
|
@ -218,13 +228,13 @@ step.
|
|||
CLI Plugins
|
||||
-----------
|
||||
|
||||
Flask extensions can always patch the `Flask.cli` instance with more
|
||||
Flask extensions can always patch the :attr:`Flask.cli` instance with more
|
||||
commands if they want. However there is a second way to add CLI plugins
|
||||
to Flask which is through `setuptools`. If you make a Python package that
|
||||
should export a Flask command line plugin you can ship a `setup.py` file
|
||||
to Flask which is through ``setuptools``. If you make a Python package that
|
||||
should export a Flask command line plugin you can ship a :file:`setup.py` file
|
||||
that declares an entrypoint that points to a click command:
|
||||
|
||||
Example `setup.py`::
|
||||
Example :file:`setup.py`::
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
|
|
@ -237,7 +247,7 @@ Example `setup.py`::
|
|||
''',
|
||||
)
|
||||
|
||||
Inside `mypackage/commands.py` you can then export a Click object::
|
||||
Inside :file:`mypackage/commands.py` you can then export a Click object::
|
||||
|
||||
import click
|
||||
|
||||
|
|
|
|||
91
docs/conf.py
91
docs/conf.py
|
|
@ -11,13 +11,19 @@
|
|||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
from __future__ import print_function
|
||||
import sys, os
|
||||
import os
|
||||
import sys
|
||||
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,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
sys.path.append(os.path.abspath('_themes'))
|
||||
sys.path.append(os.path.abspath('.'))
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '_themes'))
|
||||
sys.path.append(os.path.dirname(__file__))
|
||||
|
||||
# -- General configuration -----------------------------------------------------
|
||||
|
||||
|
|
@ -32,6 +38,14 @@ extensions = [
|
|||
'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.
|
||||
templates_path = ['_templates']
|
||||
|
||||
|
|
@ -46,22 +60,21 @@ master_doc = 'index'
|
|||
|
||||
# General information about the project.
|
||||
project = u'Flask'
|
||||
copyright = u'2015, Armin Ronacher'
|
||||
copyright = u'2010 - {0}, Armin Ronacher'.format(BUILD_DATE.year)
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
import pkg_resources
|
||||
try:
|
||||
release = pkg_resources.get_distribution('Flask').version
|
||||
except pkg_resources.DistributionNotFound:
|
||||
print('Flask must be installed to build the documentation.')
|
||||
print('Install from source using `pip install -e .` in a virtualenv.')
|
||||
sys.exit(1)
|
||||
del pkg_resources
|
||||
|
||||
if 'dev' in release:
|
||||
release = release.split('dev')[0] + 'dev'
|
||||
release = ''.join(release.partition('dev')[:2])
|
||||
|
||||
version = '.'.join(release.split('.')[:2])
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
|
|
@ -100,14 +113,12 @@ exclude_patterns = ['_build']
|
|||
|
||||
# The theme to use for HTML and HTML Help pages. Major themes that come with
|
||||
# Sphinx are currently 'default' and 'sphinxdoc'.
|
||||
html_theme = 'flask'
|
||||
# html_theme = 'default'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
html_theme_options = {
|
||||
'touch_icon': 'touch-icon.png'
|
||||
}
|
||||
# html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
html_theme_path = ['_themes']
|
||||
|
|
@ -126,7 +137,7 @@ html_theme_path = ['_themes']
|
|||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
html_favicon = "flask-favicon.ico"
|
||||
html_favicon = '_static/flask-favicon.ico'
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
|
|
@ -143,9 +154,18 @@ html_static_path = ['_static']
|
|||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
html_sidebars = {
|
||||
'index': ['sidebarintro.html', 'sourcelink.html', 'searchbox.html'],
|
||||
'**': ['sidebarlogo.html', 'localtoc.html', 'relations.html',
|
||||
'sourcelink.html', 'searchbox.html']
|
||||
'index': [
|
||||
'sidebarintro.html',
|
||||
'sourcelink.html',
|
||||
'searchbox.html'
|
||||
],
|
||||
'**': [
|
||||
'sidebarlogo.html',
|
||||
'localtoc.html',
|
||||
'relations.html',
|
||||
'sourcelink.html',
|
||||
'searchbox.html'
|
||||
]
|
||||
}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
|
|
@ -187,8 +207,7 @@ htmlhelp_basename = 'Flaskdoc'
|
|||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||
latex_documents = [
|
||||
('latexindex', 'Flask.tex', u'Flask Documentation',
|
||||
u'Armin Ronacher', 'manual'),
|
||||
('latexindex', 'Flask.tex', u'Flask Documentation', u'Armin Ronacher', 'manual'),
|
||||
]
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
|
|
@ -198,10 +217,10 @@ latex_documents = [
|
|||
latex_use_modindex = False
|
||||
|
||||
latex_elements = {
|
||||
'fontpkg': r'\usepackage{mathpazo}',
|
||||
'papersize': 'a4paper',
|
||||
'pointsize': '12pt',
|
||||
'preamble': r'\usepackage{flaskstyle}'
|
||||
'fontpkg': r'\usepackage{mathpazo}',
|
||||
'papersize': 'a4paper',
|
||||
'pointsize': '12pt',
|
||||
'preamble': r'\usepackage{flaskstyle}'
|
||||
}
|
||||
latex_use_parts = True
|
||||
|
||||
|
|
@ -223,7 +242,7 @@ latex_additional_files = ['flaskstyle.sty', 'logo.pdf']
|
|||
# The scheme of the identifier. Typical schemes are ISBN or URL.
|
||||
#epub_scheme = ''
|
||||
|
||||
# The unique identifier of the text. This can be a ISBN number
|
||||
# The unique identifier of the text. This can be an ISBN number
|
||||
# or the project homepage.
|
||||
#epub_identifier = ''
|
||||
|
||||
|
|
@ -245,21 +264,23 @@ latex_additional_files = ['flaskstyle.sty', 'logo.pdf']
|
|||
#epub_tocdepth = 3
|
||||
|
||||
intersphinx_mapping = {
|
||||
'https://docs.python.org/dev': None,
|
||||
'http://werkzeug.pocoo.org/docs/': None,
|
||||
'http://click.pocoo.org/': None,
|
||||
'http://jinja.pocoo.org/docs/': None,
|
||||
'http://www.sqlalchemy.org/docs/': None,
|
||||
'https://wtforms.readthedocs.io/en/latest/': None,
|
||||
'https://pythonhosted.org/blinker/': None
|
||||
'python': ('https://docs.python.org/3/', None),
|
||||
'werkzeug': ('http://werkzeug.pocoo.org/docs/', None),
|
||||
'click': ('http://click.pocoo.org/', None),
|
||||
'jinja': ('http://jinja.pocoo.org/docs/', None),
|
||||
'sqlalchemy': ('https://docs.sqlalchemy.org/en/latest/', None),
|
||||
'wtforms': ('https://wtforms.readthedocs.io/en/latest/', None),
|
||||
'blinker': ('https://pythonhosted.org/blinker/', None)
|
||||
}
|
||||
|
||||
pygments_style = 'flask_theme_support.FlaskyStyle'
|
||||
|
||||
# fall back if theme is not there
|
||||
try:
|
||||
__import__('flask_theme_support')
|
||||
except ImportError as e:
|
||||
pygments_style = 'flask_theme_support.FlaskyStyle'
|
||||
html_theme = 'flask'
|
||||
html_theme_options = {
|
||||
'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:')
|
||||
|
|
@ -267,10 +288,6 @@ except ImportError as e:
|
|||
print(' git submodule update --init')
|
||||
print('-' * 74)
|
||||
|
||||
pygments_style = 'tango'
|
||||
html_theme = 'default'
|
||||
html_theme_options = {}
|
||||
|
||||
|
||||
# unwrap decorators
|
||||
def unwrap_decorators():
|
||||
|
|
|
|||
|
|
@ -44,6 +44,21 @@ method::
|
|||
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
|
||||
----------------------------
|
||||
|
||||
|
|
@ -52,7 +67,8 @@ The following configuration values are used internally by Flask:
|
|||
.. tabularcolumns:: |p{6.5cm}|p{8.5cm}|
|
||||
|
||||
================================= =========================================
|
||||
``DEBUG`` enable/disable debug mode
|
||||
``DEBUG`` enable/disable debug mode when using
|
||||
``Flask.run()`` method to start server
|
||||
``TESTING`` enable/disable testing mode
|
||||
``PROPAGATE_EXCEPTIONS`` explicitly enable or disable the
|
||||
propagation of exceptions. If not set or
|
||||
|
|
@ -116,13 +132,13 @@ The following configuration values are used internally by Flask:
|
|||
by default enables URL generation
|
||||
without a request context but with an
|
||||
application context.
|
||||
``APPLICATION_ROOT`` If the application does not occupy
|
||||
a whole domain or subdomain this can
|
||||
be set to the path where the application
|
||||
is configured to live. This is for
|
||||
session cookie as path value. If
|
||||
domains are used, this should be
|
||||
``None``.
|
||||
``APPLICATION_ROOT`` The path value used for the session
|
||||
cookie if ``SESSION_COOKIE_PATH`` isn't
|
||||
set. If it's also ``None`` ``'/'`` is used.
|
||||
Note that to actually serve your Flask
|
||||
app under a subpath you need to tell
|
||||
your WSGI container the ``SCRIPT_NAME``
|
||||
WSGI environment variable.
|
||||
``MAX_CONTENT_LENGTH`` If set to a value in bytes, Flask will
|
||||
reject incoming requests with a
|
||||
content length greater than this by
|
||||
|
|
@ -177,12 +193,10 @@ The following configuration values are used internally by Flask:
|
|||
behavior by changing this variable.
|
||||
This is not recommended but might give
|
||||
you a performance improvement on the
|
||||
cost of cachability.
|
||||
``JSONIFY_PRETTYPRINT_REGULAR`` If this is set to ``True`` (the default)
|
||||
jsonify responses will be pretty printed
|
||||
if they are not requested by an
|
||||
XMLHttpRequest object (controlled by
|
||||
the ``X-Requested-With`` header)
|
||||
cost of cacheability.
|
||||
``JSONIFY_PRETTYPRINT_REGULAR`` If this is set to ``True`` or the Flask app
|
||||
is running in debug mode, jsonify responses
|
||||
will be pretty printed.
|
||||
``JSONIFY_MIMETYPE`` MIME type used for jsonify responses.
|
||||
``TEMPLATES_AUTO_RELOAD`` Whether to check for modifications of
|
||||
the template source and reload it
|
||||
|
|
@ -262,7 +276,7 @@ So a common pattern is this::
|
|||
|
||||
This first loads the configuration from the
|
||||
`yourapplication.default_settings` module and then overrides the values
|
||||
with the contents of the file the :envvar:``YOURAPPLICATION_SETTINGS``
|
||||
with the contents of the file the :envvar:`YOURAPPLICATION_SETTINGS`
|
||||
environment variable points to. This environment variable can be set on
|
||||
Linux or OS X with the export command in the shell before starting the
|
||||
server::
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ A basic FastCGI configuration for lighttpd looks like that::
|
|||
)
|
||||
|
||||
alias.url = (
|
||||
"/static/" => "/path/to/your/static"
|
||||
"/static/" => "/path/to/your/static/"
|
||||
)
|
||||
|
||||
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
|
||||
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).
|
||||
|
||||
Configuring nginx
|
||||
|
|
@ -234,7 +234,7 @@ python path. Common problems are:
|
|||
web server.
|
||||
- Different python interpreters being used.
|
||||
|
||||
.. _nginx: http://nginx.org/
|
||||
.. _lighttpd: http://www.lighttpd.net/
|
||||
.. _nginx: https://nginx.org/
|
||||
.. _lighttpd: https://www.lighttpd.net/
|
||||
.. _cherokee: http://cherokee-project.com/
|
||||
.. _flup: https://pypi.python.org/pypi/flup
|
||||
|
|
|
|||
|
|
@ -21,8 +21,10 @@ Hosted options
|
|||
- `Deploying Flask on OpenShift <https://developers.openshift.com/en/python-flask.html>`_
|
||||
- `Deploying Flask on Webfaction <http://flask.pocoo.org/snippets/65/>`_
|
||||
- `Deploying Flask on Google App Engine <https://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/>`_
|
||||
- `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
|
||||
-------------------
|
||||
|
|
|
|||
|
|
@ -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
|
||||
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`
|
||||
---------------------
|
||||
|
|
@ -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`_.
|
||||
|
||||
.. _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
|
||||
|
||||
|
|
@ -130,12 +130,12 @@ to httpd 2.4 syntax
|
|||
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/
|
||||
.. _installation instructions: http://code.google.com/p/modwsgi/wiki/QuickInstallationGuide
|
||||
.. _mod_wsgi: https://github.com/GrahamDumpleton/mod_wsgi
|
||||
.. _installation instructions: https://modwsgi.readthedocs.io/en/develop/installation.html
|
||||
.. _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
|
||||
---------------
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ Given a flask application in myapp.py, use the following command:
|
|||
|
||||
.. sourcecode:: text
|
||||
|
||||
$ uwsgi -s /tmp/uwsgi.sock --manage-script-name --mount /yourapplication=myapp:app
|
||||
$ uwsgi -s /tmp/yourapplication.sock --manage-script-name --mount /yourapplication=myapp:app
|
||||
|
||||
The ``--manage-script-name`` will move the handling of ``SCRIPT_NAME`` to uwsgi,
|
||||
since its smarter about that. It is used together with the ``--mount`` directive
|
||||
|
|
@ -66,7 +66,7 @@ to have it in the URL root its a bit simpler::
|
|||
uwsgi_pass unix:/tmp/yourapplication.sock;
|
||||
}
|
||||
|
||||
.. _nginx: http://nginx.org/
|
||||
.. _lighttpd: http://www.lighttpd.net/
|
||||
.. _nginx: https://nginx.org/
|
||||
.. _lighttpd: https://www.lighttpd.net/
|
||||
.. _cherokee: http://cherokee-project.com/
|
||||
.. _uwsgi: http://projects.unbit.it/uwsgi/
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ Error Logging Tools
|
|||
Sending error mails, even if just for critical ones, can become
|
||||
overwhelming if enough users are hitting the error and log files are
|
||||
typically never looked at. This is why we recommend using `Sentry
|
||||
<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
|
||||
<https://github.com/getsentry/sentry>`__ and is also available as a `hosted version
|
||||
<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
|
||||
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
|
||||
sentry = Sentry(dsn='YOUR_DSN_HERE')
|
||||
|
|
@ -77,7 +77,7 @@ You might want to show custom error pages to the user when an error occurs.
|
|||
This can be done by registering error handlers.
|
||||
|
||||
Error handlers are normal :ref:`views` but instead of being registered for
|
||||
routes they are registered for exceptions that are rised while trying to
|
||||
routes, they are registered for exceptions that are raised while trying to
|
||||
do something else.
|
||||
|
||||
Registering
|
||||
|
|
@ -89,7 +89,7 @@ Register error handlers using :meth:`~flask.Flask.errorhandler` or
|
|||
@app.errorhandler(werkzeug.exceptions.BadRequest)
|
||||
def handle_bad_request(e):
|
||||
return 'bad request!'
|
||||
|
||||
|
||||
app.register_error_handler(400, lambda e: 'bad request!')
|
||||
|
||||
Those two ways are equivalent, but the first one is more clear and leaves
|
||||
|
|
@ -216,7 +216,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
|
||||
do that in the log formatter format string.
|
||||
|
||||
Here some example setups:
|
||||
Here are some example setups:
|
||||
|
||||
Email
|
||||
`````
|
||||
|
|
@ -276,8 +276,9 @@ that this list is not complete, consult the official documentation of the
|
|||
| ``%(lineno)d`` | Source line number where the logging call was |
|
||||
| | issued (if available). |
|
||||
+------------------+----------------------------------------------------+
|
||||
| ``%(asctime)s`` | Human-readable time when the LogRecord` was |
|
||||
| | created. By default this is of the form |
|
||||
| ``%(asctime)s`` | Human-readable time when the |
|
||||
| | :class:`~logging.LogRecord` was created. |
|
||||
| | By default this is of the form |
|
||||
| | ``"2003-07-08 16:49:45,896"`` (the numbers after |
|
||||
| | the comma are millisecond portion of the time). |
|
||||
| | This can be changed by subclassing the formatter |
|
||||
|
|
|
|||
|
|
@ -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
|
||||
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
|
||||
that it works with multiple Flask application instances at once. This is
|
||||
a requirement because many people will use patterns like the
|
||||
|
|
@ -393,8 +387,6 @@ extension to be approved you have to follow these guidelines:
|
|||
Python 2.7
|
||||
|
||||
|
||||
.. _ext-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``.
|
||||
|
||||
|
||||
.. _OAuth extension: http://pythonhosted.org/Flask-OAuth/
|
||||
.. _OAuth extension: https://pythonhosted.org/Flask-OAuth/
|
||||
.. _mailinglist: http://flask.pocoo.org/mailinglist/
|
||||
.. _IRC channel: http://flask.pocoo.org/community/irc/
|
||||
|
|
|
|||
|
|
@ -3,174 +3,173 @@
|
|||
Installation
|
||||
============
|
||||
|
||||
Flask depends on some external libraries, like `Werkzeug
|
||||
<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.
|
||||
Python Version
|
||||
--------------
|
||||
|
||||
So how do you get all that on your computer quickly? There are many ways you
|
||||
could do that, but the most kick-ass method is virtualenv, so let's have a look
|
||||
at that first.
|
||||
We recommend using the latest version of Python 3. Flask supports Python 3.3
|
||||
and newer, Python 2.6 and newer, and PyPy.
|
||||
|
||||
You will need Python 2.6 or newer to get started, so be sure to have an
|
||||
up-to-date Python 2.x installation. For using Flask with Python 3 have a
|
||||
look at :ref:`python3-support`.
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
.. _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
|
||||
shell access to your production machines, you'll probably want to use it there,
|
||||
too.
|
||||
.. _Werkzeug: http://werkzeug.pocoo.org/
|
||||
.. _Jinja: http://jinja.pocoo.org/
|
||||
.. _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,
|
||||
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?
|
||||
Optional dependencies
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Virtualenv to the rescue! Virtualenv enables multiple side-by-side
|
||||
installations of Python, one for each project. It doesn't actually install
|
||||
separate copies of Python, but it does provide a clever way to keep different
|
||||
project environments isolated. Let's see how virtualenv works.
|
||||
These distributions will not be installed automatically. Flask will detect and
|
||||
use them if you install them.
|
||||
|
||||
If you are on Mac OS X or Linux, chances are that one of the following two
|
||||
commands will work for you::
|
||||
* `Blinker`_ provides support for :ref:`signals`.
|
||||
* `SimpleJSON`_ is a fast JSON implementation that is compatible with
|
||||
Python's ``json`` module. It is preferred for JSON operations if it is
|
||||
installed.
|
||||
|
||||
$ sudo easy_install virtualenv
|
||||
.. _Blinker: https://pythonhosted.org/blinker/
|
||||
.. _SimpleJSON: https://simplejson.readthedocs.io/
|
||||
|
||||
or even better::
|
||||
Virtual environments
|
||||
--------------------
|
||||
|
||||
$ sudo pip install virtualenv
|
||||
Use a virtual environment to manage the dependencies for your project, both in
|
||||
development and in production.
|
||||
|
||||
One of these will probably install virtualenv on your system. Maybe it's even
|
||||
in your package manager. If you use Ubuntu, try::
|
||||
What problem does a virtual environment solve? The more Python projects you
|
||||
have, the more likely it is that you need to work with different versions of
|
||||
Python libraries, or even Python itself. Newer versions of libraries for one
|
||||
project can break compatibility in another project.
|
||||
|
||||
$ sudo apt-get install python-virtualenv
|
||||
Virtual environments are independent groups of Python libraries, one for each
|
||||
project. Packages installed for one project will not affect other projects or
|
||||
the operating system's packages.
|
||||
|
||||
If you are on Windows and don't have the :command:`easy_install` command, you must
|
||||
install it first. Check the :ref:`windows-easy-install` section for more
|
||||
information about how to do that. Once you have it installed, run the same
|
||||
commands as above, but without the :command:`sudo` prefix.
|
||||
Python 3 comes bundled with the :mod:`venv` module to create virtual
|
||||
environments. If you're using a modern version of Python, you can continue on
|
||||
to the next section.
|
||||
|
||||
Once you have virtualenv installed, just fire up a shell and create
|
||||
your own environment. I usually create a project folder and a :file:`venv`
|
||||
folder within::
|
||||
If you're using Python 2, see :ref:`install-install-virtualenv` first.
|
||||
|
||||
$ mkdir myproject
|
||||
$ cd myproject
|
||||
$ virtualenv venv
|
||||
New python executable in venv/bin/python
|
||||
Installing setuptools, pip............done.
|
||||
.. _install-create-env:
|
||||
|
||||
Now, whenever you want to work on a project, you only have to activate the
|
||||
corresponding environment. On OS X and Linux, do the following::
|
||||
Create an environment
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
$ . venv/bin/activate
|
||||
Create a project folder and a :file:`venv` folder within:
|
||||
|
||||
If you are a Windows user, the following command is for you::
|
||||
.. code-block:: sh
|
||||
|
||||
$ venv\scripts\activate
|
||||
mkdir myproject
|
||||
cd myproject
|
||||
python3 -m venv venv
|
||||
|
||||
Either way, you should now be using your virtualenv (notice how the prompt of
|
||||
your shell has changed to show the active environment).
|
||||
On Windows:
|
||||
|
||||
And if you want to go back to the real world, use the following command::
|
||||
.. code-block:: bat
|
||||
|
||||
$ deactivate
|
||||
py -3 -m venv venv
|
||||
|
||||
After doing this, the prompt of your shell should be as familiar as before.
|
||||
If you needed to install virtualenv because you are on an older version of
|
||||
Python, use the following command instead:
|
||||
|
||||
Now, let's move on. Enter the following command to get Flask activated in your
|
||||
virtualenv::
|
||||
.. code-block:: sh
|
||||
|
||||
$ pip install Flask
|
||||
virtualenv venv
|
||||
|
||||
A few seconds later and you are good to go.
|
||||
On Windows:
|
||||
|
||||
.. code-block:: bat
|
||||
|
||||
System-Wide Installation
|
||||
------------------------
|
||||
\Python27\Scripts\virtualenv.exe venv
|
||||
|
||||
This is possible as well, though I do not recommend it. Just run
|
||||
:command:`pip` with root privileges::
|
||||
Activate the environment
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
$ sudo pip install Flask
|
||||
Before you work on your project, activate the corresponding environment:
|
||||
|
||||
(On Windows systems, run it in a command-prompt window with administrator
|
||||
privileges, and leave out :command:`sudo`.)
|
||||
.. code-block:: sh
|
||||
|
||||
. venv/bin/activate
|
||||
|
||||
Living on the Edge
|
||||
On Windows:
|
||||
|
||||
.. code-block:: bat
|
||||
|
||||
venv\Scripts\activate
|
||||
|
||||
Your shell prompt will change to show the name of the activated environment.
|
||||
|
||||
Install Flask
|
||||
-------------
|
||||
|
||||
Within the activated environment, use the following command to install Flask:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
pip install Flask
|
||||
|
||||
Living on the edge
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If you want to work with the latest Flask code before it's released, install or
|
||||
update the code from the master branch:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
pip install -U https://github.com/pallets/flask/archive/master.tar.gz
|
||||
|
||||
.. _install-install-virtualenv:
|
||||
|
||||
Install virtualenv
|
||||
------------------
|
||||
|
||||
If you want to work with the latest version of Flask, there are two ways: you
|
||||
can either let :command:`pip` pull in the development version, or you can tell
|
||||
it to operate on a git checkout. Either way, virtualenv is recommended.
|
||||
If you are using Python 2, the venv module is not available. Instead,
|
||||
install `virtualenv`_.
|
||||
|
||||
Get the git checkout in a new virtualenv and run in development mode::
|
||||
On Linux, virtualenv is provided by your package manager:
|
||||
|
||||
$ git clone http://github.com/pallets/flask.git
|
||||
Initialized empty Git repository in ~/dev/flask/.git/
|
||||
$ cd flask
|
||||
$ virtualenv venv
|
||||
New python executable in venv/bin/python
|
||||
Installing setuptools, pip............done.
|
||||
$ . venv/bin/activate
|
||||
$ python setup.py develop
|
||||
...
|
||||
Finished processing dependencies for Flask
|
||||
.. code-block:: sh
|
||||
|
||||
This will pull in the dependencies and activate the git head as the current
|
||||
version inside the virtualenv. Then all you have to do is run ``git pull
|
||||
origin`` to update to the latest version.
|
||||
# Debian, Ubuntu
|
||||
sudo apt-get install python-virtualenv
|
||||
|
||||
# CentOS, Fedora
|
||||
sudo yum install python-virtualenv
|
||||
|
||||
.. _windows-easy-install:
|
||||
# Arch
|
||||
sudo pacman -S python-virtualenv
|
||||
|
||||
`pip` and `setuptools` on Windows
|
||||
---------------------------------
|
||||
If you are on Mac OS X or Windows, download `get-pip.py`_, then:
|
||||
|
||||
Sometimes getting the standard "Python packaging tools" like *pip*, *setuptools*
|
||||
and *virtualenv* can be a little trickier, but nothing very hard. The two crucial
|
||||
packages you will need are setuptools and pip - these will let you install
|
||||
anything else (like virtualenv). Fortunately there are two "bootstrap scripts"
|
||||
you can run to install either.
|
||||
.. code-block:: sh
|
||||
|
||||
If you don't currently have either, then `get-pip.py` will install both for you
|
||||
(you won't need to run ez_setup.py).
|
||||
sudo python2 Downloads/get-pip.py
|
||||
sudo python2 -m pip install virtualenv
|
||||
|
||||
`get-pip.py`_
|
||||
On Windows, as an administrator:
|
||||
|
||||
To install the latest setuptools, you can use its bootstrap file:
|
||||
.. code-block:: bat
|
||||
|
||||
`ez_setup.py`_
|
||||
\Python27\python.exe Downloads\get-pip.py
|
||||
\Python27\python.exe -m pip install virtualenv
|
||||
|
||||
Either should be double-clickable once you download them. If you already have pip,
|
||||
you can upgrade them by running::
|
||||
|
||||
> pip install --upgrade pip setuptools
|
||||
|
||||
Most often, once you pull up a command prompt you want to be able to type :command:`pip`
|
||||
and :command:`python` which will run those things, but this might not automatically happen
|
||||
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
|
||||
(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 :command:`python` to bring up the interpreter.
|
||||
|
||||
Finally, to install `virtualenv`_, you can simply run::
|
||||
|
||||
> 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
|
||||
.. _ez_setup.py: https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ Application Factories
|
|||
If you are already using packages and blueprints for your application
|
||||
(:ref:`blueprints`) there are a couple of really nice ways to further improve
|
||||
the experience. A common pattern is creating the application object when
|
||||
the blueprint is imported. But if you move the creation of this object,
|
||||
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.
|
||||
|
||||
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
|
||||
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::
|
||||
|
||||
def create_app(config_filename):
|
||||
|
|
|
|||
|
|
@ -1,24 +1,27 @@
|
|||
Celery Based Background Tasks
|
||||
=============================
|
||||
Celery Background Tasks
|
||||
=======================
|
||||
|
||||
Celery is a task queue for Python with batteries included. It used to
|
||||
have a Flask integration but it became unnecessary after some
|
||||
restructuring of the internals of Celery with Version 3. This guide fills
|
||||
in the blanks in how to properly use Celery with Flask but assumes that
|
||||
you generally already read the `First Steps with Celery
|
||||
<http://docs.celeryproject.org/en/latest/getting-started/first-steps-with-celery.html>`_
|
||||
guide in the official Celery documentation.
|
||||
If your application has a long running task, such as processing some uploaded
|
||||
data or sending email, you don't want to wait for it to finish during a
|
||||
request. Instead, use a task queue to send the necessary data to another
|
||||
process that will run the task in the background while the request returns
|
||||
immediately.
|
||||
|
||||
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
|
||||
standard Python tools like :command:`pip` or :command:`easy_install`::
|
||||
Install
|
||||
-------
|
||||
|
||||
Celery is a separate Python package. Install it from PyPI using pip::
|
||||
|
||||
$ pip install celery
|
||||
|
||||
Configuring Celery
|
||||
------------------
|
||||
Configure
|
||||
---------
|
||||
|
||||
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`
|
||||
|
|
@ -36,15 +39,18 @@ This is all that is necessary to properly integrate Celery with Flask::
|
|||
from celery import Celery
|
||||
|
||||
def make_celery(app):
|
||||
celery = Celery(app.import_name, backend=app.config['CELERY_BACKEND'],
|
||||
broker=app.config['CELERY_BROKER_URL'])
|
||||
celery = Celery(
|
||||
app.import_name,
|
||||
backend=app.config['CELERY_RESULT_BACKEND'],
|
||||
broker=app.config['CELERY_BROKER_URL']
|
||||
)
|
||||
celery.conf.update(app.config)
|
||||
TaskBase = celery.Task
|
||||
class ContextTask(TaskBase):
|
||||
abstract = True
|
||||
|
||||
class ContextTask(celery.Task):
|
||||
def __call__(self, *args, **kwargs):
|
||||
with app.app_context():
|
||||
return TaskBase.__call__(self, *args, **kwargs)
|
||||
return self.run(*args, **kwargs)
|
||||
|
||||
celery.Task = ContextTask
|
||||
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
|
||||
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
|
||||
Flask::
|
||||
Let's write a task that adds two numbers together and returns the result. We
|
||||
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
|
||||
|
||||
|
|
@ -68,26 +75,27 @@ Flask::
|
|||
)
|
||||
celery = make_celery(flask_app)
|
||||
|
||||
|
||||
@celery.task()
|
||||
def add_together(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.wait()
|
||||
65
|
||||
result = add_together.delay(23, 42)
|
||||
result.wait() # 65
|
||||
|
||||
Running the Celery Worker
|
||||
-------------------------
|
||||
Run a worker
|
||||
------------
|
||||
|
||||
Now if you jumped in and already executed the above code you will be
|
||||
disappointed to learn that your ``.wait()`` will never actually return.
|
||||
That's because you also need to run celery. You can do that by running
|
||||
celery as a worker::
|
||||
If you jumped in and already executed the above code you will be
|
||||
disappointed to learn that ``.wait()`` will never actually return.
|
||||
That's because you also need to run a Celery worker to receive and execute the
|
||||
task. ::
|
||||
|
||||
$ celery -A your_application.celery worker
|
||||
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -3,71 +3,43 @@
|
|||
Deferred Request Callbacks
|
||||
==========================
|
||||
|
||||
One of the design principles of Flask is that response objects are created
|
||||
and passed down a chain of potential callbacks that can modify them or
|
||||
replace them. When the request handling starts, there is no response
|
||||
object yet. It is created as necessary either by a view function or by
|
||||
some other component in the system.
|
||||
One of the design principles of Flask is that response objects are created and
|
||||
passed down a chain of potential callbacks that can modify them or replace
|
||||
them. When the request handling starts, there is no response object yet. It is
|
||||
created as necessary either by a view function or by some other component in
|
||||
the system.
|
||||
|
||||
But what happens if you want to modify the response at a point where the
|
||||
response does not exist yet? A common example for that would be a
|
||||
before-request function that wants to set a cookie on the response object.
|
||||
What happens if you want to modify the response at a point where the response
|
||||
does not exist yet? A common example for that would be a
|
||||
:meth:`~flask.Flask.before_request` callback that wants to set a cookie on the
|
||||
response object.
|
||||
|
||||
One way is to avoid the situation. Very often that is possible. For
|
||||
instance you can try to move that logic into an after-request callback
|
||||
instead. Sometimes however moving that code there is just not a very
|
||||
pleasant experience or makes code look very awkward.
|
||||
One way is to avoid the situation. Very often that is possible. For instance
|
||||
you can try to move that logic into a :meth:`~flask.Flask.after_request`
|
||||
callback instead. However, sometimes moving code there makes it more
|
||||
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.
|
||||
This way you can defer code execution from anywhere in the application.
|
||||
|
||||
|
||||
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
|
||||
-------------------
|
||||
As an alternative, you can use :func:`~flask.after_this_request` to register
|
||||
callbacks that will execute after only the current request. This way you can
|
||||
defer code execution from anywhere in the application, based on the current
|
||||
request.
|
||||
|
||||
At any time during a request, we can register a function to be called at the
|
||||
end of the request. For example you can remember the current language of the
|
||||
user in a cookie in the before-request function::
|
||||
end of the request. For example you can remember the current language of the
|
||||
user in a cookie in a :meth:`~flask.Flask.before_request` callback::
|
||||
|
||||
from flask import request
|
||||
from flask import request, after_this_request
|
||||
|
||||
@app.before_request
|
||||
def detect_user_language():
|
||||
language = request.cookies.get('user_lang')
|
||||
|
||||
if language is None:
|
||||
language = guess_language_from_request()
|
||||
|
||||
# when the response exists, set a cookie with the language
|
||||
@after_this_request
|
||||
def remember_language(response):
|
||||
response.set_cookie('user_lang', language)
|
||||
|
||||
g.language = language
|
||||
|
|
|
|||
|
|
@ -39,10 +39,8 @@ the process, also read the :ref:`fabric-deployment` chapter.
|
|||
Basic Setup Script
|
||||
------------------
|
||||
|
||||
Because you have Flask running, you have setuptools available on your system anyways.
|
||||
Flask already depends upon setuptools. If you do not, fear not, there is a
|
||||
script to install it for you: `ez_setup.py`_. Just download and
|
||||
run with your Python interpreter.
|
||||
Because you have Flask installed, you have setuptools available on your system.
|
||||
Flask already depends upon setuptools.
|
||||
|
||||
Standard disclaimer applies: :ref:`you better use a virtualenv
|
||||
<virtualenv>`.
|
||||
|
|
@ -67,7 +65,7 @@ A basic :file:`setup.py` file for a Flask application looks like this::
|
|||
|
||||
Please keep in mind that you have to list subpackages explicitly. If you
|
||||
want setuptools to lookup the packages for you automatically, you can use
|
||||
the `find_packages` function::
|
||||
the ``find_packages`` function::
|
||||
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
|
|
@ -76,12 +74,12 @@ the `find_packages` function::
|
|||
packages=find_packages()
|
||||
)
|
||||
|
||||
Most parameters to the `setup` function should be self explanatory,
|
||||
`include_package_data` and `zip_safe` might not be.
|
||||
`include_package_data` tells setuptools to look for a :file:`MANIFEST.in` file
|
||||
Most parameters to the ``setup`` function should be self explanatory,
|
||||
``include_package_data`` and ``zip_safe`` might not be.
|
||||
``include_package_data`` tells setuptools to look for a :file:`MANIFEST.in` file
|
||||
and install all the entries that match as package data. We will use this
|
||||
to distribute the static files and templates along with the Python module
|
||||
(see :ref:`distributing-resources`). The `zip_safe` flag can be used to
|
||||
(see :ref:`distributing-resources`). The ``zip_safe`` flag can be used to
|
||||
force or prevent zip Archive creation. In general you probably don't want
|
||||
your packages to be installed as zip files because some tools do not
|
||||
support them and they make debugging a lot harder.
|
||||
|
|
@ -123,13 +121,13 @@ your tarball::
|
|||
|
||||
Don't forget that even if you enlist them in your :file:`MANIFEST.in` file, they
|
||||
won't be installed for you unless you set the `include_package_data`
|
||||
parameter of the `setup` function to ``True``!
|
||||
parameter of the ``setup`` function to ``True``!
|
||||
|
||||
|
||||
Declaring Dependencies
|
||||
----------------------
|
||||
|
||||
Dependencies are declared in the `install_requires` parameter as a list.
|
||||
Dependencies are declared in the ``install_requires`` parameter as a list.
|
||||
Each item in that list is the name of a package that should be pulled from
|
||||
PyPI on installation. By default it will always use the most recent
|
||||
version, but you can also provide minimum and maximum version
|
||||
|
|
@ -159,21 +157,21 @@ Installing / Developing
|
|||
-----------------------
|
||||
|
||||
To install your application (ideally into a virtualenv) just run the
|
||||
:file:`setup.py` script with the `install` parameter. It will install your
|
||||
:file:`setup.py` script with the ``install`` parameter. It will install your
|
||||
application into the virtualenv's site-packages folder and also download
|
||||
and install all dependencies::
|
||||
|
||||
$ python setup.py install
|
||||
|
||||
If you are developing on the package and also want the requirements to be
|
||||
installed, you can use the `develop` command instead::
|
||||
installed, you can use the ``develop`` command instead::
|
||||
|
||||
$ python setup.py develop
|
||||
|
||||
This has the advantage of just installing a link to the site-packages
|
||||
folder instead of copying the data over. You can then continue to work on
|
||||
the code without having to run `install` again after each change.
|
||||
the code without having to run ``install`` again after each change.
|
||||
|
||||
|
||||
.. _pip: https://pypi.python.org/pypi/pip
|
||||
.. _Setuptools: https://pythonhosted.org/setuptools
|
||||
.. _Setuptools: https://pypi.python.org/pypi/setuptools
|
||||
|
|
|
|||
|
|
@ -47,37 +47,53 @@ even if the application behaves correctly:
|
|||
Error Handlers
|
||||
--------------
|
||||
|
||||
An error handler is a function, just like a view function, but it is
|
||||
called when an error happens and is passed that error. The error is most
|
||||
likely a :exc:`~werkzeug.exceptions.HTTPException`, but in one case it
|
||||
can be a different error: a handler for internal server errors will be
|
||||
passed other exception instances as well if they are uncaught.
|
||||
An error handler is a function that returns a response when a type of error is
|
||||
raised, similar to how a view is a function that returns a response when a
|
||||
request URL is matched. It is passed the instance of the error being handled,
|
||||
which is most likely a :exc:`~werkzeug.exceptions.HTTPException`. An error
|
||||
handler for "500 Internal Server Error" will be passed uncaught exceptions in
|
||||
addition to explicit 500 errors.
|
||||
|
||||
An error handler is registered with the :meth:`~flask.Flask.errorhandler`
|
||||
decorator and the error code of the exception. Keep in mind that Flask
|
||||
will *not* set the error code for you, so make sure to also provide the
|
||||
HTTP status code when returning a response.
|
||||
decorator or the :meth:`~flask.Flask.register_error_handler` method. A handler
|
||||
can be registered for a status code, like 404, or for an exception class.
|
||||
|
||||
Please note that if you add an error handler for "500 Internal Server
|
||||
Error", Flask will not trigger it if it's running in Debug mode.
|
||||
The status code of the response will not be set to the handler's code. Make
|
||||
sure to provide the appropriate HTTP status code when returning a response from
|
||||
a handler.
|
||||
|
||||
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
|
||||
|
||||
@app.errorhandler(404)
|
||||
def page_not_found(e):
|
||||
# note that we set the 404 status explicitly
|
||||
return render_template('404.html'), 404
|
||||
|
||||
When using the :ref:`application factory pattern <app-factories>`::
|
||||
|
||||
from flask import Flask, render_template
|
||||
|
||||
def page_not_found(e):
|
||||
return render_template('404.html'), 404
|
||||
|
||||
def create_app(config_filename):
|
||||
app = Flask(__name__)
|
||||
app.register_error_handler(404, page_not_found)
|
||||
return app
|
||||
|
||||
An example template might be this:
|
||||
|
||||
.. sourcecode:: html+jinja
|
||||
|
||||
{% extends "layout.html" %}
|
||||
{% block title %}Page Not Found{% endblock %}
|
||||
{% block body %}
|
||||
<h1>Page Not Found</h1>
|
||||
<p>What you were looking for is just not there.
|
||||
<p><a href="{{ url_for('index') }}">go somewhere nice</a>
|
||||
{% endblock %}
|
||||
|
||||
{% extends "layout.html" %}
|
||||
{% block title %}Page Not Found{% endblock %}
|
||||
{% block body %}
|
||||
<h1>Page Not Found</h1>
|
||||
<p>What you were looking for is just not there.
|
||||
<p><a href="{{ url_for('index') }}">go somewhere nice</a>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -49,5 +49,5 @@ web server's documentation.
|
|||
See also
|
||||
--------
|
||||
|
||||
* The `Favicon <http://en.wikipedia.org/wiki/Favicon>`_ article on
|
||||
* The `Favicon <https://en.wikipedia.org/wiki/Favicon>`_ article on
|
||||
Wikipedia
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ specific upload folder and displays a file to the user. Let's look at the
|
|||
bootstrapping code for our application::
|
||||
|
||||
import os
|
||||
from flask import Flask, request, redirect, url_for
|
||||
from flask import Flask, flash, request, redirect, url_for
|
||||
from werkzeug.utils import secure_filename
|
||||
|
||||
UPLOAD_FOLDER = '/path/to/the/uploads'
|
||||
|
|
@ -47,7 +47,7 @@ the file and redirects the user to the URL for the uploaded file::
|
|||
|
||||
def allowed_file(filename):
|
||||
return '.' in filename and \
|
||||
filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS
|
||||
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
|
||||
|
||||
@app.route('/', methods=['GET', 'POST'])
|
||||
def upload_file():
|
||||
|
|
@ -58,7 +58,7 @@ the file and redirects the user to the URL for the uploaded file::
|
|||
return redirect(request.url)
|
||||
file = request.files['file']
|
||||
# if user does not select file, browser also
|
||||
# submit a empty part without filename
|
||||
# submit an empty part without filename
|
||||
if file.filename == '':
|
||||
flash('No selected file')
|
||||
return redirect(request.url)
|
||||
|
|
@ -71,9 +71,9 @@ the file and redirects the user to the URL for the uploaded file::
|
|||
<!doctype html>
|
||||
<title>Upload new File</title>
|
||||
<h1>Upload new File</h1>
|
||||
<form action="" method=post enctype=multipart/form-data>
|
||||
<p><input type=file name=file>
|
||||
<input type=submit value=Upload>
|
||||
<form method=post enctype=multipart/form-data>
|
||||
<input type=file name=file>
|
||||
<input type=submit value=Upload>
|
||||
</form>
|
||||
'''
|
||||
|
||||
|
|
@ -104,9 +104,9 @@ before storing it directly on the filesystem.
|
|||
>>> secure_filename('../../../../home/username/.bashrc')
|
||||
'home_username_.bashrc'
|
||||
|
||||
Now one last thing is missing: the serving of the uploaded files. In the
|
||||
:func:`upload_file()` we redirect the user to
|
||||
``url_for('uploaded_file', filename=filename)``, that is, ``/uploads/filename``.
|
||||
Now one last thing is missing: the serving of the uploaded files. In the
|
||||
:func:`upload_file()` we redirect the user to
|
||||
``url_for('uploaded_file', filename=filename)``, that is, ``/uploads/filename``.
|
||||
So we write the :func:`uploaded_file` function to return the file of that name. As
|
||||
of Flask 0.5 we can use a function that does that for us::
|
||||
|
||||
|
|
@ -181,4 +181,4 @@ applications dealing with uploads, there is also a Flask extension called
|
|||
blacklisting of extensions and more.
|
||||
|
||||
.. _jQuery: https://jquery.com/
|
||||
.. _Flask-Uploads: http://pythonhosted.org/Flask-Uploads/
|
||||
.. _Flask-Uploads: https://pythonhosted.org/Flask-Uploads/
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ And here is the :file:`login.html` template which also inherits from
|
|||
{% if error %}
|
||||
<p class=error><strong>Error:</strong> {{ error }}
|
||||
{% endif %}
|
||||
<form action="" method=post>
|
||||
<form method=post>
|
||||
<dl>
|
||||
<dt>Username:
|
||||
<dd><input type=text name=username value="{{
|
||||
|
|
|
|||
|
|
@ -90,14 +90,19 @@ Then you can define your central place to combine the views like this::
|
|||
You can further optimize this in terms of amount of keystrokes needed to
|
||||
write this by having a function that calls into
|
||||
:meth:`~flask.Flask.add_url_rule` by prefixing a string with the project
|
||||
name and a dot, and by wrapping `view_func` in a `LazyView` as needed::
|
||||
name and a dot, and by wrapping `view_func` in a `LazyView` as needed. ::
|
||||
|
||||
def url(url_rule, import_name, **options):
|
||||
def url(import_name, url_rules=[], **options):
|
||||
view = LazyView('yourapplication.' + import_name)
|
||||
app.add_url_rule(url_rule, view_func=view, **options)
|
||||
for url_rule in url_rules:
|
||||
app.add_url_rule(url_rule, view_func=view, **options)
|
||||
|
||||
url('/', 'views.index')
|
||||
url('/user/<username>', 'views.user')
|
||||
# add a single route to the index view
|
||||
url('views.index', ['/'])
|
||||
|
||||
# add two routes to a single function endpoint
|
||||
url_rules = ['/user/','/user/<username>']
|
||||
url('views.user', url_rules)
|
||||
|
||||
One thing to keep in mind is that before and after request handlers have
|
||||
to be in a file that is imported upfront to work properly on the first
|
||||
|
|
|
|||
|
|
@ -8,15 +8,19 @@ module. That is quite simple. Imagine a small application looks like
|
|||
this::
|
||||
|
||||
/yourapplication
|
||||
/yourapplication.py
|
||||
yourapplication.py
|
||||
/static
|
||||
/style.css
|
||||
style.css
|
||||
/templates
|
||||
layout.html
|
||||
index.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
|
||||
---------------
|
||||
|
||||
|
|
@ -29,9 +33,9 @@ You should then end up with something like that::
|
|||
|
||||
/yourapplication
|
||||
/yourapplication
|
||||
/__init__.py
|
||||
__init__.py
|
||||
/static
|
||||
/style.css
|
||||
style.css
|
||||
/templates
|
||||
layout.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
|
||||
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
|
||||
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::
|
||||
|
||||
from yourapplication import app
|
||||
app.run(debug=True)
|
||||
from setuptools import setup
|
||||
|
||||
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
|
||||
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::
|
||||
|
||||
/yourapplication
|
||||
/runserver.py
|
||||
setup.py
|
||||
/yourapplication
|
||||
/__init__.py
|
||||
/views.py
|
||||
__init__.py
|
||||
views.py
|
||||
/static
|
||||
/style.css
|
||||
style.css
|
||||
/templates
|
||||
layout.html
|
||||
index.html
|
||||
|
|
@ -105,6 +134,7 @@ You should then end up with something like that::
|
|||
|
||||
|
||||
.. _working-with-modules:
|
||||
.. _the full src for this example here: https://github.com/pallets/flask/tree/master/examples/patterns/largerapp
|
||||
|
||||
Working with Blueprints
|
||||
-----------------------
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ if you want to get started quickly.
|
|||
You can download `Flask-SQLAlchemy`_ from `PyPI
|
||||
<https://pypi.python.org/pypi/Flask-SQLAlchemy>`_.
|
||||
|
||||
.. _Flask-SQLAlchemy: http://pythonhosted.org/Flask-SQLAlchemy/
|
||||
.. _Flask-SQLAlchemy: http://flask-sqlalchemy.pocoo.org/
|
||||
|
||||
|
||||
Declarative
|
||||
|
|
@ -108,9 +108,9 @@ Querying is simple as well:
|
|||
>>> User.query.filter(User.name == 'admin').first()
|
||||
<User u'admin'>
|
||||
|
||||
.. _SQLAlchemy: http://www.sqlalchemy.org/
|
||||
.. _SQLAlchemy: https://www.sqlalchemy.org/
|
||||
.. _declarative:
|
||||
http://docs.sqlalchemy.org/en/latest/orm/extensions/declarative/
|
||||
https://docs.sqlalchemy.org/en/latest/orm/extensions/declarative/
|
||||
|
||||
Manual Object Relational Mapping
|
||||
--------------------------------
|
||||
|
|
@ -135,7 +135,7 @@ Here is an example :file:`database.py` module for your application::
|
|||
def init_db():
|
||||
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
|
||||
application module::
|
||||
|
||||
|
|
@ -215,4 +215,4 @@ You can also pass strings of SQL statements to the
|
|||
(1, u'admin', u'admin@localhost')
|
||||
|
||||
For more information about SQLAlchemy, head over to the
|
||||
`website <http://www.sqlalchemy.org/>`_.
|
||||
`website <https://www.sqlalchemy.org/>`_.
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
Using SQLite 3 with Flask
|
||||
=========================
|
||||
|
||||
In Flask you can easily implement the opening of database connections on
|
||||
demand and closing them when the context dies (usually at the end of the
|
||||
In Flask you can easily implement the opening of database connections on
|
||||
demand and closing them when the context dies (usually at the end of the
|
||||
request).
|
||||
|
||||
Here is a simple example of how you can use SQLite 3 with Flask::
|
||||
|
|
@ -71,7 +71,8 @@ Now in each request handling function you can access `g.db` to get the
|
|||
current open database connection. To simplify working with SQLite, a
|
||||
row factory function is useful. It is executed for every result returned
|
||||
from the database to convert the result. For instance, in order to get
|
||||
dictionaries instead of tuples, this could be inserted into ``get_db``::
|
||||
dictionaries instead of tuples, this could be inserted into the ``get_db``
|
||||
function we created above::
|
||||
|
||||
def make_dicts(cursor, row):
|
||||
return dict((cursor.description[idx][0], value)
|
||||
|
|
@ -79,21 +80,37 @@ dictionaries instead of tuples, this could be inserted into ``get_db``::
|
|||
|
||||
db.row_factory = make_dicts
|
||||
|
||||
Or even simpler::
|
||||
This will make the sqlite3 module return dicts for this database connection, which are much nicer to deal with. Even more simply, we could place this in ``get_db`` instead::
|
||||
|
||||
db.row_factory = sqlite3.Row
|
||||
|
||||
This would use Row objects rather than dicts to return the results of queries. These are ``namedtuple`` s, so we can access them either by index or by key. For example, assuming we have a ``sqlite3.Row`` called ``r`` for the rows ``id``, ``FirstName``, ``LastName``, and ``MiddleInitial``::
|
||||
|
||||
>>> # You can get values based on the row's name
|
||||
>>> r['FirstName']
|
||||
John
|
||||
>>> # Or, you can get them based on index
|
||||
>>> r[1]
|
||||
John
|
||||
# Row objects are also iterable:
|
||||
>>> for value in r:
|
||||
... print(value)
|
||||
1
|
||||
John
|
||||
Doe
|
||||
M
|
||||
|
||||
Additionally, it is a good idea to provide a query function that combines
|
||||
getting the cursor, executing and fetching the results::
|
||||
|
||||
|
||||
def query_db(query, args=(), one=False):
|
||||
cur = get_db().execute(query, args)
|
||||
rv = cur.fetchall()
|
||||
cur.close()
|
||||
return (rv[0] if rv else None) if one else rv
|
||||
|
||||
This handy little function, in combination with a row factory, makes
|
||||
working with the database much more pleasant than it is by just using the
|
||||
This handy little function, in combination with a row factory, makes
|
||||
working with the database much more pleasant than it is by just using the
|
||||
raw cursor and connection objects.
|
||||
|
||||
Here is how you can use it::
|
||||
|
|
@ -114,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
|
||||
the SQL statement with string formatting because this makes it possible
|
||||
to attack the application using `SQL Injections
|
||||
<http://en.wikipedia.org/wiki/SQL_injection>`_.
|
||||
<https://en.wikipedia.org/wiki/SQL_injection>`_.
|
||||
|
||||
Initial Schemas
|
||||
---------------
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ forms.
|
|||
fun. You can get it from `PyPI
|
||||
<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
|
||||
---------
|
||||
|
|
@ -108,7 +108,7 @@ takes advantage of the :file:`_formhelpers.html` template:
|
|||
.. sourcecode:: html+jinja
|
||||
|
||||
{% from "_formhelpers.html" import render_field %}
|
||||
<form method=post action="/register">
|
||||
<form method=post>
|
||||
<dl>
|
||||
{{ render_field(form.username) }}
|
||||
{{ render_field(form.email) }}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ So what did that code do?
|
|||
class will be our WSGI application.
|
||||
2. Next we create an instance of this class. The first argument is the name of
|
||||
the application's module or package. If you are using a single module (as
|
||||
in this example), you should use `__name__` because depending on if it's
|
||||
in this example), you should use ``__name__`` because depending on if it's
|
||||
started as application or imported as module the name will be different
|
||||
(``'__main__'`` versus the actual import name). This is needed so that
|
||||
Flask knows where to look for templates, static files, and so on. For more
|
||||
|
|
@ -42,17 +42,17 @@ your application :file:`flask.py` because this would conflict with Flask
|
|||
itself.
|
||||
|
||||
To run the application you can either use the :command:`flask` command or
|
||||
python's :option:`-m` switch with Flask. Before you can do that you need
|
||||
python's ``-m`` switch with Flask. Before you can do that you need
|
||||
to tell your terminal the application to work with by exporting the
|
||||
`FLASK_APP` environment variable::
|
||||
``FLASK_APP`` environment variable::
|
||||
|
||||
$ export FLASK_APP=hello.py
|
||||
$ flask run
|
||||
* 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 you need to use ``set`` instead of ``export``.
|
||||
|
||||
Alternatively you can use `python -m flask`::
|
||||
Alternatively you can use :command:`python -m flask`::
|
||||
|
||||
$ export FLASK_APP=hello.py
|
||||
$ python -m flask run
|
||||
|
|
@ -86,7 +86,7 @@ should see your hello world greeting.
|
|||
What to do if the Server does not Start
|
||||
---------------------------------------
|
||||
|
||||
In case the ``python -m flask`` fails or :command:`flask` does not exist,
|
||||
In case the :command:`python -m flask` fails or :command:`flask` does not exist,
|
||||
there are multiple reasons this might be the case. First of all you need
|
||||
to look at the error message.
|
||||
|
||||
|
|
@ -95,17 +95,17 @@ Old Version of Flask
|
|||
|
||||
Versions of Flask older than 0.11 use to have different ways to start the
|
||||
application. In short, the :command:`flask` command did not exist, and
|
||||
neither did ``python -m flask``. In that case you have two options:
|
||||
neither did :command:`python -m flask`. In that case you have two options:
|
||||
either upgrade to newer Flask versions or have a look at the :ref:`server`
|
||||
docs to see the alternative method for running a server.
|
||||
|
||||
Invalid Import Name
|
||||
```````````````````
|
||||
|
||||
The :option:`-a` argument to :command:`flask` is the name of the module to
|
||||
import. In case that module is incorrectly named you will get an 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.
|
||||
The ``FLASK_APP`` environment variable is the name of the module to import at
|
||||
:command:`flask run`. In case that module is incorrectly named you will get an
|
||||
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.
|
||||
|
||||
The most common reason is a typo or because you did not actually create an
|
||||
``app`` object.
|
||||
|
|
@ -123,13 +123,13 @@ That is not very nice and Flask can do better. If you enable debug
|
|||
support the server will reload itself on code changes, and it will also
|
||||
provide you with a helpful debugger if things go wrong.
|
||||
|
||||
To enable debug mode you can export the `FLASK_DEBUG` environment variable
|
||||
To enable debug mode you can 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`).
|
||||
(On Windows you need to use ``set`` instead of ``export``).
|
||||
|
||||
This does the following things:
|
||||
|
||||
|
|
@ -153,20 +153,22 @@ Screenshot of the debugger in action:
|
|||
:class: screenshot
|
||||
: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`.
|
||||
|
||||
|
||||
Routing
|
||||
-------
|
||||
|
||||
Modern web applications have beautiful URLs. This helps people remember
|
||||
the URLs, which is especially handy for applications that are used from
|
||||
mobile devices with slower network connections. If the user can directly
|
||||
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.
|
||||
Modern web applications use meaningful URLs to help users. Users are more
|
||||
likely to like a page and come back if the page uses a meaningful URL they can
|
||||
remember and use to directly visit a page.
|
||||
|
||||
As you have seen above, the :meth:`~flask.Flask.route` decorator is used to
|
||||
bind a function to a URL. Here are some basic examples::
|
||||
Use the :meth:`~flask.Flask.route` decorator to bind a function to a URL. ::
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
|
|
@ -176,16 +178,16 @@ bind a function to a URL. Here are some basic examples::
|
|||
def hello():
|
||||
return 'Hello, World'
|
||||
|
||||
But there is more to it! You can make certain parts of the URL dynamic and
|
||||
attach multiple rules to a function.
|
||||
You can do more! You can make parts of the URL dynamic and attach multiple
|
||||
rules to a function.
|
||||
|
||||
Variable Rules
|
||||
``````````````
|
||||
|
||||
To add variable parts to a URL you can mark these special sections as
|
||||
``<variable_name>``. Such a part is then passed as a keyword argument to your
|
||||
function. Optionally a converter can be used by specifying a rule with
|
||||
``<converter:variable_name>``. Here are some nice examples::
|
||||
You can add variable sections to a URL by marking sections with
|
||||
``<variable_name>``. Your function then receives the ``<variable_name>``
|
||||
as a keyword argument. Optionally, you can use a converter to specify the type
|
||||
of the argument like ``<converter:variable_name>``. ::
|
||||
|
||||
@app.route('/user/<username>')
|
||||
def show_user_profile(username):
|
||||
|
|
@ -197,111 +199,111 @@ function. Optionally a converter can be used by specifying a rule with
|
|||
# show the post with the given id, the id is an integer
|
||||
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
|
||||
|
||||
=========== ===============================================
|
||||
`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
|
||||
=========== ===============================================
|
||||
Converter types:
|
||||
|
||||
.. 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
|
||||
behind that module is to ensure beautiful and unique URLs based on
|
||||
precedents laid down by Apache and earlier HTTP servers.
|
||||
Unique URLs / Redirection Behavior
|
||||
``````````````````````````````````
|
||||
|
||||
Take these two rules::
|
||||
Take these two rules::
|
||||
|
||||
@app.route('/projects/')
|
||||
def projects():
|
||||
return 'The project page'
|
||||
@app.route('/projects/')
|
||||
def projects():
|
||||
return 'The project page'
|
||||
|
||||
@app.route('/about')
|
||||
def about():
|
||||
return 'The about page'
|
||||
@app.route('/about')
|
||||
def about():
|
||||
return 'The about page'
|
||||
|
||||
Though they look rather similar, they differ in their use of the trailing
|
||||
slash in the URL *definition*. In the first case, the canonical URL for the
|
||||
`projects` endpoint has a trailing slash. In that sense, it is similar to
|
||||
a folder on a filesystem. Accessing it without a trailing slash will cause
|
||||
Flask to redirect to the canonical URL with the trailing slash.
|
||||
Though they look similar, they differ in their use of the trailing slash in
|
||||
the URL. In the first case, the canonical URL for the ``projects`` endpoint
|
||||
uses a trailing slash. It's similar to a folder in a file system; if you
|
||||
access the URL without a trailing slash, Flask redirects you to the
|
||||
canonical URL with the trailing slash.
|
||||
|
||||
In the second case, however, the URL is defined without a trailing slash,
|
||||
rather like the pathname of a file on UNIX-like systems. Accessing the URL
|
||||
with a trailing slash will produce a 404 "Not Found" error.
|
||||
|
||||
This behavior allows relative URLs to continue working even if the trailing
|
||||
slash is omitted, consistent with how Apache and other servers work. Also,
|
||||
the URLs will stay unique, which helps search engines avoid indexing the
|
||||
same page twice.
|
||||
In the second case, however, the URL definition lacks a trailing slash,
|
||||
like the pathname of a file on UNIX-like systems. Accessing the URL with a
|
||||
trailing slash produces a 404 “Not Found” error.
|
||||
|
||||
This behavior allows relative URLs to continue working even if the trailing
|
||||
slash is omitted, consistent with how Apache and other servers work. Also,
|
||||
the URLs will stay unique, which helps search engines avoid indexing the
|
||||
same page twice.
|
||||
|
||||
.. _url-building:
|
||||
|
||||
URL Building
|
||||
````````````
|
||||
|
||||
If it can match URLs, can Flask also generate them? Of course it can. To
|
||||
build a URL to a specific function you can use the :func:`~flask.url_for`
|
||||
function. It accepts the name of the function as first argument and a number
|
||||
of keyword arguments, each corresponding to the variable part of the URL rule.
|
||||
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`).
|
||||
To build a URL to a specific function, use the :func:`~flask.url_for` function.
|
||||
It accepts the name of the function as its first argument and any number of
|
||||
keyword arguments, each corresponding to a variable part of the URL rule.
|
||||
Unknown variable parts are appended to the URL as query parameters.
|
||||
|
||||
Why would you want to build URLs using the URL reversing function
|
||||
: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
|
||||
importantly, it allows you to change URLs in one go, without having to
|
||||
remember to change URLs all over the place.
|
||||
2. URL building will handle escaping of special characters and Unicode
|
||||
data transparently for you, so you don't have to deal with them.
|
||||
3. If your application is placed outside the URL root (say, in
|
||||
``/myapplication`` instead of ``/``), :func:`~flask.url_for` will handle
|
||||
that properly for you.
|
||||
1. Reversing is often more descriptive than hard-coding the URLs.
|
||||
2. You can change your URLs in one go instead of needing to remember to
|
||||
manually change hard-coded URLs.
|
||||
3. URL building handles escaping of special characters and Unicode data
|
||||
transparently.
|
||||
4. If your application is placed outside the URL root, for example, in
|
||||
``/myapplication`` instead of ``/``, :func:`~flask.url_for` properly
|
||||
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 (the protocol web applications are speaking) knows different methods for
|
||||
accessing URLs. By default, a route only answers to ``GET`` requests, but that
|
||||
can be changed by providing the `methods` argument to the
|
||||
:meth:`~flask.Flask.route` decorator. Here are some examples::
|
||||
|
||||
from flask import request
|
||||
Web applications use different HTTP methods when accessing URLs. You should
|
||||
familiarize yourself with the HTTP methods as you work with Flask. By default,
|
||||
a route only answers to ``GET`` requests. You can use the ``methods`` argument
|
||||
of the :meth:`~flask.Flask.route` decorator to handle different HTTP methods.
|
||||
::
|
||||
|
||||
@app.route('/login', methods=['GET', 'POST'])
|
||||
def login():
|
||||
|
|
@ -310,64 +312,11 @@ can be changed by providing the `methods` argument to the
|
|||
else:
|
||||
show_the_login_form()
|
||||
|
||||
If ``GET`` is present, ``HEAD`` will be added automatically for you. You
|
||||
don't have to deal with that. It will also make sure that ``HEAD`` requests
|
||||
are handled as the `HTTP RFC`_ (the document describing the HTTP
|
||||
protocol) demands, so you can completely ignore that part of the HTTP
|
||||
specification. Likewise, as of Flask 0.6, ``OPTIONS`` is implemented for you
|
||||
automatically as well.
|
||||
If ``GET`` is present, Flask automatically adds support for the ``HEAD`` method
|
||||
and handles ``HEAD`` requests according to the the `HTTP RFC`_. Likewise,
|
||||
``OPTIONS`` is automatically implemented for you.
|
||||
|
||||
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
|
||||
.. _HTTP RFC: https://www.ietf.org/rfc/rfc2068.txt
|
||||
|
||||
Static Files
|
||||
------------
|
||||
|
|
@ -446,22 +395,22 @@ know how that works, head over to the :ref:`template-inheritance` pattern
|
|||
documentation. Basically template inheritance makes it possible to keep
|
||||
certain elements on each page (like header, navigation and footer).
|
||||
|
||||
Automatic escaping is enabled, so if `name` contains HTML it will be escaped
|
||||
Automatic escaping is enabled, so if ``name`` contains HTML it will be escaped
|
||||
automatically. If you can trust a variable and you know that it will be
|
||||
safe HTML (for example because it came from a module that converts wiki
|
||||
markup to HTML) you can mark it as safe by using the
|
||||
:class:`~jinja2.Markup` class or by using the ``|safe`` filter in the
|
||||
template. Head over to the Jinja 2 documentation for more examples.
|
||||
|
||||
Here is a basic introduction to how the :class:`~jinja2.Markup` class works:
|
||||
Here is a basic introduction to how the :class:`~jinja2.Markup` class works::
|
||||
|
||||
>>> from flask import Markup
|
||||
>>> Markup('<strong>Hello %s!</strong>') % '<blink>hacker</blink>'
|
||||
Markup(u'<strong>Hello <blink>hacker</blink>!</strong>')
|
||||
>>> Markup.escape('<blink>hacker</blink>')
|
||||
Markup(u'<blink>hacker</blink>')
|
||||
>>> Markup('<em>Marked up</em> » HTML').striptags()
|
||||
u'Marked up \xbb HTML'
|
||||
>>> from flask import Markup
|
||||
>>> Markup('<strong>Hello %s!</strong>') % '<blink>hacker</blink>'
|
||||
Markup(u'<strong>Hello <blink>hacker</blink>!</strong>')
|
||||
>>> Markup.escape('<blink>hacker</blink>')
|
||||
Markup(u'<blink>hacker</blink>')
|
||||
>>> Markup('<em>Marked up</em> » HTML').striptags()
|
||||
u'Marked up \xbb HTML'
|
||||
|
||||
.. versionchanged:: 0.5
|
||||
|
||||
|
|
@ -538,16 +487,16 @@ The Request Object
|
|||
``````````````````
|
||||
|
||||
The request object is documented in the API section and we will not cover
|
||||
it here in detail (see :class:`~flask.request`). Here is a broad overview of
|
||||
it here in detail (see :class:`~flask.Request`). Here is a broad overview of
|
||||
some of the most common operations. First of all you have to import it from
|
||||
the `flask` module::
|
||||
the ``flask`` module::
|
||||
|
||||
from flask import request
|
||||
|
||||
The current request method is available by using the
|
||||
:attr:`~flask.request.method` attribute. To access form data (data
|
||||
:attr:`~flask.Request.method` attribute. To access form data (data
|
||||
transmitted in a ``POST`` or ``PUT`` request) you can use the
|
||||
:attr:`~flask.request.form` attribute. Here is a full example of the two
|
||||
:attr:`~flask.Request.form` attribute. Here is a full example of the two
|
||||
attributes mentioned above::
|
||||
|
||||
@app.route('/login', methods=['POST', 'GET'])
|
||||
|
|
@ -563,14 +512,14 @@ attributes mentioned above::
|
|||
# was GET or the credentials were invalid
|
||||
return render_template('login.html', error=error)
|
||||
|
||||
What happens if the key does not exist in the `form` attribute? In that
|
||||
What happens if the key does not exist in the ``form`` attribute? In that
|
||||
case a special :exc:`KeyError` is raised. You can catch it like a
|
||||
standard :exc:`KeyError` but if you don't do that, a HTTP 400 Bad Request
|
||||
error page is shown instead. So for many situations you don't have to
|
||||
deal with that problem.
|
||||
|
||||
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', '')
|
||||
|
||||
|
|
@ -579,7 +528,7 @@ We recommend accessing URL parameters with `get` or by catching the
|
|||
bad request page in that case is not user friendly.
|
||||
|
||||
For a full list of methods and attributes of the request object, head over
|
||||
to the :class:`~flask.request` documentation.
|
||||
to the :class:`~flask.Request` documentation.
|
||||
|
||||
|
||||
File Uploads
|
||||
|
|
@ -725,17 +674,15 @@ converting return values into response objects is as follows:
|
|||
3. If a tuple is returned the items in the tuple can provide extra
|
||||
information. Such tuples have to be in the form ``(response, status,
|
||||
headers)`` or ``(response, headers)`` where at least one item has
|
||||
to be in the tuple. The `status` value will override the status code
|
||||
and `headers` can be a list or dictionary of additional header values.
|
||||
to be in the tuple. The ``status`` value will override the status code
|
||||
and ``headers`` can be a list or dictionary of additional header values.
|
||||
4. If none of that works, Flask will assume the return value is a
|
||||
valid WSGI application and convert that into a response object.
|
||||
|
||||
If you want to get hold of the resulting response object inside the view
|
||||
you can use the :func:`~flask.make_response` function.
|
||||
|
||||
Imagine you have a view like this:
|
||||
|
||||
.. sourcecode:: python
|
||||
Imagine you have a view like this::
|
||||
|
||||
@app.errorhandler(404)
|
||||
def not_found(error):
|
||||
|
|
@ -743,9 +690,7 @@ Imagine you have a view like this:
|
|||
|
||||
You just need to wrap the return expression with
|
||||
:func:`~flask.make_response` and get the response object to modify it, then
|
||||
return it:
|
||||
|
||||
.. sourcecode:: python
|
||||
return it::
|
||||
|
||||
@app.errorhandler(404)
|
||||
def not_found(error):
|
||||
|
|
@ -784,7 +729,7 @@ sessions work::
|
|||
session['username'] = request.form['username']
|
||||
return redirect(url_for('index'))
|
||||
return '''
|
||||
<form action="" method="post">
|
||||
<form method="post">
|
||||
<p><input type=text name=username>
|
||||
<p><input type=submit value=Login>
|
||||
</form>
|
||||
|
|
@ -807,13 +752,13 @@ not using the template engine (as in this example).
|
|||
The problem with random is that it's hard to judge what is truly random. And
|
||||
a secret key should be as random as possible. Your operating system
|
||||
has ways to generate pretty random stuff based on a cryptographic
|
||||
random generator which can be used to get such a key:
|
||||
random generator which can be used to get such a key::
|
||||
|
||||
>>> import os
|
||||
>>> os.urandom(24)
|
||||
'\xfd{H\xe5<\x95\xf9\xe3\x96.5\xd1\x01O<!\xd5\xa2\xa0\x9fR"\xa1\xa8'
|
||||
>>> import os
|
||||
>>> os.urandom(24)
|
||||
'\xfd{H\xe5<\x95\xf9\xe3\x96.5\xd1\x01O<!\xd5\xa2\xa0\x9fR"\xa1\xa8'
|
||||
|
||||
Just take that thing and copy/paste it into your code and you're done.
|
||||
Just take that thing and copy/paste it into your code and you're done.
|
||||
|
||||
A note on cookie-based sessions: Flask will take the values you put into the
|
||||
session object and serialize them into a cookie. If you are finding some
|
||||
|
|
@ -821,6 +766,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
|
||||
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
|
||||
----------------
|
||||
|
|
|
|||
|
|
@ -119,9 +119,9 @@ understand what is actually happening. The new behavior is quite simple:
|
|||
not executed yet or at all (for example in test environments sometimes
|
||||
you might want to not execute before-request callbacks).
|
||||
|
||||
Now what happens on errors? In production mode if an exception is not
|
||||
caught, the 500 internal server handler is called. In development mode
|
||||
however the exception is not further processed and bubbles up to the WSGI
|
||||
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 debug mode
|
||||
however the exception is not further processed and bubbles up to the WSGI
|
||||
server. That way things like the interactive debugger can provide helpful
|
||||
debug information.
|
||||
|
||||
|
|
@ -214,10 +214,11 @@ provide you with important information.
|
|||
Starting with Flask 0.7 you have finer control over that behavior by
|
||||
setting the ``PRESERVE_CONTEXT_ON_EXCEPTION`` configuration variable. By
|
||||
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
|
||||
as it will cause your application to leak memory on exceptions. However
|
||||
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,
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
tags. For more information on that have a look at the Wikipedia article
|
||||
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
|
||||
explicitly told otherwise. This should rule out all XSS problems caused
|
||||
|
|
|
|||
|
|
@ -167,7 +167,7 @@ Docstring conventions:
|
|||
"""
|
||||
|
||||
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
|
||||
standard docstring::
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ In order to test the application, we add a second module
|
|||
(:file:`flaskr_tests.py`) and create a unittest skeleton there::
|
||||
|
||||
import os
|
||||
import flaskr
|
||||
from flaskr import flaskr
|
||||
import unittest
|
||||
import tempfile
|
||||
|
||||
|
|
@ -41,7 +41,7 @@ In order to test the application, we add a second module
|
|||
|
||||
def setUp(self):
|
||||
self.db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp()
|
||||
flaskr.app.config['TESTING'] = True
|
||||
flaskr.app.testing = True
|
||||
self.app = flaskr.app.test_client()
|
||||
with flaskr.app.app_context():
|
||||
flaskr.init_db()
|
||||
|
|
@ -98,8 +98,10 @@ test method to our class, like this::
|
|||
|
||||
def setUp(self):
|
||||
self.db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp()
|
||||
flaskr.app.testing = True
|
||||
self.app = flaskr.app.test_client()
|
||||
flaskr.init_db()
|
||||
with flaskr.app.app_context():
|
||||
flaskr.init_db()
|
||||
|
||||
def tearDown(self):
|
||||
os.close(self.db_fd)
|
||||
|
|
@ -152,13 +154,13 @@ invalid credentials. Add this new test to the class::
|
|||
|
||||
def test_login_logout(self):
|
||||
rv = self.login('admin', 'default')
|
||||
assert 'You were logged in' in rv.data
|
||||
assert b'You were logged in' in rv.data
|
||||
rv = self.logout()
|
||||
assert 'You were logged out' in rv.data
|
||||
assert b'You were logged out' in rv.data
|
||||
rv = self.login('adminx', 'default')
|
||||
assert 'Invalid username' in rv.data
|
||||
assert b'Invalid username' in rv.data
|
||||
rv = self.login('admin', 'defaultx')
|
||||
assert 'Invalid password' in rv.data
|
||||
assert b'Invalid password' in rv.data
|
||||
|
||||
Test Adding Messages
|
||||
--------------------
|
||||
|
|
@ -172,9 +174,9 @@ like this::
|
|||
title='<Hello>',
|
||||
text='<strong>HTML</strong> allowed here'
|
||||
), follow_redirects=True)
|
||||
assert 'No entries here so far' not in rv.data
|
||||
assert '<Hello>' in rv.data
|
||||
assert '<strong>HTML</strong> allowed here' in rv.data
|
||||
assert b'No entries here so far' not in rv.data
|
||||
assert b'<Hello>' 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,
|
||||
which is the intended behavior.
|
||||
|
|
@ -208,7 +210,7 @@ temporarily. With this you can access the :class:`~flask.request`,
|
|||
functions. Here is a full example that demonstrates this approach::
|
||||
|
||||
import flask
|
||||
|
||||
|
||||
app = flask.Flask(__name__)
|
||||
|
||||
with app.test_request_context('/?name=Peter'):
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
.. _tutorial-css:
|
||||
|
||||
Step 7: Adding Style
|
||||
Step 8: Adding Style
|
||||
====================
|
||||
|
||||
Now that everything else works, it's time to add some style to the
|
||||
application. Just create a stylesheet called :file:`style.css` in the
|
||||
:file:`static` folder we created before:
|
||||
:file:`static` folder:
|
||||
|
||||
.. sourcecode:: css
|
||||
|
||||
|
|
|
|||
|
|
@ -1,25 +1,23 @@
|
|||
.. _tutorial-dbcon:
|
||||
|
||||
Step 3: Database Connections
|
||||
Step 4: Database Connections
|
||||
----------------------------
|
||||
|
||||
We have created a function for establishing a database connection with
|
||||
`connect_db`, but by itself, that's not particularly useful. Creating and
|
||||
closing database connections all the time is very inefficient, so we want
|
||||
to keep it around for longer. Because database connections encapsulate a
|
||||
transaction, we also need to make sure that only one request at the time
|
||||
uses the connection. How can we elegantly do that with Flask?
|
||||
You currently have a function for establishing a database connection with
|
||||
`connect_db`, but by itself, it is not particularly useful. Creating and
|
||||
closing database connections all the time is very inefficient, so you will
|
||||
need to keep it around for longer. Because database connections
|
||||
encapsulate a transaction, you will need to make sure that only one
|
||||
request at a time uses the connection. An elegant way to do this is by
|
||||
utilizing the *application context*.
|
||||
|
||||
This is where the application context comes into play, so let's start
|
||||
there.
|
||||
|
||||
Flask provides us with two contexts: the application context and the
|
||||
request context. For the time being, all you have to know is that there
|
||||
Flask provides two contexts: the *application context* and the
|
||||
*request context*. For the time being, all you have to know is that there
|
||||
are special variables that use these. For instance, the
|
||||
:data:`~flask.request` variable is the request object associated with
|
||||
the current request, whereas :data:`~flask.g` is a general purpose
|
||||
variable associated with the current application context. We will go into
|
||||
the details of this a bit later.
|
||||
variable associated with the current application context. The tutorial
|
||||
will cover some more details of this later on.
|
||||
|
||||
For the time being, all you have to know is that you can store information
|
||||
safely on the :data:`~flask.g` object.
|
||||
|
|
@ -37,7 +35,7 @@ already established connection::
|
|||
g.sqlite_db = connect_db()
|
||||
return g.sqlite_db
|
||||
|
||||
So now we know how to connect, but how do we properly disconnect? For
|
||||
Now you know how to connect, but how can you properly disconnect? For
|
||||
that, Flask provides us with the :meth:`~flask.Flask.teardown_appcontext`
|
||||
decorator. It's executed every time the application context tears down::
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
.. _tutorial-dbinit:
|
||||
|
||||
Step 4: Creating The Database
|
||||
Step 5: Creating The Database
|
||||
=============================
|
||||
|
||||
As outlined earlier, Flaskr is a database powered application, and more
|
||||
|
|
@ -16,13 +16,14 @@ Such a schema can be created by piping the ``schema.sql`` file into the
|
|||
|
||||
The downside of this is that it requires the ``sqlite3`` command to be
|
||||
installed, which is not necessarily the case on every system. This also
|
||||
requires that we provide the path to the database, which can introduce
|
||||
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
|
||||
for you to the application.
|
||||
for you, to the application.
|
||||
|
||||
To do this, we can create a function and hook it into the :command:`flask`
|
||||
command that initializes the database. Let me show you the code first. Just
|
||||
add this function below the `connect_db` function in :file:`flaskr.py`::
|
||||
To do this, you can create a function and hook it into a :command:`flask`
|
||||
command that initializes the database. For now just 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():
|
||||
db = get_db()
|
||||
|
|
@ -34,27 +35,27 @@ add this function below the `connect_db` function in :file:`flaskr.py`::
|
|||
def initdb_command():
|
||||
"""Initializes the database."""
|
||||
init_db()
|
||||
print 'Initialized the database.'
|
||||
print('Initialized the database.')
|
||||
|
||||
The ``app.cli.command()`` decorator registers a new command with the
|
||||
:command:`flask` script. When the command executes, Flask will automatically
|
||||
create an application context for us bound to the right application.
|
||||
Within the function, we can then access :attr:`flask.g` and other things as
|
||||
we would expect. When the script ends, the application context tears down
|
||||
create an application context which is bound to the right application.
|
||||
Within the function, you can then access :attr:`flask.g` and other things as
|
||||
you might expect. When the script ends, the application context tears down
|
||||
and the database connection is released.
|
||||
|
||||
We want to keep an actual function around that initializes the database,
|
||||
You will want to keep an actual function around that initializes the database,
|
||||
though, so that we can easily create databases in unit tests later on. (For
|
||||
more information see :ref:`testing`.)
|
||||
|
||||
The :func:`~flask.Flask.open_resource` method of the application object
|
||||
is a convenient helper function that will open a resource that the
|
||||
application provides. This function opens a file from the resource
|
||||
location (your ``flaskr`` folder) and allows you to read from it. We are
|
||||
using this here to execute a script on the database connection.
|
||||
location (the :file:`flaskr/flaskr` folder) and allows you to read from it.
|
||||
It is used in this example to execute a script on the database connection.
|
||||
|
||||
The connection object provided by SQLite can give us a cursor object.
|
||||
On that cursor, there is a method to execute a complete script. Finally, we
|
||||
The connection object provided by SQLite can give you a cursor object.
|
||||
On that cursor, there is a method to execute a complete script. Finally, you
|
||||
only have to commit the changes. SQLite3 and other transactional
|
||||
databases will not commit unless you explicitly tell it to.
|
||||
|
||||
|
|
|
|||
|
|
@ -3,21 +3,25 @@
|
|||
Step 0: Creating The Folders
|
||||
============================
|
||||
|
||||
Before we get started, let's create the folders needed for this
|
||||
Before getting started, you will need to create the folders needed for this
|
||||
application::
|
||||
|
||||
/flaskr
|
||||
/static
|
||||
/templates
|
||||
/flaskr
|
||||
/static
|
||||
/templates
|
||||
|
||||
The ``flaskr`` folder is not a Python package, but just something where we
|
||||
drop our files. Later on, we will put our database schema as well as main
|
||||
module into this folder. It is done in the following way. The files inside
|
||||
the :file:`static` folder are 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 `Jinja2`_ templates. The
|
||||
templates you create later on in the tutorial will go in this directory.
|
||||
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
|
||||
how to run ``flaskr`` later on in this tutorial. 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.
|
||||
|
||||
Continue with :ref:`tutorial-schema`.
|
||||
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
|
||||
JavaScript files go. Inside the :file:`templates` folder, Flask will look for
|
||||
`Jinja2`_ templates. You will see examples of this later on.
|
||||
|
||||
For now you should continue with :ref:`tutorial-schema`.
|
||||
|
||||
.. _Jinja2: http://jinja.pocoo.org/
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ the `example source`_.
|
|||
folders
|
||||
schema
|
||||
setup
|
||||
packaging
|
||||
dbcon
|
||||
dbinit
|
||||
views
|
||||
|
|
|
|||
|
|
@ -3,8 +3,9 @@
|
|||
Introducing Flaskr
|
||||
==================
|
||||
|
||||
We will call our blogging application Flaskr, but feel free to choose your own
|
||||
less Web-2.0-ish name ;) Essentially, we want it to do the following things:
|
||||
This tutorial will demonstrate a blogging application named Flaskr, but feel
|
||||
free to choose your own less Web-2.0-ish name ;) Essentially, it will do the
|
||||
following things:
|
||||
|
||||
1. Let the user sign in and out with credentials specified in the
|
||||
configuration. Only one user is supported.
|
||||
|
|
@ -14,8 +15,8 @@ less Web-2.0-ish name ;) Essentially, we want it to do the following things:
|
|||
3. The index page shows all entries so far in reverse chronological order
|
||||
(newest on top) and the user can add new ones from there if logged in.
|
||||
|
||||
We will be using SQLite3 directly for this application because it's good
|
||||
enough for an application of this size. For larger applications, however,
|
||||
SQLite3 will be used directly for this application because it's good enough
|
||||
for an application of this size. For larger applications, however,
|
||||
it makes a lot of sense to use `SQLAlchemy`_, as it handles database
|
||||
connections in a more intelligent way, allowing you to target different
|
||||
relational databases at once and more. You might also want to consider
|
||||
|
|
@ -30,4 +31,4 @@ Here a screenshot of the final application:
|
|||
|
||||
Continue with :ref:`tutorial-folders`.
|
||||
|
||||
.. _SQLAlchemy: http://www.sqlalchemy.org/
|
||||
.. _SQLAlchemy: https://www.sqlalchemy.org/
|
||||
|
|
|
|||
108
docs/tutorial/packaging.rst
Normal file
108
docs/tutorial/packaging.rst
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
.. _tutorial-packaging:
|
||||
|
||||
Step 3: Installing flaskr as a Package
|
||||
======================================
|
||||
|
||||
Flask is now shipped with built-in support for `Click`_. Click provides
|
||||
Flask with enhanced and extensible command line utilities. Later in this
|
||||
tutorial you will see exactly how to extend the ``flask`` command line
|
||||
interface (CLI).
|
||||
|
||||
A useful pattern to manage a Flask application is to install your app
|
||||
following the `Python Packaging Guide`_. Presently this involves
|
||||
creating two new files; :file:`setup.py` and :file:`MANIFEST.in` in the
|
||||
projects root directory. You also need to add an :file:`__init__.py`
|
||||
file to make the :file:`flaskr/flaskr` directory a package. After these
|
||||
changes, your code structure should be::
|
||||
|
||||
/flaskr
|
||||
/flaskr
|
||||
__init__.py
|
||||
/static
|
||||
/templates
|
||||
flaskr.py
|
||||
schema.sql
|
||||
setup.py
|
||||
MANIFEST.in
|
||||
|
||||
The content of the ``setup.py`` file for ``flaskr`` is:
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
setup(
|
||||
name='flaskr',
|
||||
packages=['flaskr'],
|
||||
include_package_data=True,
|
||||
install_requires=[
|
||||
'flask',
|
||||
],
|
||||
)
|
||||
|
||||
When using setuptools, it is also necessary to specify any special files
|
||||
that should be included in your package (in the :file:`MANIFEST.in`).
|
||||
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
|
||||
following lines::
|
||||
|
||||
graft flaskr/templates
|
||||
graft flaskr/static
|
||||
include flaskr/schema.sql
|
||||
|
||||
To simplify locating the application, add the following import statement
|
||||
into this file, :file:`flaskr/__init__.py`:
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
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 it 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
|
||||
is recommended to install your Flask application within a `virtualenv`_.
|
||||
With that said, go ahead and install the application with::
|
||||
|
||||
pip install --editable .
|
||||
|
||||
The above installation command assumes that it is run within the projects
|
||||
root directory, `flaskr/`. The `editable` flag allows editing
|
||||
source code without having to reinstall the Flask app each time you make
|
||||
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.
|
||||
Do this with the following commands::
|
||||
|
||||
export FLASK_APP=flaskr
|
||||
export FLASK_DEBUG=true
|
||||
flask run
|
||||
|
||||
(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.
|
||||
*Never leave debug mode activated in a production system*, because it will
|
||||
allow users to execute code on the server!
|
||||
|
||||
You will see a message telling you that server has started along with
|
||||
the address at which you can access it.
|
||||
|
||||
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,
|
||||
but first, you should get the database working.
|
||||
|
||||
.. admonition:: Externally Visible Server
|
||||
|
||||
Want your server to be publicly available? Check out the
|
||||
:ref:`externally visible server <public-server>` section for more
|
||||
information.
|
||||
|
||||
Continue with :ref:`tutorial-dbcon`.
|
||||
|
||||
.. _Click: http://click.pocoo.org
|
||||
.. _Python Packaging Guide: https://packaging.python.org
|
||||
.. _virtualenv: https://virtualenv.pypa.io
|
||||
|
|
@ -3,10 +3,10 @@
|
|||
Step 1: Database Schema
|
||||
=======================
|
||||
|
||||
First, we want to create the database schema. Only a single table is needed
|
||||
for this application and we only want to support SQLite, so creating the
|
||||
database schema is quite easy. Just put the following contents into a file
|
||||
named `schema.sql` in the just created `flaskr` folder:
|
||||
In this step, you will create the database schema. Only a single table is
|
||||
needed for this application and it will only support SQLite. All you need to do
|
||||
is put the following contents into a file named :file:`schema.sql` in the
|
||||
:file:`flaskr/flaskr` folder:
|
||||
|
||||
.. sourcecode:: sql
|
||||
|
||||
|
|
@ -17,7 +17,7 @@ named `schema.sql` in the just created `flaskr` folder:
|
|||
'text' text not null
|
||||
);
|
||||
|
||||
This schema consists of a single table called ``entries``. Each row in
|
||||
This schema consists of a single table called ``entries``. Each row in
|
||||
this table has an ``id``, a ``title``, and a ``text``. The ``id`` is an
|
||||
automatically incrementing integer and a primary key, the other two are
|
||||
strings that must not be null.
|
||||
|
|
|
|||
|
|
@ -3,15 +3,16 @@
|
|||
Step 2: Application Setup Code
|
||||
==============================
|
||||
|
||||
Now that we have the schema in place, we can create the application module.
|
||||
Let's call it ``flaskr.py``. We will place this file inside the ``flaskr``
|
||||
folder. We will begin by adding the imports we need and by adding the config
|
||||
section. For small applications, it is possible to drop the configuration
|
||||
directly into the module, and this is what we will be doing here. However,
|
||||
a cleaner solution would be to create a separate ``.ini`` or ``.py`` file,
|
||||
load that, and import the values from there.
|
||||
Now that the schema is in place, you can create the application module,
|
||||
:file:`flaskr.py`. This file should be placed inside of the
|
||||
:file:`flaskr/flaskr` folder. 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 ``.ini`` or ``.py`` file, load that, and
|
||||
import the values from there.
|
||||
|
||||
First, we add the imports in :file:`flaskr.py`::
|
||||
Here are the import statements (in :file:`flaskr.py`)::
|
||||
|
||||
# all the imports
|
||||
import os
|
||||
|
|
@ -19,12 +20,13 @@ First, we add the imports in :file:`flaskr.py`::
|
|||
from flask import Flask, request, session, g, redirect, url_for, abort, \
|
||||
render_template, flash
|
||||
|
||||
Next, we can create our actual application and initialize it with the
|
||||
config from the same file in :file:`flaskr.py`::
|
||||
The next couple lines will create the actual application instance and
|
||||
initialize it with the config from the same file in :file:`flaskr.py`:
|
||||
|
||||
# create our little application :)
|
||||
app = Flask(__name__)
|
||||
app.config.from_object(__name__)
|
||||
.. sourcecode:: python
|
||||
|
||||
app = Flask(__name__) # create the application instance :)
|
||||
app.config.from_object(__name__) # load config from this file , flaskr.py
|
||||
|
||||
# Load default config and override config from an environment variable
|
||||
app.config.update(dict(
|
||||
|
|
@ -35,8 +37,8 @@ config from the same file in :file:`flaskr.py`::
|
|||
))
|
||||
app.config.from_envvar('FLASKR_SETTINGS', silent=True)
|
||||
|
||||
The :class:`~flask.Config` object works similarly to a dictionary so we
|
||||
can update it with new values.
|
||||
The :class:`~flask.Config` object works similarly to a dictionary, so it can be
|
||||
updated with new values.
|
||||
|
||||
.. admonition:: Database Path
|
||||
|
||||
|
|
@ -55,10 +57,10 @@ can update it with new values.
|
|||
|
||||
Usually, it is a good idea to load a separate, environment-specific
|
||||
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.
|
||||
|
||||
.. code-block:: python
|
||||
.. sourcecode:: python
|
||||
|
||||
app.config.from_envvar('FLASKR_SETTINGS', silent=True)
|
||||
|
||||
|
|
@ -74,15 +76,15 @@ that in all cases, only variable names that are uppercase are considered.
|
|||
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.
|
||||
|
||||
We will also add a method that allows for easy connections to the
|
||||
specified database. This can be used to open a connection on request and
|
||||
Lastly, you will add a method that allows for easy connections to the
|
||||
specified database. 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. We create a simple database connection through SQLite and
|
||||
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 us to treat the rows as if they were dictionaries instead of
|
||||
This allows the rows to be treated as if they were dictionaries instead of
|
||||
tuples.
|
||||
|
||||
::
|
||||
.. sourcecode:: python
|
||||
|
||||
def connect_db():
|
||||
"""Connects to the specific database."""
|
||||
|
|
@ -90,29 +92,6 @@ tuples.
|
|||
rv.row_factory = sqlite3.Row
|
||||
return rv
|
||||
|
||||
With that out of the way, you should be able to start up the application
|
||||
without problems. Do this with the following commands::
|
||||
In the next section you will see how to run the application.
|
||||
|
||||
export FLASK_APP=flaskr
|
||||
export FLASK_DEBUG=1
|
||||
flask run
|
||||
|
||||
(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.
|
||||
*Never leave debug mode activated in a production system*, because it will
|
||||
allow users to execute code on the server!
|
||||
|
||||
You will see a message telling you that server has started along with
|
||||
the address at which you can access it.
|
||||
|
||||
When you head over to the server in your browser, you will get a 404 error
|
||||
because we don't have any views yet. We will focus on that a little later,
|
||||
but first, we should get the database working.
|
||||
|
||||
.. admonition:: Externally Visible Server
|
||||
|
||||
Want your server to be publicly available? Check out the
|
||||
:ref:`externally visible server <public-server>` section for more
|
||||
information.
|
||||
|
||||
Continue with :ref:`tutorial-dbcon`.
|
||||
Continue with :ref:`tutorial-packaging`.
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
.. _tutorial-templates:
|
||||
|
||||
Step 6: The Templates
|
||||
Step 7: The Templates
|
||||
=====================
|
||||
|
||||
Now we should start working on the templates. If we were to request the URLs
|
||||
now, we would only get an exception that Flask cannot find the templates. The
|
||||
templates are using `Jinja2`_ syntax and have autoescaping enabled by
|
||||
Now it is time to start working on the templates. As you may have
|
||||
noticed, if you make requests with the app running, you will get
|
||||
an exception that Flask cannot find the templates. The templates
|
||||
are using `Jinja2`_ syntax and have autoescaping enabled by
|
||||
default. This means that unless you mark a value in the code with
|
||||
:class:`~flask.Markup` or with the ``|safe`` filter in the template,
|
||||
Jinja2 will ensure that special characters such as ``<`` or ``>`` are
|
||||
|
|
@ -57,9 +58,9 @@ show_entries.html
|
|||
|
||||
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
|
||||
in with the :func:`~flask.render_template` function. We also tell the
|
||||
form to submit to your `add_entry` function and use ``POST`` as HTTP
|
||||
method:
|
||||
in with the :func:`~flask.render_template` function. Notice that the form is
|
||||
configured to submit to the `add_entry` view function and use ``POST`` as
|
||||
HTTP method:
|
||||
|
||||
.. sourcecode:: html+jinja
|
||||
|
||||
|
|
@ -78,9 +79,9 @@ method:
|
|||
{% endif %}
|
||||
<ul class=entries>
|
||||
{% for entry in entries %}
|
||||
<li><h2>{{ entry.title }}</h2>{{ entry.text|safe }}
|
||||
<li><h2>{{ entry.title }}</h2>{{ entry.text|safe }}</li>
|
||||
{% else %}
|
||||
<li><em>Unbelievable. No entries here so far</em>
|
||||
<li><em>Unbelievable. No entries here so far</em></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -8,3 +8,89 @@ expected, it's probably not a bad idea to add automated tests to simplify
|
|||
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
|
||||
documentation. Go there to see how easy it is to test Flask applications.
|
||||
|
||||
Adding tests to flaskr
|
||||
----------------------
|
||||
|
||||
Assuming you have seen the :ref:`testing` section and have either written
|
||||
your own tests for ``flaskr`` or have followed along with the examples
|
||||
provided, you might be wondering about ways to organize the project.
|
||||
|
||||
One possible and recommended project structure is::
|
||||
|
||||
flaskr/
|
||||
flaskr/
|
||||
__init__.py
|
||||
static/
|
||||
templates/
|
||||
tests/
|
||||
test_flaskr.py
|
||||
setup.py
|
||||
MANIFEST.in
|
||||
|
||||
For now go ahead a create the :file:`tests/` directory as well as the
|
||||
:file:`test_flaskr.py` file.
|
||||
|
||||
Running the tests
|
||||
-----------------
|
||||
|
||||
At this point you can run the tests. Here ``pytest`` will be used.
|
||||
|
||||
.. 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::
|
||||
|
||||
pip install -e .
|
||||
pip install pytest
|
||||
|
||||
Run and watch the tests pass, within the top-level :file:`flaskr/`
|
||||
directory as::
|
||||
|
||||
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
|
||||
|
||||
setup(
|
||||
name='flaskr',
|
||||
packages=['flaskr'],
|
||||
include_package_data=True,
|
||||
install_requires=[
|
||||
'flask',
|
||||
],
|
||||
setup_requires=[
|
||||
'pytest-runner',
|
||||
],
|
||||
tests_require=[
|
||||
'pytest',
|
||||
],
|
||||
)
|
||||
|
||||
Now create :file:`setup.cfg` in the project root (alongside
|
||||
:file:`setup.py`)::
|
||||
|
||||
[aliases]
|
||||
test=pytest
|
||||
|
||||
Now you can run::
|
||||
|
||||
python setup.py test
|
||||
|
||||
This calls on the alias created in :file:`setup.cfg` which in turn runs
|
||||
``pytest`` via ``pytest-runner``, as the :file:`setup.py` script has
|
||||
been called. (Recall the `setup_requires` argument in :file:`setup.py`)
|
||||
Following the standard rules of test-discovery your tests will be
|
||||
found, run, and hopefully pass.
|
||||
|
||||
This is one possible way to run and manage testing. Here ``pytest`` is
|
||||
used, but there are other options such as ``nose``. Integrating testing
|
||||
with ``setuptools`` is convenient because it is not necessary to actually
|
||||
download ``pytest`` or any other testing framework one might use.
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
.. _tutorial-views:
|
||||
|
||||
Step 5: The View Functions
|
||||
Step 6: The View Functions
|
||||
==========================
|
||||
|
||||
Now that the database connections are working, we can start writing the
|
||||
view functions. We will need four of them:
|
||||
Now that the database connections are working, you can start writing the
|
||||
view functions. You will need four of them:
|
||||
|
||||
Show Entries
|
||||
------------
|
||||
|
|
@ -30,7 +30,7 @@ Add New Entry
|
|||
|
||||
This view lets the user add new entries if they are logged in. This only
|
||||
responds to ``POST`` requests; the actual form is shown on the
|
||||
`show_entries` page. If everything worked out well, we will
|
||||
`show_entries` page. If everything worked out well, it will
|
||||
:func:`~flask.flash` an information message to the next request and
|
||||
redirect back to the `show_entries` page::
|
||||
|
||||
|
|
@ -45,8 +45,8 @@ redirect back to the `show_entries` page::
|
|||
flash('New entry was successfully posted')
|
||||
return redirect(url_for('show_entries'))
|
||||
|
||||
Note that we check that the user is logged in here (the `logged_in` key is
|
||||
present in the session and ``True``).
|
||||
Note that this view checks that the user is logged in (that is, if the
|
||||
`logged_in` key is present in the session and ``True``).
|
||||
|
||||
.. admonition:: Security Note
|
||||
|
||||
|
|
@ -81,11 +81,11 @@ notified about that, and the user is asked again::
|
|||
return render_template('login.html', error=error)
|
||||
|
||||
The `logout` function, on the other hand, removes that key from the session
|
||||
again. We use a neat trick here: if you use the :meth:`~dict.pop` method
|
||||
again. There is a neat trick here: if you use the :meth:`~dict.pop` method
|
||||
of the dict and pass a second parameter to it (the default), the method
|
||||
will delete the key from the dictionary if present or do nothing when that
|
||||
key is not in there. This is helpful because now we don't have to check
|
||||
if the user was logged in.
|
||||
key is not in there. This is helpful because now it is not necessary to
|
||||
check if the user was logged in.
|
||||
|
||||
::
|
||||
|
||||
|
|
@ -97,21 +97,21 @@ if the user was logged in.
|
|||
|
||||
.. admonition:: Security Note
|
||||
|
||||
Passwords should never be stored in plain text in a production
|
||||
system. This tutorial uses plain text passwords for simplicity. If you
|
||||
plan to release a project based off this tutorial out into the world,
|
||||
passwords should be both `hashed and salted`_ before being stored in a
|
||||
database or file.
|
||||
Passwords should never be stored in plain text in a production
|
||||
system. This tutorial uses plain text passwords for simplicity. If you
|
||||
plan to release a project based off this tutorial out into the world,
|
||||
passwords should be both `hashed and salted`_ before being stored in a
|
||||
database or file.
|
||||
|
||||
Fortunately, there are Flask extensions for the purpose of
|
||||
hashing passwords and verifying passwords against hashes, so adding
|
||||
this functionality is fairly straight forward. There are also
|
||||
Fortunately, there are Flask extensions for the purpose of
|
||||
hashing passwords and verifying passwords against hashes, so adding
|
||||
this functionality is fairly straight forward. There are also
|
||||
many general python libraries that can be used for hashing.
|
||||
|
||||
You can find a list of recommended Flask extensions
|
||||
You can find a list of recommended Flask extensions
|
||||
`here <http://flask.pocoo.org/extensions/>`_
|
||||
|
||||
|
||||
Continue with :ref:`tutorial-templates`.
|
||||
|
||||
.. _hashed and salted: https://blog.codinghorror.com/youre-probably-storing-passwords-incorrectly/
|
||||
.. _hashed and salted: https://blog.codinghorror.com/youre-probably-storing-passwords-incorrectly/
|
||||
|
|
|
|||
|
|
@ -14,12 +14,50 @@ This section of the documentation enumerates all the changes in Flask from
|
|||
release to release and how you can change your code to have a painless
|
||||
updating experience.
|
||||
|
||||
If you want to use the :command:`easy_install` command to upgrade your Flask
|
||||
installation, make sure to pass it the :option:`-U` parameter::
|
||||
Use the :command:`pip` command to upgrade your existing Flask installation by
|
||||
providing the ``--upgrade`` parameter::
|
||||
|
||||
$ easy_install -U 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
|
||||
------------
|
||||
|
|
@ -30,7 +68,7 @@ to the release we decided to push out a 0.11 release first with some
|
|||
changes removed to make the transition easier. If you have been tracking
|
||||
the master branch which was 1.0 you might see some unexpected changes.
|
||||
|
||||
In case you did track the master branch you will notice that `flask --app`
|
||||
In case you did track the master branch you will notice that :command:`flask --app`
|
||||
is removed now. You need to use the environment variable to specify an
|
||||
application.
|
||||
|
||||
|
|
@ -68,7 +106,7 @@ Templating
|
|||
The :func:`~flask.templating.render_template_string` function has changed to
|
||||
autoescape template variables by default. This better matches the behavior
|
||||
of :func:`~flask.templating.render_template`.
|
||||
|
||||
|
||||
Extension imports
|
||||
`````````````````
|
||||
|
||||
|
|
@ -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
|
||||
:attr:`flask.Flask.app_ctx_globals_class`.
|
||||
|
||||
.. _Flask-OldSessions: http://pythonhosted.org/Flask-OldSessions/
|
||||
.. _Flask-OldSessions: https://pythonhosted.org/Flask-OldSessions/
|
||||
|
||||
Version 0.9
|
||||
-----------
|
||||
|
|
@ -133,7 +171,7 @@ Version 0.8
|
|||
-----------
|
||||
|
||||
Flask introduced a new session interface system. We also noticed that
|
||||
there was a naming collision between `flask.session` the module that
|
||||
there was a naming collision between ``flask.session`` the module that
|
||||
implements sessions and :data:`flask.session` which is the global session
|
||||
object. With that introduction we moved the implementation details for
|
||||
the session system into a new module called :mod:`flask.sessions`. If you
|
||||
|
|
@ -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
|
||||
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
|
||||
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
|
||||
|
|
@ -199,7 +237,7 @@ Please note that deprecation warnings are disabled by default starting
|
|||
with Python 2.7. In order to see the deprecation warnings that might be
|
||||
emitted you have to enabled them with the :mod:`warnings` module.
|
||||
|
||||
If you are working with windows and you lack the `patch` command line
|
||||
If you are working with windows and you lack the ``patch`` command line
|
||||
utility you can get it as part of various Unix runtime environments for
|
||||
windows including cygwin, msysgit or ming32. Also source control systems
|
||||
like svn, hg or git have builtin support for applying unified diffs as
|
||||
|
|
@ -316,7 +354,7 @@ to upgrade. What changed?
|
|||
runtime.
|
||||
- Blueprints have an inverse behavior for :meth:`url_for`. Previously
|
||||
``.foo`` told :meth:`url_for` that it should look for the endpoint
|
||||
`foo` on the application. Now it means “relative to current module”.
|
||||
``foo`` on the application. Now it means “relative to current module”.
|
||||
The script will inverse all calls to :meth:`url_for` automatically for
|
||||
you. It will do this in a very eager way so you might end up with
|
||||
some unnecessary leading dots in your code if you're not using
|
||||
|
|
@ -334,7 +372,7 @@ to upgrade. What changed?
|
|||
name into that folder if you want :file:`blueprintname/template.html` as
|
||||
the template name.
|
||||
|
||||
If you continue to use the `Module` object which is deprecated, Flask will
|
||||
If you continue to use the ``Module`` object which is deprecated, Flask will
|
||||
restore the previous behavior as good as possible. However we strongly
|
||||
recommend upgrading to the new blueprints as they provide a lot of useful
|
||||
improvement such as the ability to attach a blueprint multiple times,
|
||||
|
|
@ -354,7 +392,7 @@ change the order.
|
|||
|
||||
Another change that breaks backwards compatibility is that context
|
||||
processors will no longer override values passed directly to the template
|
||||
rendering function. If for example `request` is as variable passed
|
||||
rendering function. If for example ``request`` is as variable passed
|
||||
directly to the template, the default context processor will not override
|
||||
it with the current request object. This makes it easier to extend
|
||||
context processors later to inject additional variables without breaking
|
||||
|
|
@ -380,7 +418,7 @@ The following changes may be relevant to your application:
|
|||
for this feature. Removing support for this makes the Flask internal
|
||||
code easier to understand and fixes a couple of small issues that make
|
||||
debugging harder than necessary.
|
||||
- The `create_jinja_loader` function is gone. If you want to customize
|
||||
- The ``create_jinja_loader`` function is gone. If you want to customize
|
||||
the Jinja loader now, use the
|
||||
:meth:`~flask.Flask.create_jinja_environment` method instead.
|
||||
|
||||
|
|
|
|||
1
examples/flaskr/.gitignore
vendored
1
examples/flaskr/.gitignore
vendored
|
|
@ -1 +1,2 @@
|
|||
flaskr.db
|
||||
.eggs/
|
||||
|
|
|
|||
3
examples/flaskr/MANIFEST.in
Normal file
3
examples/flaskr/MANIFEST.in
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
graft flaskr/templates
|
||||
graft flaskr/static
|
||||
include flaskr/schema.sql
|
||||
|
|
@ -13,15 +13,19 @@
|
|||
export an FLASKR_SETTINGS environment variable
|
||||
pointing to a configuration file.
|
||||
|
||||
2. Instruct flask to use the right application
|
||||
2. install the app from the root of the project directory
|
||||
|
||||
pip install --editable .
|
||||
|
||||
3. Instruct flask to use the right application
|
||||
|
||||
export FLASK_APP=flaskr
|
||||
|
||||
3. initialize the database with this command:
|
||||
4. initialize the database with this command:
|
||||
|
||||
flask initdb
|
||||
|
||||
4. now you can run flaskr:
|
||||
5. now you can run flaskr:
|
||||
|
||||
flask run
|
||||
|
||||
|
|
@ -30,5 +34,5 @@
|
|||
|
||||
~ Is it tested?
|
||||
|
||||
You betcha. Run the `test_flaskr.py` file to see
|
||||
You betcha. Run `python setup.py test` to see
|
||||
the tests pass.
|
||||
|
|
|
|||
1
examples/flaskr/flaskr/__init__.py
Normal file
1
examples/flaskr/flaskr/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
from .flaskr import app
|
||||
|
|
@ -13,9 +13,9 @@
|
|||
{% endif %}
|
||||
<ul class="entries">
|
||||
{% for entry in entries %}
|
||||
<li><h2>{{ entry.title }}</h2>{{ entry.text|safe }}
|
||||
<li><h2>{{ entry.title }}</h2>{{ entry.text|safe }}</li>
|
||||
{% else %}
|
||||
<li><em>Unbelievable. No entries here so far</em>
|
||||
<li><em>Unbelievable. No entries here so far</em></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endblock %}
|
||||
2
examples/flaskr/setup.cfg
Normal file
2
examples/flaskr/setup.cfg
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
[tool:pytest]
|
||||
test=pytest
|
||||
16
examples/flaskr/setup.py
Normal file
16
examples/flaskr/setup.py
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
from setuptools import setup
|
||||
|
||||
setup(
|
||||
name='flaskr',
|
||||
packages=['flaskr'],
|
||||
include_package_data=True,
|
||||
install_requires=[
|
||||
'flask',
|
||||
],
|
||||
setup_requires=[
|
||||
'pytest-runner',
|
||||
],
|
||||
tests_require=[
|
||||
'pytest',
|
||||
],
|
||||
)
|
||||
|
|
@ -9,11 +9,10 @@
|
|||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
import os
|
||||
import flaskr
|
||||
import tempfile
|
||||
import pytest
|
||||
from flaskr import flaskr
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
2
examples/minitwit/.gitignore
vendored
Normal file
2
examples/minitwit/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
minitwit.db
|
||||
.eggs/
|
||||
3
examples/minitwit/MANIFEST.in
Normal file
3
examples/minitwit/MANIFEST.in
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
graft minitwit/templates
|
||||
graft minitwit/static
|
||||
include minitwit/schema.sql
|
||||
|
|
@ -14,15 +14,19 @@
|
|||
export an MINITWIT_SETTINGS environment variable
|
||||
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
|
||||
|
||||
2. fire up a shell and run this:
|
||||
4. fire up a shell and run this:
|
||||
|
||||
flask initdb
|
||||
|
||||
3. now you can run minitwit:
|
||||
5. now you can run minitwit:
|
||||
|
||||
flask run
|
||||
|
||||
|
|
@ -31,5 +35,5 @@
|
|||
|
||||
~ 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.
|
||||
|
|
|
|||
1
examples/minitwit/minitwit/__init__.py
Normal file
1
examples/minitwit/minitwit/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
from .minitwit import app
|
||||
|
|
@ -85,7 +85,7 @@ def format_datetime(timestamp):
|
|||
|
||||
def gravatar_url(email, size=80):
|
||||
"""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)
|
||||
|
||||
|
||||
2
examples/minitwit/setup.cfg
Normal file
2
examples/minitwit/setup.cfg
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
[aliases]
|
||||
test=pytest
|
||||
16
examples/minitwit/setup.py
Normal file
16
examples/minitwit/setup.py
Normal file
|
|
@ -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',
|
||||
],
|
||||
)
|
||||
|
|
@ -9,9 +9,9 @@
|
|||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
import os
|
||||
import minitwit
|
||||
import tempfile
|
||||
import pytest
|
||||
from minitwit import minitwit
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
10
examples/patterns/largerapp/setup.py
Normal file
10
examples/patterns/largerapp/setup.py
Normal file
|
|
@ -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
Normal file
12
examples/patterns/largerapp/tests/test_largerapp.py
Normal file
|
|
@ -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
Normal file
4
examples/patterns/largerapp/yourapplication/__init__.py
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
from flask import Flask
|
||||
app = Flask(__name__)
|
||||
|
||||
import yourapplication.views
|
||||
5
examples/patterns/largerapp/yourapplication/views.py
Normal file
5
examples/patterns/largerapp/yourapplication/views.py
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
from yourapplication import app
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
return 'Hello World!'
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
A simple example for integrating [Persona](https://login.persona.org/) into a
|
||||
Flask application. In addition to Flask, it requires the
|
||||
[requests](www.python-requests.org/) library.
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
from flask import Flask, render_template, session, request, abort, g
|
||||
|
||||
import requests
|
||||
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config.update(
|
||||
DEBUG=True,
|
||||
SECRET_KEY='my development key',
|
||||
PERSONA_JS='https://login.persona.org/include.js',
|
||||
PERSONA_VERIFIER='https://verifier.login.persona.org/verify',
|
||||
)
|
||||
app.config.from_envvar('PERSONA_SETTINGS', silent=True)
|
||||
|
||||
|
||||
@app.before_request
|
||||
def get_current_user():
|
||||
g.user = None
|
||||
email = session.get('email')
|
||||
if email is not None:
|
||||
g.user = email
|
||||
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
"""Just a generic index page to show."""
|
||||
return render_template('index.html')
|
||||
|
||||
|
||||
@app.route('/_auth/login', methods=['GET', 'POST'])
|
||||
def login_handler():
|
||||
"""This is used by the persona.js file to kick off the
|
||||
verification securely from the server side. If all is okay
|
||||
the email address is remembered on the server.
|
||||
"""
|
||||
resp = requests.post(app.config['PERSONA_VERIFIER'], data={
|
||||
'assertion': request.form['assertion'],
|
||||
'audience': request.host_url,
|
||||
}, verify=True)
|
||||
if resp.ok:
|
||||
verification_data = resp.json()
|
||||
if verification_data['status'] == 'okay':
|
||||
session['email'] = verification_data['email']
|
||||
return 'OK'
|
||||
|
||||
abort(400)
|
||||
|
||||
|
||||
@app.route('/_auth/logout', methods=['POST'])
|
||||
def logout_handler():
|
||||
"""This is what persona.js will call to sign the user
|
||||
out again.
|
||||
"""
|
||||
session.clear()
|
||||
return 'OK'
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
$(function() {
|
||||
/* convert the links into clickable buttons that go to the
|
||||
persona service */
|
||||
$('a.signin').on('click', function() {
|
||||
navigator.id.request({
|
||||
siteName: 'Flask Persona Example'
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
$('a.signout').on('click', function() {
|
||||
navigator.id.logout();
|
||||
return false;
|
||||
});
|
||||
|
||||
/* watch persona state changes */
|
||||
navigator.id.watch({
|
||||
loggedInUser: $CURRENT_USER,
|
||||
onlogin: function(assertion) {
|
||||
/* because the login needs to verify the provided assertion
|
||||
with the persona service which requires an HTTP request,
|
||||
this could take a bit. To not confuse the user we show
|
||||
a progress box */
|
||||
var box = $('<div class=signinprogress></div>')
|
||||
.hide()
|
||||
.text('Please wait ...')
|
||||
.appendTo('body')
|
||||
.fadeIn('fast');
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: $URL_ROOT + '_auth/login',
|
||||
data: {assertion: assertion},
|
||||
success: function(res, status, xhr) { window.location.reload(); },
|
||||
error: function(xhr, status, err) {
|
||||
box.remove();
|
||||
navigator.id.logout();
|
||||
alert('Login failure: ' + err);
|
||||
}
|
||||
});
|
||||
},
|
||||
onlogout: function() {
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: $URL_ROOT + '_auth/logout',
|
||||
success: function(res, status, xhr) { window.location.reload(); },
|
||||
error: function(xhr, status, err) {
|
||||
alert('Logout failure: ' + err);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 30 KiB |
|
|
@ -1,39 +0,0 @@
|
|||
html {
|
||||
background: #eee;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Verdana', sans-serif;
|
||||
font-size: 15px;
|
||||
margin: 30px auto;
|
||||
width: 720px;
|
||||
background: white;
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
h1, h2, a {
|
||||
color: #d00;
|
||||
}
|
||||
|
||||
div.authbar {
|
||||
background: #eee;
|
||||
padding: 0 15px;
|
||||
margin: 10px -15px;
|
||||
line-height: 25px;
|
||||
height: 25px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
div.signinprogress {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(255, 255, 255, 0.8) url(spinner.png) center center no-repeat;
|
||||
font-size: 0;
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
{% extends "layout.html" %}
|
||||
{% block title %}Welcome{% endblock %}
|
||||
{% block body %}
|
||||
<h2>Welcome</h2>
|
||||
<p>
|
||||
This is a small example application that shows how to integrate
|
||||
Mozilla's persona signin service into a Flask application.
|
||||
<p>
|
||||
The advantage of persona over your own login system is that the
|
||||
password is managed outside of your application and you get
|
||||
a verified mail address as primary identifier for your user.
|
||||
<p>
|
||||
In this example nothing is actually stored on the server, it
|
||||
just takes over the email address from the persona verifier
|
||||
and stores it in a session.
|
||||
{% if g.user %}
|
||||
<p>
|
||||
You are now logged in as <strong>{{ g.user }}</strong>
|
||||
{% else %}
|
||||
<p>
|
||||
To sign in click the sign in button above.
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
<!doctype html>
|
||||
<title>{% block title %}{% endblock %} | Flask Persona Example</title>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
|
||||
<script src="{{ config.PERSONA_JS }}"></script>
|
||||
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
|
||||
<script>
|
||||
/* the url root is useful for doing HTTP requests */
|
||||
var $URL_ROOT = {{ request.url_root|tojson }};
|
||||
|
||||
/* we store the current user here so that the persona
|
||||
javascript support knows about the current user */
|
||||
var $CURRENT_USER = {{ g.user|tojson }};
|
||||
</script>
|
||||
<script src="{{ url_for('static', filename='persona.js') }}"></script>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
||||
<header>
|
||||
<h1>Mozilla Persona Example</h1>
|
||||
<div class="authbar">
|
||||
{% if g.user %}
|
||||
Signed in as <em>{{ g.user }}</em>
|
||||
(<a href="#" class="signout">Sign out</a>)
|
||||
{% else %}
|
||||
Not signed in. <a href="#" class="signin">Sign in</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</header>
|
||||
{% block body %}{% endblock %}
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
: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
|
||||
# in the module but are exported as public interface.
|
||||
|
|
@ -40,7 +40,7 @@ from .signals import signals_available, template_rendered, request_started, \
|
|||
# it.
|
||||
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.
|
||||
jsonify = json.jsonify
|
||||
|
||||
|
|
|
|||
|
|
@ -65,17 +65,25 @@ def with_metaclass(meta, *bases):
|
|||
|
||||
|
||||
# Certain versions of pypy have a bug where clearing the exception stack
|
||||
# breaks the __exit__ function in a very peculiar way. This is currently
|
||||
# true for pypy 2.2.1 for instance. The second level of exception blocks
|
||||
# is necessary because pypy seems to forget to check if an exception
|
||||
# happened until the next bytecode instruction?
|
||||
# breaks the __exit__ function in a very peculiar way. The second level of
|
||||
# exception blocks is necessary because pypy seems to forget to check if an
|
||||
# exception happened until the next bytecode instruction?
|
||||
#
|
||||
# Relevant PyPy bugfix commit:
|
||||
# https://bitbucket.org/pypy/pypy/commits/77ecf91c635a287e88e60d8ddb0f4e9df4003301
|
||||
# According to ronan on #pypy IRC, it is released in PyPy2 2.3 and later
|
||||
# versions.
|
||||
#
|
||||
# Ubuntu 14.04 has PyPy 2.2.1, which does exhibit this bug.
|
||||
BROKEN_PYPY_CTXMGR_EXIT = False
|
||||
if hasattr(sys, 'pypy_version_info'):
|
||||
class _Mgr(object):
|
||||
def __enter__(self):
|
||||
return self
|
||||
def __exit__(self, *args):
|
||||
sys.exc_clear()
|
||||
if hasattr(sys, 'exc_clear'):
|
||||
# Python 3 (PyPy3) doesn't have exc_clear
|
||||
sys.exc_clear()
|
||||
try:
|
||||
try:
|
||||
with _Mgr():
|
||||
|
|
|
|||
384
flask/app.py
384
flask/app.py
|
|
@ -10,31 +10,30 @@
|
|||
"""
|
||||
import os
|
||||
import sys
|
||||
from threading import Lock
|
||||
from datetime import timedelta
|
||||
from itertools import chain
|
||||
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 werkzeug.datastructures import ImmutableDict, Headers
|
||||
from werkzeug.exceptions import BadRequest, HTTPException, \
|
||||
InternalServerError, MethodNotAllowed, default_exceptions
|
||||
from werkzeug.routing import BuildError, Map, RequestRedirect, Rule
|
||||
|
||||
from .helpers import _PackageBoundObject, url_for, get_flashed_messages, \
|
||||
locked_cached_property, _endpoint_from_view_func, find_package, \
|
||||
get_debug_flag
|
||||
from . import json, cli
|
||||
from .wrappers import Request, Response
|
||||
from .config import ConfigAttribute, Config
|
||||
from .ctx import RequestContext, AppContext, _AppCtxGlobals
|
||||
from .globals import _request_ctx_stack, request, session, g
|
||||
from . import cli, json
|
||||
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 .signals import appcontext_tearing_down, got_request_exception, \
|
||||
request_finished, request_started, request_tearing_down
|
||||
from .templating import DispatchingJinjaLoader, Environment, \
|
||||
_default_template_ctx_processor
|
||||
from .signals import request_started, request_finished, got_request_exception, \
|
||||
request_tearing_down, appcontext_tearing_down
|
||||
from ._compat import reraise, string_types, text_type, integer_types
|
||||
_default_template_ctx_processor
|
||||
from .wrappers import Request, Response
|
||||
|
||||
# a lock used for logger initialization
|
||||
_logger_lock = Lock()
|
||||
|
|
@ -124,6 +123,9 @@ class Flask(_PackageBoundObject):
|
|||
.. versionadded:: 0.11
|
||||
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 static_url_path: can be used to specify a different path for the
|
||||
static files on the web. Defaults to the name
|
||||
|
|
@ -131,6 +133,13 @@ class Flask(_PackageBoundObject):
|
|||
:param static_folder: the folder with static files that should be served
|
||||
at `static_url_path`. Defaults to the ``'static'``
|
||||
folder in the root path of the application.
|
||||
folder in the root path of the application. Defaults
|
||||
to None.
|
||||
: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
|
||||
be used by the application. Defaults to
|
||||
``'templates'`` folder in the root path of the
|
||||
|
|
@ -315,7 +324,7 @@ class Flask(_PackageBoundObject):
|
|||
'PREFERRED_URL_SCHEME': 'http',
|
||||
'JSON_AS_ASCII': True,
|
||||
'JSON_SORT_KEYS': True,
|
||||
'JSONIFY_PRETTYPRINT_REGULAR': True,
|
||||
'JSONIFY_PRETTYPRINT_REGULAR': False,
|
||||
'JSONIFY_MIMETYPE': 'application/json',
|
||||
'TEMPLATES_AUTO_RELOAD': None,
|
||||
})
|
||||
|
|
@ -338,7 +347,8 @@ class Flask(_PackageBoundObject):
|
|||
session_interface = SecureCookieSessionInterface()
|
||||
|
||||
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,
|
||||
root_path=None):
|
||||
_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
|
||||
#: function.
|
||||
#:
|
||||
#: To register a error handler, use the :meth:`errorhandler`
|
||||
#: To register an error handler, use the :meth:`errorhandler`
|
||||
#: decorator.
|
||||
self.error_handler_spec = {None: self._error_handlers}
|
||||
|
||||
|
|
@ -405,17 +415,16 @@ class Flask(_PackageBoundObject):
|
|||
#: .. versionadded:: 0.9
|
||||
self.url_build_error_handlers = []
|
||||
|
||||
#: A dictionary with lists of functions that should be called at the
|
||||
#: beginning of the request. The key of the dictionary is the name of
|
||||
#: the blueprint this function is active for, ``None`` for all requests.
|
||||
#: This can for example be used to open database connections or
|
||||
#: getting hold of the currently logged in user. To register a
|
||||
#: function here, use the :meth:`before_request` decorator.
|
||||
#: A dictionary with lists of functions that will be called at the
|
||||
#: beginning of each request. The key of the dictionary is the name of
|
||||
#: the blueprint this function is active for, or ``None`` for all
|
||||
#: requests. To register a function, use the :meth:`before_request`
|
||||
#: decorator.
|
||||
self.before_request_funcs = {}
|
||||
|
||||
#: A lists of functions that should be called at the beginning of the
|
||||
#: first request to this instance. To register a function here, use
|
||||
#: the :meth:`before_first_request` decorator.
|
||||
#: A list of functions that will be called at the beginning of the
|
||||
#: first request to this instance. To register a function, use the
|
||||
#: :meth:`before_first_request` decorator.
|
||||
#:
|
||||
#: .. versionadded:: 0.8
|
||||
self.before_first_request_funcs = []
|
||||
|
|
@ -447,12 +456,11 @@ class Flask(_PackageBoundObject):
|
|||
#: .. versionadded:: 0.9
|
||||
self.teardown_appcontext_funcs = []
|
||||
|
||||
#: A dictionary with lists of functions that can be used as URL
|
||||
#: value processor functions. Whenever a URL is built these functions
|
||||
#: are called to modify the dictionary of values in place. The key
|
||||
#: ``None`` here is used for application wide
|
||||
#: callbacks, otherwise the key is the name of the blueprint.
|
||||
#: Each of these functions has the chance to modify the dictionary
|
||||
#: A dictionary with lists of functions that are called before the
|
||||
#: :attr:`before_request_funcs` functions. The key of the dictionary is
|
||||
#: the name of the blueprint this function is active for, or ``None``
|
||||
#: for all requests. To register a function, use
|
||||
#: :meth:`url_value_preprocessor`.
|
||||
#:
|
||||
#: .. versionadded:: 0.7
|
||||
self.url_value_preprocessors = {}
|
||||
|
|
@ -519,26 +527,29 @@ class Flask(_PackageBoundObject):
|
|||
#: def to_python(self, value):
|
||||
#: return value.split(',')
|
||||
#: def to_url(self, values):
|
||||
#: return ','.join(BaseConverter.to_url(value)
|
||||
#: return ','.join(super(ListConverter, self).to_url(value)
|
||||
#: for value in values)
|
||||
#:
|
||||
#: app = Flask(__name__)
|
||||
#: app.url_map.converters['list'] = ListConverter
|
||||
self.url_map = Map()
|
||||
|
||||
self.url_map.host_matching = host_matching
|
||||
|
||||
# tracks internally if the application already handled at least one
|
||||
# request.
|
||||
self._got_first_request = False
|
||||
self._before_request_lock = Lock()
|
||||
|
||||
# register the static folder for the application. Do that even
|
||||
# if the folder does not exist. First of all it might be created
|
||||
# while the server is running (usually happens during development)
|
||||
# but also because google appengine stores static files somewhere
|
||||
# else when mapped with the .yml file.
|
||||
# Add a static route using the provided static_url_path, static_host,
|
||||
# and static_folder if there is a configured static_folder.
|
||||
# Note we do this without checking if static_folder exists.
|
||||
# For one, it might be created while the server is running (e.g. during
|
||||
# development). Also, Google App Engine stores static files somewhere
|
||||
if self.has_static_folder:
|
||||
assert bool(static_host) == host_matching, 'Invalid static_host/host_matching combination'
|
||||
self.add_url_rule(self.static_url_path + '/<path:filename>',
|
||||
endpoint='static',
|
||||
endpoint='static', host=static_host,
|
||||
view_func=self.send_static_file)
|
||||
|
||||
#: 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
|
||||
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
|
||||
port defined in the ``SERVER_NAME`` config variable if
|
||||
present.
|
||||
|
|
@ -825,25 +837,31 @@ class Flask(_PackageBoundObject):
|
|||
:func:`werkzeug.serving.run_simple` for more
|
||||
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
|
||||
if host is None:
|
||||
host = '127.0.0.1'
|
||||
if port is None:
|
||||
server_name = self.config['SERVER_NAME']
|
||||
if server_name and ':' in server_name:
|
||||
port = int(server_name.rsplit(':', 1)[1])
|
||||
else:
|
||||
port = 5000
|
||||
_host = '127.0.0.1'
|
||||
_port = 5000
|
||||
server_name = self.config.get("SERVER_NAME")
|
||||
sn_host, sn_port = None, None
|
||||
if server_name:
|
||||
sn_host, _, sn_port = server_name.partition(':')
|
||||
host = host or sn_host or _host
|
||||
port = int(port or sn_port or _port)
|
||||
if debug is not None:
|
||||
self.debug = bool(debug)
|
||||
options.setdefault('use_reloader', self.debug)
|
||||
options.setdefault('use_debugger', self.debug)
|
||||
options.setdefault('passthrough_errors', True)
|
||||
try:
|
||||
run_simple(host, port, self, **options)
|
||||
finally:
|
||||
# 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.
|
||||
self._got_first_request = False
|
||||
|
||||
|
|
@ -877,9 +895,9 @@ class Flask(_PackageBoundObject):
|
|||
from flask.testing import FlaskClient
|
||||
|
||||
class CustomClient(FlaskClient):
|
||||
def __init__(self, authentication=None, *args, **kwargs):
|
||||
FlaskClient.__init__(*args, **kwargs)
|
||||
self._authentication = authentication
|
||||
def __init__(self, *args, **kwargs):
|
||||
self._authentication = kwargs.pop("authentication")
|
||||
super(CustomClient,self).__init__( *args, **kwargs)
|
||||
|
||||
app.test_client_class = CustomClient
|
||||
client = app.test_client(authentication='Basic ....')
|
||||
|
|
@ -935,22 +953,7 @@ class Flask(_PackageBoundObject):
|
|||
|
||||
@setupmethod
|
||||
def register_blueprint(self, blueprint, **options):
|
||||
"""Register a blueprint on the application. For information about
|
||||
blueprints head over to :ref:`blueprints`.
|
||||
|
||||
The blueprint name is passed in as the first argument.
|
||||
Options are passed as additional keyword arguments and forwarded to
|
||||
`blueprints` in an "options" dictionary.
|
||||
|
||||
:param subdomain: set a subdomain for the blueprint
|
||||
:param url_prefix: set the prefix for all URLs defined on the blueprint.
|
||||
``(url_prefix='/<lang code>')``
|
||||
:param url_defaults: a dictionary with URL defaults that is added to
|
||||
each and every URL defined with this blueprint
|
||||
:param static_folder: add a static folder to urls in this blueprint
|
||||
:param static_url_path: add a static url path to urls in this blueprint
|
||||
:param template_folder: set an alternate template folder
|
||||
:param root_path: set an alternate root path for this blueprint
|
||||
"""Registers a blueprint on the application.
|
||||
|
||||
.. versionadded:: 0.7
|
||||
"""
|
||||
|
|
@ -975,7 +978,7 @@ class Flask(_PackageBoundObject):
|
|||
return iter(self._blueprint_order)
|
||||
|
||||
@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`
|
||||
decorator. If a view_func is provided it will be registered with the
|
||||
endpoint.
|
||||
|
|
@ -1015,6 +1018,10 @@ class Flask(_PackageBoundObject):
|
|||
endpoint
|
||||
:param view_func: the function to call when serving a request to the
|
||||
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
|
||||
:class:`~werkzeug.routing.Rule` object. A change
|
||||
to Werkzeug is handling of method options. methods
|
||||
|
|
@ -1044,8 +1051,9 @@ class Flask(_PackageBoundObject):
|
|||
|
||||
# starting with Flask 0.8 the view_func object can disable and
|
||||
# force-enable the automatic options handling.
|
||||
provide_automatic_options = getattr(view_func,
|
||||
'provide_automatic_options', None)
|
||||
if provide_automatic_options is None:
|
||||
provide_automatic_options = getattr(view_func,
|
||||
'provide_automatic_options', None)
|
||||
|
||||
if provide_automatic_options is None:
|
||||
if 'OPTIONS' not in methods:
|
||||
|
|
@ -1131,7 +1139,7 @@ class Flask(_PackageBoundObject):
|
|||
|
||||
@setupmethod
|
||||
def errorhandler(self, code_or_exception):
|
||||
"""A decorator that is used to register a function give a given
|
||||
"""A decorator that is used to register a function given an
|
||||
error code. Example::
|
||||
|
||||
@app.errorhandler(404)
|
||||
|
|
@ -1169,7 +1177,8 @@ class Flask(_PackageBoundObject):
|
|||
that do not necessarily have to be a subclass of the
|
||||
: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):
|
||||
self._register_error_handler(None, code_or_exception, f)
|
||||
|
|
@ -1303,11 +1312,13 @@ class Flask(_PackageBoundObject):
|
|||
@setupmethod
|
||||
def before_request(self, f):
|
||||
"""Registers a function to run before each request.
|
||||
|
||||
For example, this can be used to open a database connection, or to load
|
||||
the logged in user from the session.
|
||||
|
||||
The function will be called without any arguments.
|
||||
If the function returns a non-None value, it's handled as
|
||||
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)
|
||||
return f
|
||||
|
|
@ -1363,7 +1374,7 @@ class Flask(_PackageBoundObject):
|
|||
will have to surround the execution of these code by try/except
|
||||
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.
|
||||
|
||||
The return values of teardown functions are ignored.
|
||||
|
|
@ -1426,9 +1437,17 @@ class Flask(_PackageBoundObject):
|
|||
|
||||
@setupmethod
|
||||
def url_value_preprocessor(self, f):
|
||||
"""Registers a function as URL value preprocessor for all view
|
||||
functions of the application. It's called before the view functions
|
||||
are called and can modify the url values provided.
|
||||
"""Register a URL value preprocessor function for all view
|
||||
functions in the application. These functions will be called before the
|
||||
: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)
|
||||
return f
|
||||
|
|
@ -1452,24 +1471,13 @@ class Flask(_PackageBoundObject):
|
|||
def find_handler(handler_map):
|
||||
if not handler_map:
|
||||
return
|
||||
queue = deque(exc_class.__mro__)
|
||||
# Protect from geniuses who might create circular references in
|
||||
# __mro__
|
||||
done = set()
|
||||
|
||||
while queue:
|
||||
cls = queue.popleft()
|
||||
if cls in done:
|
||||
continue
|
||||
done.add(cls)
|
||||
for cls in exc_class.__mro__:
|
||||
handler = handler_map.get(cls)
|
||||
if handler is not None:
|
||||
# cache for next time exc_class is raised
|
||||
handler_map[exc_class] = handler
|
||||
return handler
|
||||
|
||||
queue.extend(cls.__mro__)
|
||||
|
||||
# try blueprint handlers
|
||||
handler = find_handler(self.error_handler_spec
|
||||
.get(request.blueprint, {})
|
||||
|
|
@ -1571,7 +1579,7 @@ class Flask(_PackageBoundObject):
|
|||
self.log_exception((exc_type, exc_value, tb))
|
||||
if handler is None:
|
||||
return InternalServerError()
|
||||
return handler(e)
|
||||
return self.finalize_request(handler(e), from_error_handler=True)
|
||||
|
||||
def log_exception(self, exc_info):
|
||||
"""Logs an exception. This is called by :meth:`handle_exception`
|
||||
|
|
@ -1639,9 +1647,30 @@ class Flask(_PackageBoundObject):
|
|||
rv = self.dispatch_request()
|
||||
except Exception as 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.process_response(response)
|
||||
request_finished.send(self, response=response)
|
||||
try:
|
||||
response = self.process_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
|
||||
|
||||
def try_trigger_before_first_request_functions(self):
|
||||
|
|
@ -1694,62 +1723,106 @@ class Flask(_PackageBoundObject):
|
|||
return False
|
||||
|
||||
def make_response(self, rv):
|
||||
"""Converts the return value from a view function to a real
|
||||
response object that is an instance of :attr:`response_class`.
|
||||
"""Convert the return value from a view function to an instance of
|
||||
: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
|
||||
without returning, is not allowed. The following types are allowed
|
||||
for ``view_rv``:
|
||||
|
||||
.. tabularcolumns:: |p{3.5cm}|p{9.5cm}|
|
||||
|
||||
======================= ===========================================
|
||||
:attr:`response_class` the object is returned unchanged
|
||||
:class:`str` a response object is created with the
|
||||
string as body
|
||||
:class:`unicode` a response object is created with the
|
||||
string encoded to utf-8 as body
|
||||
a WSGI function the function is called as WSGI application
|
||||
and buffered as response object
|
||||
:class:`tuple` A tuple in the form ``(response, status,
|
||||
headers)`` or ``(response, headers)``
|
||||
where `response` is any of the
|
||||
types defined here, `status` is a string
|
||||
or an integer and `headers` is a list or
|
||||
a dictionary with header values.
|
||||
======================= ===========================================
|
||||
|
||||
:param rv: the return value from the view function
|
||||
``str`` (``unicode`` in Python 2)
|
||||
A response object is created with the string encoded to UTF-8
|
||||
as the body.
|
||||
|
||||
``bytes`` (``str`` in Python 2)
|
||||
A response object is created with the bytes as the body.
|
||||
|
||||
``tuple``
|
||||
Either ``(body, status, headers)``, ``(body, status)``, or
|
||||
``(body, headers)``, where ``body`` is any of the other types
|
||||
allowed here, ``status`` is a string or an integer, and
|
||||
``headers`` is a dictionary or a list of ``(key, value)``
|
||||
tuples. If ``body`` is a :attr:`response_class` instance,
|
||||
``status`` overwrites the exiting value and ``headers`` are
|
||||
extended.
|
||||
|
||||
: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
|
||||
Previously a tuple was interpreted as the arguments for the
|
||||
response object.
|
||||
"""
|
||||
status_or_headers = headers = None
|
||||
if isinstance(rv, tuple):
|
||||
rv, status_or_headers, headers = rv + (None,) * (3 - len(rv))
|
||||
|
||||
status = headers = None
|
||||
|
||||
# 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).'
|
||||
)
|
||||
|
||||
# the body must not be None
|
||||
if rv is None:
|
||||
raise ValueError('View function did not return a response')
|
||||
|
||||
if isinstance(status_or_headers, (dict, list)):
|
||||
headers, status_or_headers = status_or_headers, 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):
|
||||
# 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)):
|
||||
rv = self.response_class(rv, headers=headers,
|
||||
status=status_or_headers)
|
||||
headers = status_or_headers = None
|
||||
# let the response class set the status and headers instead of
|
||||
# waiting to do it manually, so that the class can handle any
|
||||
# special logic
|
||||
rv = self.response_class(rv, status=status, headers=headers)
|
||||
status = headers = None
|
||||
else:
|
||||
rv = self.response_class.force_type(rv, request.environ)
|
||||
# evaluate a WSGI callable, or coerce a different response
|
||||
# class to the correct type
|
||||
try:
|
||||
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:
|
||||
if isinstance(status_or_headers, string_types):
|
||||
rv.status = status_or_headers
|
||||
# prefer the status if it was provided
|
||||
if status is not None:
|
||||
if isinstance(status, (text_type, bytes, bytearray)):
|
||||
rv.status = status
|
||||
else:
|
||||
rv.status_code = status_or_headers
|
||||
rv.status_code = status
|
||||
|
||||
# extend existing headers with provided headers
|
||||
if headers:
|
||||
rv.headers.extend(headers)
|
||||
|
||||
|
|
@ -1812,16 +1885,16 @@ class Flask(_PackageBoundObject):
|
|||
raise error
|
||||
|
||||
def preprocess_request(self):
|
||||
"""Called before the actual request dispatching and will
|
||||
call each :meth:`before_request` decorated function, passing no
|
||||
arguments.
|
||||
If any of these functions returns a value, it's handled as
|
||||
if it was the return value from the view and further
|
||||
request handling is stopped.
|
||||
|
||||
This also triggers the :meth:`url_value_processor` functions before
|
||||
the actual :meth:`before_request` functions are called.
|
||||
"""Called before the request is dispatched. Calls
|
||||
:attr:`url_value_preprocessors` registered with the app and the
|
||||
current blueprint (if any). Then calls :attr:`before_request_funcs`
|
||||
registered with the app and the blueprint.
|
||||
|
||||
If any :meth:`before_request` handler 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.
|
||||
"""
|
||||
|
||||
bp = _request_ctx_stack.top.request.blueprint
|
||||
|
||||
funcs = self.url_value_preprocessors.get(None, ())
|
||||
|
|
@ -1981,14 +2054,17 @@ class Flask(_PackageBoundObject):
|
|||
exception context to start the response
|
||||
"""
|
||||
ctx = self.request_context(environ)
|
||||
ctx.push()
|
||||
error = None
|
||||
try:
|
||||
try:
|
||||
ctx.push()
|
||||
response = self.full_dispatch_request()
|
||||
except Exception as 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)
|
||||
finally:
|
||||
if self.should_ignore_error(error):
|
||||
|
|
|
|||
|
|
@ -89,6 +89,13 @@ class Blueprint(_PackageBoundObject):
|
|||
warn_on_modifications = 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,
|
||||
static_url_path=None, template_folder=None,
|
||||
url_prefix=None, subdomain=None, url_defaults=None,
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue