Browse Source

Merge pull request #1 from pallets/master

update-2018-03-22
pull/2682/head
zhiguan 7 years ago committed by GitHub
parent
commit
d2f8858335
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 23
      .appveyor.yml
  2. 2
      .gitattributes
  3. 0
      .github/ISSUE_TEMPLATE.md
  4. 0
      .github/PULL_REQUEST_TEMPLATE.md
  5. 13
      .gitignore
  6. 33
      .travis.yml
  7. 45
      AUTHORS
  8. 248
      CHANGES.rst
  9. 22
      CONTRIBUTING.rst
  10. 48
      LICENSE
  11. 2
      MANIFEST.in
  12. 18
      Makefile
  13. 49
      README
  14. 63
      README.rst
  15. BIN
      docs/_static/pycharm-runconfig.png
  16. 8
      docs/advanced_foreword.rst
  17. 25
      docs/api.rst
  18. 2
      docs/changelog.rst
  19. 107
      docs/cli.rst
  20. 16
      docs/conf.py
  21. 113
      docs/config.rst
  22. 1
      docs/contents.rst.inc
  23. 2
      docs/deploying/fastcgi.rst
  24. 5
      docs/deploying/index.rst
  25. 2
      docs/deploying/wsgi-standalone.rst
  26. 4
      docs/errorhandling.rst
  27. 82
      docs/extensiondev.rst
  28. 71
      docs/extensions.rst
  29. 29
      docs/index.rst
  30. 4
      docs/installation.rst
  31. 16
      docs/patterns/appfactories.rst
  32. 2
      docs/patterns/fileuploads.rst
  33. 6
      docs/patterns/packages.rst
  34. 2
      docs/patterns/sqlite3.rst
  35. 23
      docs/python3.rst
  36. 41
      docs/quickstart.rst
  37. 16
      docs/security.rst
  38. 20
      docs/server.rst
  39. 57
      docs/testing.rst
  40. 6
      docs/tutorial/packaging.rst
  41. 9
      examples/blueprintexample/blueprintexample.py
  42. 6
      examples/blueprintexample/test_blueprintexample.py
  43. 2
      examples/flaskr/flaskr/blueprints/flaskr.py
  44. 2
      examples/flaskr/flaskr/factory.py
  45. 2
      examples/flaskr/setup.py
  46. 26
      examples/flaskr/tests/test_flaskr.py
  47. 3
      examples/jqueryexample/jqueryexample.py
  48. 2
      examples/minitwit/minitwit/minitwit.py
  49. 3
      examples/minitwit/tests/test_minitwit.py
  50. 9
      examples/patterns/largerapp/tests/test_largerapp.py
  51. 9
      examples/patterns/largerapp/yourapplication/__init__.py
  52. 9
      examples/patterns/largerapp/yourapplication/views.py
  53. 2
      flask/__init__.py
  54. 3
      flask/__main__.py
  55. 3
      flask/_compat.py
  56. 170
      flask/app.py
  57. 5
      flask/blueprints.py
  58. 214
      flask/cli.py
  59. 6
      flask/config.py
  60. 2
      flask/ctx.py
  61. 3
      flask/debughelpers.py
  62. 2
      flask/globals.py
  63. 48
      flask/helpers.py
  64. 10
      flask/json/__init__.py
  65. 3
      flask/json/tag.py
  66. 9
      flask/logging.py
  67. 77
      flask/sessions.py
  68. 3
      flask/signals.py
  69. 3
      flask/templating.py
  70. 42
      flask/testing.py
  71. 3
      flask/views.py
  72. 3
      flask/wrappers.py
  73. 309
      scripts/flaskext_tester.py
  74. 84
      scripts/make-release.py
  75. 2
      setup.cfg
  76. 97
      setup.py
  77. 3
      test-requirements.txt
  78. 47
      tests/conftest.py
  79. 4
      tests/test_appctx.py
  80. 77
      tests/test_basic.py
  81. 17
      tests/test_blueprints.py
  82. 98
      tests/test_cli.py
  83. 3
      tests/test_config.py
  84. 46
      tests/test_helpers.py
  85. 3
      tests/test_instance_config.py
  86. 9
      tests/test_json_tag.py
  87. 18
      tests/test_logging.py
  88. 2
      tests/test_regression.py
  89. 2
      tests/test_reqctx.py
  90. 2
      tests/test_signals.py
  91. 2
      tests/test_subclassing.py
  92. 2
      tests/test_templating.py
  93. 58
      tests/test_testing.py
  94. 8
      tests/test_user_error_handler.py
  95. 2
      tests/test_views.py
  96. 22
      tox.ini

23
.appveyor.yml

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

2
.gitattributes vendored

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

0
.github/ISSUE_TEMPLATE.rst → .github/ISSUE_TEMPLATE.md

0
.github/PULL_REQUEST_TEMPLATE.rst → .github/PULL_REQUEST_TEMPLATE.md

13
.gitignore vendored

@ -3,19 +3,20 @@
.flaskenv
*.pyc
*.pyo
env
env/
env*
dist
build
dist/
build/
*.egg
*.egg-info
*.egg-info/
_mailinglist
.tox
.tox/
.cache/
.pytest_cache/
.idea/
# Coverage reports
htmlcov
htmlcov/
.coverage
.coverage.*
*,cover

33
.travis.yml

@ -1,28 +1,41 @@
os: linux
sudo: false
language: python
matrix:
include:
- python: 3.6
env: TOXENV=py,codecov
env: TOXENV=py,simplejson,devel,lowest,codecov
- python: 3.6
env: TOXENV=docs-html
- python: 3.5
env: TOXENV=py,codecov
- python: 3.4
env: TOXENV=py,codecov
- python: 3.3
env: TOXENV=py,codecov
- python: 2.7
env: TOXENV=py,codecov
- python: 2.6
env: TOXENV=py,codecov
env: TOXENV=py,simplejson,devel,lowest,codecov
- python: pypy
env: TOXENV=py,codecov
- python: nightly
env: TOXENV=py
- python: 3.6
env: TOXENV=docs-html
- python: 3.6
env: TOXENV=py-simplejson,codecov
- os: osx
language: generic
env: TOXENV=py
allow_failures:
- python: nightly
env: TOXENV=py
- os: osx
language: generic
env: TOXENV=py
fast_finish: true
before_install:
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
brew update;
brew install python3 redis memcached;
virtualenv -p python3 ~/py-env;
. ~/py-env/bin/activate;
fi
install:
- pip install tox

45
AUTHORS

@ -1,39 +1,12 @@
Flask is written and maintained by Armin Ronacher and
various contributors:
Flask is developed and maintained by the Pallets team and community
contributors. It was created by Armin Ronacher. The core maintainers
are:
Development Lead
````````````````
- David Lord (davidism)
- Adrian Mönnich (ThiefMaster)
- Armin Ronacher (mitsuhiko)
- Marcus Unterwaditzer (untitaker)
- Armin Ronacher <armin.ronacher@active-4.com>
A full list of contributors is available from git with::
Patches and Suggestions
```````````````````````
- Adam Byrtek
- Adam Zapletal
- Ali Afshar
- Chris Edgemon
- Chris Grindstaff
- Christopher Grebs
- Daniel Neuhäuser
- Dan Sully
- David Lord @davidism
- Edmond Burnett
- Florent Xicluna
- Georg Brandl
- Jeff Widman @jeffwidman
- Joshua Bronson @jab
- Justin Quick
- Kenneth Reitz
- Keyan Pishdadian
- Marian Sigler
- Martijn Pieters
- Matt Campell
- Matthew Frazier
- Michael van Tellingen
- Ron DuPlain
- Sebastien Estienne
- Simon Sapin
- Stephane Wirtel
- Thomas Schranz
- Zhao Xiaohong
git shortlog -sne

248
CHANGES → CHANGES.rst

@ -1,121 +1,155 @@
.. currentmodule:: flask
Flask Changelog
===============
Here you can see the full list of changes between each Flask release.
Version 0.13
------------
Version 1.0
-----------
Major release, unreleased
- Minimum Werkzeug version bumped to 0.9, but please use the latest version.
- Minimum Click version bumped to 4, but please use the latest version.
- Make ``app.run()`` into a noop if a Flask application is run from the
development server on the command line. This avoids some behavior that
was confusing to debug for newcomers.
- Change default configuration ``JSONIFY_PRETTYPRINT_REGULAR=False``.
``jsonify()`` method returns compressed response by default, and pretty
response in debug mode. (`#2193`_)
- Change ``Flask.__init__`` to accept two new keyword arguments,
``host_matching`` and ``static_host``. This enables ``host_matching`` to be
set properly by the time the constructor adds the static route, and enables
the static route to be properly associated with the required host.
(``#1559``)
- ``send_file`` supports Unicode in ``attachment_filename``. (`#2223`_)
- Pass ``_scheme`` argument from ``url_for`` to ``handle_build_error``.
(`#2017`_)
- Add support for ``provide_automatic_options`` in ``add_url_rule`` to disable
adding OPTIONS method when the ``view_func`` argument is not a class.
(`#1489`_).
- ``MethodView`` can inherit method handlers from base classes. (`#1936`_)
- Errors caused while opening the session at the beginning of the request are
handled by the app's error handlers. (`#2254`_)
- Blueprints gained ``json_encoder`` and ``json_decoder`` attributes to
override the app's encoder and decoder. (`#1898`_)
- ``Flask.make_response`` raises ``TypeError`` instead of ``ValueError`` for
bad response types. The error messages have been improved to describe why the
type is invalid. (`#2256`_)
- Add ``routes`` CLI command to output routes registered on the application.
(`#2259`_)
unreleased
- **Python 2.6 and 3.3 are no longer supported.** (`pallets/meta#24`_)
- Bump minimum dependency versions to the latest stable versions:
Werkzeug >= 0.14, Jinja >= 2.10, itsdangerous >= 0.24, Click >= 5.1.
(`#2586`_)
- Skip :meth:`app.run <Flask.run>` when a Flask application is run from
the command line. This avoids some behavior that was confusing to
debug.
- Change the default for :data:`JSONIFY_PRETTYPRINT_REGULAR` to
``False``. :func:`~json.jsonify` returns a compact format by default,
and an indented format in debug mode. (`#2193`_)
- :meth:`Flask.__init__ <Flask>` accepts the ``host_matching`` argument
and sets it on :attr:`~Flask.url_map`. (`#1559`_)
- :meth:`Flask.__init__ <Flask>` accepts the ``static_host`` argument
and passes it as the ``host`` argument when defining the static route.
(`#1559`_)
- :func:`send_file` supports Unicode in ``attachment_filename``.
(`#2223`_)
- Pass ``_scheme`` argument from :func:`url_for` to
:meth:`~Flask.handle_url_build_error`. (`#2017`_)
- :meth:`~Flask.add_url_rule` accepts the ``provide_automatic_options``
argument to disable adding the ``OPTIONS`` method. (`#1489`_)
- :class:`~views.MethodView` subclasses 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 :attr:`~Blueprint.json_encoder` and
:attr:`~Blueprint.json_decoder` attributes to override the app's
encoder and decoder. (`#1898`_)
- :meth:`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`_)
address, as these may not behave properly in some browsers, such as
Chrome. (`#2282`_)
- Allow IP address as exact session cookie domain. (`#2282`_)
- ``SESSION_COOKIE_DOMAIN`` is set if it is detected through ``SERVER_NAME``.
(`#2282`_)
- Auto-detect zero-argument app factory called ``create_app`` or ``make_app``
from ``FLASK_APP``. (`#2297`_)
- Factory functions are not required to take a ``script_info`` parameter to
work with the ``flask`` command. If they take a single parameter or a
parameter named ``script_info``, the ``ScriptInfo`` object will be passed.
(`#2319`_)
- FLASK_APP=myproject.app:create_app('dev') support.
- ``FLASK_APP`` can be set to an app factory, with arguments if needed, for
example ``FLASK_APP=myproject.app:create_app('dev')``. (`#2326`_)
- ``FLASK_APP`` can point to local packages that are not installed in dev mode,
although `pip install -e` should still be preferred. (`#2414`_)
- ``View.provide_automatic_options = True`` is set on the view function from
``View.as_view``, to be detected in ``app.add_url_rule``. (`#2316`_)
- ``SESSION_COOKIE_DOMAIN`` is set if it is detected through
``SERVER_NAME``. (`#2282`_)
- Auto-detect zero-argument app factory called ``create_app`` or
``make_app`` from ``FLASK_APP``. (`#2297`_)
- Factory functions are not required to take a ``script_info`` parameter
to work with the ``flask`` command. If they take a single parameter or
a parameter named ``script_info``, the :class:`~cli.ScriptInfo` object
will be passed. (`#2319`_)
- ``FLASK_APP`` can be set to an app factory, with arguments if needed,
for example ``FLASK_APP=myproject.app:create_app('dev')``. (`#2326`_)
- ``FLASK_APP`` can point to local packages that are not installed in
editable mode, although ``pip install -e`` is still preferred.
(`#2414`_)
- The :class:`~views.View` class attribute
:attr:`~views.View.provide_automatic_options` is set in
:meth:`~views.View.as_view`, to be detected by
:meth:`~Flask.add_url_rule`. (`#2316`_)
- Error handling will try handlers registered for ``blueprint, code``,
``app, code``, ``blueprint, exception``, ``app, exception``. (`#2314`_)
- ``Cookie`` is added to the response's ``Vary`` header if the session is
accessed at all during the request (and it wasn't deleted). (`#2288`_)
- ``app.test_request_context()`` take ``subdomain`` and ``url_scheme``
parameters for use when building base URL. (`#1621`_)
- Set ``APPLICATION_ROOT = '/'`` by default. This was already the implicit
default when it was set to ``None``.
- ``TRAP_BAD_REQUEST_ERRORS`` is enabled by default in debug mode.
``BadRequestKeyError`` has a message with the bad key in debug mode instead
of the generic bad request message. (`#2348`_)
- Allow registering new tags with ``TaggedJSONSerializer`` to support
storing other types in the session cookie. (`#2352`_)
- Only open the session if the request has not been pushed onto the context
stack yet. This allows ``stream_with_context`` generators to access the same
session that the containing view uses. (`#2354`_)
- Add ``json`` keyword argument for the test client request methods. This will
dump the given object as JSON and set the appropriate content type.
(`#2358`_)
- Extract JSON handling to a mixin applied to both the request and response
classes used by Flask. This adds the ``is_json`` and ``get_json`` methods to
the response to make testing JSON response much easier. (`#2358`_)
- Removed error handler caching because it caused unexpected results for some
exception inheritance hierarchies. Register handlers explicitly for each
exception if you don't want to traverse the MRO. (`#2362`_)
``app, code``, ``blueprint, exception``, ``app, exception``.
(`#2314`_)
- ``Cookie`` is added to the response's ``Vary`` header if the session
is accessed at all during the request (and not deleted). (`#2288`_)
- :meth:`~Flask.test_request_context` accepts ``subdomain`` and
``url_scheme`` arguments for use when building the base URL.
(`#1621`_)
- Set :data:`APPLICATION_ROOT` to ``'/'`` by default. This was already
the implicit default when it was set to ``None``.
- :data:`TRAP_BAD_REQUEST_ERRORS` is enabled by default in debug mode.
``BadRequestKeyError`` has a message with the bad key in debug mode
instead of the generic bad request message. (`#2348`_)
- Allow registering new tags with
:class:`~json.tag.TaggedJSONSerializer` to support storing other types
in the session cookie. (`#2352`_)
- Only open the session if the request has not been pushed onto the
context stack yet. This allows :func:`~stream_with_context`
generators to access the same session that the containing view uses.
(`#2354`_)
- Add ``json`` keyword argument for the test client request methods.
This will dump the given object as JSON and set the appropriate
content type. (`#2358`_)
- Extract JSON handling to a mixin applied to both the :class:`Request`
and :class:`Response` classes. This adds the :meth:`~Response.is_json`
and :meth:`~Response.get_json` methods to the response to make testing
JSON response much easier. (`#2358`_)
- Removed error handler caching because it caused unexpected results for
some exception inheritance hierarchies. Register handlers explicitly
for each exception if you want to avoid traversing the MRO. (`#2362`_)
- Fix incorrect JSON encoding of aware, non-UTC datetimes. (`#2374`_)
- Template auto reloading will honor the ``run`` command's ``debug`` flag even
if ``app.jinja_env`` was already accessed. (`#2373`_)
- Template auto reloading will honor debug mode even even if
:attr:`~Flask.jinja_env` was already accessed. (`#2373`_)
- The following old deprecated code was removed. (`#2385`_)
- ``flask.ext`` - import extensions directly by their name instead of
through the ``flask.ext`` namespace. For example,
``import flask.ext.sqlalchemy`` becomes ``import flask_sqlalchemy``.
- ``Flask.init_jinja_globals`` - extend ``Flask.create_jinja_environment``
instead.
- ``Flask.error_handlers`` - tracked by ``Flask.error_handler_spec``,
use ``@app.errorhandler`` to register handlers.
- ``Flask.request_globals_class`` - use ``Flask.app_ctx_globals_class``
instead.
- ``Flask.static_path`` - use ``Flask.static_url_path`` instead.
- ``Request.module`` - use ``Request.blueprint`` instead.
- The ``request.json`` property is no longer deprecated. (`#1421`_)
- Support passing an existing ``EnvironBuilder`` or ``dict`` to
``test_client.open``. (`#2412`_)
- The ``flask`` command and ``app.run`` will load environment variables using
from ``.env`` and ``.flaskenv`` files if python-dotenv is installed.
(`#2416`_)
- When passing a full URL to the test client, use the scheme in the URL instead
of the ``PREFERRED_URL_SCHEME``. (`#2430`_)
- ``app.logger`` has been simplified. ``LOGGER_NAME`` and
``LOGGER_HANDLER_POLICY`` config was removed. The logger is always named
``flask.app``. The level is only set on first access, it doesn't check
``app.debug`` each time. Only one format is used, not different ones
depending on ``app.debug``. No handlers are removed, and a handler is only
added if no handlers are already configured. (`#2436`_)
- Blueprint view function name may not contain dots. (`#2450`_)
- ``Flask.init_jinja_globals`` - extend
:meth:`Flask.create_jinja_environment` instead.
- ``Flask.error_handlers`` - tracked by
:attr:`Flask.error_handler_spec`, use :meth:`Flask.errorhandler` to
register handlers.
- ``Flask.request_globals_class`` - use
:attr:`Flask.app_ctx_globals_class` instead.
- ``Flask.static_path`` - use :attr:`Flask.static_url_path` instead.
- ``Request.module`` - use :attr:`Request.blueprint` instead.
- The :attr:`Request.json` property is no longer deprecated. (`#1421`_)
- Support passing a :class:`~werkzeug.test.EnvironBuilder` or
``dict`` to :meth:`test_client.open <werkzeug.test.Client.open>`.
(`#2412`_)
- The ``flask`` command and :meth:`Flask.run` will load environment
variables from ``.env`` and ``.flaskenv`` files if python-dotenv is
installed. (`#2416`_)
- When passing a full URL to the test client, the scheme in the URL is
used instead of :data:`PREFERRED_URL_SCHEME`. (`#2430`_)
- :attr:`Flask.logger` has been simplified. ``LOGGER_NAME`` and
``LOGGER_HANDLER_POLICY`` config was removed. The logger is always
named ``flask.app``. The level is only set on first access, it doesn't
check :attr:`Flask.debug` each time. Only one format is used, not
different ones depending on :attr:`Flask.debug`. No handlers are
removed, and a handler is only added if no handlers are already
configured. (`#2436`_)
- Blueprint view function names may not contain dots. (`#2450`_)
- Fix a ``ValueError`` caused by invalid ``Range`` requests in some
cases. (`#2526`_)
- The development server uses threads by default. (`#2529`_)
- Loading config files with ``silent=True`` will ignore
:data:`~errno.ENOTDIR` errors. (`#2581`_)
- Pass ``--cert`` and ``--key`` options to ``flask run`` to run the
development server over HTTPS. (`#2606`_)
- Added :data:`SESSION_COOKIE_SAMESITE` to control the ``SameSite``
attribute on the session cookie. (`#2607`_)
- Added :meth:`~flask.Flask.test_cli_runner` to create a Click runner
that can invoke Flask CLI commands for testing. (`#2636`_)
- Subdomain matching is disabled by default and setting
:data:`SERVER_NAME` does not implicily enable it. It can be enabled by
passing ``subdomain_matching=True`` to the ``Flask`` constructor.
(`#2635`_)
- A single trailing slash is stripped from the blueprint ``url_prefix``
when it is registered with the app. (`#2629`_)
.. _pallets/meta#24: https://github.com/pallets/meta/issues/24
.. _#1421: https://github.com/pallets/flask/issues/1421
.. _#1489: https://github.com/pallets/flask/pull/1489
.. _#1559: https://github.com/pallets/flask/issues/1559
.. _#1621: https://github.com/pallets/flask/pull/1621
.. _#1898: https://github.com/pallets/flask/pull/1898
.. _#1936: https://github.com/pallets/flask/pull/1936
@ -146,6 +180,16 @@ Major release, unreleased
.. _#2430: https://github.com/pallets/flask/pull/2430
.. _#2436: https://github.com/pallets/flask/pull/2436
.. _#2450: https://github.com/pallets/flask/pull/2450
.. _#2526: https://github.com/pallets/flask/issues/2526
.. _#2529: https://github.com/pallets/flask/pull/2529
.. _#2586: https://github.com/pallets/flask/issues/2586
.. _#2581: https://github.com/pallets/flask/pull/2581
.. _#2606: https://github.com/pallets/flask/pull/2606
.. _#2607: https://github.com/pallets/flask/pull/2607
.. _#2636: https://github.com/pallets/flask/pull/2636
.. _#2635: https://github.com/pallets/flask/pull/2635
.. _#2629: https://github.com/pallets/flask/pull/2629
Version 0.12.2
--------------

22
CONTRIBUTING.rst

@ -109,8 +109,8 @@ depends on which part of Flask you're working on. Travis-CI will run the full
suite when you submit your pull request.
The full test suite takes a long time to run because it tests multiple
combinations of Python and dependencies. You need to have Python 2.6, 2.7, 3.3,
3.4, 3.5 3.6, and PyPy 2.7 installed to run all of the environments. Then run::
combinations of Python and dependencies. You need to have Python 2.7, 3.4,
3.5 3.6, and PyPy 2.7 installed to run all of the environments. Then run::
tox
@ -131,8 +131,22 @@ Read more about `coverage <https://coverage.readthedocs.io>`_.
Running the full test suite with ``tox`` will combine the coverage reports
from all runs.
``make`` targets
~~~~~~~~~~~~~~~~
Building the docs
~~~~~~~~~~~~~~~~~
Build the docs in the ``docs`` directory using Sphinx::
cd docs
make html
Open ``_build/html/index.html`` in your browser to view the docs.
Read more about `Sphinx <http://www.sphinx-doc.org>`_.
make targets
~~~~~~~~~~~~
Flask provides a ``Makefile`` with various shortcuts. They will ensure that
all dependencies are installed.

48
LICENSE

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

2
MANIFEST.in

@ -1,4 +1,4 @@
include Makefile CHANGES LICENSE AUTHORS
include Makefile CHANGES.rst LICENSE AUTHORS tox.ini
graft artwork
graft tests

18
Makefile

@ -9,7 +9,6 @@ test: clean-pyc install-dev
pytest
coverage: clean-pyc install-dev
pip install -q -e .[test]
coverage run -m pytest
coverage report
coverage html
@ -34,20 +33,3 @@ clean-pyc:
find . -name '*.pyc' -exec rm -f {} +
find . -name '*.pyo' -exec rm -f {} +
find . -name '*~' -exec rm -f {} +
upload-docs:
$(MAKE) -C docs html dirhtml latex epub
$(MAKE) -C docs/_build/latex all-pdf
cd docs/_build/; mv html flask-docs; zip -r flask-docs.zip flask-docs; mv flask-docs html
rsync -a docs/_build/dirhtml/ flow.srv.pocoo.org:/srv/websites/flask.pocoo.org/docs/
rsync -a docs/_build/latex/Flask.pdf flow.srv.pocoo.org:/srv/websites/flask.pocoo.org/docs/flask-docs.pdf
rsync -a docs/_build/flask-docs.zip flow.srv.pocoo.org:/srv/websites/flask.pocoo.org/docs/flask-docs.zip
rsync -a docs/_build/epub/Flask.epub flow.srv.pocoo.org:/srv/websites/flask.pocoo.org/docs/flask-docs.epub
# ebook-convert docs: http://manual.calibre-ebook.com/cli/ebook-convert.html
ebook:
@echo 'Using .epub from `make upload-docs` to create .mobi.'
@echo 'Command `ebook-covert` is provided by calibre package.'
@echo 'Requires X-forwarding for Qt features used in conversion (ssh -X).'
@echo 'Do not mind "Invalid value for ..." CSS errors if .mobi renders.'
ssh -X pocoo.org ebook-convert /var/www/flask.pocoo.org/docs/flask-docs.epub /var/www/flask.pocoo.org/docs/flask-docs.mobi --cover http://flask.pocoo.org/docs/_images/logo-full.png --authors 'Armin Ronacher'

49
README

@ -1,49 +0,0 @@
// Flask //
web development, one drop at a time
~ What is Flask?
Flask is a microframework for Python based on Werkzeug
and Jinja2. It's intended for getting started very quickly
and was developed with best intentions in mind.
~ Is it ready?
It's still not 1.0 but it's shaping up nicely and is
already widely used. Consider the API to slightly
improve over time but we don't plan on breaking it.
~ What do I need?
All dependencies are installed by using `pip install Flask`.
We encourage you to use a virtualenv. Check the docs for
complete installation and usage instructions.
~ Where are the docs?
Go to http://flask.pocoo.org/docs/ for a prebuilt version
of the current documentation. Otherwise build them yourself
from the sphinx sources in the docs folder.
~ Where are the tests?
Good that you're asking. The tests are in the
tests/ folder. To run the tests use the
`pytest` testing tool:
$ pytest
Details on contributing can be found in CONTRIBUTING.rst
~ Where can I get help?
Either use the #pocoo IRC channel on irc.freenode.net or
ask on the mailinglist: http://flask.pocoo.org/mailinglist/
See http://flask.pocoo.org/community/ for more resources.

63
README.rst

@ -0,0 +1,63 @@
Flask
=====
Flask is a lightweight `WSGI`_ web application framework. It is designed
to make getting started quick and easy, with the ability to scale up to
complex applications. It began as a simple wrapper around `Werkzeug`_
and `Jinja`_ and has become one of the most popular Python web
application frameworks.
Flask offers suggestions, but doesn't enforce any dependencies or
project layout. It is up to the developer to choose the tools and
libraries they want to use. There are many extensions provided by the
community that make adding new functionality easy.
Installing
----------
Install and update using `pip`_:
.. code-block:: text
pip install -U Flask
A Simple Example
----------------
.. code-block:: python
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return 'Hello, World!'
.. code-block:: none
$ FLASK_APP=hello.py flask run
* Running on http://localhost:5000/
Links
-----
* Website: https://www.palletsprojects.com/p/flask/
* Documentation: http://flask.pocoo.org/docs/
* Releases: https://pypi.org/project/Flask/
* Code: https://github.com/pallets/flask
* Issue tracker: https://github.com/pallets/flask/issues
* Test status:
* Linux, Mac: https://travis-ci.org/pallets/flask
* Windows: https://ci.appveyor.com/project/pallets/flask
* Test coverage: https://codecov.io/gh/pallets/flask
.. _WSGI: https://wsgi.readthedocs.io
.. _Werkzeug: https://www.palletsprojects.com/p/werkzeug/
.. _Jinja: https://www.palletsprojects.com/p/jinja/
.. _pip: https://pip.pypa.io/en/stable/quickstart/

BIN
docs/_static/pycharm-runconfig.png vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

After

Width:  |  Height:  |  Size: 17 KiB

8
docs/advanced_foreword.rst

@ -45,11 +45,3 @@ spam, links to malicious software, and the like.
Flask is no different from any other framework in that you the developer must
build with caution, watching for exploits when building to your requirements.
Python 3 Support in Flask
-------------------------
Flask, its dependencies, and most Flask extensions all support Python 3.
If you want to use Flask with Python 3 have a look at the :ref:`python3-support` page.
Continue to :ref:`installation` or the :ref:`quickstart`.

25
docs/api.rst

@ -188,6 +188,15 @@ Test Client
:members:
Test CLI Runner
---------------
.. currentmodule:: flask.testing
.. autoclass:: FlaskCliRunner
:members:
Application Globals
-------------------
@ -361,22 +370,6 @@ Configuration
.. autoclass:: Config
:members:
Extensions
----------
.. data:: flask.ext
This module acts as redirect import module to Flask extensions. It was
added in 0.8 as the canonical way to import Flask extensions and makes
it possible for us to have more flexibility in how we distribute
extensions.
If you want to use an extension named “Flask-Foo” you would import it
from :data:`~flask.ext` as follows::
from flask.ext import foo
.. versionadded:: 0.8
Stream Helpers
--------------

2
docs/changelog.rst

@ -1 +1 @@
.. include:: ../CHANGES
.. include:: ../CHANGES.rst

107
docs/cli.rst

@ -117,23 +117,41 @@ context will be active, and the app instance will be imported. ::
Use :meth:`~Flask.shell_context_processor` to add other automatic imports.
Debug Mode
----------
Environments
------------
.. versionadded:: 1.0
The environment in which the Flask app runs is set by the
:envvar:`FLASK_ENV` environment variable. If not set it defaults to
``production``. The other recognized environment is ``development``.
Flask and extensions may choose to enable behaviors based on the
environment.
Set the :envvar:`FLASK_DEBUG` environment variable to override the
application's :attr:`~Flask.debug` flag. The value ``1`` enables it, ``0``
disables it. Forcing the debug flag on also enables the debugger and reloader
when running the development server. ::
If the env is set to ``development``, the ``flask`` command will enable
debug mode and ``flask run`` will enable the interactive debugger and
reloader.
$ FLASK_DEBUG=1 flask run
::
$ FLASK_ENV=development flask run
* Serving Flask app "hello"
* Forcing debug mode on
* Environment: development
* Debug mode: on
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
* Restarting with inotify reloader
* Debugger is active!
* Debugger PIN: 223-456-919
Debug Mode
----------
Debug mode will be enabled when :envvar:`FLASK_ENV` is ``development``,
as described above. If you want to control debug mode separately, use
:envvar:`FLASK_DEBUG`. The value ``1`` enables it, ``0`` disables it.
.. _dotenv:
Environment Variables From dotenv
@ -176,7 +194,7 @@ Unix Bash, :file:`venv/bin/activate`::
export FLASK_APP=hello
Windows CMD, :file:`venv\Scripts\activate.bat`::
Windows CMD, :file:`venv\\Scripts\\activate.bat`::
set FLASK_APP=hello
@ -229,6 +247,9 @@ group. This is useful if you want to organize multiple related commands. ::
flask user create demo
See :ref:`testing-cli` for an overview of how to test your custom
commands.
Application Context
~~~~~~~~~~~~~~~~~~~
@ -337,47 +358,67 @@ script is available. Note that you don't need to set ``FLASK_APP``. ::
$ pip install -e .
$ wiki run
.. admonition:: Errors in Custom Scripts
When using a custom script, if you introduce an error in your
module-level code, the reloader will fail because it can no longer
load the entry point.
The ``flask`` command, being separate from your code, does not have
this issue and is recommended in most cases.
.. _console script: https://packaging.python.org/tutorials/distributing-packages/#console-scripts
PyCharm Integration
-------------------
The new Flask CLI features aren't yet fully integrated into the PyCharm IDE,
so we have to do a few tweaks to get them working smoothly. These instructions
should be similar for any other IDE you might want to use.
Prior to PyCharm 2018.1, the Flask CLI features weren't yet fully
integrated into PyCharm. We have to do a few tweaks to get them working
smoothly. These instructions should be similar for any other IDE you
might want to use.
In PyCharm, with your project open, click on *Run* from the menu bar and go to
*Edit Configurations*. You'll be greeted by a screen similar to this:
In PyCharm, with your project open, click on *Run* from the menu bar and
go to *Edit Configurations*. You'll be greeted by a screen similar to
this:
.. image:: _static/pycharm-runconfig.png
:align: center
:class: screenshot
:alt: screenshot of pycharm's run configuration settings
There's quite a few options to change, but once we've done it for one command,
we can easily copy the entire configuration and make a single tweak to give us
access to other commands, including any custom ones you may implement yourself.
There's quite a few options to change, but once we've done it for one
command, we can easily copy the entire configuration and make a single
tweak to give us access to other commands, including any custom ones you
may implement yourself.
Click the + (*Add New Configuration*) button and select *Python*. Give
the configuration a good descriptive name such as "Run Flask Server".
For the ``flask run`` command, check "Single instance only" since you
can't run the server more than once at the same time.
Select *Module name* from the dropdown (**A**) then input ``flask``.
For the *Script* input (**A**), navigate to your project's virtual environment.
Within that folder, pick the ``flask`` executable which will reside in the
``bin`` folder, or in the ``Scripts`` on Windows.
The *Parameters* field (**B**) is set to the CLI command to execute
(with any arguments). In this example we use ``run``, which will run
the development server.
The *Script Parameter* field (**B**) is set to the CLI command you to execute.
In this example we use ``run``, which will run the development server.
You can skip this next step if you're using :ref:`dotenv`. We need to
add an environment variable (**C**) to identify our application. Click
on the browse button and add an entry with ``FLASK_APP`` on the left and
the Python import or file on the right (``hello`` for example).
You can skip this next step if you're using :ref:`dotenv`. We need to add an
environment variable (**C**) to identify our application. Click on the browse
button and add an entry with ``FLASK_APP`` on the left and the name of the
Python file or package on the right (``app.py`` for example).
Next we need to set the working directory (**D**) to be the folder where
our application resides.
Next we need to set the working directory (**D**) to be the same folder where
our application file or package resides. PyCharm changed it to the directory
with the ``flask`` executable when we selected it earlier, which is incorrect.
If you have installed your project as a package in your virtualenv, you
may untick the *PYTHONPATH* options (**E**). This will more accurately
match how you deploy the app later.
Finally, untick the *PYTHONPATH* options (**E**) and give the configuration a
good descriptive name, such as "Run Flask Server", and click *Apply*.
Click *Apply* to save the configuration, or *OK* to save and close the
window. Select the configuration in the main PyCharm window and click
the play button next to it to run the server.
Now that we have a configuration which runs ``flask run`` from within PyCharm,
we can simply copy that configuration and alter the *Script* argument
Now that we have a configuration which runs ``flask run`` from within
PyCharm, we can copy that configuration and alter the *Script* argument
to run a different CLI command, e.g. ``flask shell``.

16
docs/conf.py

@ -17,6 +17,8 @@ import pkg_resources
import time
import datetime
from sphinx.application import Sphinx
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,
@ -296,3 +298,17 @@ def unwrap_decorators():
unwrap_decorators()
del unwrap_decorators
def setup(app: Sphinx):
def cut_module_meta(app, what, name, obj, options, lines):
"""Remove metadata from autodoc output."""
if what != 'module':
return
lines[:] = [
line for line in lines
if not line.startswith((':copyright:', ':license:'))
]
app.connect('autodoc-process-docstring', cut_module_meta)

113
docs/config.rst

@ -20,6 +20,7 @@ object. This is the place where Flask itself puts certain configuration
values and also where extensions can put their configuration values. But
this is also where you can have your own configuration.
Configuration Basics
--------------------
@ -27,50 +28,92 @@ The :attr:`~flask.Flask.config` is actually a subclass of a dictionary and
can be modified just like any dictionary::
app = Flask(__name__)
app.config['DEBUG'] = True
app.config['TESTING'] = True
Certain configuration values are also forwarded to the
:attr:`~flask.Flask` object so you can read and write them from there::
app.debug = True
app.testing = True
To update multiple keys at once you can use the :meth:`dict.update`
method::
app.config.update(
DEBUG=True,
TESTING=True,
SECRET_KEY=b'_5#y2L"F4Q8z\n\xec]/'
)
.. 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::
Environment and Debug Features
------------------------------
The :data:`ENV` and :data:`DEBUG` config values are special because they
may behave inconsistently if changed after the app has begun setting up.
In order to set the environment and debug mode reliably, Flask uses
environment variables.
The environment is used to indicate to Flask, extensions, and other
programs, like Sentry, what context Flask is running in. It is
controlled with the :envvar:`FLASK_ENV` environment variable and
defaults to ``production``.
Setting :envvar:`FLASK_ENV` to ``development`` will enable debug mode.
``flask run`` will use the interactive debugger and reloader by default
in debug mode. To control this separately from the environment, use the
:envvar:`FLASK_DEBUG` flag.
.. versionchanged:: 1.0
Added :envvar:`FLASK_ENV` to control the environment separately
from debug mode. The development environment enables debug mode.
To switch Flask to the development environment and enable debug mode,
set :envvar:`FLASK_ENV`::
$ export FLASK_DEBUG=1
$ export FLASK_ENV=development
$ flask run
(On Windows you need to use ``set`` instead of ``export``).
(On Windows, use ``set`` instead of ``export``.)
Using the environment variables as described above is recommended. While
it is possible to set :data:`ENV` and :data:`DEBUG` in your config or
code, this is strongly discouraged. They can't be read early by the
``flask`` command, and some systems or extensions may have already
configured themselves based on a previous value.
``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
----------------------------
The following configuration values are used internally by Flask:
.. py:data:: ENV
What environment the app is running in. Flask and extensions may
enable behaviors based on the environment, such as enabling debug
mode. The :attr:`~flask.Flask.env` attribute maps to this config
key. This is set by the :envvar:`FLASK_ENV` environment variable and
may not behave as expected if set in code.
**Do not enable development when deploying in production.**
Default: ``'production'``
.. versionadded:: 1.0
.. py:data:: DEBUG
Enable debug mode. When using the development server with ``flask run`` or
``app.run``, an interactive debugger will be shown for unhanlded
exceptions, and the server will be reloaded when code changes.
Whether debug mode is enabled. When using ``flask run`` to start the
development server, an interactive debugger will be shown for
unhandled exceptions, and the server will be reloaded when code
changes. The :attr:`~flask.Flask.debug` attribute maps to this
config key. This is enabled when :data:`ENV` is ``'development'``
and is overridden by the ``FLASK_DEBUG`` environment variable. It
may not behave as expected if set in code.
**Do not enable debug mode in production.**
**Do not enable debug mode when deploying in production.**
Default: ``False``
Default: ``True`` if :data:`ENV` is ``'production'``, or ``False``
otherwise.
.. py:data:: TESTING
@ -104,7 +147,7 @@ The following configuration values are used internally by Flask:
Default: ``False``
.. py:data:: TRAP_BAD_REQUEST_ERRORS``
.. py:data:: TRAP_BAD_REQUEST_ERRORS
Trying to access a key that doesn't exist from request dicts like ``args``
and ``form`` will return a 400 Bad Request error page. Enable this to treat
@ -138,8 +181,8 @@ The following configuration values are used internally by Flask:
.. py:data:: SESSION_COOKIE_DOMAIN
The domain match rule that the session cookie will be valid for. If not
set, the cookie will be valid for all subdomains of ``SERVER_NAME``. If
``False``, the cookie's domain will not be set.
set, the cookie will be valid for all subdomains of :data:`SERVER_NAME`.
If ``False``, the cookie's domain will not be set.
Default: ``None``
@ -165,6 +208,16 @@ The following configuration values are used internally by Flask:
Default: ``False``
.. py:data:: SESSION_COOKIE_SAMESITE
Restrict how cookies are sent with requests from external sites. Can
be set to ``'Lax'`` (recommended) or ``'Strict'``.
See :ref:`security-cookie`.
Default: ``None``
.. versionadded:: 1.0
.. py:data:: PERMANENT_SESSION_LIFETIME
If ``session.permanent`` is true, the cookie's expiration will be set this
@ -204,13 +257,14 @@ The following configuration values are used internally by Flask:
.. py:data:: SERVER_NAME
Inform the application what host and port it is bound to. Required for
subdomain route matching support.
Inform the application what host and port it is bound to. Required
for subdomain route matching support.
If set, will be used for the session cookie domain if
``SESSION_COOKIE_DOMAIN`` is not set. Modern web browsers will not allow
setting cookies for domains without a dot. To use a domain locally,
add any names that should route to the app to your ``hosts`` file. ::
:data:`SESSION_COOKIE_DOMAIN` is not set. Modern web browsers will
not allow setting cookies for domains without a dot. To use a domain
locally, add any names that should route to the app to your
``hosts`` file. ::
127.0.0.1 localhost.dev
@ -318,10 +372,16 @@ The following configuration values are used internally by Flask:
``LOGGER_HANDLER_POLICY``, ``EXPLAIN_TEMPLATE_LOADING``
.. versionchanged:: 1.0
``LOGGER_NAME`` and ``LOGGER_HANDLER_POLICY`` were removed. See
:ref:`logging` for information about configuration.
Added :data:`ENV` to reflect the :envvar:`FLASK_ENV` environment
variable.
Added :data:`SESSION_COOKIE_SAMESITE` to control the session
cookie's ``SameSite`` option.
Configuring from Files
----------------------
@ -588,4 +648,3 @@ Example usage for both::
# or via open_instance_resource:
with app.open_instance_resource('application.cfg') as f:
config = f.read()

1
docs/contents.rst.inc

@ -56,7 +56,6 @@ Design notes, legal information and changelog are here for the interested.
unicode
extensiondev
styleguide
python3
upgrading
changelog
license

2
docs/deploying/fastcgi.rst

@ -111,7 +111,7 @@ Set yourapplication.fcgi::
#!/usr/bin/python
#: optional path to your local python site-packages folder
import sys
sys.path.insert(0, '<your_local_path>/lib/python2.6/site-packages')
sys.path.insert(0, '<your_local_path>/lib/python<your_python_version>/site-packages')
from flup.server.fcgi import WSGIServer
from yourapplication import app

5
docs/deploying/index.rst

@ -4,9 +4,8 @@ Deployment Options
==================
While lightweight and easy to use, **Flask's built-in server is not suitable
for production** as it doesn't scale well and by default serves only one
request at a time. Some of the options available for properly running Flask in
production are documented here.
for production** as it doesn't scale well. Some of the options available for
properly running Flask in production are documented here.
If you want to deploy your Flask application to a WSGI server not listed here,
look up the server documentation about how to use a WSGI app with it. Just

2
docs/deploying/wsgi-standalone.rst

@ -78,7 +78,7 @@ as well; see ``twistd -h`` and ``twistd web -h`` for more information. For
example, to run a Twisted Web server in the foreground, on port 8080, with an
application from ``myproject``::
twistd -n web --port 8080 --wsgi myproject.app
twistd -n web --port tcp:8080 --wsgi myproject.app
.. _Twisted: https://twistedmatrix.com/
.. _Twisted Web: https://twistedmatrix.com/trac/wiki/TwistedWeb

4
docs/errorhandling.rst

@ -42,9 +42,9 @@ aggregates duplicate errors, captures the full stack trace and local
variables for debugging, and sends you mails based on new errors or
frequency thresholds.
To use Sentry you need to install the `raven` client::
To use Sentry you need to install the `raven` client with extra `flask` dependencies::
$ pip install raven
$ pip install raven[flask]
And then add this to your Flask app::

82
docs/extensiondev.rst

@ -159,19 +159,10 @@ The Extension Code
Here's the contents of the `flask_sqlite3.py` for copy/paste::
import sqlite3
from flask import current_app
# Find the stack on which we want to store the database connection.
# Starting with Flask 0.9, the _app_ctx_stack is the correct one,
# before that we need to use the _request_ctx_stack.
try:
from flask import _app_ctx_stack as stack
except ImportError:
from flask import _request_ctx_stack as stack
from flask import current_app, _app_ctx_stack
class SQLite3(object):
def __init__(self, app=None):
self.app = app
if app is not None:
@ -179,24 +170,19 @@ Here's the contents of the `flask_sqlite3.py` for copy/paste::
def init_app(self, app):
app.config.setdefault('SQLITE3_DATABASE', ':memory:')
# Use the newstyle teardown_appcontext if it's available,
# otherwise fall back to the request context
if hasattr(app, 'teardown_appcontext'):
app.teardown_appcontext(self.teardown)
else:
app.teardown_request(self.teardown)
def connect(self):
return sqlite3.connect(current_app.config['SQLITE3_DATABASE'])
def teardown(self, exception):
ctx = stack.top
ctx = _app_ctx_stack.top
if hasattr(ctx, 'sqlite3_db'):
ctx.sqlite3_db.close()
@property
def connection(self):
ctx = stack.top
ctx = _app_ctx_stack.top
if ctx is not None:
if not hasattr(ctx, 'sqlite3_db'):
ctx.sqlite3_db = self.connect()
@ -212,9 +198,7 @@ So here's what these lines of code do:
factory pattern for creating applications. The ``init_app`` will set the
configuration for the database, defaulting to an in memory database if
no configuration is supplied. In addition, the ``init_app`` method attaches
the ``teardown`` handler. It will try to use the newstyle app context
handler and if it does not exist, falls back to the request context
one.
the ``teardown`` handler.
3. Next, we define a ``connect`` method that opens a database connection.
4. Finally, we add a ``connection`` property that on first access opens
the database connection and stores it on the context. This is also
@ -224,9 +208,7 @@ So here's what these lines of code do:
Note here that we're attaching our database connection to the top
application context via ``_app_ctx_stack.top``. Extensions should use
the top context for storing their own information with a sufficiently
complex name. Note that we're falling back to the
``_request_ctx_stack.top`` if the application is using an older
version of Flask that does not support it.
complex name.
So why did we decide on a class-based approach here? Because using our
extension looks something like this::
@ -245,9 +227,8 @@ You can then use the database from views like this::
cur = db.connection.cursor()
cur.execute(...)
Likewise if you are outside of a request but you are using Flask 0.9 or
later with the app context support, you can use the database in the same
way::
Likewise if you are outside of a request you can use the database by
pushing an app context::
with app.app_context():
cur = db.connection.cursor()
@ -291,34 +272,6 @@ teardown of a request, the ``sqlite3_db`` connection is closed. By using
this pattern, the *same* connection to the sqlite3 database is accessible
to anything that needs it for the duration of the request.
If the :data:`~flask._app_ctx_stack` does not exist because the user uses
an old version of Flask, it is recommended to fall back to
:data:`~flask._request_ctx_stack` which is bound to a request.
Teardown Behavior
-----------------
*This is only relevant if you want to support Flask 0.6 and older*
Due to the change in Flask 0.7 regarding functions that are run at the end
of the request your extension will have to be extra careful there if it
wants to continue to support older versions of Flask. The following
pattern is a good way to support both::
def close_connection(response):
ctx = _request_ctx_stack.top
ctx.sqlite3_db.close()
return response
if hasattr(app, 'teardown_request'):
app.teardown_request(close_connection)
else:
app.after_request(close_connection)
Strictly speaking the above code is wrong, because teardown functions are
passed the exception and typically don't return anything. However because
the return value is discarded this will just work assuming that the code
in between does not touch the passed parameter.
Learn from Others
-----------------
@ -383,26 +336,7 @@ extension to be approved you have to follow these guidelines:
(``PackageName==dev``).
9. The ``zip_safe`` flag in the setup script must be set to ``False``,
even if the extension would be safe for zipping.
10. An extension currently has to support Python 2.7, Python 3.3 and higher.
Extension Import Transition
---------------------------
In early versions of Flask we recommended using namespace packages for Flask
extensions, of the form ``flaskext.foo``. This turned out to be problematic in
practice because it meant that multiple ``flaskext`` packages coexist.
Consequently we have recommended to name extensions ``flask_foo`` over
``flaskext.foo`` for a long time.
Flask 0.8 introduced a redirect import system as a compatibility aid for app
developers: Importing ``flask.ext.foo`` would try ``flask_foo`` and
``flaskext.foo`` in that order.
As of Flask 0.11, most Flask extensions have transitioned to the new naming
schema. The ``flask.ext.foo`` compatibility alias is still in Flask 0.11 but is
now deprecated -- you should use ``flask_foo``.
10. An extension currently has to support Python 3.4 and newer and 2.7.
.. _OAuth extension: https://pythonhosted.org/Flask-OAuth/

71
docs/extensions.rst

@ -1,58 +1,53 @@
.. _extensions:
Flask Extensions
================
Extensions
==========
Extensions are extra packages that add functionality to a Flask
application. For example, an extension might add support for sending
email or connecting to a database. Some extensions add entire new
frameworks to help build certain types of applications, like a ReST API.
Flask extensions extend the functionality of Flask in various different
ways. For instance they add support for databases and other common tasks.
Finding Extensions
------------------
Flask extensions are listed on the `Flask Extension Registry`_ and can be
downloaded with :command:`easy_install` or :command:`pip`. If you add a Flask extension
as dependency to your :file:`requirements.txt` or :file:`setup.py` file they are
usually installed with a simple command or when your application installs.
Flask extensions are usually named "Flask-Foo" or "Foo-Flask". Many
extensions are listed in the `Extension Registry`_, which can be updated
by extension developers. You can also search PyPI for packages tagged
with `Framework :: Flask <pypi_>`_.
Using Extensions
----------------
Extensions typically have documentation that goes along that shows how to
use it. There are no general rules in how extensions are supposed to
behave but they are imported from common locations. If you have an
extension called ``Flask-Foo`` or ``Foo-Flask`` it should be always
importable from ``flask_foo``::
Consult each extension's documentation for installation, configuration,
and usage instructions. Generally, extensions pull their own
configuration from :attr:`app.config <flask.Flask.config>` and are
passed an application instance during initialization. For example,
an extension caled "Flask-Foo" might be used like this::
import flask_foo
from flask_foo import Foo
Building Extensions
-------------------
foo = Foo()
While `Flask Extension Registry`_ contains many Flask extensions, you may not find
an extension that fits your need. If this is the case, you can always create your own.
Consider reading :ref:`extension-dev` to develop your own Flask extension.
app = Flask(__name__)
app.config.update(
FOO_BAR='baz',
FOO_SPAM='eggs',
)
Flask Before 0.8
----------------
foo.init_app(app)
If you are using Flask 0.7 or earlier the :data:`flask.ext` package will not
exist, instead you have to import from ``flaskext.foo`` or ``flask_foo``
depending on how the extension is distributed. If you want to develop an
application that supports Flask 0.7 or earlier you should still import
from the :data:`flask.ext` package. We provide you with a compatibility
module that provides this package for older versions of Flask. You can
download it from GitHub: `flaskext_compat.py`_
And here is how you can use it::
import flaskext_compat
flaskext_compat.activate()
from flask.ext import foo
Building Extensions
-------------------
Once the ``flaskext_compat`` module is activated the :data:`flask.ext` will
exist and you can start importing from there.
While the `Extension Registry`_ contains many Flask extensions, you may
not find an extension that fits your need. If this is the case, you can
create your own. Read :ref:`extension-dev` to develop your own Flask
extension.
.. _Flask Extension Registry: http://flask.pocoo.org/extensions/
.. _flaskext_compat.py: https://raw.githubusercontent.com/pallets/flask/master/scripts/flaskext_compat.py
.. _Extension Registry: http://flask.pocoo.org/extensions/
.. _pypi: https://pypi.python.org/pypi?:action=browse&c=585

29
docs/index.rst

@ -7,25 +7,20 @@ Welcome to Flask
:alt: Flask: web development, one drop at a time
:class: floatingflask
Welcome to Flask's documentation. This documentation is divided into
different parts. I recommend that you get started with
:ref:`installation` and then head over to the :ref:`quickstart`.
Besides the quickstart, there is also a more detailed :ref:`tutorial` that
shows how to create a complete (albeit small) application with Flask. If
you'd rather dive into the internals of Flask, check out
the :ref:`api` documentation. Common patterns are described in the
:ref:`patterns` section.
Welcome to Flask's documentation. Get started with :ref:`installation`
and then get an overview with the :ref:`quickstart`. There is also a
more detailed :ref:`tutorial` that shows how to create a small but
complete application with Flask. Common patterns are described in the
:ref:`patterns` section. The rest of the docs desribe each component of
Flask in detail, with a full reference in the :ref:`api` section.
Flask depends on two external libraries: the `Jinja2`_ template
engine and the `Werkzeug`_ WSGI toolkit. These libraries are not documented
here. If you want to dive into their documentation, check out the
following links:
Flask depends on the `Jinja`_ template engine and the `Werkzeug`_ WSGI
toolkit. The documentation for these libraries can be found at:
- `Jinja2 Documentation <http://jinja.pocoo.org/docs>`_
- `Werkzeug Documentation <http://werkzeug.pocoo.org/docs>`_
- `Jinja documentation <http://jinja.pocoo.org/docs>`_
- `Werkzeug documentation <http://werkzeug.pocoo.org/docs>`_
.. _Jinja2: http://jinja.pocoo.org/
.. _Werkzeug: http://werkzeug.pocoo.org/
.. _Jinja: https://www.palletsprojects.com/p/jinja/
.. _Werkzeug: https://www.palletsprojects.com/p/werkzeug/
.. include:: contents.rst.inc

4
docs/installation.rst

@ -6,8 +6,8 @@ Installation
Python Version
--------------
We recommend using the latest version of Python 3. Flask supports Python 3.3
and newer, Python 2.6 and newer, and PyPy.
We recommend using the latest version of Python 3. Flask supports Python 3.4
and newer, Python 2.7, and PyPy.
Dependencies
------------

16
docs/patterns/appfactories.rst

@ -89,18 +89,20 @@ For more information about the design of extensions refer to :doc:`/extensiondev
Using Applications
------------------
To use such an application you have to create it in a separate file first,
otherwise the :command:`flask` command won't be able to find it. Here's an
example :file:`exampleapp.py` file that creates such an application::
To run such an application, you can use the :command:`flask` command::
from yourapplication import create_app
app = create_app('/path/to/config.cfg')
export FLASK_APP=myapp
flask run
It can then be used with the :command:`flask` command::
Flask will automatically detect the factory (``create_app`` or ``make_app``)
in ``myapp``. You can also pass arguments to the factory like this::
export FLASK_APP=exampleapp
export FLASK_APP="myapp:create_app('dev')"
flask run
Then the ``create_app`` factory in ``myapp`` is called with the string
``'dev'`` as the argument. See :doc:`/cli` for more detail.
Factory Improvements
--------------------

2
docs/patterns/fileuploads.rst

@ -65,7 +65,7 @@ the file and redirects the user to the URL for the uploaded file::
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
return redirect(url_for('uploaded_file',
return redirect(url_for('upload_file',
filename=filename))
return '''
<!doctype html>

6
docs/patterns/packages.rst

@ -65,10 +65,10 @@ 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::
path to your application directory. Similarly you can turn on the
development features like this::
export FLASK_DEBUG=true
export FLASK_ENV=development
In order to install and run the application you need to issue the following
commands::

2
docs/patterns/sqlite3.rst

@ -67,7 +67,7 @@ the application context by hand::
Easy Querying
-------------
Now in each request handling function you can access `g.db` to get the
Now in each request handling function you can access `get_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

23
docs/python3.rst

@ -1,23 +0,0 @@
.. _python3-support:
Python 3 Support
================
Flask, its dependencies, and most Flask extensions support Python 3.
You should start using Python 3 for your next project,
but there are a few things to be aware of.
You need to use Python 3.3 or higher. 3.2 and older are *not* supported.
You should use the latest versions of all Flask-related packages.
Flask 0.10 and Werkzeug 0.9 were the first versions to introduce Python 3 support.
Python 3 changed how unicode and bytes are handled, which complicates how low
level code handles HTTP data. This mainly affects WSGI middleware interacting
with the WSGI ``environ`` data. Werkzeug wraps that information in high-level
helpers, so encoding issues should not affect you.
The majority of the upgrade work is in the lower-level libraries like
Flask and Werkzeug, not the high-level application code.
For example, all of the examples in the Flask repository work on both Python 2 and 3
and did not require a single line of code changed.

41
docs/quickstart.rst

@ -130,13 +130,14 @@ 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 all development features (including debug mode) you can export
the ``FLASK_ENV`` environment variable and set it to ``development``
before running the server::
$ export FLASK_DEBUG=1
$ export FLASK_ENV=development
$ 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:
@ -144,6 +145,9 @@ This does the following things:
2. it activates the automatic reloader
3. it enables the debug mode on the Flask application.
You can also control debug mode separately from the environment by
exporting ``FLASK_DEBUG=1``.
There are more parameters that are explained in the :ref:`server` docs.
.. admonition:: Attention
@ -224,7 +228,7 @@ Converter types:
Unique URLs / Redirection Behavior
``````````````````````````````````
Take these two rules::
The following two rules differ in their use of a trailing slash. ::
@app.route('/projects/')
def projects():
@ -234,20 +238,17 @@ Take these two rules::
def about():
return 'The about page'
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.
The canonical URL for the ``projects`` endpoint has 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 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.
The canonical URL for the ``about`` endpoint does not have a trailing
slash. It's similar to the pathname of a file. Accessing the URL with a
trailing slash produces a 404 "Not Found" error. This helps keep URLs
unique for these resources, which helps search engines avoid indexing
the same page twice.
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:
@ -267,7 +268,9 @@ Why would you want to build URLs using the URL reversing function
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
4. The generated paths are always absolute, avoiding unexpected behavior
of relative paths in browsers.
5. If your application is placed outside the URL root, for example, in
``/myapplication`` instead of ``/``, :func:`~flask.url_for` properly
handles that for you.
@ -315,9 +318,9 @@ of the :meth:`~flask.Flask.route` decorator to handle different HTTP methods.
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
do_the_login()
return do_the_login()
else:
show_the_login_form()
return show_the_login_form()
If ``GET`` is present, Flask automatically adds support for the ``HEAD`` method
and handles ``HEAD`` requests according to the the `HTTP RFC`_. Likewise,

16
docs/security.rst

@ -184,6 +184,9 @@ contains the same data. ::
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection
.. _security-cookie:
Set-Cookie options
~~~~~~~~~~~~~~~~~~
@ -194,17 +197,21 @@ They can be set on other cookies too.
- ``Secure`` limits cookies to HTTPS traffic only.
- ``HttpOnly`` protects the contents of cookies from being read with
JavaScript.
- ``SameSite`` ensures that cookies can only be requested from the same
domain that created them. It is not supported by Flask yet.
- ``SameSite`` restricts how cookies are sent with requests from
external sites. Can be set to ``'Lax'`` (recommended) or ``'Strict'``.
``Lax`` prevents sending cookies with CSRF-prone requests from
external sites, such as submitting a form. ``Strict`` prevents sending
cookies with all external requests, including following regular links.
::
app.config.update(
SESSION_COOKIE_SECURE=True,
SESSION_COOKIE_HTTPONLY=True,
SESSION_COOKIE_SAMESITE='Lax',
)
response.set_cookie('username', 'flask', secure=True, httponly=True)
response.set_cookie('username', 'flask', secure=True, httponly=True, samesite='Lax')
Specifying ``Expires`` or ``Max-Age`` options, will remove the cookie after
the given time, or the current time plus the age, respectively. If neither
@ -237,6 +244,9 @@ values (or any values that need secure signatures).
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie
.. _samesite_support: https://caniuse.com/#feat=same-site-cookie-attribute
HTTP Public Key Pinning (HPKP)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

20
docs/server.rst

@ -12,15 +12,17 @@ but you can also continue using the :meth:`Flask.run` method.
Command Line
------------
The :command:`flask` command line script (:ref:`cli`) is strongly recommended for
development because it provides a superior reload experience due to how it
loads the application. The basic usage is like this::
The :command:`flask` command line script (:ref:`cli`) is strongly
recommended for development because it provides a superior reload
experience due to how it loads the application. The basic usage is like
this::
$ export FLASK_APP=my_application
$ export FLASK_DEBUG=1
$ export FLASK_ENV=development
$ flask run
This will enable the debugger, the reloader and then start the server on
This enables the development environment, including the interactive
debugger and reloader, and then starts the server on
*http://localhost:5000/*.
The individual features of the server can be controlled by passing more
@ -29,6 +31,14 @@ disabled::
$ flask run --no-reload
.. note::
Prior to Flask 1.0 the :envvar:`FLASK_ENV` environment variable was
not supported and you needed to enable debug mode by exporting
``FLASK_DEBUG=1``. This can still be used to control debug mode, but
you should prefer setting the development environment as shown
above.
In Code
-------

57
docs/testing.rst

@ -406,3 +406,60 @@ Passing the ``json`` argument in the test client methods sets the request data
to the JSON-serialized object and sets the content type to
``application/json``. You can get the JSON data from the request or response
with ``get_json``.
.. _testing-cli:
Testing CLI Commands
--------------------
Click comes with `utilities for testing`_ your CLI commands. A
:class:`~click.testing.CliRunner` runs commands in isolation and
captures the output in a :class:`~click.testing.Result` object.
Flask provides :meth:`~flask.Flask.test_cli_runner` to create a
:class:`~flask.testing.FlaskCliRunner` that passes the Flask app to the
CLI automatically. Use its :meth:`~flask.testing.FlaskCliRunner.invoke`
method to call commands in the same way they would be called from the
command line. ::
import click
@app.cli.command('hello')
@click.option('--name', default='World')
def hello_command(name)
click.echo(f'Hello, {name}!')
def test_hello():
runner = app.test_cli_runner()
# invoke the command directly
result = runner.invoke(hello_command, ['--name', 'Flask'])
assert 'Hello, Flask' in result.output
# or by name
result = runner.invoke(args=['hello'])
assert 'World' in result.output
In the example above, invoking the command by name is useful because it
verifies that the command was correctly registered with the app.
If you want to test how your command parses parameters, without running
the command, use its :meth:`~click.BaseCommand.make_context` method.
This is useful for testing complex validation rules and custom types. ::
def upper(ctx, param, value):
if value is not None:
return value.upper()
@app.cli.command('hello')
@click.option('--name', default='World', callback=upper)
def hello_command(name)
click.echo(f'Hello, {name}!')
def test_hello_params():
context = hello_command.make_context('hello', ['--name', 'flask'])
assert context.params['name'] == 'FLASK'
.. _click: http://click.pocoo.org/
.. _utilities for testing: http://click.pocoo.org/testing

6
docs/tutorial/packaging.rst

@ -78,11 +78,13 @@ With that out of the way, you should be able to start up the application.
Do this on Mac or Linux with the following commands in ``flaskr/``::
export FLASK_APP=flaskr
export FLASK_DEBUG=true
export FLASK_ENV=development
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.
Exporting ``FLASK_ENV=development`` turns on all development features
such as enabling the interactive debugger.
*Never leave debug mode activated in a production system*, because it will
allow users to execute code on the server!

9
examples/blueprintexample/blueprintexample.py

@ -1,3 +1,12 @@
# -*- coding: utf-8 -*-
"""
Blueprint Example
~~~~~~~~~~~~~~~~~
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""
from flask import Flask
from simple_page.simple_page import simple_page

6
examples/blueprintexample/test_blueprintexample.py

@ -1,10 +1,12 @@
# -*- coding: utf-8 -*-
"""
Blueprint Example Tests
~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~
Tests the Blueprint example app
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""
import pytest
import blueprintexample

2
examples/flaskr/flaskr/blueprints/flaskr.py

@ -6,7 +6,7 @@
A microblog example application written as Flask tutorial with
Flask and sqlite3.
:copyright: (c) 2015 by Armin Ronacher.
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""

2
examples/flaskr/flaskr/factory.py

@ -6,7 +6,7 @@
A microblog example application written as Flask tutorial with
Flask and sqlite3.
:copyright: (c) 2015 by Armin Ronacher.
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""

2
examples/flaskr/setup.py

@ -5,7 +5,7 @@
Tests the Flaskr application.
:copyright: (c) 2015 by Armin Ronacher.
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""

26
examples/flaskr/tests/test_flaskr.py

@ -5,7 +5,7 @@
Tests the Flaskr application.
:copyright: (c) 2015 by Armin Ronacher.
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""
@ -17,33 +17,25 @@ from flaskr.blueprints.flaskr import init_db
@pytest.fixture
def app(request):
db_fd, temp_db_location = tempfile.mkstemp()
def app():
db_fd, db_path = tempfile.mkstemp()
config = {
'DATABASE': temp_db_location,
'DATABASE': db_path,
'TESTING': True,
'DB_FD': db_fd
}
app = create_app(config=config)
with app.app_context():
init_db()
yield app
os.close(db_fd)
os.unlink(db_path)
@pytest.fixture
def client(request, app):
client = app.test_client()
def teardown():
os.close(app.config['DB_FD'])
os.unlink(app.config['DATABASE'])
request.addfinalizer(teardown)
return client
@pytest.fixture
def client(app):
return app.test_client()
def login(client, username, password):

3
examples/jqueryexample/jqueryexample.py

@ -5,9 +5,10 @@
A simple application that shows how Flask and jQuery get along.
:copyright: (c) 2015 by Armin Ronacher.
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""
from flask import Flask, jsonify, render_template, request
app = Flask(__name__)

2
examples/minitwit/minitwit/minitwit.py

@ -5,7 +5,7 @@
A microblogging application written with Flask and sqlite3.
:copyright: (c) 2015 by Armin Ronacher.
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""

3
examples/minitwit/tests/test_minitwit.py

@ -5,9 +5,10 @@
Tests the MiniTwit application.
:copyright: (c) 2015 by Armin Ronacher.
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""
import os
import tempfile
import pytest

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

@ -1,3 +1,12 @@
# -*- coding: utf-8 -*-
"""
Larger App Tests
~~~~~~~~~~~~~~~~
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""
from yourapplication import app
import pytest

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

@ -1,3 +1,12 @@
# -*- coding: utf-8 -*-
"""
yourapplication
~~~~~~~~~~~~~~~
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""
from flask import Flask
app = Flask('yourapplication')

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

@ -1,3 +1,12 @@
# -*- coding: utf-8 -*-
"""
yourapplication.views
~~~~~~~~~~~~~~~~~~~~~
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""
from yourapplication import app
@app.route('/')

2
flask/__init__.py

@ -6,7 +6,7 @@
A microframework based on Werkzeug. It's extensively documented
and follows best practice patterns.
:copyright: (c) 2015 by Armin Ronacher.
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""

3
flask/__main__.py

@ -5,11 +5,10 @@
Alias for flask.run for the command line.
:copyright: (c) 2015 by Armin Ronacher.
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""
if __name__ == '__main__':
from .cli import main
main(as_module=True)

3
flask/_compat.py

@ -7,9 +7,10 @@
version of six so we don't have to depend on a specific version
of it.
:copyright: (c) 2015 by Armin Ronacher.
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""
import sys
PY2 = sys.version_info[0] == 2

170
flask/app.py

@ -5,9 +5,10 @@
This module implements the central WSGI application object.
:copyright: (c) 2015 by Armin Ronacher.
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""
import os
import sys
import warnings
@ -27,7 +28,7 @@ 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, \
_endpoint_from_view_func, find_package, get_env, get_debug_flag, \
get_flashed_messages, locked_cached_property, url_for
from .logging import create_logger
from .sessions import SecureCookieSessionInterface
@ -122,8 +123,13 @@ class Flask(_PackageBoundObject):
.. versionadded:: 0.11
The `root_path` parameter was added.
.. versionadded:: 0.13
The `host_matching` and `static_host` parameters were added.
.. versionadded:: 1.0
The ``host_matching`` and ``static_host`` parameters were added.
.. versionadded:: 1.0
The ``subdomain_matching`` parameter was added. Subdomain
matching needs to be enabled manually now. Setting
:data:`SERVER_NAME` does not implicitly enable it.
:param import_name: the name of the application package
:param static_url_path: can be used to specify a different path for the
@ -132,11 +138,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.
:param host_matching: sets the app's ``url_map.host_matching`` to the 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``
: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 host_matching: set ``url_map.host_matching`` attribute.
Defaults to False.
:param subdomain_matching: consider the subdomain relative to
:data:`SERVER_NAME` when matching routes. Defaults to False.
: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
@ -196,15 +204,6 @@ class Flask(_PackageBoundObject):
#: .. versionadded:: 0.11
config_class = Config
#: The debug flag. Set this to ``True`` to enable debugging of the
#: application. In debug mode the debugger will kick in when an unhandled
#: exception occurs and the integrated server will automatically reload
#: the application if changes in the code are detected.
#:
#: This attribute can also be configured from the config with the ``DEBUG``
#: configuration key. Defaults to ``False``.
debug = ConfigAttribute('DEBUG')
#: The testing flag. Set this to ``True`` to enable the test mode of
#: Flask extensions (and in the future probably also Flask itself).
#: For example this might activate test helpers that have an
@ -278,7 +277,8 @@ class Flask(_PackageBoundObject):
#: Default configuration parameters.
default_config = ImmutableDict({
'DEBUG': get_debug_flag(default=False),
'ENV': None,
'DEBUG': None,
'TESTING': False,
'PROPAGATE_EXCEPTIONS': None,
'PRESERVE_CONTEXT_ON_EXCEPTION': None,
@ -292,6 +292,7 @@ class Flask(_PackageBoundObject):
'SESSION_COOKIE_PATH': None,
'SESSION_COOKIE_HTTPONLY': True,
'SESSION_COOKIE_SECURE': False,
'SESSION_COOKIE_SAMESITE': None,
'SESSION_REFRESH_EACH_REQUEST': True,
'MAX_CONTENT_LENGTH': None,
'SEND_FILE_MAX_AGE_DEFAULT': timedelta(hours=12),
@ -317,6 +318,14 @@ class Flask(_PackageBoundObject):
#: .. versionadded:: 0.7
test_client_class = None
#: The :class:`~click.testing.CliRunner` subclass, by default
#: :class:`~flask.testing.FlaskCliRunner` that is used by
#: :meth:`test_cli_runner`. Its ``__init__`` method should take a
#: Flask app object as the first argument.
#:
#: .. versionadded:: 1.0
test_cli_runner_class = None
#: the session interface to use. By default an instance of
#: :class:`~flask.sessions.SecureCookieSessionInterface` is used here.
#:
@ -345,6 +354,7 @@ class Flask(_PackageBoundObject):
static_folder='static',
static_host=None,
host_matching=False,
subdomain_matching=False,
template_folder='templates',
instance_path=None,
instance_relative_config=False,
@ -528,6 +538,7 @@ class Flask(_PackageBoundObject):
self.url_map = Map()
self.url_map.host_matching = host_matching
self.subdomain_matching = subdomain_matching
# tracks internally if the application already handled at least one
# request.
@ -647,7 +658,10 @@ class Flask(_PackageBoundObject):
root_path = self.root_path
if instance_relative:
root_path = self.instance_path
return self.config_class(root_path, self.default_config)
defaults = dict(self.default_config)
defaults['ENV'] = get_env()
defaults['DEBUG'] = get_debug_flag()
return self.config_class(root_path, defaults)
def auto_find_instance_path(self):
"""Tries to locate the instance path if it was not provided to the
@ -790,25 +804,41 @@ class Flask(_PackageBoundObject):
rv.update(processor())
return rv
def _reconfigure_for_run_debug(self, debug):
"""The ``run`` commands will set the application's debug flag. Some
application configuration may already be calculated based on the
previous debug value. This method will recalculate affected values.
Called by the :func:`flask.cli.run` command or :meth:`Flask.run`
method if the debug flag is set explicitly in the call.
#: What environment the app is running in. Flask and extensions may
#: enable behaviors based on the environment, such as enabling debug
#: mode. This maps to the :data:`ENV` config key. This is set by the
#: :envvar:`FLASK_ENV` environment variable and may not behave as
#: expected if set in code.
#:
#: **Do not enable development when deploying in production.**
#:
#: Default: ``'production'``
env = ConfigAttribute('ENV')
:param debug: the new value of the debug flag
def _get_debug(self):
return self.config['DEBUG']
.. versionadded:: 1.0
Reconfigures ``app.jinja_env.auto_reload``.
"""
self.debug = debug
def _set_debug(self, value):
self.config['DEBUG'] = value
self.jinja_env.auto_reload = self.templates_auto_reload
def run(
self, host=None, port=None, debug=None, load_dotenv=True, **options
):
#: Whether debug mode is enabled. When using ``flask run`` to start
#: the development server, an interactive debugger will be shown for
#: unhandled exceptions, and the server will be reloaded when code
#: changes. This maps to the :data:`DEBUG` config key. This is
#: enabled when :attr:`env` is ``'development'`` and is overridden
#: by the ``FLASK_DEBUG`` environment variable. It may not behave as
#: expected if set in code.
#:
#: **Do not enable debug mode when deploying in production.**
#:
#: Default: ``True`` if :attr:`env` is ``'development'``, or
#: ``False`` otherwise.
debug = property(_get_debug, _set_debug)
del _get_debug, _set_debug
def run(self, host=None, port=None, debug=None,
load_dotenv=True, **options):
"""Runs the application on a local development server.
Do not use ``run()`` in a production setting. It is not intended to
@ -856,9 +886,15 @@ class Flask(_PackageBoundObject):
If installed, python-dotenv will be used to load environment
variables from :file:`.env` and :file:`.flaskenv` files.
.. versionchanged:: 0.10
The default port is now picked from the ``SERVER_NAME`` variable.
If set, the :envvar:`FLASK_ENV` and :envvar:`FLASK_DEBUG`
environment variables will override :attr:`env` and
:attr:`debug`.
Threaded mode is enabled by default.
.. versionchanged:: 0.10
The default port is now picked from the ``SERVER_NAME``
variable.
"""
# Change this into a no-op if the server is invoked from the
# command line. Have a look at cli.py for more information.
@ -868,15 +904,22 @@ class Flask(_PackageBoundObject):
return
if load_dotenv:
from flask.cli import load_dotenv
load_dotenv()
cli.load_dotenv()
# if set, let env vars override previous values
if 'FLASK_ENV' in os.environ:
self.env = get_env()
self.debug = get_debug_flag()
elif 'FLASK_DEBUG' in os.environ:
self.debug = get_debug_flag()
# debug passed to method overrides all other sources
if debug is not None:
self._reconfigure_for_run_debug(bool(debug))
self.debug = bool(debug)
_host = '127.0.0.1'
_port = 5000
server_name = self.config.get("SERVER_NAME")
server_name = self.config.get('SERVER_NAME')
sn_host, sn_port = None, None
if server_name:
@ -884,8 +927,12 @@ class Flask(_PackageBoundObject):
host = host or sn_host or _host
port = int(port or sn_port or _port)
options.setdefault('use_reloader', self.debug)
options.setdefault('use_debugger', self.debug)
options.setdefault('threaded', True)
cli.show_server_banner(self.env, self.debug, self.name)
from werkzeug.serving import run_simple
@ -953,6 +1000,23 @@ class Flask(_PackageBoundObject):
from flask.testing import FlaskClient as cls
return cls(self, self.response_class, use_cookies=use_cookies, **kwargs)
def test_cli_runner(self, **kwargs):
"""Create a CLI runner for testing CLI commands.
See :ref:`testing-cli`.
Returns an instance of :attr:`test_cli_runner_class`, by default
:class:`~flask.testing.FlaskCliRunner`. The Flask app object is
passed as the first argument.
.. versionadded:: 1.0
"""
cls = self.test_cli_runner_class
if cls is None:
from flask.testing import FlaskCliRunner as cls
return cls(self, **kwargs)
def open_session(self, request):
"""Creates or opens a new session. Default implementation stores all
session data in a signed cookie. This requires that the
@ -1055,7 +1119,8 @@ class Flask(_PackageBoundObject):
return iter(self._blueprint_order)
@setupmethod
def add_url_rule(self, rule, endpoint=None, view_func=None, provide_automatic_options=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.
@ -1922,18 +1987,29 @@ class Flask(_PackageBoundObject):
def create_url_adapter(self, request):
"""Creates a URL adapter for the given request. The URL adapter
is created at a point where the request context is not yet set up
so the request is passed explicitly.
is created at a point where the request context is not yet set
up so the request is passed explicitly.
.. versionadded:: 0.6
.. versionchanged:: 0.9
This can now also be called without a request object when the
URL adapter is created for the application context.
.. versionchanged:: 1.0
:data:`SERVER_NAME` no longer implicitly enables subdomain
matching. Use :attr:`subdomain_matching` instead.
"""
if request is not None:
return self.url_map.bind_to_environ(request.environ,
server_name=self.config['SERVER_NAME'])
# If subdomain matching is disabled (the default), use the
# default subdomain in all cases. This should be the default
# in Werkzeug but it currently does not have that feature.
subdomain = ((self.url_map.default_subdomain or None)
if not self.subdomain_matching else None)
return self.url_map.bind_to_environ(
request.environ,
server_name=self.config['SERVER_NAME'],
subdomain=subdomain)
# We need at the very least the server name to be set for this
# to work.
if self.config['SERVER_NAME'] is not None:
@ -2109,8 +2185,8 @@ class Flask(_PackageBoundObject):
return RequestContext(self, environ)
def test_request_context(self, *args, **kwargs):
"""Creates a WSGI environment from the given values (see
:class:`werkzeug.test.EnvironBuilder` for more information, this
"""Creates a :class:`~flask.ctx.RequestContext` from the given values
(see :class:`werkzeug.test.EnvironBuilder` for more information, this
function accepts the same arguments plus two additional).
Additional arguments (only if ``base_url`` is not specified):

5
flask/blueprints.py

@ -6,7 +6,7 @@
Blueprints are the recommended way to implement larger or more
pluggable applications in Flask 0.7 and later.
:copyright: (c) 2015 by Armin Ronacher.
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""
from functools import update_wrapper
@ -52,6 +52,9 @@ class BlueprintSetupState(object):
#: The prefix that should be used for all URLs defined on the
#: blueprint.
if url_prefix and url_prefix[-1] == '/':
url_prefix = url_prefix[:-1]
self.url_prefix = url_prefix
#: A dictionary with URL defaults that is added to each and every

214
flask/cli.py

@ -5,15 +5,17 @@
A simple command line application to run flask apps.
:copyright: (c) 2015 by Armin Ronacher.
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""
from __future__ import print_function
import ast
import inspect
import os
import re
import ssl
import sys
import traceback
from functools import update_wrapper
@ -21,11 +23,12 @@ from operator import attrgetter
from threading import Lock, Thread
import click
from werkzeug.utils import import_string
from . import __version__
from ._compat import getargspec, iteritems, reraise
from ._compat import getargspec, iteritems, reraise, text_type
from .globals import current_app
from .helpers import get_debug_flag
from .helpers import get_debug_flag, get_env
try:
import dotenv
@ -75,6 +78,8 @@ def find_best_app(script_info, module):
if isinstance(app, Flask):
return app
except TypeError:
if not _called_with_wrong_args(app_factory):
raise
raise NoAppException(
'Detected factory "{factory}" in module "{module}", but '
'could not call it without arguments. Use '
@ -111,6 +116,30 @@ def call_factory(script_info, app_factory, arguments=()):
return app_factory()
def _called_with_wrong_args(factory):
"""Check whether calling a function raised a ``TypeError`` because
the call failed or because something in the factory raised the
error.
:param factory: the factory function that was called
:return: true if the call failed
"""
tb = sys.exc_info()[2]
try:
while tb is not None:
if tb.tb_frame.f_code is factory.__code__:
# in the factory, it was called successfully
return False
tb = tb.tb_next
# didn't reach the factory
return True
finally:
del tb
def find_app_by_string(script_info, module, app_name):
"""Checks if the given string is a variable name or a function. If it is a
function, it checks for specified arguments and whether it takes a
@ -148,6 +177,9 @@ def find_app_by_string(script_info, module, app_name):
try:
app = call_factory(script_info, attr, args)
except TypeError as e:
if not _called_with_wrong_args(attr):
raise
raise NoAppException(
'{e}\nThe factory "{app_name}" in module "{module}" could not '
'be called with the specified arguments.'.format(
@ -341,9 +373,8 @@ class ScriptInfo(object):
else:
for path in ('wsgi.py', 'app.py'):
import_name = prepare_import(path)
app = locate_app(
self, import_name, None, raise_if_not_found=False
)
app = locate_app(self, import_name, None,
raise_if_not_found=False)
if app:
break
@ -357,8 +388,10 @@ class ScriptInfo(object):
debug = get_debug_flag()
# Update the app's debug flag through the descriptor so that other
# values repopulate as well.
if debug is not None:
app._reconfigure_for_run_debug(debug)
app.debug = debug
self._loaded_app = app
return app
@ -432,10 +465,8 @@ class FlaskGroup(AppGroup):
from :file:`.env` and :file:`.flaskenv` files.
"""
def __init__(
self, add_default_commands=True, create_app=None,
add_version_option=True, load_dotenv=True, **extra
):
def __init__(self, add_default_commands=True, create_app=None,
add_version_option=True, load_dotenv=True, **extra):
params = list(extra.pop('params', None) or ())
if add_version_option:
@ -578,11 +609,118 @@ def load_dotenv(path=None):
return new_dir is not None # at least one file was located and loaded
def show_server_banner(env, debug, app_import_path):
"""Show extra startup messages the first time the server is run,
ignoring the reloader.
"""
if os.environ.get('WERKZEUG_RUN_MAIN') == 'true':
return
if app_import_path is not None:
print(' * Serving Flask app "{0}"'.format(app_import_path))
print(' * Environment: {0}'.format(env))
if env == 'production':
click.secho(
' WARNING: Do not use the development server in a production'
' environment.', fg='red')
click.secho(' Use a production WSGI server instead.', dim=True)
if debug is not None:
print(' * Debug mode: {0}'.format('on' if debug else 'off'))
class CertParamType(click.ParamType):
"""Click option type for the ``--cert`` option. Allows either an
existing file, the string ``'adhoc'``, or an import for a
:class:`~ssl.SSLContext` object.
"""
name = 'path'
def __init__(self):
self.path_type = click.Path(
exists=True, dir_okay=False, resolve_path=True)
def convert(self, value, param, ctx):
try:
return self.path_type(value, param, ctx)
except click.BadParameter:
value = click.STRING(value, param, ctx).lower()
if value == 'adhoc':
try:
import OpenSSL
except ImportError:
raise click.BadParameter(
'Using ad-hoc certificates requires pyOpenSSL.',
ctx, param)
return value
obj = import_string(value, silent=True)
if sys.version_info < (2, 7):
if obj:
return obj
else:
if isinstance(obj, ssl.SSLContext):
return obj
raise
def _validate_key(ctx, param, value):
"""The ``--key`` option must be specified when ``--cert`` is a file.
Modifies the ``cert`` param to be a ``(cert, key)`` pair if needed.
"""
cert = ctx.params.get('cert')
is_adhoc = cert == 'adhoc'
if sys.version_info < (2, 7):
is_context = cert and not isinstance(cert, (text_type, bytes))
else:
is_context = isinstance(cert, ssl.SSLContext)
if value is not None:
if is_adhoc:
raise click.BadParameter(
'When "--cert" is "adhoc", "--key" is not used.',
ctx, param)
if is_context:
raise click.BadParameter(
'When "--cert" is an SSLContext object, "--key is not used.',
ctx, param)
if not cert:
raise click.BadParameter(
'"--cert" must also be specified.',
ctx, param)
ctx.params['cert'] = cert, value
else:
if cert and not (is_adhoc or is_context):
raise click.BadParameter(
'Required when using "--cert".',
ctx, param)
return value
@click.command('run', short_help='Runs a development server.')
@click.option('--host', '-h', default='127.0.0.1',
help='The interface to bind to.')
@click.option('--port', '-p', default=5000,
help='The port to bind to.')
@click.option('--cert', type=CertParamType(),
help='Specify a certificate file to use HTTPS.')
@click.option('--key',
type=click.Path(exists=True, dir_okay=False, resolve_path=True),
callback=_validate_key, expose_value=False,
help='The key file to use when specifying a certificate.')
@click.option('--reload/--no-reload', default=None,
help='Enable or disable the reloader. By default the reloader '
'is active if debug is enabled.')
@ -592,48 +730,36 @@ def load_dotenv(path=None):
@click.option('--eager-loading/--lazy-loader', default=None,
help='Enable or disable eager loading. By default eager '
'loading is enabled if the reloader is disabled.')
@click.option('--with-threads/--without-threads', default=False,
@click.option('--with-threads/--without-threads', default=True,
help='Enable or disable multithreading.')
@pass_script_info
def run_command(info, host, port, reload, debugger, eager_loading,
with_threads):
"""Runs a local development server for the Flask application.
with_threads, cert):
"""Run a local development server.
This local server is recommended for development purposes only but it
can also be used for simple intranet deployments. By default it will
not support any sort of concurrency at all to simplify debugging. This
can be changed with the --with-threads option which will enable basic
multithreading.
This server is for development purposes only. It does not provide
the stability, security, or performance of production WSGI servers.
The reloader and debugger are by default enabled if the debug flag of
Flask is enabled and disabled otherwise.
The reloader and debugger are enabled by default if
FLASK_ENV=development or FLASK_DEBUG=1.
"""
from werkzeug.serving import run_simple
debug = get_debug_flag()
if reload is None:
reload = bool(debug)
reload = debug
if debugger is None:
debugger = bool(debug)
debugger = debug
if eager_loading is None:
eager_loading = not reload
show_server_banner(get_env(), debug, info.app_import_path)
app = DispatchingApp(info.load_app, use_eager_loading=eager_loading)
# Extra startup messages. This depends a bit on Werkzeug internals to
# not double execute when the reloader kicks in.
if os.environ.get('WERKZEUG_RUN_MAIN') != 'true':
# If we have an import path we can print it out now which can help
# people understand what's being served. If we do not have an
# import path because the app was loaded through a callback then
# we won't print anything.
if info.app_import_path is not None:
print(' * Serving Flask app "%s"' % info.app_import_path)
if debug is not None:
print(' * Forcing debug mode %s' % (debug and 'on' or 'off'))
run_simple(host, port, app, use_reloader=reload,
use_debugger=debugger, threaded=with_threads)
from werkzeug.serving import run_simple
run_simple(host, port, app, use_reloader=reload, use_debugger=debugger,
threaded=with_threads, ssl_context=cert)
@click.command('shell', short_help='Runs a shell in the app context.')
@ -649,11 +775,11 @@ def shell_command():
import code
from flask.globals import _app_ctx_stack
app = _app_ctx_stack.top.app
banner = 'Python %s on %s\nApp: %s%s\nInstance: %s' % (
banner = 'Python %s on %s\nApp: %s [%s]\nInstance: %s' % (
sys.version,
sys.platform,
app.import_name,
app.debug and ' [debug]' or '',
app.env,
app.instance_path,
)
ctx = {}
@ -722,12 +848,12 @@ A general utility script for Flask applications.
Provides commands from Flask, extensions, and the application. Loads the
application defined in the FLASK_APP environment variable, or from a wsgi.py
file. Debug mode can be controlled with the FLASK_DEBUG
environment variable.
file. Setting the FLASK_ENV environment variable to 'development' will enable
debug mode.
\b
{prefix}{cmd} FLASK_APP=hello.py
{prefix}{cmd} FLASK_DEBUG=1
{prefix}{cmd} FLASK_ENV=development
{prefix}flask run
""".format(
cmd='export' if os.name == 'posix' else 'set',

6
flask/config.py

@ -5,7 +5,7 @@
Implements the configuration related objects.
:copyright: (c) 2015 by Armin Ronacher.
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""
@ -129,7 +129,9 @@ class Config(dict):
with open(filename, mode='rb') as config_file:
exec(compile(config_file.read(), filename, 'exec'), d.__dict__)
except IOError as e:
if silent and e.errno in (errno.ENOENT, errno.EISDIR):
if silent and e.errno in (
errno.ENOENT, errno.EISDIR, errno.ENOTDIR
):
return False
e.strerror = 'Unable to load configuration file (%s)' % e.strerror
raise

2
flask/ctx.py

@ -5,7 +5,7 @@
Implements the objects required to keep the context.
:copyright: (c) 2015 by Armin Ronacher.
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""

3
flask/debughelpers.py

@ -5,9 +5,10 @@
Various helpers to make the development experience better.
:copyright: (c) 2015 by Armin Ronacher.
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""
import os
from warnings import warn

2
flask/globals.py

@ -6,7 +6,7 @@
Defines all the global objects that are proxies to the current
active context.
:copyright: (c) 2015 by Armin Ronacher.
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""

48
flask/helpers.py

@ -5,7 +5,7 @@
Implements various helpers.
:copyright: (c) 2015 by Armin Ronacher.
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""
@ -22,28 +22,18 @@ import unicodedata
from werkzeug.routing import BuildError
from functools import update_wrapper
try:
from werkzeug.urls import url_quote
except ImportError:
from urlparse import quote as url_quote
from werkzeug.datastructures import Headers, Range
from werkzeug.exceptions import BadRequest, NotFound, \
RequestedRangeNotSatisfiable
# this was moved in 0.7
try:
from werkzeug.wsgi import wrap_file
except ImportError:
from werkzeug.utils import wrap_file
from jinja2 import FileSystemLoader
from .signals import message_flashed
from .globals import session, _request_ctx_stack, _app_ctx_stack, \
current_app, request
from ._compat import string_types, text_type
from ._compat import string_types, text_type, PY2
# sentinel
_missing = object()
@ -56,10 +46,25 @@ _os_alt_seps = list(sep for sep in [os.path.sep, os.path.altsep]
if sep not in (None, '/'))
def get_debug_flag(default=None):
def get_env():
"""Get the environment the app is running in, indicated by the
:envvar:`FLASK_ENV` environment variable. The default is
``'production'``.
"""
return os.environ.get('FLASK_ENV') or 'production'
def get_debug_flag():
"""Get whether debug mode should be enabled for the app, indicated
by the :envvar:`FLASK_DEBUG` environment variable. The default is
``True`` if :func:`.get_env` returns ``'development'``, or ``False``
otherwise.
"""
val = os.environ.get('FLASK_DEBUG')
if not val:
return default
return get_env() == 'development'
return val.lower() not in ('0', 'false', 'no')
@ -603,17 +608,13 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False,
'headers' % filename, stacklevel=2)
if conditional:
if callable(getattr(Range, 'to_content_range_header', None)):
# Werkzeug supports Range Requests
# Remove this test when support for Werkzeug <0.12 is dropped
try:
rv = rv.make_conditional(request, accept_ranges=True,
complete_length=fsize)
except RequestedRangeNotSatisfiable:
if file is not None:
file.close()
raise
else:
rv = rv.make_conditional(request)
# make sure we don't send x-sendfile for servers that
# ignore the 304 status code for x-sendfile.
if rv.status_code == 304:
@ -1001,12 +1002,21 @@ def total_seconds(td):
def is_ip(value):
"""Determine if the given string is an IP address.
Python 2 on Windows doesn't provide ``inet_pton``, so this only
checks IPv4 addresses in that environment.
:param value: value to check
:type value: str
:return: True if string is an IP address
:rtype: bool
"""
if PY2 and os.name == 'nt':
try:
socket.inet_aton(value)
return True
except socket.error:
return False
for family in (socket.AF_INET, socket.AF_INET6):
try:

10
flask/json/__init__.py

@ -1,4 +1,12 @@
# -*- coding: utf-8 -*-
"""
flask.json
~~~~~~~~~~
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""
import io
import uuid
from datetime import date, datetime
@ -264,7 +272,7 @@ def jsonify(*args, **kwargs):
data = args or kwargs
return current_app.response_class(
(dumps(data, indent=indent, separators=separators), '\n'),
dumps(data, indent=indent, separators=separators) + '\n',
mimetype=current_app.config['JSONIFY_MIMETYPE']
)

3
flask/json/tag.py

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
"""
Tagged JSON
~~~~~~~~~~~
@ -37,6 +38,8 @@ processes dicts first, so insert the new tag at the front of the order since
app.session_interface.serializer.register(TagOrderedDict, 0)
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""
from base64 import b64decode, b64encode

9
flask/logging.py

@ -1,3 +1,12 @@
# -*- coding: utf-8 -*-
"""
flask.logging
~~~~~~~~~~~~~
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""
from __future__ import absolute_import
import logging

77
flask/sessions.py

@ -5,11 +5,13 @@
Implements cookie based sessions based on itsdangerous.
:copyright: (c) 2015 by Armin Ronacher.
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""
import hashlib
import warnings
from collections import MutableMapping
from datetime import datetime
from itsdangerous import BadSignature, URLSafeTimedSerializer
@ -19,43 +21,55 @@ from flask.helpers import is_ip, total_seconds
from flask.json.tag import TaggedJSONSerializer
class SessionMixin(object):
"""Expands a basic dictionary with an accessors that are expected
by Flask extensions and users for the session.
"""
class SessionMixin(MutableMapping):
"""Expands a basic dictionary with session attributes."""
def _get_permanent(self):
@property
def permanent(self):
"""This reflects the ``'_permanent'`` key in the dict."""
return self.get('_permanent', False)
def _set_permanent(self, value):
@permanent.setter
def permanent(self, value):
self['_permanent'] = bool(value)
#: this reflects the ``'_permanent'`` key in the dict.
permanent = property(_get_permanent, _set_permanent)
del _get_permanent, _set_permanent
#: some session backends can tell you if a session is new, but that is
#: not necessarily guaranteed. Use with caution. The default mixin
#: implementation just hardcodes ``False`` in.
#: Some implementations can detect whether a session is newly
#: created, but that is not guaranteed. Use with caution. The mixin
# default is hard-coded ``False``.
new = False
#: for some backends this will always be ``True``, but some backends will
#: default this to false and detect changes in the dictionary for as
#: long as changes do not happen on mutable structures in the session.
#: The default mixin implementation just hardcodes ``True`` in.
#: Some implementations can detect changes to the session and set
#: this when that happens. The mixin default is hard coded to
#: ``True``.
modified = True
#: the accessed variable indicates whether or not the session object has
#: been accessed in that request. This allows flask to append a `Vary:
#: Cookie` header to the response if the session is being accessed. This
#: allows caching proxy servers, like Varnish, to use both the URL and the
#: session cookie as keys when caching pages, preventing multiple users
#: from being served the same cache.
#: Some implementations can detect when session data is read or
#: written and set this when that happens. The mixin default is hard
#: coded to ``True``.
accessed = True
class SecureCookieSession(CallbackDict, SessionMixin):
"""Base class for sessions based on signed cookies."""
"""Base class for sessions based on signed cookies.
This session backend will set the :attr:`modified` and
:attr:`accessed` attributes. It cannot reliably track whether a
session is new (vs. empty), so :attr:`new` remains hard coded to
``False``.
"""
#: When data is changed, this is set to ``True``. Only the session
#: dictionary itself is tracked; if the session contains mutable
#: data (for example a nested dict) then this must be set to
#: ``True`` manually when modifying that data. The session cookie
#: will only be written to the response if this is ``True``.
modified = False
#: When data is read or written, this is set to ``True``. Used by
# :class:`.SecureCookieSessionInterface` to add a ``Vary: Cookie``
#: header, which allows caching proxies to cache different pages for
#: different users.
accessed = False
def __init__(self, initial=None):
def on_update(self):
@ -63,8 +77,6 @@ class SecureCookieSession(CallbackDict, SessionMixin):
self.accessed = True
super(SecureCookieSession, self).__init__(initial, on_update)
self.modified = False
self.accessed = False
def __getitem__(self, key):
self.accessed = True
@ -238,6 +250,13 @@ class SessionInterface(object):
"""
return app.config['SESSION_COOKIE_SECURE']
def get_cookie_samesite(self, app):
"""Return ``'Strict'`` or ``'Lax'`` if the cookie should use the
``SameSite`` attribute. This currently just returns the value of
the :data:`SESSION_COOKIE_SAMESITE` setting.
"""
return app.config['SESSION_COOKIE_SAMESITE']
def get_expiration_time(self, app, session):
"""A helper method that returns an expiration date for the session
or ``None`` if the session is linked to the browser session. The
@ -351,6 +370,7 @@ class SecureCookieSessionInterface(SessionInterface):
httponly = self.get_cookie_httponly(app)
secure = self.get_cookie_secure(app)
samesite = self.get_cookie_samesite(app)
expires = self.get_expiration_time(app, session)
val = self.get_signing_serializer(app).dumps(dict(session))
response.set_cookie(
@ -360,5 +380,6 @@ class SecureCookieSessionInterface(SessionInterface):
httponly=httponly,
domain=domain,
path=path,
secure=secure
secure=secure,
samesite=samesite
)

3
flask/signals.py

@ -6,9 +6,10 @@
Implements signals based on blinker if available, otherwise
falls silently back to a noop.
:copyright: (c) 2015 by Armin Ronacher.
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""
signals_available = False
try:
from blinker import Namespace

3
flask/templating.py

@ -5,9 +5,10 @@
Implements the bridge to Jinja2.
:copyright: (c) 2015 by Armin Ronacher.
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""
from jinja2 import BaseLoader, Environment as BaseEnvironment, \
TemplateNotFound

42
flask/testing.py

@ -6,20 +6,19 @@
Implements test support helpers. This module is lazily imported
and usually not used in production environments.
:copyright: (c) 2015 by Armin Ronacher.
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""
import werkzeug
from contextlib import contextmanager
from click.testing import CliRunner
from flask.cli import ScriptInfo
from werkzeug.test import Client, EnvironBuilder
from flask import _request_ctx_stack
from flask.json import dumps as json_dumps
try:
from werkzeug.urls import url_parse
except ImportError:
from urlparse import urlsplit as url_parse
def make_test_environ_builder(
@ -197,3 +196,36 @@ class FlaskClient(Client):
top = _request_ctx_stack.top
if top is not None and top.preserved:
top.pop()
class FlaskCliRunner(CliRunner):
"""A :class:`~click.testing.CliRunner` for testing a Flask app's
CLI commands. Typically created using
:meth:`~flask.Flask.test_cli_runner`. See :ref:`testing-cli`.
"""
def __init__(self, app, **kwargs):
self.app = app
super(FlaskCliRunner, self).__init__(**kwargs)
def invoke(self, cli=None, args=None, **kwargs):
"""Invokes a CLI command in an isolated environment. See
:meth:`CliRunner.invoke <click.testing.CliRunner.invoke>` for
full method documentation. See :ref:`testing-cli` for examples.
If the ``obj`` argument is not given, passes an instance of
:class:`~flask.cli.ScriptInfo` that knows how to load the Flask
app being tested.
:param cli: Command object to invoke. Default is the app's
:attr:`~flask.app.Flask.cli` group.
:param args: List of strings to invoke the command with.
:return: a :class:`~click.testing.Result` object.
"""
if cli is None:
cli = self.app.cli
if 'obj' not in kwargs:
kwargs['obj'] = ScriptInfo(create_app=lambda: self.app)
return super(FlaskCliRunner, self).invoke(cli, args, **kwargs)

3
flask/views.py

@ -5,9 +5,10 @@
This module provides class-based views inspired by the ones in Django.
:copyright: (c) 2015 by Armin Ronacher.
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""
from .globals import request
from ._compat import with_metaclass

3
flask/wrappers.py

@ -5,9 +5,10 @@
Implements the WSGI wrappers (request and response).
:copyright: (c) 2015 by Armin Ronacher.
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""
from werkzeug.exceptions import BadRequest
from werkzeug.wrappers import Request as RequestBase, Response as ResponseBase

309
scripts/flaskext_tester.py

@ -1,309 +0,0 @@
# -*- coding: utf-8 -*-
"""
Flask Extension Tests
~~~~~~~~~~~~~~~~~~~~~
Tests the Flask extensions.
:copyright: (c) 2015 by Ali Afshar.
:license: BSD, see LICENSE for more details.
"""
import os
import sys
import shutil
import urllib2
import tempfile
import subprocess
import argparse
from flask import json
from setuptools.package_index import PackageIndex
from setuptools.archive_util import unpack_archive
flask_svc_url = 'http://flask.pocoo.org/extensions/'
# OS X has awful paths when using mkstemp or gettempdir(). I don't
# care about security or clashes here, so pick something that is
# actually memorable.
if sys.platform == 'darwin':
_tempdir = '/private/tmp'
else:
_tempdir = tempfile.gettempdir()
tdir = _tempdir + '/flaskext-test'
flaskdir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
# virtualenv hack *cough*
os.environ['PYTHONDONTWRITEBYTECODE'] = ''
RESULT_TEMPATE = u'''\
<!doctype html>
<title>Flask-Extension Test Results</title>
<style type=text/css>
body { font-family: 'Georgia', serif; font-size: 17px; color: #000; }
a { color: #004B6B; }
a:hover { color: #6D4100; }
h1, h2, h3 { font-family: 'Garamond', 'Georgia', serif; font-weight: normal; }
h1 { font-size: 30px; margin: 15px 0 5px 0; }
h2 { font-size: 24px; margin: 15px 0 5px 0; }
h3 { font-size: 19px; margin: 15px 0 5px 0; }
textarea, code,
pre { font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono',
'Bitstream Vera Sans Mono', monospace!important; font-size: 15px;
background: #eee; }
pre { padding: 7px 15px; line-height: 1.3; }
p { line-height: 1.4; }
table { border: 1px solid black; border-collapse: collapse;
margin: 15px 0; }
td, th { border: 1px solid black; padding: 4px 10px;
text-align: left; }
th { background: #eee; font-weight: normal; }
tr.success { background: #D3F5CC; }
tr.failed { background: #F5D2CB; }
</style>
<h1>Flask-Extension Test Results</h1>
<p>
This page contains the detailed test results for the test run of
all {{ 'approved' if approved }} Flask extensions.
<h2>Summary</h2>
<table class=results>
<thead>
<tr>
<th>Extension
<th>Version
<th>Author
<th>License
<th>Outcome
{%- for iptr, _ in results[0].logs|dictsort %}
<th>{{ iptr }}
{%- endfor %}
</tr>
</thead>
<tbody>
{%- for result in results %}
{% set outcome = 'success' if result.success else 'failed' %}
<tr class={{ outcome }}>
<th>{{ result.name }}
<td>{{ result.version }}
<td>{{ result.author }}
<td>{{ result.license }}
<td>{{ outcome }}
{%- for iptr, _ in result.logs|dictsort %}
<td><a href="#{{ result.name }}-{{ iptr }}">see log</a>
{%- endfor %}
</tr>
{%- endfor %}
</tbody>
</table>
<h2>Test Logs</h2>
<p>Detailed test logs for all tests on all platforms:
{%- for result in results %}
{%- for iptr, log in result.logs|dictsort %}
<h3 id="{{ result.name }}-{{ iptr }}">
{{ result.name }} - {{ result.version }} [{{ iptr }}]</h3>
<pre>{{ log }}</pre>
{%- endfor %}
{%- endfor %}
'''
def log(msg, *args):
print('[EXTTEST] ' + (msg % args))
class TestResult(object):
def __init__(self, name, folder, statuscode, interpreters):
intrptr = os.path.join(folder, '.tox/%s/bin/python'
% interpreters[0])
self.statuscode = statuscode
self.folder = folder
self.success = statuscode == 0
def fetch(field):
try:
c = subprocess.Popen([intrptr, 'setup.py',
'--' + field], cwd=folder,
stdout=subprocess.PIPE)
return c.communicate()[0].strip()
except OSError:
return '?'
self.name = name
self.license = fetch('license')
self.author = fetch('author')
self.version = fetch('version')
self.logs = {}
for interpreter in interpreters:
logfile = os.path.join(folder, '.tox/%s/log/test.log'
% interpreter)
if os.path.isfile(logfile):
self.logs[interpreter] = open(logfile).read()
else:
self.logs[interpreter] = ''
def create_tdir():
try:
shutil.rmtree(tdir)
except Exception:
pass
os.mkdir(tdir)
def package_flask():
distfolder = tdir + '/.flask-dist'
c = subprocess.Popen(['python', 'setup.py', 'sdist', '--formats=gztar',
'--dist', distfolder], cwd=flaskdir)
c.wait()
return os.path.join(distfolder, os.listdir(distfolder)[0])
def get_test_command(checkout_dir):
if os.path.isfile(checkout_dir + '/Makefile'):
return 'make test'
return 'python setup.py test'
def fetch_extensions_list():
req = urllib2.Request(flask_svc_url, headers={'accept':'application/json'})
d = urllib2.urlopen(req).read()
data = json.loads(d)
for ext in data['extensions']:
yield ext
def checkout_extension(name):
log('Downloading extension %s to temporary folder', name)
root = os.path.join(tdir, name)
os.mkdir(root)
checkout_path = PackageIndex().download(name, root)
unpack_archive(checkout_path, root)
path = None
for fn in os.listdir(root):
path = os.path.join(root, fn)
if os.path.isdir(path):
break
log('Downloaded to %s', path)
return path
tox_template = """[tox]
envlist=%(env)s
[testenv]
deps=
%(deps)s
distribute
py
commands=bash flaskext-runtest.sh {envlogdir}/test.log
downloadcache=%(cache)s
"""
def create_tox_ini(checkout_path, interpreters, flask_dep):
tox_path = os.path.join(checkout_path, 'tox-flask-test.ini')
if not os.path.exists(tox_path):
with open(tox_path, 'w') as f:
f.write(tox_template % {
'env': ','.join(interpreters),
'cache': tdir,
'deps': flask_dep
})
return tox_path
def iter_extensions(only_approved=True):
for ext in fetch_extensions_list():
if ext['approved'] or not only_approved:
yield ext['name']
def test_extension(name, interpreters, flask_dep):
checkout_path = checkout_extension(name)
log('Running tests with tox in %s', checkout_path)
# figure out the test command and write a wrapper script. We
# can't write that directly into the tox ini because tox does
# not invoke the command from the shell so we have no chance
# to pipe the output into a logfile. The /dev/null hack is
# to trick py.test (if used) into not guessing widths from the
# invoking terminal.
test_command = get_test_command(checkout_path)
log('Test command: %s', test_command)
f = open(checkout_path + '/flaskext-runtest.sh', 'w')
f.write(test_command + ' &> "$1" < /dev/null\n')
f.close()
# if there is a tox.ini, remove it, it will cause troubles
# for us. Remove it if present, we are running tox ourselves
# afterall.
create_tox_ini(checkout_path, interpreters, flask_dep)
rv = subprocess.call(['tox', '-c', 'tox-flask-test.ini'], cwd=checkout_path)
return TestResult(name, checkout_path, rv, interpreters)
def run_tests(extensions, interpreters):
results = {}
create_tdir()
log('Packaging Flask')
flask_dep = package_flask()
log('Running extension tests')
log('Temporary Environment: %s', tdir)
for name in extensions:
log('Testing %s', name)
result = test_extension(name, interpreters, flask_dep)
if result.success:
log('Extension test succeeded')
else:
log('Extension test failed')
results[name] = result
return results
def render_results(results, approved):
from jinja2 import Template
items = results.values()
items.sort(key=lambda x: x.name.lower())
rv = Template(RESULT_TEMPATE, autoescape=True).render(results=items,
approved=approved)
fd, filename = tempfile.mkstemp(suffix='.html')
os.fdopen(fd, 'w').write(rv.encode('utf-8') + '\n')
return filename
def main():
parser = argparse.ArgumentParser(description='Runs Flask extension tests')
parser.add_argument('--all', dest='all', action='store_true',
help='run against all extensions, not just approved')
parser.add_argument('--browse', dest='browse', action='store_true',
help='show browser with the result summary')
parser.add_argument('--env', dest='env', default='py25,py26,py27',
help='the tox environments to run against')
parser.add_argument('--extension=', dest='extension', default=None,
help='tests a single extension')
args = parser.parse_args()
if args.extension is not None:
only_approved = False
extensions = [args.extension]
else:
only_approved = not args.all
extensions = iter_extensions(only_approved)
results = run_tests(extensions, [x.strip() for x in args.env.split(',')])
filename = render_results(results, only_approved)
if args.browse:
import webbrowser
webbrowser.open('file:///' + filename.lstrip('/'))
print('Results written to {}'.format(filename))
if __name__ == '__main__':
main()

84
scripts/make-release.py

@ -1,42 +1,41 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
make-release
~~~~~~~~~~~~
Helper script that performs a release. Does pretty much everything
automatically for us.
:copyright: (c) 2015 by Armin Ronacher.
:license: BSD, see LICENSE for more details.
"""
from __future__ import print_function
import sys
import os
import re
from datetime import datetime, date
from subprocess import Popen, PIPE
import sys
from datetime import date, datetime
from subprocess import PIPE, Popen
_date_clean_re = re.compile(r'(\d+)(st|nd|rd|th)')
_date_strip_re = re.compile(r'(?<=\d)(st|nd|rd|th)')
def parse_changelog():
with open('CHANGES') as f:
with open('CHANGES.rst') as f:
lineiter = iter(f)
for line in lineiter:
match = re.search('^Version\s+(.*)', line.strip())
if match is None:
continue
version = match.group(1).strip()
if lineiter.next().count('-') != len(match.group(0)):
if next(lineiter).count('-') != len(match.group(0)):
continue
while 1:
change_info = lineiter.next().strip()
change_info = next(lineiter).strip()
if change_info:
break
match = re.search(r'released on (\w+\s+\d+\w+\s+\d+)'
r'(?:, codename (.*))?(?i)', change_info)
match = re.search(
r'released on (\w+\s+\d+\w+\s+\d+)(?:, codename (.*))?',
change_info,
flags=re.IGNORECASE
)
if match is None:
continue
@ -46,15 +45,16 @@ def parse_changelog():
def bump_version(version):
try:
parts = map(int, version.split('.'))
parts = [int(i) for i in version.split('.')]
except ValueError:
fail('Current version is not numeric')
parts[-1] += 1
return '.'.join(map(str, parts))
def parse_date(string):
string = _date_clean_re.sub(r'\1', string)
string = _date_strip_re.sub('', string)
return datetime.strptime(string, '%B %d %Y')
@ -65,9 +65,13 @@ def set_filename_version(filename, version_number, pattern):
before, old, after = match.groups()
changed.append(True)
return before + version_number + after
with open(filename) as f:
contents = re.sub(r"^(\s*%s\s*=\s*')(.+?)(')(?sm)" % pattern,
inject_version, f.read())
contents = re.sub(
r"^(\s*%s\s*=\s*')(.+?)(')" % pattern,
inject_version, f.read(),
flags=re.DOTALL | re.MULTILINE
)
if not changed:
fail('Could not find %s in %s', pattern, filename)
@ -81,8 +85,9 @@ def set_init_version(version):
set_filename_version('flask/__init__.py', version, '__version__')
def build_and_upload():
Popen([sys.executable, 'setup.py', 'release', 'sdist', 'bdist_wheel', 'upload']).wait()
def build():
cmd = [sys.executable, 'setup.py', 'sdist', 'bdist_wheel']
Popen(cmd).wait()
def fail(message, *args):
@ -95,7 +100,9 @@ def info(message, *args):
def get_git_tags():
return set(Popen(['git', 'tag'], stdout=PIPE).communicate()[0].splitlines())
return set(
Popen(['git', 'tag'], stdout=PIPE).communicate()[0].splitlines()
)
def git_is_clean():
@ -116,29 +123,40 @@ def main():
os.chdir(os.path.join(os.path.dirname(__file__), '..'))
rv = parse_changelog()
if rv is None:
fail('Could not parse changelog')
version, release_date, codename = rv
dev_version = bump_version(version) + '-dev'
dev_version = bump_version(version) + '.dev'
info('Releasing %s (codename %s, release date %s)',
version, codename, release_date.strftime('%d/%m/%Y'))
info(
'Releasing %s (codename %s, release date %s)',
version, codename, release_date.strftime('%d/%m/%Y')
)
tags = get_git_tags()
if version in tags:
fail('Version "%s" is already tagged', version)
if release_date.date() != date.today():
fail('Release date is not today (%s != %s)',
release_date.date(), date.today())
fail(
'Release date is not today (%s != %s)',
release_date.date(), date.today()
)
if not git_is_clean():
fail('You have uncommitted changes in git')
try:
import wheel # noqa: F401
except ImportError:
fail('You need to install the wheel package.')
set_init_version(version)
make_git_commit('Bump version number to %s', version)
make_git_tag(version)
build_and_upload()
build()
set_init_version(dev_version)

2
setup.cfg

@ -1,5 +1,5 @@
[aliases]
release = egg_info -RDb ''
release = egg_info -Db ''
[bdist_wheel]
universal = 1

97
setup.py

@ -1,112 +1,67 @@
"""
Flask
-----
Flask is a microframework for Python based on Werkzeug, Jinja 2 and good
intentions. And before you ask: It's BSD licensed!
Flask is Fun
````````````
Save in a hello.py:
.. code:: python
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello World!"
if __name__ == "__main__":
app.run()
And Easy to Setup
`````````````````
And run it:
.. code:: bash
$ pip install Flask
$ python hello.py
* Running on http://localhost:5000/
Ready for production? `Read this first <http://flask.pocoo.org/docs/deploying/>`.
Links
`````
* `website <http://flask.pocoo.org/>`_
* `documentation <http://flask.pocoo.org/docs/>`_
* `development version
<https://github.com/pallets/flask/zipball/master#egg=Flask-dev>`_
"""
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import io
import re
import ast
from setuptools import setup
_version_re = re.compile(r'__version__\s+=\s+(.*)')
with io.open('README.rst', 'rt', encoding='utf8') as f:
readme = f.read()
with open('flask/__init__.py', 'rb') as f:
version = str(ast.literal_eval(_version_re.search(
f.read().decode('utf-8')).group(1)))
with io.open('flask/__init__.py', 'rt', encoding='utf8') as f:
version = re.search(r'__version__ = \'(.*?)\'', f.read()).group(1)
setup(
name='Flask',
version=version,
url='https://github.com/pallets/flask/',
url='https://www.palletsprojects.com/p/flask/',
license='BSD',
author='Armin Ronacher',
author_email='armin.ronacher@active-4.com',
description='A microframework based on Werkzeug, Jinja2 '
'and good intentions',
long_description=__doc__,
maintainer='Pallets team',
maintainer_email='contact@palletsprojects.com',
description='A simple framework for building complex web applications.',
long_description=readme,
packages=['flask', 'flask.json'],
include_package_data=True,
zip_safe=False,
platforms='any',
install_requires=[
'Werkzeug>=0.9',
'Jinja2>=2.4',
'itsdangerous>=0.21',
'click>=4.0',
'Werkzeug>=0.14',
'Jinja2>=2.10',
'itsdangerous>=0.24',
'click>=5.1',
],
extras_require={
'dotenv': ['python-dotenv'],
'dev': [
'blinker',
'python-dotenv',
'greenlet',
'pytest>=3',
'coverage',
'tox',
'sphinx',
'sphinxcontrib-log-cabinet'
],
},
classifiers=[
'Development Status :: 4 - Beta',
'Development Status :: 5 - Production/Stable',
'Environment :: Web Environment',
'Framework :: Flask',
'Intended Audience :: Developers',
'License :: OSI Approved :: BSD License',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
'Topic :: Software Development :: Libraries :: Python Modules'
'Topic :: Internet :: WWW/HTTP :: WSGI :: Application',
'Topic :: Software Development :: Libraries :: Application Frameworks',
'Topic :: Software Development :: Libraries :: Python Modules',
],
entry_points='''
[console_scripts]
flask=flask.cli:main
'''
entry_points={
'console_scripts': [
'flask = flask.cli:main',
],
},
)

3
test-requirements.txt

@ -1,3 +0,0 @@
tox
pytest
pytest-cov

47
tests/conftest.py

@ -3,19 +3,56 @@
tests.conftest
~~~~~~~~~~~~~~
:copyright: (c) 2015 by the Flask Team, see AUTHORS for more details.
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""
import flask
import gc
import os
import sys
import pkgutil
import pytest
import sys
import textwrap
import pytest
from _pytest import monkeypatch
import flask
from flask import Flask as _Flask
@pytest.fixture(scope='session', autouse=True)
def _standard_os_environ():
"""Set up ``os.environ`` at the start of the test session to have
standard values. Returns a list of operations that is used by
:func:`._reset_os_environ` after each test.
"""
mp = monkeypatch.MonkeyPatch()
out = (
(os.environ, 'FLASK_APP', monkeypatch.notset),
(os.environ, 'FLASK_ENV', monkeypatch.notset),
(os.environ, 'FLASK_DEBUG', monkeypatch.notset),
(os.environ, 'FLASK_RUN_FROM_CLI', monkeypatch.notset),
(os.environ, 'WERKZEUG_RUN_MAIN', monkeypatch.notset),
)
for _, key, value in out:
if value is monkeypatch.notset:
mp.delenv(key, False)
else:
mp.setenv(key, value)
yield out
mp.undo()
@pytest.fixture(autouse=True)
def _reset_os_environ(monkeypatch, _standard_os_environ):
"""Reset ``os.environ`` to the standard environ after each test,
in case a test changed something without cleaning up.
"""
monkeypatch._setitem.extend(_standard_os_environ)
class Flask(_Flask):
testing = True
secret_key = 'test key'
@ -23,7 +60,7 @@ class Flask(_Flask):
@pytest.fixture
def app():
app = Flask(__name__)
app = Flask('flask_test', root_path=os.path.dirname(__file__))
return app

4
tests/test_appctx.py

@ -5,7 +5,7 @@
Tests the application context.
:copyright: (c) 2015 by Armin Ronacher.
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""
@ -215,5 +215,5 @@ def test_clean_pop(app):
except ZeroDivisionError:
pass
assert called == ['conftest', 'TEARDOWN']
assert called == ['flask_test', 'TEARDOWN']
assert not flask.current_app

77
tests/test_basic.py

@ -5,7 +5,7 @@
The basic functionality.
:copyright: (c) 2015 by Armin Ronacher.
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""
@ -221,12 +221,21 @@ def test_endpoint_decorator(app, client):
def test_session(app, client):
@app.route('/set', methods=['POST'])
def set():
assert not flask.session.accessed
assert not flask.session.modified
flask.session['value'] = flask.request.form['value']
assert flask.session.accessed
assert flask.session.modified
return 'value set'
@app.route('/get')
def get():
return flask.session['value']
assert not flask.session.accessed
assert not flask.session.modified
v = flask.session.get('value', 'None')
assert flask.session.accessed
assert not flask.session.modified
return v
assert client.post('/set', data={'value': '42'}).data == b'value set'
assert client.get('/get').data == b'42'
@ -310,6 +319,7 @@ def test_session_using_session_settings(app, client):
SESSION_COOKIE_DOMAIN='.example.com',
SESSION_COOKIE_HTTPONLY=False,
SESSION_COOKIE_SECURE=True,
SESSION_COOKIE_SAMESITE='Lax',
SESSION_COOKIE_PATH='/'
)
@ -324,6 +334,34 @@ def test_session_using_session_settings(app, client):
assert 'path=/' in cookie
assert 'secure' in cookie
assert 'httponly' not in cookie
assert 'samesite' in cookie
def test_session_using_samesite_attribute(app, client):
@app.route('/')
def index():
flask.session['testing'] = 42
return 'Hello World'
app.config.update(SESSION_COOKIE_SAMESITE='invalid')
with pytest.raises(ValueError):
client.get('/')
app.config.update(SESSION_COOKIE_SAMESITE=None)
rv = client.get('/')
cookie = rv.headers['set-cookie'].lower()
assert 'samesite' not in cookie
app.config.update(SESSION_COOKIE_SAMESITE='Strict')
rv = client.get('/')
cookie = rv.headers['set-cookie'].lower()
assert 'samesite=strict' in cookie
app.config.update(SESSION_COOKIE_SAMESITE='Lax')
rv = client.get('/')
cookie = rv.headers['set-cookie'].lower()
assert 'samesite=lax' in cookie
def test_session_localhost_warning(recwarn, app, client):
@ -1391,10 +1429,12 @@ def test_request_locals():
assert not flask.g
def test_test_app_proper_environ(app, client):
def test_test_app_proper_environ():
app = flask.Flask(__name__, subdomain_matching=True)
app.config.update(
SERVER_NAME='localhost.localdomain:5000'
)
client = app.test_client()
@app.route('/')
def index():
@ -1745,8 +1785,10 @@ def test_g_iteration_protocol(app_ctx):
assert sorted(flask.g) == ['bar', 'foo']
def test_subdomain_basic_support(app, client):
def test_subdomain_basic_support():
app = flask.Flask(__name__, subdomain_matching=True)
app.config['SERVER_NAME'] = 'localhost.localdomain'
client = app.test_client()
@app.route('/')
def normal_index():
@ -1763,7 +1805,9 @@ def test_subdomain_basic_support(app, client):
assert rv.data == b'test index'
def test_subdomain_matching(app, client):
def test_subdomain_matching():
app = flask.Flask(__name__, subdomain_matching=True)
client = app.test_client()
app.config['SERVER_NAME'] = 'localhost.localdomain'
@app.route('/', subdomain='<user>')
@ -1774,8 +1818,10 @@ def test_subdomain_matching(app, client):
assert rv.data == b'index for mitsuhiko'
def test_subdomain_matching_with_ports(app, client):
def test_subdomain_matching_with_ports():
app = flask.Flask(__name__, subdomain_matching=True)
app.config['SERVER_NAME'] = 'localhost.localdomain:3000'
client = app.test_client()
@app.route('/', subdomain='<user>')
def index(user):
@ -1785,6 +1831,25 @@ def test_subdomain_matching_with_ports(app, client):
assert rv.data == b'index for mitsuhiko'
@pytest.mark.parametrize('matching', (False, True))
def test_subdomain_matching_other_name(matching):
app = flask.Flask(__name__, subdomain_matching=matching)
app.config['SERVER_NAME'] = 'localhost.localdomain:3000'
client = app.test_client()
@app.route('/')
def index():
return '', 204
# ip address can't match name
rv = client.get('/', 'http://127.0.0.1:3000/')
assert rv.status_code == 404 if matching else 204
# allow all subdomains if matching is disabled
rv = client.get('/', 'http://www.localhost.localdomain:3000/')
assert rv.status_code == 404 if matching else 204
def test_multi_route_rules(app, client):
@app.route('/')
@app.route('/<test>/')

17
tests/test_blueprints.py

@ -5,7 +5,7 @@
Blueprints (and currently modules)
:copyright: (c) 2015 by Armin Ronacher.
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""
@ -114,7 +114,20 @@ def test_blueprint_app_error_handling(app, client):
assert client.get('/nope').data == b'you shall not pass'
def test_blueprint_url_definitions(app, client):
def test_blueprint_prefix_slash(app, client):
bp = flask.Blueprint('test', __name__, url_prefix='/bar/')
@bp.route('/foo')
def foo():
return '', 204
app.register_blueprint(bp)
app.register_blueprint(bp, url_prefix='/spam/')
assert client.get('/bar/foo').status_code == 204
assert client.get('/spam/foo').status_code == 204
def test_blueprint_url_defaults(app, client):
bp = flask.Blueprint('test', __name__)
@bp.route('/foo', defaults={'baz': 42})

98
tests/test_cli.py

@ -3,18 +3,19 @@
tests.test_cli
~~~~~~~~~~~~~~
:copyright: (c) 2016 by the Flask Team, see AUTHORS for more details.
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""
#
# This file was part of Flask-CLI and was modified under the terms its license,
# the Revised BSD License.
# Copyright (C) 2015 CERN.
#
# This file was part of Flask-CLI and was modified under the terms of
# its Revised BSD License. Copyright © 2015 CERN.
from __future__ import absolute_import
import os
import ssl
import sys
import types
from functools import partial
import click
@ -23,9 +24,11 @@ from _pytest.monkeypatch import notset
from click.testing import CliRunner
from flask import Flask, current_app
from flask.cli import AppGroup, FlaskGroup, NoAppException, ScriptInfo, dotenv, \
find_best_app, get_version, load_dotenv, locate_app, prepare_import, \
from flask.cli import (
AppGroup, FlaskGroup, NoAppException, ScriptInfo, dotenv, find_best_app,
get_version, load_dotenv, locate_app, prepare_import, run_command,
with_appcontext
)
cwd = os.getcwd()
test_path = os.path.abspath(os.path.join(
@ -33,19 +36,6 @@ test_path = os.path.abspath(os.path.join(
))
@pytest.fixture(autouse=True)
def manage_os_environ(monkeypatch):
# can't use monkeypatch.delitem since we don't want to restore a value
os.environ.pop('FLASK_APP', None)
os.environ.pop('FLASK_DEBUG', None)
# use monkeypatch internals to force-delete environ keys
monkeypatch._setitem.extend((
(os.environ, 'FLASK_APP', notset),
(os.environ, 'FLASK_DEBUG', notset),
(os.environ, 'FLASK_RUN_FROM_CLI', notset),
))
@pytest.fixture
def runner():
return CliRunner()
@ -144,6 +134,13 @@ def test_find_best_app(test_apps):
pytest.raises(NoAppException, find_best_app, script_info, Module)
class Module:
@staticmethod
def create_app():
raise TypeError('bad bad factory!')
pytest.raises(TypeError, find_best_app, script_info, Module)
@pytest.mark.parametrize('value,path,result', (
('test', cwd, 'test'),
@ -475,3 +472,62 @@ def test_dotenv_optional(monkeypatch):
monkeypatch.chdir(test_path)
load_dotenv()
assert 'FOO' not in os.environ
def test_run_cert_path():
# no key
with pytest.raises(click.BadParameter):
run_command.make_context('run', ['--cert', __file__])
# no cert
with pytest.raises(click.BadParameter):
run_command.make_context('run', ['--key', __file__])
ctx = run_command.make_context(
'run', ['--cert', __file__, '--key', __file__])
assert ctx.params['cert'] == (__file__, __file__)
def test_run_cert_adhoc(monkeypatch):
monkeypatch.setitem(sys.modules, 'OpenSSL', None)
# pyOpenSSL not installed
with pytest.raises(click.BadParameter):
run_command.make_context('run', ['--cert', 'adhoc'])
# pyOpenSSL installed
monkeypatch.setitem(sys.modules, 'OpenSSL', types.ModuleType('OpenSSL'))
ctx = run_command.make_context('run', ['--cert', 'adhoc'])
assert ctx.params['cert'] == 'adhoc'
# no key with adhoc
with pytest.raises(click.BadParameter):
run_command.make_context('run', ['--cert', 'adhoc', '--key', __file__])
def test_run_cert_import(monkeypatch):
monkeypatch.setitem(sys.modules, 'not_here', None)
# ImportError
with pytest.raises(click.BadParameter):
run_command.make_context('run', ['--cert', 'not_here'])
# not an SSLContext
if sys.version_info >= (2, 7):
with pytest.raises(click.BadParameter):
run_command.make_context('run', ['--cert', 'flask'])
# SSLContext
if sys.version_info < (2, 7):
ssl_context = object()
else:
ssl_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
monkeypatch.setitem(sys.modules, 'ssl_context', ssl_context)
ctx = run_command.make_context('run', ['--cert', 'ssl_context'])
assert ctx.params['cert'] is ssl_context
# no --key with SSLContext
with pytest.raises(click.BadParameter):
run_command.make_context(
'run', ['--cert', 'ssl_context', '--key', __file__])

3
tests/test_config.py

@ -3,11 +3,10 @@
tests.test_config
~~~~~~~~~~~~~~~~~
:copyright: (c) 2015 by the Flask Team, see AUTHORS for more details.
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""
from datetime import timedelta
import os
import textwrap

46
tests/test_helpers.py

@ -5,7 +5,7 @@
Various helpers.
:copyright: (c) 2015 by Armin Ronacher.
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""
@ -21,7 +21,7 @@ from werkzeug.http import http_date, parse_cache_control_header, \
import flask
from flask._compat import StringIO, text_type
from flask.helpers import get_debug_flag
from flask.helpers import get_debug_flag, get_env
def has_encoding(name):
@ -506,7 +506,7 @@ class TestSendfile(object):
@pytest.mark.skipif(
not callable(getattr(Range, 'to_content_range_header', None)),
reason="not implement within werkzeug"
reason="not implemented within werkzeug"
)
def test_send_file_range_request(self, app, client):
@app.route('/')
@ -563,7 +563,32 @@ class TestSendfile(object):
assert rv.status_code == 200
rv.close()
@pytest.mark.skipif(
not callable(getattr(Range, 'to_content_range_header', None)),
reason="not implemented within werkzeug"
)
def test_send_file_range_request_xsendfile_invalid(self, app, client):
# https://github.com/pallets/flask/issues/2526
app.use_x_sendfile = True
@app.route('/')
def index():
return flask.send_file('static/index.html', conditional=True)
rv = client.get('/', headers={'Range': 'bytes=1000-'})
assert rv.status_code == 416
rv.close()
def test_attachment(self, app, req_ctx):
app = flask.Flask(__name__)
with app.test_request_context():
with open(os.path.join(app.root_path, 'static/index.html')) as f:
rv = flask.send_file(f, as_attachment=True,
attachment_filename='index.html')
value, options = \
parse_options_header(rv.headers['Content-Disposition'])
assert value == 'attachment'
rv.close()
with open(os.path.join(app.root_path, 'static/index.html')) as f:
rv = flask.send_file(f, as_attachment=True,
@ -861,7 +886,7 @@ class TestSafeJoin(object):
class TestHelpers(object):
@pytest.mark.parametrize('debug, expected_flag, expected_default_flag', [
('', None, True),
('', False, False),
('0', False, False),
('False', False, False),
('No', False, False),
@ -873,7 +898,18 @@ class TestHelpers(object):
assert get_debug_flag() is None
else:
assert get_debug_flag() == expected_flag
assert get_debug_flag(default=True) == expected_default_flag
assert get_debug_flag() == expected_default_flag
@pytest.mark.parametrize('env, ref_env, debug', [
('', 'production', False),
('production', 'production', False),
('development', 'development', True),
('other', 'other', False),
])
def test_get_env(self, monkeypatch, env, ref_env, debug):
monkeypatch.setenv('FLASK_ENV', env)
assert get_debug_flag() == debug
assert get_env() == ref_env
def test_make_response(self):
app = flask.Flask(__name__)

3
tests/test_instance_config.py

@ -3,9 +3,10 @@
tests.test_instance
~~~~~~~~~~~~~~~~~~~
:copyright: (c) 2015 by the Flask Team, see AUTHORS for more details.
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""
import os
import sys

9
tests/test_json_tag.py

@ -1,3 +1,12 @@
# -*- coding: utf-8 -*-
"""
tests.test_json_tag
~~~~~~~~~~~~~~~~~~~
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""
from datetime import datetime
from uuid import uuid4

18
tests/test_logging.py

@ -1,3 +1,12 @@
# -*- coding: utf-8 -*-
"""
tests.test_logging
~~~~~~~~~~~~~~~~~~~
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""
import logging
import sys
@ -9,14 +18,18 @@ from flask.logging import default_handler, has_level_handler, \
@pytest.fixture(autouse=True)
def reset_logging(monkeypatch):
def reset_logging(pytestconfig):
root_handlers = logging.root.handlers[:]
logging.root.handlers = []
root_level = logging.root.level
logger = logging.getLogger('flask.app')
logger.handlers = []
logger.setLevel(logging.NOTSET)
logging_plugin = pytestconfig.pluginmanager.unregister(
name='logging-plugin')
yield
logging.root.handlers[:] = root_handlers
@ -25,6 +38,9 @@ def reset_logging(monkeypatch):
logger.handlers = []
logger.setLevel(logging.NOTSET)
if logging_plugin:
pytestconfig.pluginmanager.register(logging_plugin, 'logging-plugin')
def test_logger(app):
assert app.logger.name == 'flask.app'

2
tests/test_regression.py

@ -5,7 +5,7 @@
Tests regressions.
:copyright: (c) 2015 by Armin Ronacher.
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""

2
tests/test_reqctx.py

@ -5,7 +5,7 @@
Tests the request context.
:copyright: (c) 2015 by Armin Ronacher.
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""

2
tests/test_signals.py

@ -5,7 +5,7 @@
Signalling.
:copyright: (c) 2015 by Armin Ronacher.
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""

2
tests/test_subclassing.py

@ -6,7 +6,7 @@
Test that certain behavior of flask can be customized by
subclasses.
:copyright: (c) 2015 by Armin Ronacher.
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""

2
tests/test_templating.py

@ -5,7 +5,7 @@
Template functionality
:copyright: (c) 2015 by Armin Ronacher.
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""

58
tests/test_testing.py

@ -5,17 +5,19 @@
Test client and more.
:copyright: (c) 2015 by Armin Ronacher.
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""
import click
import pytest
import flask
import werkzeug
from flask._compat import text_type
from flask.cli import ScriptInfo
from flask.json import jsonify
from flask.testing import make_test_environ_builder
from flask.testing import make_test_environ_builder, FlaskCliRunner
def test_environ_defaults_from_config(app, client):
@ -112,9 +114,11 @@ def test_path_is_url(app):
assert eb.path == '/'
def test_blueprint_with_subdomain(app, client):
def test_blueprint_with_subdomain():
app = flask.Flask(__name__, subdomain_matching=True)
app.config['SERVER_NAME'] = 'example.com:1234'
app.config['APPLICATION_ROOT'] = '/foo'
client = app.test_client()
bp = flask.Blueprint('company', __name__, subdomain='xxx')
@ -302,8 +306,10 @@ def test_json_request_and_response(app, client):
assert rv.get_json() == json_data
def test_subdomain(app, client):
def test_subdomain():
app = flask.Flask(__name__, subdomain_matching=True)
app.config['SERVER_NAME'] = 'example.com'
client = app.test_client()
@app.route('/', subdomain='<company_id>')
def view(company_id):
@ -334,3 +340,47 @@ def test_nosubdomain(app, client):
assert 200 == response.status_code
assert b'xxx' == response.data
def test_cli_runner_class(app):
runner = app.test_cli_runner()
assert isinstance(runner, FlaskCliRunner)
class SubRunner(FlaskCliRunner):
pass
app.test_cli_runner_class = SubRunner
runner = app.test_cli_runner()
assert isinstance(runner, SubRunner)
def test_cli_invoke(app):
@app.cli.command('hello')
def hello_command():
click.echo('Hello, World!')
runner = app.test_cli_runner()
# invoke with command name
result = runner.invoke(args=['hello'])
assert 'Hello' in result.output
# invoke with command object
result = runner.invoke(hello_command)
assert 'Hello' in result.output
def test_cli_custom_obj(app):
class NS(object):
called = False
def create_app():
NS.called = True
return app
@app.cli.command('hello')
def hello_command():
click.echo('Hello, World!')
script_info = ScriptInfo(create_app=create_app)
runner = app.test_cli_runner()
runner.invoke(hello_command, obj=script_info)
assert NS.called

8
tests/test_user_error_handler.py

@ -1,4 +1,12 @@
# -*- coding: utf-8 -*-
"""
tests.test_user_error_handler
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""
from werkzeug.exceptions import (
Forbidden,
InternalServerError,

2
tests/test_views.py

@ -5,7 +5,7 @@
Pluggable views.
:copyright: (c) 2015 by Armin Ronacher.
:copyright: © 2010 by the Pallets team.
:license: BSD, see LICENSE for more details.
"""

22
tox.ini

@ -1,15 +1,14 @@
[tox]
envlist =
py{36,35,34,33,27,26,py}
py{36,35,34,27,py}
py{36,27,py}-simplejson
py{36,33,27,26,py}-devel
py{36,33,27,26,py}-lowest
py{36,27,py}-devel
py{36,27,py}-lowest
docs-html
coverage-report
[testenv]
passenv = LANG
usedevelop = true
deps =
pytest>=3
coverage
@ -17,10 +16,10 @@ deps =
blinker
python-dotenv
lowest: Werkzeug==0.9
lowest: Jinja2==2.4
lowest: itsdangerous==0.21
lowest: Click==4.0
lowest: Werkzeug==0.14
lowest: Jinja2==2.10
lowest: itsdangerous==0.24
lowest: Click==5.1
devel: https://github.com/pallets/werkzeug/archive/master.tar.gz
devel: https://github.com/pallets/markupsafe/archive/master.tar.gz
@ -29,6 +28,7 @@ deps =
devel: https://github.com/pallets/click/archive/master.tar.gz
simplejson: simplejson
commands =
# the examples need to be installed to test successfully
pip install -e examples/flaskr -q
@ -40,11 +40,11 @@ commands =
[testenv:docs-html]
deps = sphinx
commands = sphinx-build -W -b html -d {envtmpdir}/doctrees docs docs/_build/html
commands = sphinx-build -W -b html -d {envtmpdir}/doctrees docs {envtmpdir}/html
[testenv:docs-linkcheck]
deps = sphinx
commands = sphinx-build -W -b linkcheck -d {envtmpdir}/doctrees docs docs/_build/linkcheck
commands = sphinx-build -W -b linkcheck -d {envtmpdir}/doctrees docs {envtmpdir}/linkcheck
[testenv:coverage-report]
deps = coverage
@ -59,8 +59,6 @@ passenv = CI TRAVIS TRAVIS_*
deps = codecov
skip_install = true
commands =
# install argparse for 2.6
python -c 'import sys, pip; sys.version_info < (2, 7) and pip.main(["install", "argparse", "-q"])'
coverage combine
coverage report
codecov

Loading…
Cancel
Save