├── .appveyor.yml ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .travis.yml ├── AUTHORS ├── CHANGES.rst ├── CONTRIBUTING.rst ├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.rst ├── artwork ├── LICENSE ├── logo-full.svg └── logo-lineart.svg ├── docs ├── Makefile ├── _static │ ├── debugger.png │ ├── flask-favicon.ico │ ├── flask.png │ ├── logo-full.png │ ├── no.png │ ├── pycharm-runconfig.png │ ├── touch-icon.png │ └── yes.png ├── advanced_foreword.rst ├── api.rst ├── appcontext.rst ├── becomingbig.rst ├── blueprints.rst ├── changelog.rst ├── cli.rst ├── conf.py ├── config.rst ├── contents.rst.inc ├── contributing.rst ├── deploying │ ├── cgi.rst │ ├── fastcgi.rst │ ├── index.rst │ ├── mod_wsgi.rst │ ├── uwsgi.rst │ └── wsgi-standalone.rst ├── design.rst ├── errorhandling.rst ├── extensiondev.rst ├── extensions.rst ├── flaskstyle.sty ├── foreword.rst ├── htmlfaq.rst ├── index.rst ├── installation.rst ├── latexindex.rst ├── license.rst ├── logging.rst ├── logo.pdf ├── make.bat ├── patterns │ ├── apierrors.rst │ ├── appdispatch.rst │ ├── appfactories.rst │ ├── caching.rst │ ├── celery.rst │ ├── deferredcallbacks.rst │ ├── distribute.rst │ ├── errorpages.rst │ ├── fabric.rst │ ├── favicon.rst │ ├── fileuploads.rst │ ├── flashing.rst │ ├── index.rst │ ├── jquery.rst │ ├── lazyloading.rst │ ├── methodoverrides.rst │ ├── mongokit.rst │ ├── packages.rst │ ├── requestchecksum.rst │ ├── sqlalchemy.rst │ ├── sqlite3.rst │ ├── streaming.rst │ ├── subclassing.rst │ ├── templateinheritance.rst │ ├── urlprocessors.rst │ ├── viewdecorators.rst │ └── wtforms.rst ├── quickstart.rst ├── reqcontext.rst ├── security.rst ├── server.rst ├── shell.rst ├── signals.rst ├── styleguide.rst ├── templating.rst ├── testing.rst ├── tutorial │ ├── blog.rst │ ├── database.rst │ ├── deploy.rst │ ├── factory.rst │ ├── flaskr_edit.png │ ├── flaskr_index.png │ ├── flaskr_login.png │ ├── index.rst │ ├── install.rst │ ├── layout.rst │ ├── next.rst │ ├── static.rst │ ├── templates.rst │ ├── tests.rst │ └── views.rst ├── unicode.rst ├── upgrading.rst └── views.rst ├── examples ├── javascript │ ├── .gitignore │ ├── LICENSE │ ├── MANIFEST.in │ ├── README.rst │ ├── js_example │ │ ├── __init__.py │ │ ├── templates │ │ │ ├── base.html │ │ │ ├── fetch.html │ │ │ ├── jquery.html │ │ │ └── plain.html │ │ └── views.py │ ├── setup.cfg │ ├── setup.py │ └── tests │ │ ├── conftest.py │ │ └── test_js_example.py └── tutorial │ ├── .gitignore │ ├── LICENSE │ ├── MANIFEST.in │ ├── README.rst │ ├── flaskr │ ├── __init__.py │ ├── auth.py │ ├── blog.py │ ├── db.py │ ├── schema.sql │ ├── static │ │ └── style.css │ └── templates │ │ ├── auth │ │ ├── login.html │ │ └── register.html │ │ ├── base.html │ │ └── blog │ │ ├── create.html │ │ ├── index.html │ │ └── update.html │ ├── setup.cfg │ ├── setup.py │ └── tests │ ├── conftest.py │ ├── data.sql │ ├── test_auth.py │ ├── test_blog.py │ ├── test_db.py │ └── test_factory.py ├── flask ├── __init__.py ├── __main__.py ├── _compat.py ├── app.py ├── blueprints.py ├── cli.py ├── config.py ├── ctx.py ├── debughelpers.py ├── globals.py ├── helpers.py ├── json │ ├── __init__.py │ └── tag.py ├── logging.py ├── sessions.py ├── signals.py ├── templating.py ├── testing.py ├── views.py └── wrappers.py ├── scripts └── make-release.py ├── setup.cfg ├── setup.py ├── tests ├── conftest.py ├── static │ ├── config.json │ └── index.html ├── templates │ ├── _macro.html │ ├── context_template.html │ ├── escaping_template.html │ ├── mail.txt │ ├── nested │ │ └── nested.txt │ ├── non_escaping_template.txt │ ├── simple_template.html │ ├── template_filter.html │ └── template_test.html ├── test_appctx.py ├── test_apps │ ├── .env │ ├── .flaskenv │ ├── blueprintapp │ │ ├── __init__.py │ │ └── apps │ │ │ ├── __init__.py │ │ │ ├── admin │ │ │ ├── __init__.py │ │ │ ├── static │ │ │ │ ├── css │ │ │ │ │ └── test.css │ │ │ │ └── test.txt │ │ │ └── templates │ │ │ │ └── admin │ │ │ │ └── index.html │ │ │ └── frontend │ │ │ ├── __init__.py │ │ │ └── templates │ │ │ └── frontend │ │ │ └── index.html │ ├── cliapp │ │ ├── __init__.py │ │ ├── app.py │ │ ├── factory.py │ │ ├── importerrorapp.py │ │ ├── inner1 │ │ │ ├── __init__.py │ │ │ └── inner2 │ │ │ │ ├── __init__.py │ │ │ │ └── flask.py │ │ ├── message.txt │ │ └── multiapp.py │ ├── helloworld │ │ ├── hello.py │ │ └── wsgi.py │ └── subdomaintestmodule │ │ ├── __init__.py │ │ └── static │ │ └── hello.txt ├── test_basic.py ├── test_blueprints.py ├── test_cli.py ├── test_config.py ├── test_helpers.py ├── test_instance_config.py ├── test_json_tag.py ├── test_logging.py ├── test_regression.py ├── test_reqctx.py ├── test_signals.py ├── test_subclassing.py ├── test_templating.py ├── test_testing.py ├── test_user_error_handler.py └── test_views.py └── tox.ini /.appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | global: 3 | TOXENV: py,codecov 4 | 5 | matrix: 6 | - PYTHON: C:\Python36-x64 7 | - PYTHON: C:\Python27-x64 8 | 9 | init: 10 | - SET PATH=%PYTHON%;%PATH% 11 | 12 | install: 13 | - python -m pip install -U tox 14 | 15 | build: false 16 | 17 | test_script: 18 | - python -m tox 19 | 20 | branches: 21 | only: 22 | - master 23 | - /^.*-maintenance$/ 24 | 25 | cache: 26 | - '%LOCALAPPDATA%\pip\Cache' 27 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | CHANGES.rst merge=union 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | **This issue tracker is a tool to address bugs in Flask itself. 2 | Please use the #pocoo IRC channel on freenode or Stack Overflow for general 3 | questions about using Flask or issues not related to Flask.** 4 | 5 | If you'd like to report a bug in Flask, fill out the template below. Provide 6 | any any extra information that may be useful / related to your problem. 7 | Ideally, create an [MCVE](http://stackoverflow.com/help/mcve), which helps us 8 | understand the problem and helps check that it is not caused by something in 9 | your code. 10 | 11 | --- 12 | 13 | ### Expected Behavior 14 | 15 | Tell us what should happen. 16 | 17 | ```python 18 | Paste a minimal example that causes the problem. 19 | ``` 20 | 21 | ### Actual Behavior 22 | 23 | Tell us what happens instead. 24 | 25 | ```pytb 26 | Paste the full traceback if there was an exception. 27 | ``` 28 | 29 | ### Environment 30 | 31 | * Python version: 32 | * Flask version: 33 | * Werkzeug version: 34 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Describe what this patch does to fix the issue. 2 | 3 | Link to any relevant issues or pull requests. 4 | 5 | 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .env 3 | .flaskenv 4 | *.pyc 5 | *.pyo 6 | env/ 7 | env* 8 | dist/ 9 | build/ 10 | *.egg 11 | *.egg-info/ 12 | _mailinglist 13 | .tox/ 14 | .cache/ 15 | .pytest_cache/ 16 | .idea/ 17 | docs/_build/ 18 | 19 | # Coverage reports 20 | htmlcov/ 21 | .coverage 22 | .coverage.* 23 | *,cover 24 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | os: linux 2 | sudo: false 3 | language: python 4 | 5 | matrix: 6 | include: 7 | - python: 3.6 8 | env: TOXENV=py,simplejson,devel,lowest,codecov 9 | - python: 3.6 10 | env: TOXENV=docs-html 11 | - python: 3.5 12 | env: TOXENV=py,codecov 13 | - python: 3.4 14 | env: TOXENV=py,codecov 15 | - python: 2.7 16 | env: TOXENV=py,simplejson,devel,lowest,codecov 17 | - python: pypy3 18 | env: TOXENV=py,codecov 19 | - python: nightly 20 | env: TOXENV=py 21 | - os: osx 22 | language: generic 23 | env: TOXENV=py3,py2,codecov 24 | cache: 25 | pip: false 26 | directories: 27 | - $HOME/Library/Caches/Homebrew 28 | - $HOME/Library/Caches/pip 29 | allow_failures: 30 | - python: pypy3 31 | - python: nightly 32 | - os: osx 33 | fast_finish: true 34 | 35 | before_install: 36 | - | 37 | if [[ $TRAVIS_OS_NAME == 'osx' ]]; then 38 | brew upgrade python 39 | brew install python@2; 40 | export PATH="/usr/local/opt/python/libexec/bin:${PATH}" 41 | fi 42 | 43 | install: 44 | - pip install tox 45 | 46 | script: 47 | - tox 48 | 49 | cache: 50 | - pip 51 | 52 | branches: 53 | only: 54 | - master 55 | - /^.*-maintenance$/ 56 | 57 | notifications: 58 | email: false 59 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Flask is developed and maintained by the Pallets team and community 2 | contributors. It was created by Armin Ronacher. The core maintainers 3 | are: 4 | 5 | - David Lord (davidism) 6 | - Adrian Mönnich (ThiefMaster) 7 | - Armin Ronacher (mitsuhiko) 8 | - Marcus Unterwaditzer (untitaker) 9 | 10 | A full list of contributors is available from git with:: 11 | 12 | git shortlog -sne 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2010 by the Pallets team. 2 | 3 | Some rights reserved. 4 | 5 | Redistribution and use in source and binary forms of the software as 6 | well as documentation, with or without modification, are permitted 7 | provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, 10 | this list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in the 14 | documentation and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND 21 | CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 22 | BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 23 | FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 | COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 26 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 27 | USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 28 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30 | THIS SOFTWARE AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF 31 | SUCH DAMAGE. 32 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include Makefile CHANGES.rst LICENSE AUTHORS tox.ini 2 | 3 | graft artwork 4 | graft tests 5 | graft examples 6 | graft docs 7 | 8 | global-exclude *.py[co] 9 | 10 | prune docs/_build 11 | prune docs/_themes 12 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all install-dev test coverage cov test-all tox docs release clean-pyc upload-docs ebook 2 | 3 | all: test 4 | 5 | install-dev: 6 | pip install -q -e .[dev] 7 | 8 | test: clean-pyc install-dev 9 | pytest 10 | 11 | coverage: clean-pyc install-dev 12 | coverage run -m pytest 13 | coverage report 14 | coverage html 15 | 16 | cov: coverage 17 | 18 | test-all: install-dev 19 | tox 20 | 21 | tox: test-all 22 | 23 | docs: clean-pyc install-dev 24 | $(MAKE) -C docs html 25 | 26 | release: 27 | python scripts/make-release.py 28 | 29 | clean-pyc: 30 | find . -name '*.pyc' -exec rm -f {} + 31 | find . -name '*.pyo' -exec rm -f {} + 32 | find . -name '*~' -exec rm -f {} + 33 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Flask 2 | ===== 3 | 4 | Flask is a lightweight `WSGI`_ web application framework. It is designed 5 | to make getting started quick and easy, with the ability to scale up to 6 | complex applications. It began as a simple wrapper around `Werkzeug`_ 7 | and `Jinja`_ and has become one of the most popular Python web 8 | application frameworks. 9 | 10 | Flask offers suggestions, but doesn't enforce any dependencies or 11 | project layout. It is up to the developer to choose the tools and 12 | libraries they want to use. There are many extensions provided by the 13 | community that make adding new functionality easy. 14 | 15 | 16 | Installing 17 | ---------- 18 | 19 | Install and update using `pip`_: 20 | 21 | .. code-block:: text 22 | 23 | pip install -U Flask 24 | 25 | 26 | A Simple Example 27 | ---------------- 28 | 29 | .. code-block:: python 30 | 31 | from flask import Flask 32 | 33 | app = Flask(__name__) 34 | 35 | @app.route('/') 36 | def hello(): 37 | return 'Hello, World!' 38 | 39 | .. code-block:: text 40 | 41 | $ env FLASK_APP=hello.py flask run 42 | * Serving Flask app "hello" 43 | * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) 44 | 45 | 46 | Contributing 47 | ------------ 48 | 49 | For guidance on setting up a development environment and how to make a 50 | contribution to Flask, see the `contributing guidelines`_. 51 | 52 | .. _contributing guidelines: https://github.com/pallets/flask/blob/master/CONTRIBUTING.rst 53 | 54 | 55 | Donate 56 | ------ 57 | 58 | The Pallets organization develops and supports Flask and the libraries 59 | it uses. In order to grow the community of contributors and users, and 60 | allow the maintainers to devote more time to the projects, `please 61 | donate today`_. 62 | 63 | .. _please donate today: https://psfmember.org/civicrm/contribute/transact?reset=1&id=20 64 | 65 | 66 | Links 67 | ----- 68 | 69 | * Website: https://www.palletsprojects.com/p/flask/ 70 | * Documentation: http://flask.pocoo.org/docs/ 71 | * License: `BSD `_ 72 | * Releases: https://pypi.org/project/Flask/ 73 | * Code: https://github.com/pallets/flask 74 | * Issue tracker: https://github.com/pallets/flask/issues 75 | * Test status: 76 | 77 | * Linux, Mac: https://travis-ci.org/pallets/flask 78 | * Windows: https://ci.appveyor.com/project/pallets/flask 79 | 80 | * Test coverage: https://codecov.io/gh/pallets/flask 81 | 82 | .. _WSGI: https://wsgi.readthedocs.io 83 | .. _Werkzeug: https://www.palletsprojects.com/p/werkzeug/ 84 | .. _Jinja: https://www.palletsprojects.com/p/jinja/ 85 | .. _pip: https://pip.pypa.io/en/stable/quickstart/ 86 | -------------------------------------------------------------------------------- /artwork/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 by Armin Ronacher. 2 | 3 | Some rights reserved. 4 | 5 | This logo or a modified version may be used by anyone to refer to the 6 | Flask project, but does not indicate endorsement by the project. 7 | 8 | Redistribution and use in source (the SVG file) and binary forms (rendered 9 | PNG files etc.) of the image, with or without modification, are permitted 10 | provided that the following conditions are met: 11 | 12 | * Redistributions of source code must retain the above copyright 13 | notice and this list of conditions. 14 | 15 | * The names of the contributors to the Flask software (see AUTHORS) may 16 | not be used to endorse or promote products derived from this software 17 | without specific prior written permission. 18 | 19 | Note: we would appreciate that you make the image a link to 20 | http://flask.pocoo.org/ if you use it on a web page. 21 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SPHINXPROJ = Flask 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -------------------------------------------------------------------------------- /docs/_static/debugger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyup-bot/flask/b9b88b0cdf70190dee5c7d31a83efb1cae888a63/docs/_static/debugger.png -------------------------------------------------------------------------------- /docs/_static/flask-favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyup-bot/flask/b9b88b0cdf70190dee5c7d31a83efb1cae888a63/docs/_static/flask-favicon.ico -------------------------------------------------------------------------------- /docs/_static/flask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyup-bot/flask/b9b88b0cdf70190dee5c7d31a83efb1cae888a63/docs/_static/flask.png -------------------------------------------------------------------------------- /docs/_static/logo-full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyup-bot/flask/b9b88b0cdf70190dee5c7d31a83efb1cae888a63/docs/_static/logo-full.png -------------------------------------------------------------------------------- /docs/_static/no.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyup-bot/flask/b9b88b0cdf70190dee5c7d31a83efb1cae888a63/docs/_static/no.png -------------------------------------------------------------------------------- /docs/_static/pycharm-runconfig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyup-bot/flask/b9b88b0cdf70190dee5c7d31a83efb1cae888a63/docs/_static/pycharm-runconfig.png -------------------------------------------------------------------------------- /docs/_static/touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyup-bot/flask/b9b88b0cdf70190dee5c7d31a83efb1cae888a63/docs/_static/touch-icon.png -------------------------------------------------------------------------------- /docs/_static/yes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyup-bot/flask/b9b88b0cdf70190dee5c7d31a83efb1cae888a63/docs/_static/yes.png -------------------------------------------------------------------------------- /docs/advanced_foreword.rst: -------------------------------------------------------------------------------- 1 | .. _advanced_foreword: 2 | 3 | Foreword for Experienced Programmers 4 | ==================================== 5 | 6 | Thread-Locals in Flask 7 | ---------------------- 8 | 9 | One of the design decisions in Flask was that simple tasks should be simple; 10 | they should not take a lot of code and yet they should not limit you. Because 11 | of that, Flask has a few design choices that some people might find surprising or 12 | unorthodox. For example, Flask uses thread-local objects internally so that you 13 | don’t have to pass objects around from function to function within a request in 14 | order to stay threadsafe. This approach is convenient, but requires a valid 15 | request context for dependency injection or when attempting to reuse code which 16 | uses a value pegged to the request. The Flask project is honest about 17 | thread-locals, does not hide them, and calls out in the code and documentation 18 | where they are used. 19 | 20 | Develop for the Web with Caution 21 | -------------------------------- 22 | 23 | Always keep security in mind when building web applications. 24 | 25 | If you write a web application, you are probably allowing users to register 26 | and leave their data on your server. The users are entrusting you with data. 27 | And even if you are the only user that might leave data in your application, 28 | you still want that data to be stored securely. 29 | 30 | Unfortunately, there are many ways the security of a web application can be 31 | compromised. Flask protects you against one of the most common security 32 | problems of modern web applications: cross-site scripting (XSS). Unless you 33 | deliberately mark insecure HTML as secure, Flask and the underlying Jinja2 34 | template engine have you covered. But there are many more ways to cause 35 | security problems. 36 | 37 | The documentation will warn you about aspects of web development that require 38 | attention to security. Some of these security concerns are far more complex 39 | than one might think, and we all sometimes underestimate the likelihood that a 40 | vulnerability will be exploited - until a clever attacker figures out a way to 41 | exploit our applications. And don't think that your application is not 42 | important enough to attract an attacker. Depending on the kind of attack, 43 | chances are that automated bots are probing for ways to fill your database with 44 | spam, links to malicious software, and the like. 45 | 46 | Flask is no different from any other framework in that you the developer must 47 | build with caution, watching for exploits when building to your requirements. 48 | -------------------------------------------------------------------------------- /docs/becomingbig.rst: -------------------------------------------------------------------------------- 1 | .. _becomingbig: 2 | 3 | Becoming Big 4 | ============ 5 | 6 | Here are your options when growing your codebase or scaling your application. 7 | 8 | Read the Source. 9 | ---------------- 10 | 11 | Flask started in part to demonstrate how to build your own framework on top of 12 | existing well-used tools Werkzeug (WSGI) and Jinja (templating), and as it 13 | developed, it became useful to a wide audience. As you grow your codebase, 14 | don't just use Flask -- understand it. Read the source. Flask's code is 15 | written to be read; its documentation is published so you can use its internal 16 | APIs. Flask sticks to documented APIs in upstream libraries, and documents its 17 | internal utilities so that you can find the hook points needed for your 18 | project. 19 | 20 | Hook. Extend. 21 | ------------- 22 | 23 | The :ref:`api` docs are full of available overrides, hook points, and 24 | :ref:`signals`. You can provide custom classes for things like the request and 25 | response objects. Dig deeper on the APIs you use, and look for the 26 | customizations which are available out of the box in a Flask release. Look for 27 | ways in which your project can be refactored into a collection of utilities and 28 | Flask extensions. Explore the many `extensions 29 | `_ in the community, and look for patterns to 30 | build your own extensions if you do not find the tools you need. 31 | 32 | Subclass. 33 | --------- 34 | 35 | The :class:`~flask.Flask` class has many methods designed for subclassing. You 36 | can quickly add or customize behavior by subclassing :class:`~flask.Flask` (see 37 | the linked method docs) and using that subclass wherever you instantiate an 38 | application class. This works well with :ref:`app-factories`. See :doc:`/patterns/subclassing` for an example. 39 | 40 | Wrap with middleware. 41 | --------------------- 42 | 43 | The :ref:`app-dispatch` chapter shows in detail how to apply middleware. You 44 | can introduce WSGI middleware to wrap your Flask instances and introduce fixes 45 | and changes at the layer between your Flask application and your HTTP 46 | server. Werkzeug includes several `middlewares 47 | `_. 48 | 49 | Fork. 50 | ----- 51 | 52 | If none of the above options work, fork Flask. The majority of code of Flask 53 | is within Werkzeug and Jinja2. These libraries do the majority of the work. 54 | Flask is just the paste that glues those together. For every project there is 55 | the point where the underlying framework gets in the way (due to assumptions 56 | the original developers had). This is natural because if this would not be the 57 | case, the framework would be a very complex system to begin with which causes a 58 | steep learning curve and a lot of user frustration. 59 | 60 | This is not unique to Flask. Many people use patched and modified 61 | versions of their framework to counter shortcomings. This idea is also 62 | reflected in the license of Flask. You don't have to contribute any 63 | changes back if you decide to modify the framework. 64 | 65 | The downside of forking is of course that Flask extensions will most 66 | likely break because the new framework has a different import name. 67 | Furthermore integrating upstream changes can be a complex process, 68 | depending on the number of changes. Because of that, forking should be 69 | the very last resort. 70 | 71 | Scale like a pro. 72 | ----------------- 73 | 74 | For many web applications the complexity of the code is less an issue than 75 | the scaling for the number of users or data entries expected. Flask by 76 | itself is only limited in terms of scaling by your application code, the 77 | data store you want to use and the Python implementation and webserver you 78 | are running on. 79 | 80 | Scaling well means for example that if you double the amount of servers 81 | you get about twice the performance. Scaling bad means that if you add a 82 | new server the application won't perform any better or would not even 83 | support a second server. 84 | 85 | There is only one limiting factor regarding scaling in Flask which are 86 | the context local proxies. They depend on context which in Flask is 87 | defined as being either a thread, process or greenlet. If your server 88 | uses some kind of concurrency that is not based on threads or greenlets, 89 | Flask will no longer be able to support these global proxies. However the 90 | majority of servers are using either threads, greenlets or separate 91 | processes to achieve concurrency which are all methods well supported by 92 | the underlying Werkzeug library. 93 | 94 | Discuss with the community. 95 | --------------------------- 96 | 97 | The Flask developers keep the framework accessible to users with codebases big 98 | and small. If you find an obstacle in your way, caused by Flask, don't hesitate 99 | to contact the developers on the mailinglist or IRC channel. The best way for 100 | the Flask and Flask extension developers to improve the tools for larger 101 | applications is getting feedback from users. 102 | -------------------------------------------------------------------------------- /docs/changelog.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../CHANGES.rst 2 | -------------------------------------------------------------------------------- /docs/contents.rst.inc: -------------------------------------------------------------------------------- 1 | User's Guide 2 | ------------ 3 | 4 | This part of the documentation, which is mostly prose, begins with some 5 | background information about Flask, then focuses on step-by-step 6 | instructions for web development with Flask. 7 | 8 | .. toctree:: 9 | :maxdepth: 2 10 | 11 | foreword 12 | advanced_foreword 13 | installation 14 | quickstart 15 | tutorial/index 16 | templating 17 | testing 18 | errorhandling 19 | logging 20 | config 21 | signals 22 | views 23 | appcontext 24 | reqcontext 25 | blueprints 26 | extensions 27 | cli 28 | server 29 | shell 30 | patterns/index 31 | deploying/index 32 | becomingbig 33 | 34 | API Reference 35 | ------------- 36 | 37 | If you are looking for information on a specific function, class or 38 | method, this part of the documentation is for you. 39 | 40 | .. toctree:: 41 | :maxdepth: 2 42 | 43 | api 44 | 45 | Additional Notes 46 | ---------------- 47 | 48 | Design notes, legal information and changelog are here for the interested. 49 | 50 | .. toctree:: 51 | :maxdepth: 2 52 | 53 | design 54 | htmlfaq 55 | security 56 | unicode 57 | extensiondev 58 | styleguide 59 | upgrading 60 | changelog 61 | license 62 | contributing 63 | -------------------------------------------------------------------------------- /docs/contributing.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../CONTRIBUTING.rst 2 | -------------------------------------------------------------------------------- /docs/deploying/cgi.rst: -------------------------------------------------------------------------------- 1 | CGI 2 | === 3 | 4 | If all other deployment methods do not work, CGI will work for sure. 5 | CGI is supported by all major servers but usually has a sub-optimal 6 | performance. 7 | 8 | This is also the way you can use a Flask application on Google's `App 9 | Engine`_, where execution happens in a CGI-like environment. 10 | 11 | .. admonition:: Watch Out 12 | 13 | Please make sure in advance that any ``app.run()`` calls you might 14 | have in your application file are inside an ``if __name__ == 15 | '__main__':`` block or moved to a separate file. Just make sure it's 16 | not called because this will always start a local WSGI server which 17 | we do not want if we deploy that application to CGI / app engine. 18 | 19 | With CGI, you will also have to make sure that your code does not contain 20 | any ``print`` statements, or that ``sys.stdout`` is overridden by something 21 | that doesn't write into the HTTP response. 22 | 23 | Creating a `.cgi` file 24 | ---------------------- 25 | 26 | First you need to create the CGI application file. Let's call it 27 | :file:`yourapplication.cgi`:: 28 | 29 | #!/usr/bin/python 30 | from wsgiref.handlers import CGIHandler 31 | from yourapplication import app 32 | 33 | CGIHandler().run(app) 34 | 35 | Server Setup 36 | ------------ 37 | 38 | Usually there are two ways to configure the server. Either just copy the 39 | ``.cgi`` into a :file:`cgi-bin` (and use `mod_rewrite` or something similar to 40 | rewrite the URL) or let the server point to the file directly. 41 | 42 | In Apache for example you can put something like this into the config: 43 | 44 | .. sourcecode:: apache 45 | 46 | ScriptAlias /app /path/to/the/application.cgi 47 | 48 | On shared webhosting, though, you might not have access to your Apache config. 49 | In this case, a file called ``.htaccess``, sitting in the public directory you want 50 | your app to be available, works too but the ``ScriptAlias`` directive won't 51 | work in that case: 52 | 53 | .. sourcecode:: apache 54 | 55 | RewriteEngine On 56 | RewriteCond %{REQUEST_FILENAME} !-f # Don't interfere with static files 57 | RewriteRule ^(.*)$ /path/to/the/application.cgi/$1 [L] 58 | 59 | For more information consult the documentation of your webserver. 60 | 61 | .. _App Engine: https://developers.google.com/appengine/ 62 | -------------------------------------------------------------------------------- /docs/deploying/index.rst: -------------------------------------------------------------------------------- 1 | .. _deployment: 2 | 3 | Deployment Options 4 | ================== 5 | 6 | While lightweight and easy to use, **Flask's built-in server is not suitable 7 | for production** as it doesn't scale well. Some of the options available for 8 | properly running Flask in production are documented here. 9 | 10 | If you want to deploy your Flask application to a WSGI server not listed here, 11 | look up the server documentation about how to use a WSGI app with it. Just 12 | remember that your :class:`Flask` application object is the actual WSGI 13 | application. 14 | 15 | 16 | Hosted options 17 | -------------- 18 | 19 | - `Deploying Flask on Heroku `_ 20 | - `Deploying Flask on OpenShift `_ 21 | - `Deploying Flask on Webfaction `_ 22 | - `Deploying Flask on Google App Engine `_ 23 | - `Deploying Flask on AWS Elastic Beanstalk `_ 24 | - `Sharing your Localhost Server with Localtunnel `_ 25 | - `Deploying on Azure (IIS) `_ 26 | - `Deploying on PythonAnywhere `_ 27 | 28 | Self-hosted options 29 | ------------------- 30 | 31 | .. toctree:: 32 | :maxdepth: 2 33 | 34 | wsgi-standalone 35 | uwsgi 36 | mod_wsgi 37 | fastcgi 38 | cgi 39 | -------------------------------------------------------------------------------- /docs/deploying/uwsgi.rst: -------------------------------------------------------------------------------- 1 | .. _deploying-uwsgi: 2 | 3 | uWSGI 4 | ===== 5 | 6 | uWSGI is a deployment option on servers like `nginx`_, `lighttpd`_, and 7 | `cherokee`_; see :doc:`fastcgi` and :doc:`wsgi-standalone` for other options. 8 | To use your WSGI application with uWSGI protocol you will need a uWSGI server 9 | first. uWSGI is both a protocol and an application server; the application 10 | server can serve uWSGI, FastCGI, and HTTP protocols. 11 | 12 | The most popular uWSGI server is `uwsgi`_, which we will use for this 13 | guide. Make sure to have it installed to follow along. 14 | 15 | .. admonition:: Watch Out 16 | 17 | Please make sure in advance that any ``app.run()`` calls you might 18 | have in your application file are inside an ``if __name__ == 19 | '__main__':`` block or moved to a separate file. Just make sure it's 20 | not called because this will always start a local WSGI server which 21 | we do not want if we deploy that application to uWSGI. 22 | 23 | Starting your app with uwsgi 24 | ---------------------------- 25 | 26 | `uwsgi` is designed to operate on WSGI callables found in python modules. 27 | 28 | Given a flask application in myapp.py, use the following command: 29 | 30 | .. sourcecode:: text 31 | 32 | $ uwsgi -s /tmp/yourapplication.sock --manage-script-name --mount /yourapplication=myapp:app 33 | 34 | The ``--manage-script-name`` will move the handling of ``SCRIPT_NAME`` to uwsgi, 35 | since it is smarter about that. It is used together with the ``--mount`` 36 | directive which will make requests to ``/yourapplication`` be directed to 37 | ``myapp:app``. If your application is accessible at root level, you can use a 38 | single ``/`` instead of ``/yourapplication``. ``myapp`` refers to the name of 39 | the file of your flask application (without extension) or the module which 40 | provides ``app``. ``app`` is the callable inside of your application (usually 41 | the line reads ``app = Flask(__name__)``. 42 | 43 | If you want to deploy your flask application inside of a virtual environment, 44 | you need to also add ``--virtualenv /path/to/virtual/environment``. You might 45 | also need to add ``--plugin python`` or ``--plugin python3`` depending on which 46 | python version you use for your project. 47 | 48 | Configuring nginx 49 | ----------------- 50 | 51 | A basic flask nginx configuration looks like this:: 52 | 53 | location = /yourapplication { rewrite ^ /yourapplication/; } 54 | location /yourapplication { try_files $uri @yourapplication; } 55 | location @yourapplication { 56 | include uwsgi_params; 57 | uwsgi_pass unix:/tmp/yourapplication.sock; 58 | } 59 | 60 | This configuration binds the application to ``/yourapplication``. If you want 61 | to have it in the URL root its a bit simpler:: 62 | 63 | location / { try_files $uri @yourapplication; } 64 | location @yourapplication { 65 | include uwsgi_params; 66 | uwsgi_pass unix:/tmp/yourapplication.sock; 67 | } 68 | 69 | .. _nginx: https://nginx.org/ 70 | .. _lighttpd: https://www.lighttpd.net/ 71 | .. _cherokee: http://cherokee-project.com/ 72 | .. _uwsgi: http://projects.unbit.it/uwsgi/ 73 | -------------------------------------------------------------------------------- /docs/extensions.rst: -------------------------------------------------------------------------------- 1 | .. _extensions: 2 | 3 | Extensions 4 | ========== 5 | 6 | Extensions are extra packages that add functionality to a Flask 7 | application. For example, an extension might add support for sending 8 | email or connecting to a database. Some extensions add entire new 9 | frameworks to help build certain types of applications, like a ReST API. 10 | 11 | 12 | Finding Extensions 13 | ------------------ 14 | 15 | Flask extensions are usually named "Flask-Foo" or "Foo-Flask". Many 16 | extensions are listed in the `Extension Registry`_, which can be updated 17 | by extension developers. You can also search PyPI for packages tagged 18 | with `Framework :: Flask `_. 19 | 20 | 21 | Using Extensions 22 | ---------------- 23 | 24 | Consult each extension's documentation for installation, configuration, 25 | and usage instructions. Generally, extensions pull their own 26 | configuration from :attr:`app.config ` and are 27 | passed an application instance during initialization. For example, 28 | an extension caled "Flask-Foo" might be used like this:: 29 | 30 | from flask_foo import Foo 31 | 32 | foo = Foo() 33 | 34 | app = Flask(__name__) 35 | app.config.update( 36 | FOO_BAR='baz', 37 | FOO_SPAM='eggs', 38 | ) 39 | 40 | foo.init_app(app) 41 | 42 | 43 | Building Extensions 44 | ------------------- 45 | 46 | While the `Extension Registry`_ contains many Flask extensions, you may 47 | not find an extension that fits your need. If this is the case, you can 48 | create your own. Read :ref:`extension-dev` to develop your own Flask 49 | extension. 50 | 51 | 52 | .. _Extension Registry: http://flask.pocoo.org/extensions/ 53 | .. _pypi: https://pypi.org/search/?c=Framework+%3A%3A+Flask 54 | -------------------------------------------------------------------------------- /docs/flaskstyle.sty: -------------------------------------------------------------------------------- 1 | \definecolor{TitleColor}{rgb}{0,0,0} 2 | \definecolor{InnerLinkColor}{rgb}{0,0,0} 3 | 4 | % Replace Unicode character 'PARTY POPPER' (U+1F389) with a non-breaking space. 5 | % pdfLaTeX doesn't support Unicode. 6 | \DeclareUnicodeCharacter{1F389}{\nobreakspace} 7 | 8 | \renewcommand{\maketitle}{% 9 | \begin{titlepage}% 10 | \let\footnotesize\small 11 | \let\footnoterule\relax 12 | % Apply following fix only on PDF output, i.e. pdfoutput macro is not 13 | % undefined 14 | \ifx\pdfoutput\undefined\else 15 | \begingroup 16 | % This \def is required to deal with multi-line authors; it 17 | % changes \\ to ', ' (comma-space), making it pass muster for 18 | % generating document info in the PDF file. 19 | \def\\{, } 20 | \pdfinfo{ 21 | /Author (\@author) 22 | /Title (\@title) 23 | } 24 | \endgroup 25 | \fi 26 | \begin{flushright}% 27 | %\sphinxlogo% 28 | {\center 29 | \vspace*{3cm} 30 | \includegraphics{logo.pdf} 31 | \vspace{3cm} 32 | \par 33 | {\rm\Huge \@title \par}% 34 | {\em\LARGE \py@release\releaseinfo \par} 35 | {\large 36 | \@date \par 37 | \py@authoraddress \par 38 | }}% 39 | \end{flushright}%\par 40 | \@thanks 41 | \end{titlepage}% 42 | \cleardoublepage% 43 | \setcounter{footnote}{0}% 44 | \let\thanks\relax\let\maketitle\relax 45 | %\gdef\@thanks{}\gdef\@author{}\gdef\@title{} 46 | } 47 | 48 | \fancypagestyle{normal}{ 49 | \fancyhf{} 50 | \fancyfoot[LE,RO]{{\thepage}} 51 | \fancyfoot[LO]{{\nouppercase{\rightmark}}} 52 | \fancyfoot[RE]{{\nouppercase{\leftmark}}} 53 | \fancyhead[LE,RO]{{ \@title, \py@release}} 54 | \renewcommand{\headrulewidth}{0.4pt} 55 | \renewcommand{\footrulewidth}{0.4pt} 56 | } 57 | 58 | \fancypagestyle{plain}{ 59 | \fancyhf{} 60 | \fancyfoot[LE,RO]{{\thepage}} 61 | \renewcommand{\headrulewidth}{0pt} 62 | \renewcommand{\footrulewidth}{0.4pt} 63 | } 64 | 65 | \titleformat{\section}{\Large}% 66 | {\py@TitleColor\thesection}{0.5em}{\py@TitleColor}{\py@NormalColor} 67 | \titleformat{\subsection}{\large}% 68 | {\py@TitleColor\thesubsection}{0.5em}{\py@TitleColor}{\py@NormalColor} 69 | \titleformat{\subsubsection}{}% 70 | {\py@TitleColor\thesubsubsection}{0.5em}{\py@TitleColor}{\py@NormalColor} 71 | \titleformat{\paragraph}{\large}% 72 | {\py@TitleColor}{0em}{\py@TitleColor}{\py@NormalColor} 73 | 74 | \ChNameVar{\raggedleft\normalsize} 75 | \ChNumVar{\raggedleft \bfseries\Large} 76 | \ChTitleVar{\raggedleft \rm\Huge} 77 | 78 | \renewcommand\thepart{\@Roman\c@part} 79 | \renewcommand\part{% 80 | \pagestyle{plain} 81 | \if@noskipsec \leavevmode \fi 82 | \cleardoublepage 83 | \vspace*{6cm}% 84 | \@afterindentfalse 85 | \secdef\@part\@spart} 86 | 87 | \def\@part[#1]#2{% 88 | \ifnum \c@secnumdepth >\m@ne 89 | \refstepcounter{part}% 90 | \addcontentsline{toc}{part}{\thepart\hspace{1em}#1}% 91 | \else 92 | \addcontentsline{toc}{part}{#1}% 93 | \fi 94 | {\parindent \z@ %\center 95 | \interlinepenalty \@M 96 | \normalfont 97 | \ifnum \c@secnumdepth >\m@ne 98 | \rm\Large \partname~\thepart 99 | \par\nobreak 100 | \fi 101 | \MakeUppercase{\rm\Huge #2}% 102 | \markboth{}{}\par}% 103 | \nobreak 104 | \vskip 8ex 105 | \@afterheading} 106 | \def\@spart#1{% 107 | {\parindent \z@ %\center 108 | \interlinepenalty \@M 109 | \normalfont 110 | \huge \bfseries #1\par}% 111 | \nobreak 112 | \vskip 3ex 113 | \@afterheading} 114 | 115 | % use inconsolata font 116 | \usepackage{inconsolata} 117 | 118 | % fix single quotes, for inconsolata. (does not work) 119 | %%\usepackage{textcomp} 120 | %%\begingroup 121 | %% \catcode`'=\active 122 | %% \g@addto@macro\@noligs{\let'\textsinglequote} 123 | %% \endgroup 124 | %%\endinput 125 | -------------------------------------------------------------------------------- /docs/foreword.rst: -------------------------------------------------------------------------------- 1 | Foreword 2 | ======== 3 | 4 | Read this before you get started with Flask. This hopefully answers some 5 | questions about the purpose and goals of the project, and when you 6 | should or should not be using it. 7 | 8 | What does "micro" mean? 9 | ----------------------- 10 | 11 | “Micro” does not mean that your whole web application has to fit into a single 12 | Python file (although it certainly can), nor does it mean that Flask is lacking 13 | in functionality. The "micro" in microframework means Flask aims to keep the 14 | core simple but extensible. Flask won't make many decisions for you, such as 15 | what database to use. Those decisions that it does make, such as what 16 | templating engine to use, are easy to change. Everything else is up to you, so 17 | that Flask can be everything you need and nothing you don't. 18 | 19 | By default, Flask does not include a database abstraction layer, form 20 | validation or anything else where different libraries already exist that can 21 | handle that. Instead, Flask supports extensions to add such functionality to 22 | your application as if it was implemented in Flask itself. Numerous extensions 23 | provide database integration, form validation, upload handling, various open 24 | authentication technologies, and more. Flask may be "micro", but it's ready for 25 | production use on a variety of needs. 26 | 27 | Configuration and Conventions 28 | ----------------------------- 29 | 30 | Flask has many configuration values, with sensible defaults, and a few 31 | conventions when getting started. By convention, templates and static files are 32 | stored in subdirectories within the application's Python source tree, with the 33 | names :file:`templates` and :file:`static` respectively. While this can be changed, you 34 | usually don't have to, especially when getting started. 35 | 36 | Growing with Flask 37 | ------------------ 38 | 39 | Once you have Flask up and running, you'll find a variety of extensions 40 | available in the community to integrate your project for production. The Flask 41 | core team reviews extensions and ensures approved extensions do not break with 42 | future releases. 43 | 44 | As your codebase grows, you are free to make the design decisions appropriate 45 | for your project. Flask will continue to provide a very simple glue layer to 46 | the best that Python has to offer. You can implement advanced patterns in 47 | SQLAlchemy or another database tool, introduce non-relational data persistence 48 | as appropriate, and take advantage of framework-agnostic tools built for WSGI, 49 | the Python web interface. 50 | 51 | Flask includes many hooks to customize its behavior. Should you need more 52 | customization, the Flask class is built for subclassing. If you are interested 53 | in that, check out the :ref:`becomingbig` chapter. If you are curious about 54 | the Flask design principles, head over to the section about :ref:`design`. 55 | 56 | Continue to :ref:`installation`, the :ref:`quickstart`, or the 57 | :ref:`advanced_foreword`. 58 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | Welcome to Flask 4 | ================ 5 | 6 | .. image:: _static/logo-full.png 7 | :alt: Flask: web development, one drop at a time 8 | :align: right 9 | 10 | Welcome to Flask's documentation. Get started with :ref:`installation` 11 | and then get an overview with the :ref:`quickstart`. There is also a 12 | more detailed :ref:`tutorial` that shows how to create a small but 13 | complete application with Flask. Common patterns are described in the 14 | :ref:`patterns` section. The rest of the docs describe each component of 15 | Flask in detail, with a full reference in the :ref:`api` section. 16 | 17 | Flask depends on the `Jinja`_ template engine and the `Werkzeug`_ WSGI 18 | toolkit. The documentation for these libraries can be found at: 19 | 20 | - `Jinja documentation `_ 21 | - `Werkzeug documentation `_ 22 | 23 | .. _Jinja: https://www.palletsprojects.com/p/jinja/ 24 | .. _Werkzeug: https://www.palletsprojects.com/p/werkzeug/ 25 | 26 | .. include:: contents.rst.inc 27 | -------------------------------------------------------------------------------- /docs/latexindex.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | Flask Documentation 4 | =================== 5 | 6 | .. include:: contents.rst.inc 7 | -------------------------------------------------------------------------------- /docs/license.rst: -------------------------------------------------------------------------------- 1 | License 2 | ======= 3 | 4 | Flask is licensed under a three clause BSD License. It basically means: 5 | do whatever you want with it as long as the copyright in Flask sticks 6 | around, the conditions are not modified and the disclaimer is present. 7 | Furthermore you must not use the names of the authors to promote derivatives 8 | of the software without written consent. 9 | 10 | The full license text can be found below (:ref:`flask-license`). For the 11 | documentation and artwork different licenses apply. 12 | 13 | .. _authors: 14 | 15 | Authors 16 | ------- 17 | 18 | .. include:: ../AUTHORS 19 | 20 | General License Definitions 21 | --------------------------- 22 | 23 | The following section contains the full license texts for Flask and the 24 | documentation. 25 | 26 | - "AUTHORS" hereby refers to all the authors listed in the 27 | :ref:`authors` section. 28 | 29 | - The ":ref:`flask-license`" applies to all the source code shipped as 30 | part of Flask (Flask itself as well as the examples and the unittests) 31 | as well as documentation. 32 | 33 | - The ":ref:`artwork-license`" applies to the project's Horn-Logo. 34 | 35 | .. _flask-license: 36 | 37 | Flask License 38 | ------------- 39 | 40 | .. include:: ../LICENSE 41 | 42 | 43 | .. _artwork-license: 44 | 45 | Flask Artwork License 46 | --------------------- 47 | 48 | .. include:: ../artwork/LICENSE 49 | -------------------------------------------------------------------------------- /docs/logo.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyup-bot/flask/b9b88b0cdf70190dee5c7d31a83efb1cae888a63/docs/logo.pdf -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | set SPHINXPROJ=Flask 13 | 14 | if "%1" == "" goto help 15 | 16 | %SPHINXBUILD% >NUL 2>NUL 17 | if errorlevel 9009 ( 18 | echo. 19 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 20 | echo.installed, then set the SPHINXBUILD environment variable to point 21 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 22 | echo.may add the Sphinx directory to PATH. 23 | echo. 24 | echo.If you don't have Sphinx installed, grab it from 25 | echo.http://sphinx-doc.org/ 26 | exit /b 1 27 | ) 28 | 29 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 30 | goto end 31 | 32 | :help 33 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 34 | 35 | :end 36 | popd 37 | -------------------------------------------------------------------------------- /docs/patterns/apierrors.rst: -------------------------------------------------------------------------------- 1 | Implementing API Exceptions 2 | =========================== 3 | 4 | It's very common to implement RESTful APIs on top of Flask. One of the 5 | first things that developers run into is the realization that the builtin 6 | exceptions are not expressive enough for APIs and that the content type of 7 | :mimetype:`text/html` they are emitting is not very useful for API consumers. 8 | 9 | The better solution than using ``abort`` to signal errors for invalid API 10 | usage is to implement your own exception type and install an error handler 11 | for it that produces the errors in the format the user is expecting. 12 | 13 | Simple Exception Class 14 | ---------------------- 15 | 16 | The basic idea is to introduce a new exception that can take a proper 17 | human readable message, a status code for the error and some optional 18 | payload to give more context for the error. 19 | 20 | This is a simple example:: 21 | 22 | from flask import jsonify 23 | 24 | class InvalidUsage(Exception): 25 | status_code = 400 26 | 27 | def __init__(self, message, status_code=None, payload=None): 28 | Exception.__init__(self) 29 | self.message = message 30 | if status_code is not None: 31 | self.status_code = status_code 32 | self.payload = payload 33 | 34 | def to_dict(self): 35 | rv = dict(self.payload or ()) 36 | rv['message'] = self.message 37 | return rv 38 | 39 | A view can now raise that exception with an error message. Additionally 40 | some extra payload can be provided as a dictionary through the `payload` 41 | parameter. 42 | 43 | Registering an Error Handler 44 | ---------------------------- 45 | 46 | At that point views can raise that error, but it would immediately result 47 | in an internal server error. The reason for this is that there is no 48 | handler registered for this error class. That however is easy to add:: 49 | 50 | @app.errorhandler(InvalidUsage) 51 | def handle_invalid_usage(error): 52 | response = jsonify(error.to_dict()) 53 | response.status_code = error.status_code 54 | return response 55 | 56 | Usage in Views 57 | -------------- 58 | 59 | Here is how a view can use that functionality:: 60 | 61 | @app.route('/foo') 62 | def get_foo(): 63 | raise InvalidUsage('This view is gone', status_code=410) 64 | -------------------------------------------------------------------------------- /docs/patterns/appfactories.rst: -------------------------------------------------------------------------------- 1 | .. _app-factories: 2 | 3 | Application Factories 4 | ===================== 5 | 6 | If you are already using packages and blueprints for your application 7 | (:ref:`blueprints`) there are a couple of really nice ways to further improve 8 | the experience. A common pattern is creating the application object when 9 | the blueprint is imported. But if you move the creation of this object 10 | into a function, you can then create multiple instances of this app later. 11 | 12 | So why would you want to do this? 13 | 14 | 1. Testing. You can have instances of the application with different 15 | settings to test every case. 16 | 2. Multiple instances. Imagine you want to run different versions of the 17 | same application. Of course you could have multiple instances with 18 | different configs set up in your webserver, but if you use factories, 19 | you can have multiple instances of the same application running in the 20 | same application process which can be handy. 21 | 22 | So how would you then actually implement that? 23 | 24 | Basic Factories 25 | --------------- 26 | 27 | The idea is to set up the application in a function. Like this:: 28 | 29 | def create_app(config_filename): 30 | app = Flask(__name__) 31 | app.config.from_pyfile(config_filename) 32 | 33 | from yourapplication.model import db 34 | db.init_app(app) 35 | 36 | from yourapplication.views.admin import admin 37 | from yourapplication.views.frontend import frontend 38 | app.register_blueprint(admin) 39 | app.register_blueprint(frontend) 40 | 41 | return app 42 | 43 | The downside is that you cannot use the application object in the blueprints 44 | at import time. You can however use it from within a request. How do you 45 | get access to the application with the config? Use 46 | :data:`~flask.current_app`:: 47 | 48 | from flask import current_app, Blueprint, render_template 49 | admin = Blueprint('admin', __name__, url_prefix='/admin') 50 | 51 | @admin.route('/') 52 | def index(): 53 | return render_template(current_app.config['INDEX_TEMPLATE']) 54 | 55 | Here we look up the name of a template in the config. 56 | 57 | Factories & Extensions 58 | ---------------------- 59 | 60 | It's preferable to create your extensions and app factories so that the 61 | extension object does not initially get bound to the application. 62 | 63 | Using `Flask-SQLAlchemy `_, 64 | as an example, you should not do something along those lines:: 65 | 66 | def create_app(config_filename): 67 | app = Flask(__name__) 68 | app.config.from_pyfile(config_filename) 69 | 70 | db = SQLAlchemy(app) 71 | 72 | But, rather, in model.py (or equivalent):: 73 | 74 | db = SQLAlchemy() 75 | 76 | and in your application.py (or equivalent):: 77 | 78 | def create_app(config_filename): 79 | app = Flask(__name__) 80 | app.config.from_pyfile(config_filename) 81 | 82 | from yourapplication.model import db 83 | db.init_app(app) 84 | 85 | Using this design pattern, no application-specific state is stored on the 86 | extension object, so one extension object can be used for multiple apps. 87 | For more information about the design of extensions refer to :doc:`/extensiondev`. 88 | 89 | Using Applications 90 | ------------------ 91 | 92 | To run such an application, you can use the :command:`flask` command:: 93 | 94 | export FLASK_APP=myapp 95 | flask run 96 | 97 | Flask will automatically detect the factory (``create_app`` or ``make_app``) 98 | in ``myapp``. You can also pass arguments to the factory like this:: 99 | 100 | export FLASK_APP="myapp:create_app('dev')" 101 | flask run 102 | 103 | Then the ``create_app`` factory in ``myapp`` is called with the string 104 | ``'dev'`` as the argument. See :doc:`/cli` for more detail. 105 | 106 | Factory Improvements 107 | -------------------- 108 | 109 | The factory function above is not very clever, but you can improve it. 110 | The following changes are straightforward to implement: 111 | 112 | 1. Make it possible to pass in configuration values for unit tests so that 113 | you don't have to create config files on the filesystem. 114 | 2. Call a function from a blueprint when the application is setting up so 115 | that you have a place to modify attributes of the application (like 116 | hooking in before/after request handlers etc.) 117 | 3. Add in WSGI middlewares when the application is being created if necessary. 118 | -------------------------------------------------------------------------------- /docs/patterns/caching.rst: -------------------------------------------------------------------------------- 1 | .. _caching-pattern: 2 | 3 | Caching 4 | ======= 5 | 6 | When your application runs slow, throw some caches in. Well, at least 7 | it's the easiest way to speed up things. What does a cache do? Say you 8 | have a function that takes some time to complete but the results would 9 | still be good enough if they were 5 minutes old. So then the idea is that 10 | you actually put the result of that calculation into a cache for some 11 | time. 12 | 13 | Flask itself does not provide caching for you, but Werkzeug, one of the 14 | libraries it is based on, has some very basic cache support. It supports 15 | multiple cache backends, normally you want to use a memcached server. 16 | 17 | Setting up a Cache 18 | ------------------ 19 | 20 | You create a cache object once and keep it around, similar to how 21 | :class:`~flask.Flask` objects are created. If you are using the 22 | development server you can create a 23 | :class:`~werkzeug.contrib.cache.SimpleCache` object, that one is a simple 24 | cache that keeps the item stored in the memory of the Python interpreter:: 25 | 26 | from werkzeug.contrib.cache import SimpleCache 27 | cache = SimpleCache() 28 | 29 | If you want to use memcached, make sure to have one of the memcache modules 30 | supported (you get them from `PyPI `_) and a 31 | memcached server running somewhere. This is how you connect to such an 32 | memcached server then:: 33 | 34 | from werkzeug.contrib.cache import MemcachedCache 35 | cache = MemcachedCache(['127.0.0.1:11211']) 36 | 37 | If you are using App Engine, you can connect to the App Engine memcache 38 | server easily:: 39 | 40 | from werkzeug.contrib.cache import GAEMemcachedCache 41 | cache = GAEMemcachedCache() 42 | 43 | Using a Cache 44 | ------------- 45 | 46 | Now how can one use such a cache? There are two very important 47 | operations: :meth:`~werkzeug.contrib.cache.BaseCache.get` and 48 | :meth:`~werkzeug.contrib.cache.BaseCache.set`. This is how to use them: 49 | 50 | To get an item from the cache call 51 | :meth:`~werkzeug.contrib.cache.BaseCache.get` with a string as key name. 52 | If something is in the cache, it is returned. Otherwise that function 53 | will return ``None``:: 54 | 55 | rv = cache.get('my-item') 56 | 57 | To add items to the cache, use the :meth:`~werkzeug.contrib.cache.BaseCache.set` 58 | method instead. The first argument is the key and the second the value 59 | that should be set. Also a timeout can be provided after which the cache 60 | will automatically remove item. 61 | 62 | Here a full example how this looks like normally:: 63 | 64 | def get_my_item(): 65 | rv = cache.get('my-item') 66 | if rv is None: 67 | rv = calculate_value() 68 | cache.set('my-item', rv, timeout=5 * 60) 69 | return rv 70 | -------------------------------------------------------------------------------- /docs/patterns/celery.rst: -------------------------------------------------------------------------------- 1 | Celery Background Tasks 2 | ======================= 3 | 4 | If your application has a long running task, such as processing some uploaded 5 | data or sending email, you don't want to wait for it to finish during a 6 | request. Instead, use a task queue to send the necessary data to another 7 | process that will run the task in the background while the request returns 8 | immediately. 9 | 10 | Celery is a powerful task queue that can be used for simple background tasks 11 | as well as complex multi-stage programs and schedules. This guide will show you 12 | how to configure Celery using Flask, but assumes you've already read the 13 | `First Steps with Celery `_ 14 | guide in the Celery documentation. 15 | 16 | Install 17 | ------- 18 | 19 | Celery is a separate Python package. Install it from PyPI using pip:: 20 | 21 | $ pip install celery 22 | 23 | Configure 24 | --------- 25 | 26 | The first thing you need is a Celery instance, this is called the celery 27 | application. It serves the same purpose as the :class:`~flask.Flask` 28 | object in Flask, just for Celery. Since this instance is used as the 29 | entry-point for everything you want to do in Celery, like creating tasks 30 | and managing workers, it must be possible for other modules to import it. 31 | 32 | For instance you can place this in a ``tasks`` module. While you can use 33 | Celery without any reconfiguration with Flask, it becomes a bit nicer by 34 | subclassing tasks and adding support for Flask's application contexts and 35 | hooking it up with the Flask configuration. 36 | 37 | This is all that is necessary to properly integrate Celery with Flask:: 38 | 39 | from celery import Celery 40 | 41 | def make_celery(app): 42 | celery = Celery( 43 | app.import_name, 44 | backend=app.config['CELERY_RESULT_BACKEND'], 45 | broker=app.config['CELERY_BROKER_URL'] 46 | ) 47 | celery.conf.update(app.config) 48 | 49 | class ContextTask(celery.Task): 50 | def __call__(self, *args, **kwargs): 51 | with app.app_context(): 52 | return self.run(*args, **kwargs) 53 | 54 | celery.Task = ContextTask 55 | return celery 56 | 57 | The function creates a new Celery object, configures it with the broker 58 | from the application config, updates the rest of the Celery config from 59 | the Flask config and then creates a subclass of the task that wraps the 60 | task execution in an application context. 61 | 62 | An example task 63 | --------------- 64 | 65 | Let's write a task that adds two numbers together and returns the result. We 66 | configure Celery's broker and backend to use Redis, create a ``celery`` 67 | application using the factor from above, and then use it to define the task. :: 68 | 69 | from flask import Flask 70 | 71 | flask_app = Flask(__name__) 72 | flask_app.config.update( 73 | CELERY_BROKER_URL='redis://localhost:6379', 74 | CELERY_RESULT_BACKEND='redis://localhost:6379' 75 | ) 76 | celery = make_celery(flask_app) 77 | 78 | @celery.task() 79 | def add_together(a, b): 80 | return a + b 81 | 82 | This task can now be called in the background:: 83 | 84 | result = add_together.delay(23, 42) 85 | result.wait() # 65 86 | 87 | Run a worker 88 | ------------ 89 | 90 | If you jumped in and already executed the above code you will be 91 | disappointed to learn that ``.wait()`` will never actually return. 92 | That's because you also need to run a Celery worker to receive and execute the 93 | task. :: 94 | 95 | $ celery -A your_application.celery worker 96 | 97 | The ``your_application`` string has to point to your application's package 98 | or module that creates the ``celery`` object. 99 | 100 | Now that the worker is running, ``wait`` will return the result once the task 101 | is finished. 102 | -------------------------------------------------------------------------------- /docs/patterns/deferredcallbacks.rst: -------------------------------------------------------------------------------- 1 | .. _deferred-callbacks: 2 | 3 | Deferred Request Callbacks 4 | ========================== 5 | 6 | One of the design principles of Flask is that response objects are created and 7 | passed down a chain of potential callbacks that can modify them or replace 8 | them. When the request handling starts, there is no response object yet. It is 9 | created as necessary either by a view function or by some other component in 10 | the system. 11 | 12 | What happens if you want to modify the response at a point where the response 13 | does not exist yet? A common example for that would be a 14 | :meth:`~flask.Flask.before_request` callback that wants to set a cookie on the 15 | response object. 16 | 17 | One way is to avoid the situation. Very often that is possible. For instance 18 | you can try to move that logic into a :meth:`~flask.Flask.after_request` 19 | callback instead. However, sometimes moving code there makes it more 20 | more complicated or awkward to reason about. 21 | 22 | As an alternative, you can use :func:`~flask.after_this_request` to register 23 | callbacks that will execute after only the current request. This way you can 24 | defer code execution from anywhere in the application, based on the current 25 | request. 26 | 27 | At any time during a request, we can register a function to be called at the 28 | end of the request. For example you can remember the current language of the 29 | user in a cookie in a :meth:`~flask.Flask.before_request` callback:: 30 | 31 | from flask import request, after_this_request 32 | 33 | @app.before_request 34 | def detect_user_language(): 35 | language = request.cookies.get('user_lang') 36 | 37 | if language is None: 38 | language = guess_language_from_request() 39 | 40 | # when the response exists, set a cookie with the language 41 | @after_this_request 42 | def remember_language(response): 43 | response.set_cookie('user_lang', language) 44 | 45 | g.language = language 46 | -------------------------------------------------------------------------------- /docs/patterns/errorpages.rst: -------------------------------------------------------------------------------- 1 | .. _errorpages: 2 | 3 | Custom Error Pages 4 | ================== 5 | 6 | Flask comes with a handy :func:`~flask.abort` function that aborts a 7 | request with an HTTP error code early. It will also provide a plain black 8 | and white error page for you with a basic description, but nothing fancy. 9 | 10 | Depending on the error code it is less or more likely for the user to 11 | actually see such an error. 12 | 13 | Common Error Codes 14 | ------------------ 15 | 16 | The following error codes are some that are often displayed to the user, 17 | even if the application behaves correctly: 18 | 19 | *404 Not Found* 20 | The good old "chap, you made a mistake typing that URL" message. So 21 | common that even novices to the internet know that 404 means: damn, 22 | the thing I was looking for is not there. It's a very good idea to 23 | make sure there is actually something useful on a 404 page, at least a 24 | link back to the index. 25 | 26 | *403 Forbidden* 27 | If you have some kind of access control on your website, you will have 28 | to send a 403 code for disallowed resources. So make sure the user 29 | is not lost when they try to access a forbidden resource. 30 | 31 | *410 Gone* 32 | Did you know that there the "404 Not Found" has a brother named "410 33 | Gone"? Few people actually implement that, but the idea is that 34 | resources that previously existed and got deleted answer with 410 35 | instead of 404. If you are not deleting documents permanently from 36 | the database but just mark them as deleted, do the user a favour and 37 | use the 410 code instead and display a message that what they were 38 | looking for was deleted for all eternity. 39 | 40 | *500 Internal Server Error* 41 | Usually happens on programming errors or if the server is overloaded. 42 | A terribly good idea is to have a nice page there, because your 43 | application *will* fail sooner or later (see also: 44 | :ref:`application-errors`). 45 | 46 | 47 | Error Handlers 48 | -------------- 49 | 50 | An error handler is a function that returns a response when a type of error is 51 | raised, similar to how a view is a function that returns a response when a 52 | request URL is matched. It is passed the instance of the error being handled, 53 | which is most likely a :exc:`~werkzeug.exceptions.HTTPException`. An error 54 | handler for "500 Internal Server Error" will be passed uncaught exceptions in 55 | addition to explicit 500 errors. 56 | 57 | An error handler is registered with the :meth:`~flask.Flask.errorhandler` 58 | decorator or the :meth:`~flask.Flask.register_error_handler` method. A handler 59 | can be registered for a status code, like 404, or for an exception class. 60 | 61 | The status code of the response will not be set to the handler's code. Make 62 | sure to provide the appropriate HTTP status code when returning a response from 63 | a handler. 64 | 65 | A handler for "500 Internal Server Error" will not be used when running in 66 | debug mode. Instead, the interactive debugger will be shown. 67 | 68 | Here is an example implementation for a "404 Page Not Found" exception:: 69 | 70 | from flask import render_template 71 | 72 | @app.errorhandler(404) 73 | def page_not_found(e): 74 | # note that we set the 404 status explicitly 75 | return render_template('404.html'), 404 76 | 77 | When using the :ref:`application factory pattern `:: 78 | 79 | from flask import Flask, render_template 80 | 81 | def page_not_found(e): 82 | return render_template('404.html'), 404 83 | 84 | def create_app(config_filename): 85 | app = Flask(__name__) 86 | app.register_error_handler(404, page_not_found) 87 | return app 88 | 89 | An example template might be this: 90 | 91 | .. sourcecode:: html+jinja 92 | 93 | {% extends "layout.html" %} 94 | {% block title %}Page Not Found{% endblock %} 95 | {% block body %} 96 |

Page Not Found

97 |

What you were looking for is just not there. 98 |

go somewhere nice 99 | {% endblock %} 100 | -------------------------------------------------------------------------------- /docs/patterns/favicon.rst: -------------------------------------------------------------------------------- 1 | Adding a favicon 2 | ================ 3 | 4 | A "favicon" is an icon used by browsers for tabs and bookmarks. This helps 5 | to distinguish your website and to give it a unique brand. 6 | 7 | A common question is how to add a favicon to a Flask application. First, of 8 | course, you need an icon. It should be 16 × 16 pixels and in the ICO file 9 | format. This is not a requirement but a de-facto standard supported by all 10 | relevant browsers. Put the icon in your static directory as 11 | :file:`favicon.ico`. 12 | 13 | Now, to get browsers to find your icon, the correct way is to add a link 14 | tag in your HTML. So, for example: 15 | 16 | .. sourcecode:: html+jinja 17 | 18 | 19 | 20 | That's all you need for most browsers, however some really old ones do not 21 | support this standard. The old de-facto standard is to serve this file, 22 | with this name, at the website root. If your application is not mounted at 23 | the root path of the domain you either need to configure the web server to 24 | serve the icon at the root or if you can't do that you're out of luck. If 25 | however your application is the root you can simply route a redirect:: 26 | 27 | app.add_url_rule('/favicon.ico', 28 | redirect_to=url_for('static', filename='favicon.ico')) 29 | 30 | If you want to save the extra redirect request you can also write a view 31 | using :func:`~flask.send_from_directory`:: 32 | 33 | import os 34 | from flask import send_from_directory 35 | 36 | @app.route('/favicon.ico') 37 | def favicon(): 38 | return send_from_directory(os.path.join(app.root_path, 'static'), 39 | 'favicon.ico', mimetype='image/vnd.microsoft.icon') 40 | 41 | We can leave out the explicit mimetype and it will be guessed, but we may 42 | as well specify it to avoid the extra guessing, as it will always be the 43 | same. 44 | 45 | The above will serve the icon via your application and if possible it's 46 | better to configure your dedicated web server to serve it; refer to the 47 | web server's documentation. 48 | 49 | See also 50 | -------- 51 | 52 | * The `Favicon `_ article on 53 | Wikipedia 54 | -------------------------------------------------------------------------------- /docs/patterns/flashing.rst: -------------------------------------------------------------------------------- 1 | .. _message-flashing-pattern: 2 | 3 | Message Flashing 4 | ================ 5 | 6 | Good applications and user interfaces are all about feedback. If the user 7 | does not get enough feedback they will probably end up hating the 8 | application. Flask provides a really simple way to give feedback to a 9 | user with the flashing system. The flashing system basically makes it 10 | possible to record a message at the end of a request and access it next 11 | request and only next request. This is usually combined with a layout 12 | template that does this. Note that browsers and sometimes web servers enforce 13 | a limit on cookie sizes. This means that flashing messages that are too 14 | large for session cookies causes message flashing to fail silently. 15 | 16 | Simple Flashing 17 | --------------- 18 | 19 | So here is a full example:: 20 | 21 | from flask import Flask, flash, redirect, render_template, \ 22 | request, url_for 23 | 24 | app = Flask(__name__) 25 | app.secret_key = b'_5#y2L"F4Q8z\n\xec]/' 26 | 27 | @app.route('/') 28 | def index(): 29 | return render_template('index.html') 30 | 31 | @app.route('/login', methods=['GET', 'POST']) 32 | def login(): 33 | error = None 34 | if request.method == 'POST': 35 | if request.form['username'] != 'admin' or \ 36 | request.form['password'] != 'secret': 37 | error = 'Invalid credentials' 38 | else: 39 | flash('You were successfully logged in') 40 | return redirect(url_for('index')) 41 | return render_template('login.html', error=error) 42 | 43 | And here is the :file:`layout.html` template which does the magic: 44 | 45 | .. sourcecode:: html+jinja 46 | 47 | 48 | My Application 49 | {% with messages = get_flashed_messages() %} 50 | {% if messages %} 51 |

    52 | {% for message in messages %} 53 |
  • {{ message }}
  • 54 | {% endfor %} 55 |
56 | {% endif %} 57 | {% endwith %} 58 | {% block body %}{% endblock %} 59 | 60 | Here is the :file:`index.html` template which inherits from :file:`layout.html`: 61 | 62 | .. sourcecode:: html+jinja 63 | 64 | {% extends "layout.html" %} 65 | {% block body %} 66 |

Overview

67 |

Do you want to log in? 68 | {% endblock %} 69 | 70 | And here is the :file:`login.html` template which also inherits from 71 | :file:`layout.html`: 72 | 73 | .. sourcecode:: html+jinja 74 | 75 | {% extends "layout.html" %} 76 | {% block body %} 77 |

Login

78 | {% if error %} 79 |

Error: {{ error }} 80 | {% endif %} 81 |

82 |
83 |
Username: 84 |
86 |
Password: 87 |
88 |
89 |

90 |

91 | {% endblock %} 92 | 93 | Flashing With Categories 94 | ------------------------ 95 | 96 | .. versionadded:: 0.3 97 | 98 | It is also possible to provide categories when flashing a message. The 99 | default category if nothing is provided is ``'message'``. Alternative 100 | categories can be used to give the user better feedback. For example 101 | error messages could be displayed with a red background. 102 | 103 | To flash a message with a different category, just use the second argument 104 | to the :func:`~flask.flash` function:: 105 | 106 | flash(u'Invalid password provided', 'error') 107 | 108 | Inside the template you then have to tell the 109 | :func:`~flask.get_flashed_messages` function to also return the 110 | categories. The loop looks slightly different in that situation then: 111 | 112 | .. sourcecode:: html+jinja 113 | 114 | {% with messages = get_flashed_messages(with_categories=true) %} 115 | {% if messages %} 116 |
    117 | {% for category, message in messages %} 118 |
  • {{ message }}
  • 119 | {% endfor %} 120 |
121 | {% endif %} 122 | {% endwith %} 123 | 124 | This is just one example of how to render these flashed messages. One 125 | might also use the category to add a prefix such as 126 | ``Error:`` to the message. 127 | 128 | Filtering Flash Messages 129 | ------------------------ 130 | 131 | .. versionadded:: 0.9 132 | 133 | Optionally you can pass a list of categories which filters the results of 134 | :func:`~flask.get_flashed_messages`. This is useful if you wish to 135 | render each category in a separate block. 136 | 137 | .. sourcecode:: html+jinja 138 | 139 | {% with errors = get_flashed_messages(category_filter=["error"]) %} 140 | {% if errors %} 141 |
142 | × 143 |
    144 | {%- for msg in errors %} 145 |
  • {{ msg }}
  • 146 | {% endfor -%} 147 |
148 |
149 | {% endif %} 150 | {% endwith %} 151 | -------------------------------------------------------------------------------- /docs/patterns/index.rst: -------------------------------------------------------------------------------- 1 | .. _patterns: 2 | 3 | Patterns for Flask 4 | ================== 5 | 6 | Certain things are common enough that the chances are high you will find 7 | them in most web applications. For example quite a lot of applications 8 | are using relational databases and user authentication. In that case, 9 | chances are they will open a database connection at the beginning of the 10 | request and get the information of the currently logged in user. At the 11 | end of the request, the database connection is closed again. 12 | 13 | There are more user contributed snippets and patterns in the `Flask 14 | Snippet Archives `_. 15 | 16 | .. toctree:: 17 | :maxdepth: 2 18 | 19 | packages 20 | appfactories 21 | appdispatch 22 | apierrors 23 | urlprocessors 24 | distribute 25 | fabric 26 | sqlite3 27 | sqlalchemy 28 | fileuploads 29 | caching 30 | viewdecorators 31 | wtforms 32 | templateinheritance 33 | flashing 34 | jquery 35 | errorpages 36 | lazyloading 37 | mongokit 38 | favicon 39 | streaming 40 | deferredcallbacks 41 | methodoverrides 42 | requestchecksum 43 | celery 44 | subclassing 45 | -------------------------------------------------------------------------------- /docs/patterns/lazyloading.rst: -------------------------------------------------------------------------------- 1 | Lazily Loading Views 2 | ==================== 3 | 4 | Flask is usually used with the decorators. Decorators are simple and you 5 | have the URL right next to the function that is called for that specific 6 | URL. However there is a downside to this approach: it means all your code 7 | that uses decorators has to be imported upfront or Flask will never 8 | actually find your function. 9 | 10 | This can be a problem if your application has to import quick. It might 11 | have to do that on systems like Google's App Engine or other systems. So 12 | if you suddenly notice that your application outgrows this approach you 13 | can fall back to a centralized URL mapping. 14 | 15 | The system that enables having a central URL map is the 16 | :meth:`~flask.Flask.add_url_rule` function. Instead of using decorators, 17 | you have a file that sets up the application with all URLs. 18 | 19 | Converting to Centralized URL Map 20 | --------------------------------- 21 | 22 | Imagine the current application looks somewhat like this:: 23 | 24 | from flask import Flask 25 | app = Flask(__name__) 26 | 27 | @app.route('/') 28 | def index(): 29 | pass 30 | 31 | @app.route('/user/') 32 | def user(username): 33 | pass 34 | 35 | Then, with the centralized approach you would have one file with the views 36 | (:file:`views.py`) but without any decorator:: 37 | 38 | def index(): 39 | pass 40 | 41 | def user(username): 42 | pass 43 | 44 | And then a file that sets up an application which maps the functions to 45 | URLs:: 46 | 47 | from flask import Flask 48 | from yourapplication import views 49 | app = Flask(__name__) 50 | app.add_url_rule('/', view_func=views.index) 51 | app.add_url_rule('/user/', view_func=views.user) 52 | 53 | Loading Late 54 | ------------ 55 | 56 | So far we only split up the views and the routing, but the module is still 57 | loaded upfront. The trick is to actually load the view function as needed. 58 | This can be accomplished with a helper class that behaves just like a 59 | function but internally imports the real function on first use:: 60 | 61 | from werkzeug import import_string, cached_property 62 | 63 | class LazyView(object): 64 | 65 | def __init__(self, import_name): 66 | self.__module__, self.__name__ = import_name.rsplit('.', 1) 67 | self.import_name = import_name 68 | 69 | @cached_property 70 | def view(self): 71 | return import_string(self.import_name) 72 | 73 | def __call__(self, *args, **kwargs): 74 | return self.view(*args, **kwargs) 75 | 76 | What's important here is is that `__module__` and `__name__` are properly 77 | set. This is used by Flask internally to figure out how to name the 78 | URL rules in case you don't provide a name for the rule yourself. 79 | 80 | Then you can define your central place to combine the views like this:: 81 | 82 | from flask import Flask 83 | from yourapplication.helpers import LazyView 84 | app = Flask(__name__) 85 | app.add_url_rule('/', 86 | view_func=LazyView('yourapplication.views.index')) 87 | app.add_url_rule('/user/', 88 | view_func=LazyView('yourapplication.views.user')) 89 | 90 | You can further optimize this in terms of amount of keystrokes needed to 91 | write this by having a function that calls into 92 | :meth:`~flask.Flask.add_url_rule` by prefixing a string with the project 93 | name and a dot, and by wrapping `view_func` in a `LazyView` as needed. :: 94 | 95 | def url(import_name, url_rules=[], **options): 96 | view = LazyView('yourapplication.' + import_name) 97 | for url_rule in url_rules: 98 | app.add_url_rule(url_rule, view_func=view, **options) 99 | 100 | # add a single route to the index view 101 | url('views.index', ['/']) 102 | 103 | # add two routes to a single function endpoint 104 | url_rules = ['/user/','/user/'] 105 | url('views.user', url_rules) 106 | 107 | One thing to keep in mind is that before and after request handlers have 108 | to be in a file that is imported upfront to work properly on the first 109 | request. The same goes for any kind of remaining decorator. 110 | -------------------------------------------------------------------------------- /docs/patterns/methodoverrides.rst: -------------------------------------------------------------------------------- 1 | Adding HTTP Method Overrides 2 | ============================ 3 | 4 | Some HTTP proxies do not support arbitrary HTTP methods or newer HTTP 5 | methods (such as PATCH). In that case it's possible to “proxy” HTTP 6 | methods through another HTTP method in total violation of the protocol. 7 | 8 | The way this works is by letting the client do an HTTP POST request and 9 | set the ``X-HTTP-Method-Override`` header and set the value to the 10 | intended HTTP method (such as ``PATCH``). 11 | 12 | This can easily be accomplished with an HTTP middleware:: 13 | 14 | class HTTPMethodOverrideMiddleware(object): 15 | allowed_methods = frozenset([ 16 | 'GET', 17 | 'HEAD', 18 | 'POST', 19 | 'DELETE', 20 | 'PUT', 21 | 'PATCH', 22 | 'OPTIONS' 23 | ]) 24 | bodyless_methods = frozenset(['GET', 'HEAD', 'OPTIONS', 'DELETE']) 25 | 26 | def __init__(self, app): 27 | self.app = app 28 | 29 | def __call__(self, environ, start_response): 30 | method = environ.get('HTTP_X_HTTP_METHOD_OVERRIDE', '').upper() 31 | if method in self.allowed_methods: 32 | method = method.encode('ascii', 'replace') 33 | environ['REQUEST_METHOD'] = method 34 | if method in self.bodyless_methods: 35 | environ['CONTENT_LENGTH'] = '0' 36 | return self.app(environ, start_response) 37 | 38 | To use this with Flask this is all that is necessary:: 39 | 40 | from flask import Flask 41 | 42 | app = Flask(__name__) 43 | app.wsgi_app = HTTPMethodOverrideMiddleware(app.wsgi_app) 44 | -------------------------------------------------------------------------------- /docs/patterns/packages.rst: -------------------------------------------------------------------------------- 1 | .. _larger-applications: 2 | 3 | Larger Applications 4 | =================== 5 | 6 | For larger applications it's a good idea to use a package instead of a 7 | module. That is quite simple. Imagine a small application looks like 8 | this:: 9 | 10 | /yourapplication 11 | yourapplication.py 12 | /static 13 | style.css 14 | /templates 15 | layout.html 16 | index.html 17 | login.html 18 | ... 19 | 20 | The :ref:`tutorial ` is structured this way, see the 21 | :gh:`example code `. 22 | 23 | Simple Packages 24 | --------------- 25 | 26 | To convert that into a larger one, just create a new folder 27 | :file:`yourapplication` inside the existing one and move everything below it. 28 | Then rename :file:`yourapplication.py` to :file:`__init__.py`. (Make sure to delete 29 | all ``.pyc`` files first, otherwise things would most likely break) 30 | 31 | You should then end up with something like that:: 32 | 33 | /yourapplication 34 | /yourapplication 35 | __init__.py 36 | /static 37 | style.css 38 | /templates 39 | layout.html 40 | index.html 41 | login.html 42 | ... 43 | 44 | But how do you run your application now? The naive ``python 45 | yourapplication/__init__.py`` will not work. Let's just say that Python 46 | does not want modules in packages to be the startup file. But that is not 47 | a big problem, just add a new file called :file:`setup.py` next to the inner 48 | :file:`yourapplication` folder with the following contents:: 49 | 50 | from setuptools import setup 51 | 52 | setup( 53 | name='yourapplication', 54 | packages=['yourapplication'], 55 | include_package_data=True, 56 | install_requires=[ 57 | 'flask', 58 | ], 59 | ) 60 | 61 | In order to run the application you need to export an environment variable 62 | that tells Flask where to find the application instance:: 63 | 64 | export FLASK_APP=yourapplication 65 | 66 | If you are outside of the project directory make sure to provide the exact 67 | path to your application directory. Similarly you can turn on the 68 | development features like this:: 69 | 70 | export FLASK_ENV=development 71 | 72 | In order to install and run the application you need to issue the following 73 | commands:: 74 | 75 | pip install -e . 76 | flask run 77 | 78 | What did we gain from this? Now we can restructure the application a bit 79 | into multiple modules. The only thing you have to remember is the 80 | following quick checklist: 81 | 82 | 1. the `Flask` application object creation has to be in the 83 | :file:`__init__.py` file. That way each module can import it safely and the 84 | `__name__` variable will resolve to the correct package. 85 | 2. all the view functions (the ones with a :meth:`~flask.Flask.route` 86 | decorator on top) have to be imported in the :file:`__init__.py` file. 87 | Not the object itself, but the module it is in. Import the view module 88 | **after the application object is created**. 89 | 90 | Here's an example :file:`__init__.py`:: 91 | 92 | from flask import Flask 93 | app = Flask(__name__) 94 | 95 | import yourapplication.views 96 | 97 | And this is what :file:`views.py` would look like:: 98 | 99 | from yourapplication import app 100 | 101 | @app.route('/') 102 | def index(): 103 | return 'Hello World!' 104 | 105 | You should then end up with something like that:: 106 | 107 | /yourapplication 108 | setup.py 109 | /yourapplication 110 | __init__.py 111 | views.py 112 | /static 113 | style.css 114 | /templates 115 | layout.html 116 | index.html 117 | login.html 118 | ... 119 | 120 | .. admonition:: Circular Imports 121 | 122 | Every Python programmer hates them, and yet we just added some: 123 | circular imports (That's when two modules depend on each other. In this 124 | case :file:`views.py` depends on :file:`__init__.py`). Be advised that this is a 125 | bad idea in general but here it is actually fine. The reason for this is 126 | that we are not actually using the views in :file:`__init__.py` and just 127 | ensuring the module is imported and we are doing that at the bottom of 128 | the file. 129 | 130 | There are still some problems with that approach but if you want to use 131 | decorators there is no way around that. Check out the 132 | :ref:`becomingbig` section for some inspiration how to deal with that. 133 | 134 | 135 | .. _working-with-modules: 136 | 137 | Working with Blueprints 138 | ----------------------- 139 | 140 | If you have larger applications it's recommended to divide them into 141 | smaller groups where each group is implemented with the help of a 142 | blueprint. For a gentle introduction into this topic refer to the 143 | :ref:`blueprints` chapter of the documentation. 144 | -------------------------------------------------------------------------------- /docs/patterns/requestchecksum.rst: -------------------------------------------------------------------------------- 1 | Request Content Checksums 2 | ========================= 3 | 4 | Various pieces of code can consume the request data and preprocess it. 5 | For instance JSON data ends up on the request object already read and 6 | processed, form data ends up there as well but goes through a different 7 | code path. This seems inconvenient when you want to calculate the 8 | checksum of the incoming request data. This is necessary sometimes for 9 | some APIs. 10 | 11 | Fortunately this is however very simple to change by wrapping the input 12 | stream. 13 | 14 | The following example calculates the SHA1 checksum of the incoming data as 15 | it gets read and stores it in the WSGI environment:: 16 | 17 | import hashlib 18 | 19 | class ChecksumCalcStream(object): 20 | 21 | def __init__(self, stream): 22 | self._stream = stream 23 | self._hash = hashlib.sha1() 24 | 25 | def read(self, bytes): 26 | rv = self._stream.read(bytes) 27 | self._hash.update(rv) 28 | return rv 29 | 30 | def readline(self, size_hint): 31 | rv = self._stream.readline(size_hint) 32 | self._hash.update(rv) 33 | return rv 34 | 35 | def generate_checksum(request): 36 | env = request.environ 37 | stream = ChecksumCalcStream(env['wsgi.input']) 38 | env['wsgi.input'] = stream 39 | return stream._hash 40 | 41 | To use this, all you need to do is to hook the calculating stream in 42 | before the request starts consuming data. (Eg: be careful accessing 43 | ``request.form`` or anything of that nature. ``before_request_handlers`` 44 | for instance should be careful not to access it). 45 | 46 | Example usage:: 47 | 48 | @app.route('/special-api', methods=['POST']) 49 | def special_api(): 50 | hash = generate_checksum(request) 51 | # Accessing this parses the input stream 52 | files = request.files 53 | # At this point the hash is fully constructed. 54 | checksum = hash.hexdigest() 55 | return 'Hash was: %s' % checksum 56 | -------------------------------------------------------------------------------- /docs/patterns/streaming.rst: -------------------------------------------------------------------------------- 1 | Streaming Contents 2 | ================== 3 | 4 | Sometimes you want to send an enormous amount of data to the client, much 5 | more than you want to keep in memory. When you are generating the data on 6 | the fly though, how do you send that back to the client without the 7 | roundtrip to the filesystem? 8 | 9 | The answer is by using generators and direct responses. 10 | 11 | Basic Usage 12 | ----------- 13 | 14 | This is a basic view function that generates a lot of CSV data on the fly. 15 | The trick is to have an inner function that uses a generator to generate 16 | data and to then invoke that function and pass it to a response object:: 17 | 18 | from flask import Response 19 | 20 | @app.route('/large.csv') 21 | def generate_large_csv(): 22 | def generate(): 23 | for row in iter_all_rows(): 24 | yield ','.join(row) + '\n' 25 | return Response(generate(), mimetype='text/csv') 26 | 27 | Each ``yield`` expression is directly sent to the browser. Note though 28 | that some WSGI middlewares might break streaming, so be careful there in 29 | debug environments with profilers and other things you might have enabled. 30 | 31 | Streaming from Templates 32 | ------------------------ 33 | 34 | The Jinja2 template engine also supports rendering templates piece by 35 | piece. This functionality is not directly exposed by Flask because it is 36 | quite uncommon, but you can easily do it yourself:: 37 | 38 | from flask import Response 39 | 40 | def stream_template(template_name, **context): 41 | app.update_template_context(context) 42 | t = app.jinja_env.get_template(template_name) 43 | rv = t.stream(context) 44 | rv.enable_buffering(5) 45 | return rv 46 | 47 | @app.route('/my-large-page.html') 48 | def render_large_template(): 49 | rows = iter_all_rows() 50 | return Response(stream_template('the_template.html', rows=rows)) 51 | 52 | The trick here is to get the template object from the Jinja2 environment 53 | on the application and to call :meth:`~jinja2.Template.stream` instead of 54 | :meth:`~jinja2.Template.render` which returns a stream object instead of a 55 | string. Since we're bypassing the Flask template render functions and 56 | using the template object itself we have to make sure to update the render 57 | context ourselves by calling :meth:`~flask.Flask.update_template_context`. 58 | The template is then evaluated as the stream is iterated over. Since each 59 | time you do a yield the server will flush the content to the client you 60 | might want to buffer up a few items in the template which you can do with 61 | ``rv.enable_buffering(size)``. ``5`` is a sane default. 62 | 63 | Streaming with Context 64 | ---------------------- 65 | 66 | .. versionadded:: 0.9 67 | 68 | Note that when you stream data, the request context is already gone the 69 | moment the function executes. Flask 0.9 provides you with a helper that 70 | can keep the request context around during the execution of the 71 | generator:: 72 | 73 | from flask import stream_with_context, request, Response 74 | 75 | @app.route('/stream') 76 | def streamed_response(): 77 | def generate(): 78 | yield 'Hello ' 79 | yield request.args['name'] 80 | yield '!' 81 | return Response(stream_with_context(generate())) 82 | 83 | Without the :func:`~flask.stream_with_context` function you would get a 84 | :class:`RuntimeError` at that point. 85 | -------------------------------------------------------------------------------- /docs/patterns/subclassing.rst: -------------------------------------------------------------------------------- 1 | Subclassing Flask 2 | ================= 3 | 4 | The :class:`~flask.Flask` class is designed for subclassing. 5 | 6 | For example, you may want to override how request parameters are handled to preserve their order:: 7 | 8 | from flask import Flask, Request 9 | from werkzeug.datastructures import ImmutableOrderedMultiDict 10 | class MyRequest(Request): 11 | """Request subclass to override request parameter storage""" 12 | parameter_storage_class = ImmutableOrderedMultiDict 13 | class MyFlask(Flask): 14 | """Flask subclass using the custom request class""" 15 | request_class = MyRequest 16 | 17 | This is the recommended approach for overriding or augmenting Flask's internal functionality. 18 | -------------------------------------------------------------------------------- /docs/patterns/templateinheritance.rst: -------------------------------------------------------------------------------- 1 | .. _template-inheritance: 2 | 3 | Template Inheritance 4 | ==================== 5 | 6 | The most powerful part of Jinja is template inheritance. Template inheritance 7 | allows you to build a base "skeleton" template that contains all the common 8 | elements of your site and defines **blocks** that child templates can override. 9 | 10 | Sounds complicated but is very basic. It's easiest to understand it by starting 11 | with an example. 12 | 13 | 14 | Base Template 15 | ------------- 16 | 17 | This template, which we'll call :file:`layout.html`, defines a simple HTML skeleton 18 | document that you might use for a simple two-column page. It's the job of 19 | "child" templates to fill the empty blocks with content: 20 | 21 | .. sourcecode:: html+jinja 22 | 23 | 24 | 25 | 26 | {% block head %} 27 | 28 | {% block title %}{% endblock %} - My Webpage 29 | {% endblock %} 30 | 31 | 32 |
{% block content %}{% endblock %}
33 | 38 | 39 | 40 | 41 | In this example, the ``{% block %}`` tags define four blocks that child templates 42 | can fill in. All the `block` tag does is tell the template engine that a 43 | child template may override those portions of the template. 44 | 45 | Child Template 46 | -------------- 47 | 48 | A child template might look like this: 49 | 50 | .. sourcecode:: html+jinja 51 | 52 | {% extends "layout.html" %} 53 | {% block title %}Index{% endblock %} 54 | {% block head %} 55 | {{ super() }} 56 | 59 | {% endblock %} 60 | {% block content %} 61 |

Index

62 |

63 | Welcome on my awesome homepage. 64 | {% endblock %} 65 | 66 | The ``{% extends %}`` tag is the key here. It tells the template engine that 67 | this template "extends" another template. When the template system evaluates 68 | this template, first it locates the parent. The extends tag must be the 69 | first tag in the template. To render the contents of a block defined in 70 | the parent template, use ``{{ super() }}``. 71 | -------------------------------------------------------------------------------- /docs/patterns/urlprocessors.rst: -------------------------------------------------------------------------------- 1 | Using URL Processors 2 | ==================== 3 | 4 | .. versionadded:: 0.7 5 | 6 | Flask 0.7 introduces the concept of URL processors. The idea is that you 7 | might have a bunch of resources with common parts in the URL that you 8 | don't always explicitly want to provide. For instance you might have a 9 | bunch of URLs that have the language code in it but you don't want to have 10 | to handle it in every single function yourself. 11 | 12 | URL processors are especially helpful when combined with blueprints. We 13 | will handle both application specific URL processors here as well as 14 | blueprint specifics. 15 | 16 | Internationalized Application URLs 17 | ---------------------------------- 18 | 19 | Consider an application like this:: 20 | 21 | from flask import Flask, g 22 | 23 | app = Flask(__name__) 24 | 25 | @app.route('//') 26 | def index(lang_code): 27 | g.lang_code = lang_code 28 | ... 29 | 30 | @app.route('//about') 31 | def about(lang_code): 32 | g.lang_code = lang_code 33 | ... 34 | 35 | This is an awful lot of repetition as you have to handle the language code 36 | setting on the :data:`~flask.g` object yourself in every single function. 37 | Sure, a decorator could be used to simplify this, but if you want to 38 | generate URLs from one function to another you would have to still provide 39 | the language code explicitly which can be annoying. 40 | 41 | For the latter, this is where :func:`~flask.Flask.url_defaults` functions 42 | come in. They can automatically inject values into a call for 43 | :func:`~flask.url_for` automatically. The code below checks if the 44 | language code is not yet in the dictionary of URL values and if the 45 | endpoint wants a value named ``'lang_code'``:: 46 | 47 | @app.url_defaults 48 | def add_language_code(endpoint, values): 49 | if 'lang_code' in values or not g.lang_code: 50 | return 51 | if app.url_map.is_endpoint_expecting(endpoint, 'lang_code'): 52 | values['lang_code'] = g.lang_code 53 | 54 | The method :meth:`~werkzeug.routing.Map.is_endpoint_expecting` of the URL 55 | map can be used to figure out if it would make sense to provide a language 56 | code for the given endpoint. 57 | 58 | The reverse of that function are 59 | :meth:`~flask.Flask.url_value_preprocessor`\s. They are executed right 60 | after the request was matched and can execute code based on the URL 61 | values. The idea is that they pull information out of the values 62 | dictionary and put it somewhere else:: 63 | 64 | @app.url_value_preprocessor 65 | def pull_lang_code(endpoint, values): 66 | g.lang_code = values.pop('lang_code', None) 67 | 68 | That way you no longer have to do the `lang_code` assignment to 69 | :data:`~flask.g` in every function. You can further improve that by 70 | writing your own decorator that prefixes URLs with the language code, but 71 | the more beautiful solution is using a blueprint. Once the 72 | ``'lang_code'`` is popped from the values dictionary and it will no longer 73 | be forwarded to the view function reducing the code to this:: 74 | 75 | from flask import Flask, g 76 | 77 | app = Flask(__name__) 78 | 79 | @app.url_defaults 80 | def add_language_code(endpoint, values): 81 | if 'lang_code' in values or not g.lang_code: 82 | return 83 | if app.url_map.is_endpoint_expecting(endpoint, 'lang_code'): 84 | values['lang_code'] = g.lang_code 85 | 86 | @app.url_value_preprocessor 87 | def pull_lang_code(endpoint, values): 88 | g.lang_code = values.pop('lang_code', None) 89 | 90 | @app.route('//') 91 | def index(): 92 | ... 93 | 94 | @app.route('//about') 95 | def about(): 96 | ... 97 | 98 | Internationalized Blueprint URLs 99 | -------------------------------- 100 | 101 | Because blueprints can automatically prefix all URLs with a common string 102 | it's easy to automatically do that for every function. Furthermore 103 | blueprints can have per-blueprint URL processors which removes a whole lot 104 | of logic from the :meth:`~flask.Flask.url_defaults` function because it no 105 | longer has to check if the URL is really interested in a ``'lang_code'`` 106 | parameter:: 107 | 108 | from flask import Blueprint, g 109 | 110 | bp = Blueprint('frontend', __name__, url_prefix='/') 111 | 112 | @bp.url_defaults 113 | def add_language_code(endpoint, values): 114 | values.setdefault('lang_code', g.lang_code) 115 | 116 | @bp.url_value_preprocessor 117 | def pull_lang_code(endpoint, values): 118 | g.lang_code = values.pop('lang_code') 119 | 120 | @bp.route('/') 121 | def index(): 122 | ... 123 | 124 | @bp.route('/about') 125 | def about(): 126 | ... 127 | -------------------------------------------------------------------------------- /docs/patterns/wtforms.rst: -------------------------------------------------------------------------------- 1 | Form Validation with WTForms 2 | ============================ 3 | 4 | When you have to work with form data submitted by a browser view, code 5 | quickly becomes very hard to read. There are libraries out there designed 6 | to make this process easier to manage. One of them is `WTForms`_ which we 7 | will handle here. If you find yourself in the situation of having many 8 | forms, you might want to give it a try. 9 | 10 | When you are working with WTForms you have to define your forms as classes 11 | first. I recommend breaking up the application into multiple modules 12 | (:ref:`larger-applications`) for that and adding a separate module for the 13 | forms. 14 | 15 | .. admonition:: Getting the most out of WTForms with an Extension 16 | 17 | The `Flask-WTF`_ extension expands on this pattern and adds a 18 | few little helpers that make working with forms and Flask more 19 | fun. You can get it from `PyPI 20 | `_. 21 | 22 | .. _Flask-WTF: https://flask-wtf.readthedocs.io/en/stable/ 23 | 24 | The Forms 25 | --------- 26 | 27 | This is an example form for a typical registration page:: 28 | 29 | from wtforms import Form, BooleanField, StringField, PasswordField, validators 30 | 31 | class RegistrationForm(Form): 32 | username = StringField('Username', [validators.Length(min=4, max=25)]) 33 | email = StringField('Email Address', [validators.Length(min=6, max=35)]) 34 | password = PasswordField('New Password', [ 35 | validators.DataRequired(), 36 | validators.EqualTo('confirm', message='Passwords must match') 37 | ]) 38 | confirm = PasswordField('Repeat Password') 39 | accept_tos = BooleanField('I accept the TOS', [validators.DataRequired()]) 40 | 41 | In the View 42 | ----------- 43 | 44 | In the view function, the usage of this form looks like this:: 45 | 46 | @app.route('/register', methods=['GET', 'POST']) 47 | def register(): 48 | form = RegistrationForm(request.form) 49 | if request.method == 'POST' and form.validate(): 50 | user = User(form.username.data, form.email.data, 51 | form.password.data) 52 | db_session.add(user) 53 | flash('Thanks for registering') 54 | return redirect(url_for('login')) 55 | return render_template('register.html', form=form) 56 | 57 | Notice we're implying that the view is using SQLAlchemy here 58 | (:ref:`sqlalchemy-pattern`), but that's not a requirement, of course. Adapt 59 | the code as necessary. 60 | 61 | Things to remember: 62 | 63 | 1. create the form from the request :attr:`~flask.request.form` value if 64 | the data is submitted via the HTTP ``POST`` method and 65 | :attr:`~flask.request.args` if the data is submitted as ``GET``. 66 | 2. to validate the data, call the :func:`~wtforms.form.Form.validate` 67 | method, which will return ``True`` if the data validates, ``False`` 68 | otherwise. 69 | 3. to access individual values from the form, access `form..data`. 70 | 71 | Forms in Templates 72 | ------------------ 73 | 74 | Now to the template side. When you pass the form to the templates, you can 75 | easily render them there. Look at the following example template to see 76 | how easy this is. WTForms does half the form generation for us already. 77 | To make it even nicer, we can write a macro that renders a field with 78 | label and a list of errors if there are any. 79 | 80 | Here's an example :file:`_formhelpers.html` template with such a macro: 81 | 82 | .. sourcecode:: html+jinja 83 | 84 | {% macro render_field(field) %} 85 |

{{ field.label }} 86 |
{{ field(**kwargs)|safe }} 87 | {% if field.errors %} 88 |
    89 | {% for error in field.errors %} 90 |
  • {{ error }}
  • 91 | {% endfor %} 92 |
93 | {% endif %} 94 |
95 | {% endmacro %} 96 | 97 | This macro accepts a couple of keyword arguments that are forwarded to 98 | WTForm's field function, which renders the field for us. The keyword 99 | arguments will be inserted as HTML attributes. So, for example, you can 100 | call ``render_field(form.username, class='username')`` to add a class to 101 | the input element. Note that WTForms returns standard Python unicode 102 | strings, so we have to tell Jinja2 that this data is already HTML-escaped 103 | with the ``|safe`` filter. 104 | 105 | Here is the :file:`register.html` template for the function we used above, which 106 | takes advantage of the :file:`_formhelpers.html` template: 107 | 108 | .. sourcecode:: html+jinja 109 | 110 | {% from "_formhelpers.html" import render_field %} 111 |
112 |
113 | {{ render_field(form.username) }} 114 | {{ render_field(form.email) }} 115 | {{ render_field(form.password) }} 116 | {{ render_field(form.confirm) }} 117 | {{ render_field(form.accept_tos) }} 118 |
119 |

120 |

121 | 122 | For more information about WTForms, head over to the `WTForms 123 | website`_. 124 | 125 | .. _WTForms: https://wtforms.readthedocs.io/ 126 | .. _WTForms website: https://wtforms.readthedocs.io/ 127 | -------------------------------------------------------------------------------- /docs/server.rst: -------------------------------------------------------------------------------- 1 | .. _server: 2 | 3 | Development Server 4 | ================== 5 | 6 | .. currentmodule:: flask 7 | 8 | Starting with Flask 0.11 there are multiple built-in ways to run a 9 | development server. The best one is the :command:`flask` command line utility 10 | but you can also continue using the :meth:`Flask.run` method. 11 | 12 | Command Line 13 | ------------ 14 | 15 | The :command:`flask` command line script (:ref:`cli`) is strongly 16 | recommended for development because it provides a superior reload 17 | experience due to how it loads the application. The basic usage is like 18 | this:: 19 | 20 | $ export FLASK_APP=my_application 21 | $ export FLASK_ENV=development 22 | $ flask run 23 | 24 | This enables the development environment, including the interactive 25 | debugger and reloader, and then starts the server on 26 | *http://localhost:5000/*. 27 | 28 | The individual features of the server can be controlled by passing more 29 | arguments to the ``run`` option. For instance the reloader can be 30 | disabled:: 31 | 32 | $ flask run --no-reload 33 | 34 | .. note:: 35 | 36 | Prior to Flask 1.0 the :envvar:`FLASK_ENV` environment variable was 37 | not supported and you needed to enable debug mode by exporting 38 | ``FLASK_DEBUG=1``. This can still be used to control debug mode, but 39 | you should prefer setting the development environment as shown 40 | above. 41 | 42 | In Code 43 | ------- 44 | 45 | The alternative way to start the application is through the 46 | :meth:`Flask.run` method. This will immediately launch a local server 47 | exactly the same way the :command:`flask` script does. 48 | 49 | Example:: 50 | 51 | if __name__ == '__main__': 52 | app.run() 53 | 54 | This works well for the common case but it does not work well for 55 | development which is why from Flask 0.11 onwards the :command:`flask` 56 | method is recommended. The reason for this is that due to how the reload 57 | mechanism works there are some bizarre side-effects (like executing 58 | certain code twice, sometimes crashing without message or dying when a 59 | syntax or import error happens). 60 | 61 | It is however still a perfectly valid method for invoking a non automatic 62 | reloading application. 63 | -------------------------------------------------------------------------------- /docs/shell.rst: -------------------------------------------------------------------------------- 1 | .. _shell: 2 | 3 | Working with the Shell 4 | ====================== 5 | 6 | .. versionadded:: 0.3 7 | 8 | One of the reasons everybody loves Python is the interactive shell. It 9 | basically allows you to execute Python commands in real time and 10 | immediately get results back. Flask itself does not come with an 11 | interactive shell, because it does not require any specific setup upfront, 12 | just import your application and start playing around. 13 | 14 | There are however some handy helpers to make playing around in the shell a 15 | more pleasant experience. The main issue with interactive console 16 | sessions is that you're not triggering a request like a browser does which 17 | means that :data:`~flask.g`, :data:`~flask.request` and others are not 18 | available. But the code you want to test might depend on them, so what 19 | can you do? 20 | 21 | This is where some helper functions come in handy. Keep in mind however 22 | that these functions are not only there for interactive shell usage, but 23 | also for unittesting and other situations that require a faked request 24 | context. 25 | 26 | Generally it's recommended that you read the :ref:`request-context` 27 | chapter of the documentation first. 28 | 29 | Command Line Interface 30 | ---------------------- 31 | 32 | Starting with Flask 0.11 the recommended way to work with the shell is the 33 | ``flask shell`` command which does a lot of this automatically for you. 34 | For instance the shell is automatically initialized with a loaded 35 | application context. 36 | 37 | For more information see :ref:`cli`. 38 | 39 | Creating a Request Context 40 | -------------------------- 41 | 42 | The easiest way to create a proper request context from the shell is by 43 | using the :attr:`~flask.Flask.test_request_context` method which creates 44 | us a :class:`~flask.ctx.RequestContext`: 45 | 46 | >>> ctx = app.test_request_context() 47 | 48 | Normally you would use the ``with`` statement to make this request object 49 | active, but in the shell it's easier to use the 50 | :meth:`~flask.ctx.RequestContext.push` and 51 | :meth:`~flask.ctx.RequestContext.pop` methods by hand: 52 | 53 | >>> ctx.push() 54 | 55 | From that point onwards you can work with the request object until you 56 | call `pop`: 57 | 58 | >>> ctx.pop() 59 | 60 | Firing Before/After Request 61 | --------------------------- 62 | 63 | By just creating a request context, you still don't have run the code that 64 | is normally run before a request. This might result in your database 65 | being unavailable if you are connecting to the database in a 66 | before-request callback or the current user not being stored on the 67 | :data:`~flask.g` object etc. 68 | 69 | This however can easily be done yourself. Just call 70 | :meth:`~flask.Flask.preprocess_request`: 71 | 72 | >>> ctx = app.test_request_context() 73 | >>> ctx.push() 74 | >>> app.preprocess_request() 75 | 76 | Keep in mind that the :meth:`~flask.Flask.preprocess_request` function 77 | might return a response object, in that case just ignore it. 78 | 79 | To shutdown a request, you need to trick a bit before the after request 80 | functions (triggered by :meth:`~flask.Flask.process_response`) operate on 81 | a response object: 82 | 83 | >>> app.process_response(app.response_class()) 84 | 85 | >>> ctx.pop() 86 | 87 | The functions registered as :meth:`~flask.Flask.teardown_request` are 88 | automatically called when the context is popped. So this is the perfect 89 | place to automatically tear down resources that were needed by the request 90 | context (such as database connections). 91 | 92 | 93 | Further Improving the Shell Experience 94 | -------------------------------------- 95 | 96 | If you like the idea of experimenting in a shell, create yourself a module 97 | with stuff you want to star import into your interactive session. There 98 | you could also define some more helper methods for common things such as 99 | initializing the database, dropping tables etc. 100 | 101 | Just put them into a module (like `shelltools`) and import from there: 102 | 103 | >>> from shelltools import * 104 | -------------------------------------------------------------------------------- /docs/tutorial/deploy.rst: -------------------------------------------------------------------------------- 1 | Deploy to Production 2 | ==================== 3 | 4 | This part of the tutorial assumes you have a server that you want to 5 | deploy your application to. It gives an overview of how to create the 6 | distribution file and install it, but won't go into specifics about 7 | what server or software to use. You can set up a new environment on your 8 | development computer to try out the instructions below, but probably 9 | shouldn't use it for hosting a real public application. See 10 | :doc:`/deploying/index` for a list of many different ways to host your 11 | application. 12 | 13 | 14 | Build and Install 15 | ----------------- 16 | 17 | When you want to deploy your application elsewhere, you build a 18 | distribution file. The current standard for Python distribution is the 19 | *wheel* format, with the ``.whl`` extension. Make sure the wheel library 20 | is installed first: 21 | 22 | .. code-block:: none 23 | 24 | pip install wheel 25 | 26 | Running ``setup.py`` with Python gives you a command line tool to issue 27 | build-related commands. The ``bdist_wheel`` command will build a wheel 28 | distribution file. 29 | 30 | .. code-block:: none 31 | 32 | python setup.py bdist_wheel 33 | 34 | You can find the file in ``dist/flaskr-1.0.0-py3-none-any.whl``. The 35 | file name is the name of the project, the version, and some tags about 36 | the file can install. 37 | 38 | Copy this file to another machine, 39 | :ref:`set up a new virtualenv `, then install the 40 | file with ``pip``. 41 | 42 | .. code-block:: none 43 | 44 | pip install flaskr-1.0.0-py3-none-any.whl 45 | 46 | Pip will install your project along with its dependencies. 47 | 48 | Since this is a different machine, you need to run ``init-db`` again to 49 | create the database in the instance folder. 50 | 51 | .. code-block:: none 52 | 53 | export FLASK_APP=flaskr 54 | flask init-db 55 | 56 | When Flask detects that it's installed (not in editable mode), it uses 57 | a different directory for the instance folder. You can find it at 58 | ``venv/var/flaskr-instance`` instead. 59 | 60 | 61 | Configure the Secret Key 62 | ------------------------ 63 | 64 | In the beginning of the tutorial that you gave a default value for 65 | :data:`SECRET_KEY`. This should be changed to some random bytes in 66 | production. Otherwise, attackers could use the public ``'dev'`` key to 67 | modify the session cookie, or anything else that uses the secret key. 68 | 69 | You can use the following command to output a random secret key: 70 | 71 | .. code-block:: none 72 | 73 | python -c 'import os; print(os.urandom(16))' 74 | 75 | b'_5#y2L"F4Q8z\n\xec]/' 76 | 77 | Create the ``config.py`` file in the instance folder, which the factory 78 | will read from if it exists. Copy the generated value into it. 79 | 80 | .. code-block:: python 81 | :caption: ``venv/var/flaskr-instance/config.py`` 82 | 83 | SECRET_KEY = b'_5#y2L"F4Q8z\n\xec]/' 84 | 85 | You can also set any other necessary configuration here, although 86 | ``SECRET_KEY`` is the only one needed for Flaskr. 87 | 88 | 89 | Run with a Production Server 90 | ---------------------------- 91 | 92 | When running publicly rather than in development, you should not use the 93 | built-in development server (``flask run``). The development server is 94 | provided by Werkzeug for convenience, but is not designed to be 95 | particularly efficient, stable, or secure. 96 | 97 | Instead, use a production WSGI server. For example, to use `Waitress`_, 98 | first install it in the virtual environment: 99 | 100 | .. code-block:: none 101 | 102 | pip install waitress 103 | 104 | You need to tell Waitress about your application, but it doesn't use 105 | ``FLASK_APP`` like ``flask run`` does. You need to tell it to import and 106 | call the application factory to get an application object. 107 | 108 | .. code-block:: none 109 | 110 | waitress-serve --call 'flaskr:create_app' 111 | 112 | Serving on http://0.0.0.0:8080 113 | 114 | See :doc:`/deploying/index` for a list of many different ways to host 115 | your application. Waitress is just an example, chosen for the tutorial 116 | because it supports both Windows and Linux. There are many more WSGI 117 | servers and deployment options that you may choose for your project. 118 | 119 | .. _Waitress: https://docs.pylonsproject.org/projects/waitress/ 120 | 121 | Continue to :doc:`next`. 122 | -------------------------------------------------------------------------------- /docs/tutorial/flaskr_edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyup-bot/flask/b9b88b0cdf70190dee5c7d31a83efb1cae888a63/docs/tutorial/flaskr_edit.png -------------------------------------------------------------------------------- /docs/tutorial/flaskr_index.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyup-bot/flask/b9b88b0cdf70190dee5c7d31a83efb1cae888a63/docs/tutorial/flaskr_index.png -------------------------------------------------------------------------------- /docs/tutorial/flaskr_login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyup-bot/flask/b9b88b0cdf70190dee5c7d31a83efb1cae888a63/docs/tutorial/flaskr_login.png -------------------------------------------------------------------------------- /docs/tutorial/index.rst: -------------------------------------------------------------------------------- 1 | .. _tutorial: 2 | 3 | Tutorial 4 | ======== 5 | 6 | .. toctree:: 7 | :caption: Contents: 8 | :maxdepth: 1 9 | 10 | layout 11 | factory 12 | database 13 | views 14 | templates 15 | static 16 | blog 17 | install 18 | tests 19 | deploy 20 | next 21 | 22 | This tutorial will walk you through creating a basic blog application 23 | called Flaskr. Users will be able to register, log in, create posts, 24 | and edit or delete their own posts. You will be able to package and 25 | install the application on other computers. 26 | 27 | .. image:: flaskr_index.png 28 | :align: center 29 | :class: screenshot 30 | :alt: screenshot of index page 31 | 32 | It's assumed that you're already familiar with Python. The `official 33 | tutorial`_ in the Python docs is a great way to learn or review first. 34 | 35 | .. _official tutorial: https://docs.python.org/3/tutorial/ 36 | 37 | While it's designed to give a good starting point, the tutorial doesn't 38 | cover all of Flask's features. Check out the :ref:`quickstart` for an 39 | overview of what Flask can do, then dive into the docs to find out more. 40 | The tutorial only uses what's provided by Flask and Python. In another 41 | project, you might decide to use :ref:`extensions` or other libraries to 42 | make some tasks simpler. 43 | 44 | .. image:: flaskr_login.png 45 | :align: center 46 | :class: screenshot 47 | :alt: screenshot of login page 48 | 49 | Flask is flexible. It doesn't require you to use any particular project 50 | or code layout. However, when first starting, it's helpful to use a more 51 | structured approach. This means that the tutorial will require a bit of 52 | boilerplate up front, but it's done to avoid many common pitfalls that 53 | new developers encounter, and it creates a project that's easy to expand 54 | on. Once you become more comfortable with Flask, you can step out of 55 | this structure and take full advantage of Flask's flexibility. 56 | 57 | .. image:: flaskr_edit.png 58 | :align: center 59 | :class: screenshot 60 | :alt: screenshot of login page 61 | 62 | :gh:`The tutorial project is available as an example in the Flask 63 | repository `, if you want to compare your project 64 | with the final product as you follow the tutorial. 65 | 66 | Continue to :doc:`layout`. 67 | -------------------------------------------------------------------------------- /docs/tutorial/install.rst: -------------------------------------------------------------------------------- 1 | Make the Project Installable 2 | ============================ 3 | 4 | Making your project installable means that you can build a 5 | *distribution* file and install that in another environment, just like 6 | you installed Flask in your project's environment. This makes deploying 7 | your project the same as installing any other library, so you're using 8 | all the standard Python tools to manage everything. 9 | 10 | Installing also comes with other benefits that might not be obvious from 11 | the tutorial or as a new Python user, including: 12 | 13 | * Currently, Python and Flask understand how to use the ``flaskr`` 14 | package only because you're running from your project's directory. 15 | Installing means you can import it no matter where you run from. 16 | 17 | * You can manage your project's dependencies just like other packages 18 | do, so ``pip install yourproject.whl`` installs them. 19 | 20 | * Test tools can isolate your test environment from your development 21 | environment. 22 | 23 | .. note:: 24 | This is being introduced late in the tutorial, but in your future 25 | projects you should always start with this. 26 | 27 | 28 | Describe the Project 29 | -------------------- 30 | 31 | The ``setup.py`` file describes your project and the files that belong 32 | to it. 33 | 34 | .. code-block:: python 35 | :caption: ``setup.py`` 36 | 37 | from setuptools import find_packages, setup 38 | 39 | setup( 40 | name='flaskr', 41 | version='1.0.0', 42 | packages=find_packages(), 43 | include_package_data=True, 44 | zip_safe=False, 45 | install_requires=[ 46 | 'flask', 47 | ], 48 | ) 49 | 50 | 51 | ``packages`` tells Python what package directories (and the Python files 52 | they contain) to include. ``find_packages()`` finds these directories 53 | automatically so you don't have to type them out. To include other 54 | files, such as the static and templates directories, 55 | ``include_package_data`` is set. Python needs another file named 56 | ``MANIFEST.in`` to tell what this other data is. 57 | 58 | .. code-block:: none 59 | :caption: ``MANIFEST.in`` 60 | 61 | include flaskr/schema.sql 62 | graft flaskr/static 63 | graft flaskr/templates 64 | global-exclude *.pyc 65 | 66 | This tells Python to copy everything in the ``static`` and ``templates`` 67 | directories, and the ``schema.sql`` file, but to exclude all bytecode 68 | files. 69 | 70 | See the `official packaging guide`_ for another explanation of the files 71 | and options used. 72 | 73 | .. _official packaging guide: https://packaging.python.org/tutorials/distributing-packages/ 74 | 75 | 76 | Install the Project 77 | ------------------- 78 | 79 | Use ``pip`` to install your project in the virtual environment. 80 | 81 | .. code-block:: none 82 | 83 | pip install -e . 84 | 85 | This tells pip to find ``setup.py`` in the current directory and install 86 | it in *editable* or *development* mode. Editable mode means that as you 87 | make changes to your local code, you'll only need to re-install if you 88 | change the metadata about the project, such as its dependencies. 89 | 90 | You can observe that the project is now installed with ``pip list``. 91 | 92 | .. code-block:: none 93 | 94 | pip list 95 | 96 | Package Version Location 97 | -------------- --------- ---------------------------------- 98 | click 6.7 99 | Flask 1.0 100 | flaskr 1.0.0 /home/user/Projects/flask-tutorial 101 | itsdangerous 0.24 102 | Jinja2 2.10 103 | MarkupSafe 1.0 104 | pip 9.0.3 105 | setuptools 39.0.1 106 | Werkzeug 0.14.1 107 | wheel 0.30.0 108 | 109 | Nothing changes from how you've been running your project so far. 110 | ``FLASK_APP`` is still set to ``flaskr`` and ``flask run`` still runs 111 | the application. 112 | 113 | Continue to :doc:`tests`. 114 | -------------------------------------------------------------------------------- /docs/tutorial/layout.rst: -------------------------------------------------------------------------------- 1 | Project Layout 2 | ============== 3 | 4 | Create a project directory and enter it: 5 | 6 | .. code-block:: none 7 | 8 | mkdir flask-tutorial 9 | cd flask-tutorial 10 | 11 | Then follow the :doc:`installation instructions ` to set 12 | up a Python virtual environment and install Flask for your project. 13 | 14 | The tutorial will assume you're working from the ``flask-tutorial`` 15 | directory from now on. The file names at the top of each code block are 16 | relative to this directory. 17 | 18 | ---- 19 | 20 | A Flask application can be as simple as a single file. 21 | 22 | .. code-block:: python 23 | :caption: ``hello.py`` 24 | 25 | from flask import Flask 26 | 27 | app = Flask(__name__) 28 | 29 | 30 | @app.route('/') 31 | def hello(): 32 | return 'Hello, World!' 33 | 34 | However, as a project gets bigger, it becomes overwhelming to keep all 35 | the code in one file. Python projects use *packages* to organize code 36 | into multiple modules that can be imported where needed, and the 37 | tutorial will do this as well. 38 | 39 | The project directory will contain: 40 | 41 | * ``flaskr/``, a Python package containing your application code and 42 | files. 43 | * ``tests/``, a directory containing test modules. 44 | * ``venv/``, a Python virtual environment where Flask and other 45 | dependencies are installed. 46 | * Installation files telling Python how to install your project. 47 | * Version control config, such as `git`_. You should make a habit of 48 | using some type of version control for all your projects, no matter 49 | the size. 50 | * Any other project files you might add in the future. 51 | 52 | .. _git: https://git-scm.com/ 53 | 54 | By the end, your project layout will look like this: 55 | 56 | .. code-block:: none 57 | 58 | /home/user/Projects/flask-tutorial 59 | ├── flaskr/ 60 | │   ├── __init__.py 61 | │   ├── db.py 62 | │   ├── schema.sql 63 | │   ├── auth.py 64 | │   ├── blog.py 65 | │   ├── templates/ 66 | │   │ ├── base.html 67 | │   │ ├── auth/ 68 | │   │ │   ├── login.html 69 | │   │ │   └── register.html 70 | │   │ └── blog/ 71 | │   │ ├── create.html 72 | │   │ ├── index.html 73 | │   │ └── update.html 74 | │   └── static/ 75 | │      └── style.css 76 | ├── tests/ 77 | │   ├── conftest.py 78 | │   ├── data.sql 79 | │   ├── test_factory.py 80 | │   ├── test_db.py 81 | │  ├── test_auth.py 82 | │  └── test_blog.py 83 | ├── venv/ 84 | ├── setup.py 85 | └── MANIFEST.in 86 | 87 | If you're using version control, the following files that are generated 88 | while running your project should be ignored. There may be other files 89 | based on the editor you use. In general, ignore files that you didn't 90 | write. For example, with git: 91 | 92 | .. code-block:: none 93 | :caption: ``.gitignore`` 94 | 95 | venv/ 96 | 97 | *.pyc 98 | __pycache__/ 99 | 100 | instance/ 101 | 102 | .pytest_cache/ 103 | .coverage 104 | htmlcov/ 105 | 106 | dist/ 107 | build/ 108 | *.egg-info/ 109 | 110 | Continue to :doc:`factory`. 111 | -------------------------------------------------------------------------------- /docs/tutorial/next.rst: -------------------------------------------------------------------------------- 1 | Keep Developing! 2 | ================ 3 | 4 | You've learned about quite a few Flask and Python concepts throughout 5 | the tutorial. Go back and review the tutorial and compare your code with 6 | the steps you took to get there. Compare your project to the 7 | :gh:`example project `, which might look a bit 8 | different due to the step-by-step nature of the tutorial. 9 | 10 | There's a lot more to Flask than what you've seen so far. Even so, 11 | you're now equipped to start developing your own web applications. Check 12 | out the :ref:`quickstart` for an overview of what Flask can do, then 13 | dive into the docs to keep learning. Flask uses `Jinja`_, `Click`_, 14 | `Werkzeug`_, and `ItsDangerous`_ behind the scenes, and they all have 15 | their own documentation too. You'll also be interested in 16 | :ref:`extensions` which make tasks like working with the database or 17 | validating form data easier and more powerful. 18 | 19 | If you want to keep developing your Flaskr project, here are some ideas 20 | for what to try next: 21 | 22 | * A detail view to show a single post. Click a post's title to go to 23 | its page. 24 | * Like / unlike a post. 25 | * Comments. 26 | * Tags. Clicking a tag shows all the posts with that tag. 27 | * A search box that filters the index page by name. 28 | * Paged display. Only show 5 posts per page. 29 | * Upload an image to go along with a post. 30 | * Format posts using Markdown. 31 | * An RSS feed of new posts. 32 | 33 | Have fun and make awesome applications! 34 | 35 | .. _Jinja: https://palletsprojects.com/p/jinja/ 36 | .. _Click: https://palletsprojects.com/p/click/ 37 | .. _Werkzeug: https://palletsprojects.com/p/werkzeug/ 38 | .. _ItsDangerous: https://palletsprojects.com/p/itsdangerous/ 39 | -------------------------------------------------------------------------------- /docs/tutorial/static.rst: -------------------------------------------------------------------------------- 1 | Static Files 2 | ============ 3 | 4 | The authentication views and templates work, but they look very plain 5 | right now. Some `CSS`_ can be added to add style to the HTML layout you 6 | constructed. The style won't change, so it's a *static* file rather than 7 | a template. 8 | 9 | Flask automatically adds a ``static`` view that takes a path relative 10 | to the ``flaskr/static`` directory and serves it. The ``base.html`` 11 | template already has a link to the ``style.css`` file: 12 | 13 | .. code-block:: html+jinja 14 | 15 | {{ url_for('static', filename='style.css') }} 16 | 17 | Besides CSS, other types of static files might be files with JavaScript 18 | functions, or a logo image. They are all placed under the 19 | ``flaskr/static`` directory and referenced with 20 | ``url_for('static', filename='...')``. 21 | 22 | This tutorial isn't focused on how to write CSS, so you can just copy 23 | the following into the ``flaskr/static/style.css`` file: 24 | 25 | .. code-block:: css 26 | :caption: ``flaskr/static/style.css`` 27 | 28 | html { font-family: sans-serif; background: #eee; padding: 1rem; } 29 | body { max-width: 960px; margin: 0 auto; background: white; } 30 | h1 { font-family: serif; color: #377ba8; margin: 1rem 0; } 31 | a { color: #377ba8; } 32 | hr { border: none; border-top: 1px solid lightgray; } 33 | nav { background: lightgray; display: flex; align-items: center; padding: 0 0.5rem; } 34 | nav h1 { flex: auto; margin: 0; } 35 | nav h1 a { text-decoration: none; padding: 0.25rem 0.5rem; } 36 | nav ul { display: flex; list-style: none; margin: 0; padding: 0; } 37 | nav ul li a, nav ul li span, header .action { display: block; padding: 0.5rem; } 38 | .content { padding: 0 1rem 1rem; } 39 | .content > header { border-bottom: 1px solid lightgray; display: flex; align-items: flex-end; } 40 | .content > header h1 { flex: auto; margin: 1rem 0 0.25rem 0; } 41 | .flash { margin: 1em 0; padding: 1em; background: #cae6f6; border: 1px solid #377ba8; } 42 | .post > header { display: flex; align-items: flex-end; font-size: 0.85em; } 43 | .post > header > div:first-of-type { flex: auto; } 44 | .post > header h1 { font-size: 1.5em; margin-bottom: 0; } 45 | .post .about { color: slategray; font-style: italic; } 46 | .post .body { white-space: pre-line; } 47 | .content:last-child { margin-bottom: 0; } 48 | .content form { margin: 1em 0; display: flex; flex-direction: column; } 49 | .content label { font-weight: bold; margin-bottom: 0.5em; } 50 | .content input, .content textarea { margin-bottom: 1em; } 51 | .content textarea { min-height: 12em; resize: vertical; } 52 | input.danger { color: #cc2f2e; } 53 | input[type=submit] { align-self: start; min-width: 10em; } 54 | 55 | You can find a less compact version of ``style.css`` in the 56 | :gh:`example code `. 57 | 58 | Go to http://127.0.0.1:5000/auth/login and the page should look like the 59 | screenshot below. 60 | 61 | .. image:: flaskr_login.png 62 | :align: center 63 | :class: screenshot 64 | :alt: screenshot of login page 65 | 66 | You can read more about CSS from `Mozilla's documentation `_. If 67 | you change a static file, refresh the browser page. If the change 68 | doesn't show up, try clearing your browser's cache. 69 | 70 | .. _CSS: https://developer.mozilla.org/docs/Web/CSS 71 | 72 | Continue to :doc:`blog`. 73 | -------------------------------------------------------------------------------- /docs/unicode.rst: -------------------------------------------------------------------------------- 1 | Unicode in Flask 2 | ================ 3 | 4 | Flask, like Jinja2 and Werkzeug, is totally Unicode based when it comes to 5 | text. Not only these libraries, also the majority of web related Python 6 | libraries that deal with text. If you don't know Unicode so far, you 7 | should probably read `The Absolute Minimum Every Software Developer 8 | Absolutely, Positively Must Know About Unicode and Character Sets 9 | `_. This part of the 10 | documentation just tries to cover the very basics so that you have a 11 | pleasant experience with Unicode related things. 12 | 13 | Automatic Conversion 14 | -------------------- 15 | 16 | Flask has a few assumptions about your application (which you can change 17 | of course) that give you basic and painless Unicode support: 18 | 19 | - the encoding for text on your website is UTF-8 20 | - internally you will always use Unicode exclusively for text except 21 | for literal strings with only ASCII character points. 22 | - encoding and decoding happens whenever you are talking over a protocol 23 | that requires bytes to be transmitted. 24 | 25 | So what does this mean to you? 26 | 27 | HTTP is based on bytes. Not only the protocol, also the system used to 28 | address documents on servers (so called URIs or URLs). However HTML which 29 | is usually transmitted on top of HTTP supports a large variety of 30 | character sets and which ones are used, are transmitted in an HTTP header. 31 | To not make this too complex Flask just assumes that if you are sending 32 | Unicode out you want it to be UTF-8 encoded. Flask will do the encoding 33 | and setting of the appropriate headers for you. 34 | 35 | The same is true if you are talking to databases with the help of 36 | SQLAlchemy or a similar ORM system. Some databases have a protocol that 37 | already transmits Unicode and if they do not, SQLAlchemy or your other ORM 38 | should take care of that. 39 | 40 | The Golden Rule 41 | --------------- 42 | 43 | So the rule of thumb: if you are not dealing with binary data, work with 44 | Unicode. What does working with Unicode in Python 2.x mean? 45 | 46 | - as long as you are using ASCII charpoints only (basically numbers, 47 | some special characters of latin letters without umlauts or anything 48 | fancy) you can use regular string literals (``'Hello World'``). 49 | - if you need anything else than ASCII in a string you have to mark 50 | this string as Unicode string by prefixing it with a lowercase `u`. 51 | (like ``u'Hänsel und Gretel'``) 52 | - if you are using non-Unicode characters in your Python files you have 53 | to tell Python which encoding your file uses. Again, I recommend 54 | UTF-8 for this purpose. To tell the interpreter your encoding you can 55 | put the ``# -*- coding: utf-8 -*-`` into the first or second line of 56 | your Python source file. 57 | - Jinja is configured to decode the template files from UTF-8. So make 58 | sure to tell your editor to save the file as UTF-8 there as well. 59 | 60 | Encoding and Decoding Yourself 61 | ------------------------------ 62 | 63 | If you are talking with a filesystem or something that is not really based 64 | on Unicode you will have to ensure that you decode properly when working 65 | with Unicode interface. So for example if you want to load a file on the 66 | filesystem and embed it into a Jinja2 template you will have to decode it 67 | from the encoding of that file. Here the old problem that text files do 68 | not specify their encoding comes into play. So do yourself a favour and 69 | limit yourself to UTF-8 for text files as well. 70 | 71 | Anyways. To load such a file with Unicode you can use the built-in 72 | :meth:`str.decode` method:: 73 | 74 | def read_file(filename, charset='utf-8'): 75 | with open(filename, 'r') as f: 76 | return f.read().decode(charset) 77 | 78 | To go from Unicode into a specific charset such as UTF-8 you can use the 79 | :meth:`unicode.encode` method:: 80 | 81 | def write_file(filename, contents, charset='utf-8'): 82 | with open(filename, 'w') as f: 83 | f.write(contents.encode(charset)) 84 | 85 | Configuring Editors 86 | ------------------- 87 | 88 | Most editors save as UTF-8 by default nowadays but in case your editor is 89 | not configured to do this you have to change it. Here some common ways to 90 | set your editor to store as UTF-8: 91 | 92 | - Vim: put ``set enc=utf-8`` to your ``.vimrc`` file. 93 | 94 | - Emacs: either use an encoding cookie or put this into your ``.emacs`` 95 | file:: 96 | 97 | (prefer-coding-system 'utf-8) 98 | (setq default-buffer-file-coding-system 'utf-8) 99 | 100 | - Notepad++: 101 | 102 | 1. Go to *Settings -> Preferences ...* 103 | 2. Select the "New Document/Default Directory" tab 104 | 3. Select "UTF-8 without BOM" as encoding 105 | 106 | It is also recommended to use the Unix newline format, you can select 107 | it in the same panel but this is not a requirement. 108 | -------------------------------------------------------------------------------- /examples/javascript/.gitignore: -------------------------------------------------------------------------------- 1 | venv/ 2 | *.pyc 3 | __pycache__/ 4 | instance/ 5 | .cache/ 6 | .pytest_cache/ 7 | .coverage 8 | htmlcov/ 9 | dist/ 10 | build/ 11 | *.egg-info/ 12 | .idea/ 13 | *.swp 14 | *~ 15 | -------------------------------------------------------------------------------- /examples/javascript/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2010 by the Pallets team. 2 | 3 | Some rights reserved. 4 | 5 | Redistribution and use in source and binary forms of the software as 6 | well as documentation, with or without modification, are permitted 7 | provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, 10 | this list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in the 14 | documentation and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND 21 | CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 22 | BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 23 | FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 | COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 26 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 27 | USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 28 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30 | THIS SOFTWARE AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF 31 | SUCH DAMAGE. 32 | -------------------------------------------------------------------------------- /examples/javascript/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE 2 | graft js_example/templates 3 | graft tests 4 | global-exclude *.pyc 5 | -------------------------------------------------------------------------------- /examples/javascript/README.rst: -------------------------------------------------------------------------------- 1 | JavaScript Ajax Example 2 | ======================= 3 | 4 | Demonstrates how to post form data and process a JSON response using 5 | JavaScript. This allows making requests without navigating away from the 6 | page. Demonstrates using |XMLHttpRequest|_, |fetch|_, and 7 | |jQuery.ajax|_. See the `Flask docs`_ about jQuery and Ajax. 8 | 9 | .. |XMLHttpRequest| replace:: ``XMLHttpRequest`` 10 | .. _XMLHttpRequest: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest 11 | 12 | .. |fetch| replace:: ``fetch`` 13 | .. _fetch: https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch 14 | 15 | .. |jQuery.ajax| replace:: ``jQuery.ajax`` 16 | .. _jQuery.ajax: https://api.jquery.com/jQuery.ajax/ 17 | 18 | .. _Flask docs: http://flask.pocoo.org/docs/patterns/jquery/ 19 | 20 | 21 | Install 22 | ------- 23 | 24 | :: 25 | 26 | python3 -m venv venv 27 | . venv/bin/activate 28 | pip install -e . 29 | 30 | 31 | Run 32 | --- 33 | 34 | :: 35 | 36 | export FLASK_APP=js_example 37 | flask run 38 | 39 | Open http://127.0.0.1:5000 in a browser. 40 | 41 | 42 | Test 43 | ---- 44 | 45 | :: 46 | 47 | pip install -e '.[test]' 48 | coverage run -m pytest 49 | coverage report 50 | -------------------------------------------------------------------------------- /examples/javascript/js_example/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | 3 | app = Flask(__name__) 4 | 5 | from js_example import views 6 | -------------------------------------------------------------------------------- /examples/javascript/js_example/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | JavaScript Example 3 | 4 | 5 | 14 | 23 |
24 |

{% block intro %}{% endblock %}

25 |
26 |
27 | 28 | + 29 | 30 | 31 |
32 | = 33 | {% block script %}{% endblock %} 34 | -------------------------------------------------------------------------------- /examples/javascript/js_example/templates/fetch.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block intro %} 4 | fetch 5 | is the new plain JavaScript way to make requests. It's 6 | supported in all modern browsers except IE, which requires a 7 | polyfill. 8 | {% endblock %} 9 | 10 | {% block script %} 11 | 12 | 13 | 36 | {% endblock %} 37 | -------------------------------------------------------------------------------- /examples/javascript/js_example/templates/jquery.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block intro %} 4 | jQuery is a popular library that 5 | adds cross browser APIs for common tasks. However, it requires loading 6 | an extra library. 7 | {% endblock %} 8 | 9 | {% block script %} 10 | 11 | 27 | {% endblock %} 28 | -------------------------------------------------------------------------------- /examples/javascript/js_example/templates/plain.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block intro %} 4 | XMLHttpRequest 5 | is the plain JavaScript way to make requests. It's natively supported 6 | by all browsers. 7 | {% endblock %} 8 | 9 | {% block script %} 10 | 28 | {% endblock %} 29 | -------------------------------------------------------------------------------- /examples/javascript/js_example/views.py: -------------------------------------------------------------------------------- 1 | from flask import jsonify, render_template, request 2 | 3 | from js_example import app 4 | 5 | 6 | @app.route('/', defaults={'js': 'plain'}) 7 | @app.route('/') 8 | def index(js): 9 | return render_template('{0}.html'.format(js), js=js) 10 | 11 | 12 | @app.route('/add', methods=['POST']) 13 | def add(): 14 | a = request.form.get('a', 0, type=float) 15 | b = request.form.get('b', 0, type=float) 16 | return jsonify(result=a + b) 17 | -------------------------------------------------------------------------------- /examples/javascript/setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | license_file = LICENSE 3 | 4 | [bdist_wheel] 5 | universal = True 6 | 7 | [tool:pytest] 8 | testpaths = tests 9 | 10 | [coverage:run] 11 | branch = True 12 | source = 13 | js_example 14 | -------------------------------------------------------------------------------- /examples/javascript/setup.py: -------------------------------------------------------------------------------- 1 | import io 2 | 3 | from setuptools import find_packages, setup 4 | 5 | with io.open('README.rst', 'rt', encoding='utf8') as f: 6 | readme = f.read() 7 | 8 | setup( 9 | name='js_example', 10 | version='1.0.0', 11 | url='http://flask.pocoo.org/docs/patterns/jquery/', 12 | license='BSD', 13 | maintainer='Pallets team', 14 | maintainer_email='contact@palletsprojects.com', 15 | description='Demonstrates making Ajax requests to Flask.', 16 | long_description=readme, 17 | packages=find_packages(), 18 | include_package_data=True, 19 | zip_safe=False, 20 | install_requires=[ 21 | 'flask', 22 | ], 23 | extras_require={ 24 | 'test': [ 25 | 'pytest', 26 | 'coverage', 27 | 'blinker', 28 | ], 29 | }, 30 | ) 31 | -------------------------------------------------------------------------------- /examples/javascript/tests/conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from js_example import app 4 | 5 | 6 | @pytest.fixture(name='app') 7 | def fixture_app(): 8 | app.testing = True 9 | yield app 10 | app.testing = False 11 | 12 | 13 | @pytest.fixture 14 | def client(app): 15 | return app.test_client() 16 | -------------------------------------------------------------------------------- /examples/javascript/tests/test_js_example.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from flask import template_rendered 4 | 5 | 6 | @pytest.mark.parametrize(('path', 'template_name'), ( 7 | ('/', 'plain.html'), 8 | ('/plain', 'plain.html'), 9 | ('/fetch', 'fetch.html'), 10 | ('/jquery', 'jquery.html'), 11 | )) 12 | def test_index(app, client, path, template_name): 13 | def check(sender, template, context): 14 | assert template.name == template_name 15 | 16 | with template_rendered.connected_to(check, app): 17 | client.get(path) 18 | 19 | 20 | @pytest.mark.parametrize(('a', 'b', 'result'), ( 21 | (2, 3, 5), 22 | (2.5, 3, 5.5), 23 | (2, None, 2), 24 | (2, 'b', 2), 25 | )) 26 | def test_add(client, a, b, result): 27 | response = client.post('/add', data={'a': a, 'b': b}) 28 | assert response.get_json()['result'] == result 29 | -------------------------------------------------------------------------------- /examples/tutorial/.gitignore: -------------------------------------------------------------------------------- 1 | venv/ 2 | *.pyc 3 | __pycache__/ 4 | instance/ 5 | .cache/ 6 | .pytest_cache/ 7 | .coverage 8 | htmlcov/ 9 | dist/ 10 | build/ 11 | *.egg-info/ 12 | .idea/ 13 | *.swp 14 | *~ 15 | -------------------------------------------------------------------------------- /examples/tutorial/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2010 by the Pallets team. 2 | 3 | Some rights reserved. 4 | 5 | Redistribution and use in source and binary forms of the software as 6 | well as documentation, with or without modification, are permitted 7 | provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, 10 | this list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in the 14 | documentation and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND 21 | CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 22 | BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 23 | FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 | COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 26 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 27 | USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 28 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30 | THIS SOFTWARE AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF 31 | SUCH DAMAGE. 32 | -------------------------------------------------------------------------------- /examples/tutorial/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE 2 | include flaskr/schema.sql 3 | graft flaskr/static 4 | graft flaskr/templates 5 | graft tests 6 | global-exclude *.pyc 7 | -------------------------------------------------------------------------------- /examples/tutorial/README.rst: -------------------------------------------------------------------------------- 1 | Flaskr 2 | ====== 3 | 4 | The basic blog app built in the Flask `tutorial`_. 5 | 6 | .. _tutorial: http://flask.pocoo.org/docs/tutorial/ 7 | 8 | 9 | Install 10 | ------- 11 | 12 | **Be sure to use the same version of the code as the version of the docs 13 | you're reading.** You probably want the latest tagged version, but the 14 | default Git version is the master branch. :: 15 | 16 | # clone the repository 17 | git clone https://github.com/pallets/flask 18 | cd flask 19 | # checkout the correct version 20 | git tag # shows the tagged versions 21 | git checkout latest-tag-found-above 22 | cd examples/tutorial 23 | 24 | Create a virtualenv and activate it:: 25 | 26 | python3 -m venv venv 27 | . venv/bin/activate 28 | 29 | Or on Windows cmd:: 30 | 31 | py -3 -m venv venv 32 | venv\Scripts\activate.bat 33 | 34 | Install Flaskr:: 35 | 36 | pip install -e . 37 | 38 | Or if you are using the master branch, install Flask from source before 39 | installing Flaskr:: 40 | 41 | pip install -e ../.. 42 | pip install -e . 43 | 44 | 45 | Run 46 | --- 47 | 48 | :: 49 | 50 | export FLASK_APP=flaskr 51 | export FLASK_ENV=development 52 | flask init-db 53 | flask run 54 | 55 | Or on Windows cmd:: 56 | 57 | set FLASK_APP=flaskr 58 | set FLASK_ENV=development 59 | flask init-db 60 | flask run 61 | 62 | Open http://127.0.0.1:5000 in a browser. 63 | 64 | 65 | Test 66 | ---- 67 | 68 | :: 69 | 70 | pip install '.[test]' 71 | pytest 72 | 73 | Run with coverage report:: 74 | 75 | coverage run -m pytest 76 | coverage report 77 | coverage html # open htmlcov/index.html in a browser 78 | -------------------------------------------------------------------------------- /examples/tutorial/flaskr/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from flask import Flask 4 | 5 | 6 | def create_app(test_config=None): 7 | """Create and configure an instance of the Flask application.""" 8 | app = Flask(__name__, instance_relative_config=True) 9 | app.config.from_mapping( 10 | # a default secret that should be overridden by instance config 11 | SECRET_KEY='dev', 12 | # store the database in the instance folder 13 | DATABASE=os.path.join(app.instance_path, 'flaskr.sqlite'), 14 | ) 15 | 16 | if test_config is None: 17 | # load the instance config, if it exists, when not testing 18 | app.config.from_pyfile('config.py', silent=True) 19 | else: 20 | # load the test config if passed in 21 | app.config.update(test_config) 22 | 23 | # ensure the instance folder exists 24 | try: 25 | os.makedirs(app.instance_path) 26 | except OSError: 27 | pass 28 | 29 | @app.route('/hello') 30 | def hello(): 31 | return 'Hello, World!' 32 | 33 | # register the database commands 34 | from flaskr import db 35 | db.init_app(app) 36 | 37 | # apply the blueprints to the app 38 | from flaskr import auth, blog 39 | app.register_blueprint(auth.bp) 40 | app.register_blueprint(blog.bp) 41 | 42 | # make url_for('index') == url_for('blog.index') 43 | # in another app, you might define a separate main index here with 44 | # app.route, while giving the blog blueprint a url_prefix, but for 45 | # the tutorial the blog will be the main index 46 | app.add_url_rule('/', endpoint='index') 47 | 48 | return app 49 | -------------------------------------------------------------------------------- /examples/tutorial/flaskr/auth.py: -------------------------------------------------------------------------------- 1 | import functools 2 | 3 | from flask import ( 4 | Blueprint, flash, g, redirect, render_template, request, session, url_for 5 | ) 6 | from werkzeug.security import check_password_hash, generate_password_hash 7 | 8 | from flaskr.db import get_db 9 | 10 | bp = Blueprint('auth', __name__, url_prefix='/auth') 11 | 12 | 13 | def login_required(view): 14 | """View decorator that redirects anonymous users to the login page.""" 15 | @functools.wraps(view) 16 | def wrapped_view(**kwargs): 17 | if g.user is None: 18 | return redirect(url_for('auth.login')) 19 | 20 | return view(**kwargs) 21 | 22 | return wrapped_view 23 | 24 | 25 | @bp.before_app_request 26 | def load_logged_in_user(): 27 | """If a user id is stored in the session, load the user object from 28 | the database into ``g.user``.""" 29 | user_id = session.get('user_id') 30 | 31 | if user_id is None: 32 | g.user = None 33 | else: 34 | g.user = get_db().execute( 35 | 'SELECT * FROM user WHERE id = ?', (user_id,) 36 | ).fetchone() 37 | 38 | 39 | @bp.route('/register', methods=('GET', 'POST')) 40 | def register(): 41 | """Register a new user. 42 | 43 | Validates that the username is not already taken. Hashes the 44 | password for security. 45 | """ 46 | if request.method == 'POST': 47 | username = request.form['username'] 48 | password = request.form['password'] 49 | db = get_db() 50 | error = None 51 | 52 | if not username: 53 | error = 'Username is required.' 54 | elif not password: 55 | error = 'Password is required.' 56 | elif db.execute( 57 | 'SELECT id FROM user WHERE username = ?', (username,) 58 | ).fetchone() is not None: 59 | error = 'User {0} is already registered.'.format(username) 60 | 61 | if error is None: 62 | # the name is available, store it in the database and go to 63 | # the login page 64 | db.execute( 65 | 'INSERT INTO user (username, password) VALUES (?, ?)', 66 | (username, generate_password_hash(password)) 67 | ) 68 | db.commit() 69 | return redirect(url_for('auth.login')) 70 | 71 | flash(error) 72 | 73 | return render_template('auth/register.html') 74 | 75 | 76 | @bp.route('/login', methods=('GET', 'POST')) 77 | def login(): 78 | """Log in a registered user by adding the user id to the session.""" 79 | if request.method == 'POST': 80 | username = request.form['username'] 81 | password = request.form['password'] 82 | db = get_db() 83 | error = None 84 | user = db.execute( 85 | 'SELECT * FROM user WHERE username = ?', (username,) 86 | ).fetchone() 87 | 88 | if user is None: 89 | error = 'Incorrect username.' 90 | elif not check_password_hash(user['password'], password): 91 | error = 'Incorrect password.' 92 | 93 | if error is None: 94 | # store the user id in a new session and return to the index 95 | session.clear() 96 | session['user_id'] = user['id'] 97 | return redirect(url_for('index')) 98 | 99 | flash(error) 100 | 101 | return render_template('auth/login.html') 102 | 103 | 104 | @bp.route('/logout') 105 | def logout(): 106 | """Clear the current session, including the stored user id.""" 107 | session.clear() 108 | return redirect(url_for('index')) 109 | -------------------------------------------------------------------------------- /examples/tutorial/flaskr/blog.py: -------------------------------------------------------------------------------- 1 | from flask import ( 2 | Blueprint, flash, g, redirect, render_template, request, url_for 3 | ) 4 | from werkzeug.exceptions import abort 5 | 6 | from flaskr.auth import login_required 7 | from flaskr.db import get_db 8 | 9 | bp = Blueprint('blog', __name__) 10 | 11 | 12 | @bp.route('/') 13 | def index(): 14 | """Show all the posts, most recent first.""" 15 | db = get_db() 16 | posts = db.execute( 17 | 'SELECT p.id, title, body, created, author_id, username' 18 | ' FROM post p JOIN user u ON p.author_id = u.id' 19 | ' ORDER BY created DESC' 20 | ).fetchall() 21 | return render_template('blog/index.html', posts=posts) 22 | 23 | 24 | def get_post(id, check_author=True): 25 | """Get a post and its author by id. 26 | 27 | Checks that the id exists and optionally that the current user is 28 | the author. 29 | 30 | :param id: id of post to get 31 | :param check_author: require the current user to be the author 32 | :return: the post with author information 33 | :raise 404: if a post with the given id doesn't exist 34 | :raise 403: if the current user isn't the author 35 | """ 36 | post = get_db().execute( 37 | 'SELECT p.id, title, body, created, author_id, username' 38 | ' FROM post p JOIN user u ON p.author_id = u.id' 39 | ' WHERE p.id = ?', 40 | (id,) 41 | ).fetchone() 42 | 43 | if post is None: 44 | abort(404, "Post id {0} doesn't exist.".format(id)) 45 | 46 | if check_author and post['author_id'] != g.user['id']: 47 | abort(403) 48 | 49 | return post 50 | 51 | 52 | @bp.route('/create', methods=('GET', 'POST')) 53 | @login_required 54 | def create(): 55 | """Create a new post for the current user.""" 56 | if request.method == 'POST': 57 | title = request.form['title'] 58 | body = request.form['body'] 59 | error = None 60 | 61 | if not title: 62 | error = 'Title is required.' 63 | 64 | if error is not None: 65 | flash(error) 66 | else: 67 | db = get_db() 68 | db.execute( 69 | 'INSERT INTO post (title, body, author_id)' 70 | ' VALUES (?, ?, ?)', 71 | (title, body, g.user['id']) 72 | ) 73 | db.commit() 74 | return redirect(url_for('blog.index')) 75 | 76 | return render_template('blog/create.html') 77 | 78 | 79 | @bp.route('//update', methods=('GET', 'POST')) 80 | @login_required 81 | def update(id): 82 | """Update a post if the current user is the author.""" 83 | post = get_post(id) 84 | 85 | if request.method == 'POST': 86 | title = request.form['title'] 87 | body = request.form['body'] 88 | error = None 89 | 90 | if not title: 91 | error = 'Title is required.' 92 | 93 | if error is not None: 94 | flash(error) 95 | else: 96 | db = get_db() 97 | db.execute( 98 | 'UPDATE post SET title = ?, body = ? WHERE id = ?', 99 | (title, body, id) 100 | ) 101 | db.commit() 102 | return redirect(url_for('blog.index')) 103 | 104 | return render_template('blog/update.html', post=post) 105 | 106 | 107 | @bp.route('//delete', methods=('POST',)) 108 | @login_required 109 | def delete(id): 110 | """Delete a post. 111 | 112 | Ensures that the post exists and that the logged in user is the 113 | author of the post. 114 | """ 115 | get_post(id) 116 | db = get_db() 117 | db.execute('DELETE FROM post WHERE id = ?', (id,)) 118 | db.commit() 119 | return redirect(url_for('blog.index')) 120 | -------------------------------------------------------------------------------- /examples/tutorial/flaskr/db.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | 3 | import click 4 | from flask import current_app, g 5 | from flask.cli import with_appcontext 6 | 7 | 8 | def get_db(): 9 | """Connect to the application's configured database. The connection 10 | is unique for each request and will be reused if this is called 11 | again. 12 | """ 13 | if 'db' not in g: 14 | g.db = sqlite3.connect( 15 | current_app.config['DATABASE'], 16 | detect_types=sqlite3.PARSE_DECLTYPES 17 | ) 18 | g.db.row_factory = sqlite3.Row 19 | 20 | return g.db 21 | 22 | 23 | def close_db(e=None): 24 | """If this request connected to the database, close the 25 | connection. 26 | """ 27 | db = g.pop('db', None) 28 | 29 | if db is not None: 30 | db.close() 31 | 32 | 33 | def init_db(): 34 | """Clear existing data and create new tables.""" 35 | db = get_db() 36 | 37 | with current_app.open_resource('schema.sql') as f: 38 | db.executescript(f.read().decode('utf8')) 39 | 40 | 41 | @click.command('init-db') 42 | @with_appcontext 43 | def init_db_command(): 44 | """Clear existing data and create new tables.""" 45 | init_db() 46 | click.echo('Initialized the database.') 47 | 48 | 49 | def init_app(app): 50 | """Register database functions with the Flask app. This is called by 51 | the application factory. 52 | """ 53 | app.teardown_appcontext(close_db) 54 | app.cli.add_command(init_db_command) 55 | -------------------------------------------------------------------------------- /examples/tutorial/flaskr/schema.sql: -------------------------------------------------------------------------------- 1 | -- Initialize the database. 2 | -- Drop any existing data and create empty tables. 3 | 4 | DROP TABLE IF EXISTS user; 5 | DROP TABLE IF EXISTS post; 6 | 7 | CREATE TABLE user ( 8 | id INTEGER PRIMARY KEY AUTOINCREMENT, 9 | username TEXT UNIQUE NOT NULL, 10 | password TEXT NOT NULL 11 | ); 12 | 13 | CREATE TABLE post ( 14 | id INTEGER PRIMARY KEY AUTOINCREMENT, 15 | author_id INTEGER NOT NULL, 16 | created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 17 | title TEXT NOT NULL, 18 | body TEXT NOT NULL, 19 | FOREIGN KEY (author_id) REFERENCES user (id) 20 | ); 21 | -------------------------------------------------------------------------------- /examples/tutorial/flaskr/static/style.css: -------------------------------------------------------------------------------- 1 | html { 2 | font-family: sans-serif; 3 | background: #eee; 4 | padding: 1rem; 5 | } 6 | 7 | body { 8 | max-width: 960px; 9 | margin: 0 auto; 10 | background: white; 11 | } 12 | 13 | h1, h2, h3, h4, h5, h6 { 14 | font-family: serif; 15 | color: #377ba8; 16 | margin: 1rem 0; 17 | } 18 | 19 | a { 20 | color: #377ba8; 21 | } 22 | 23 | hr { 24 | border: none; 25 | border-top: 1px solid lightgray; 26 | } 27 | 28 | nav { 29 | background: lightgray; 30 | display: flex; 31 | align-items: center; 32 | padding: 0 0.5rem; 33 | } 34 | 35 | nav h1 { 36 | flex: auto; 37 | margin: 0; 38 | } 39 | 40 | nav h1 a { 41 | text-decoration: none; 42 | padding: 0.25rem 0.5rem; 43 | } 44 | 45 | nav ul { 46 | display: flex; 47 | list-style: none; 48 | margin: 0; 49 | padding: 0; 50 | } 51 | 52 | nav ul li a, nav ul li span, header .action { 53 | display: block; 54 | padding: 0.5rem; 55 | } 56 | 57 | .content { 58 | padding: 0 1rem 1rem; 59 | } 60 | 61 | .content > header { 62 | border-bottom: 1px solid lightgray; 63 | display: flex; 64 | align-items: flex-end; 65 | } 66 | 67 | .content > header h1 { 68 | flex: auto; 69 | margin: 1rem 0 0.25rem 0; 70 | } 71 | 72 | .flash { 73 | margin: 1em 0; 74 | padding: 1em; 75 | background: #cae6f6; 76 | border: 1px solid #377ba8; 77 | } 78 | 79 | .post > header { 80 | display: flex; 81 | align-items: flex-end; 82 | font-size: 0.85em; 83 | } 84 | 85 | .post > header > div:first-of-type { 86 | flex: auto; 87 | } 88 | 89 | .post > header h1 { 90 | font-size: 1.5em; 91 | margin-bottom: 0; 92 | } 93 | 94 | .post .about { 95 | color: slategray; 96 | font-style: italic; 97 | } 98 | 99 | .post .body { 100 | white-space: pre-line; 101 | } 102 | 103 | .content:last-child { 104 | margin-bottom: 0; 105 | } 106 | 107 | .content form { 108 | margin: 1em 0; 109 | display: flex; 110 | flex-direction: column; 111 | } 112 | 113 | .content label { 114 | font-weight: bold; 115 | margin-bottom: 0.5em; 116 | } 117 | 118 | .content input, .content textarea { 119 | margin-bottom: 1em; 120 | } 121 | 122 | .content textarea { 123 | min-height: 12em; 124 | resize: vertical; 125 | } 126 | 127 | input.danger { 128 | color: #cc2f2e; 129 | } 130 | 131 | input[type=submit] { 132 | align-self: start; 133 | min-width: 10em; 134 | } 135 | -------------------------------------------------------------------------------- /examples/tutorial/flaskr/templates/auth/login.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block header %} 4 |

{% block title %}Log In{% endblock %}

5 | {% endblock %} 6 | 7 | {% block content %} 8 |
9 | 10 | 11 | 12 | 13 | 14 |
15 | {% endblock %} 16 | -------------------------------------------------------------------------------- /examples/tutorial/flaskr/templates/auth/register.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block header %} 4 |

{% block title %}Register{% endblock %}

5 | {% endblock %} 6 | 7 | {% block content %} 8 |
9 | 10 | 11 | 12 | 13 | 14 |
15 | {% endblock %} 16 | -------------------------------------------------------------------------------- /examples/tutorial/flaskr/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | {% block title %}{% endblock %} - Flaskr 3 | 4 | 16 |
17 |
18 | {% block header %}{% endblock %} 19 |
20 | {% for message in get_flashed_messages() %} 21 |
{{ message }}
22 | {% endfor %} 23 | {% block content %}{% endblock %} 24 |
25 | -------------------------------------------------------------------------------- /examples/tutorial/flaskr/templates/blog/create.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block header %} 4 |

{% block title %}New Post{% endblock %}

5 | {% endblock %} 6 | 7 | {% block content %} 8 |
9 | 10 | 11 | 12 | 13 | 14 |
15 | {% endblock %} 16 | -------------------------------------------------------------------------------- /examples/tutorial/flaskr/templates/blog/index.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block header %} 4 |

{% block title %}Posts{% endblock %}

5 | {% if g.user %} 6 | New 7 | {% endif %} 8 | {% endblock %} 9 | 10 | {% block content %} 11 | {% for post in posts %} 12 |
13 |
14 |
15 |

{{ post['title'] }}

16 |
by {{ post['username'] }} on {{ post['created'].strftime('%Y-%m-%d') }}
17 |
18 | {% if g.user['id'] == post['author_id'] %} 19 | Edit 20 | {% endif %} 21 |
22 |

{{ post['body'] }}

23 |
24 | {% if not loop.last %} 25 |
26 | {% endif %} 27 | {% endfor %} 28 | {% endblock %} 29 | -------------------------------------------------------------------------------- /examples/tutorial/flaskr/templates/blog/update.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block header %} 4 |

{% block title %}Edit "{{ post['title'] }}"{% endblock %}

5 | {% endblock %} 6 | 7 | {% block content %} 8 |
9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |
17 | 18 |
19 | {% endblock %} 20 | -------------------------------------------------------------------------------- /examples/tutorial/setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | license_file = LICENSE 3 | 4 | [bdist_wheel] 5 | universal = True 6 | 7 | [tool:pytest] 8 | testpaths = tests 9 | 10 | [coverage:run] 11 | branch = True 12 | source = 13 | flaskr 14 | -------------------------------------------------------------------------------- /examples/tutorial/setup.py: -------------------------------------------------------------------------------- 1 | import io 2 | 3 | from setuptools import find_packages, setup 4 | 5 | with io.open('README.rst', 'rt', encoding='utf8') as f: 6 | readme = f.read() 7 | 8 | setup( 9 | name='flaskr', 10 | version='1.0.0', 11 | url='http://flask.pocoo.org/docs/tutorial/', 12 | license='BSD', 13 | maintainer='Pallets team', 14 | maintainer_email='contact@palletsprojects.com', 15 | description='The basic blog app built in the Flask tutorial.', 16 | long_description=readme, 17 | packages=find_packages(), 18 | include_package_data=True, 19 | zip_safe=False, 20 | install_requires=[ 21 | 'flask', 22 | ], 23 | extras_require={ 24 | 'test': [ 25 | 'pytest', 26 | 'coverage', 27 | ], 28 | }, 29 | ) 30 | -------------------------------------------------------------------------------- /examples/tutorial/tests/conftest.py: -------------------------------------------------------------------------------- 1 | import os 2 | import tempfile 3 | 4 | import pytest 5 | from flaskr import create_app 6 | from flaskr.db import get_db, init_db 7 | 8 | # read in SQL for populating test data 9 | with open(os.path.join(os.path.dirname(__file__), 'data.sql'), 'rb') as f: 10 | _data_sql = f.read().decode('utf8') 11 | 12 | 13 | @pytest.fixture 14 | def app(): 15 | """Create and configure a new app instance for each test.""" 16 | # create a temporary file to isolate the database for each test 17 | db_fd, db_path = tempfile.mkstemp() 18 | # create the app with common test config 19 | app = create_app({ 20 | 'TESTING': True, 21 | 'DATABASE': db_path, 22 | }) 23 | 24 | # create the database and load test data 25 | with app.app_context(): 26 | init_db() 27 | get_db().executescript(_data_sql) 28 | 29 | yield app 30 | 31 | # close and remove the temporary database 32 | os.close(db_fd) 33 | os.unlink(db_path) 34 | 35 | 36 | @pytest.fixture 37 | def client(app): 38 | """A test client for the app.""" 39 | return app.test_client() 40 | 41 | 42 | @pytest.fixture 43 | def runner(app): 44 | """A test runner for the app's Click commands.""" 45 | return app.test_cli_runner() 46 | 47 | 48 | class AuthActions(object): 49 | def __init__(self, client): 50 | self._client = client 51 | 52 | def login(self, username='test', password='test'): 53 | return self._client.post( 54 | '/auth/login', 55 | data={'username': username, 'password': password} 56 | ) 57 | 58 | def logout(self): 59 | return self._client.get('/auth/logout') 60 | 61 | 62 | @pytest.fixture 63 | def auth(client): 64 | return AuthActions(client) 65 | -------------------------------------------------------------------------------- /examples/tutorial/tests/data.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO user (username, password) 2 | VALUES 3 | ('test', 'pbkdf2:sha256:50000$TCI4GzcX$0de171a4f4dac32e3364c7ddc7c14f3e2fa61f2d17574483f7ffbb431b4acb2f'), 4 | ('other', 'pbkdf2:sha256:50000$kJPKsz6N$d2d4784f1b030a9761f5ccaeeaca413f27f2ecb76d6168407af962ddce849f79'); 5 | 6 | INSERT INTO post (title, body, author_id, created) 7 | VALUES 8 | ('test title', 'test' || x'0a' || 'body', 1, '2018-01-01 00:00:00'); 9 | -------------------------------------------------------------------------------- /examples/tutorial/tests/test_auth.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from flask import g, session 3 | from flaskr.db import get_db 4 | 5 | 6 | def test_register(client, app): 7 | # test that viewing the page renders without template errors 8 | assert client.get('/auth/register').status_code == 200 9 | 10 | # test that successful registration redirects to the login page 11 | response = client.post( 12 | '/auth/register', data={'username': 'a', 'password': 'a'} 13 | ) 14 | assert 'http://localhost/auth/login' == response.headers['Location'] 15 | 16 | # test that the user was inserted into the database 17 | with app.app_context(): 18 | assert get_db().execute( 19 | "select * from user where username = 'a'", 20 | ).fetchone() is not None 21 | 22 | 23 | @pytest.mark.parametrize(('username', 'password', 'message'), ( 24 | ('', '', b'Username is required.'), 25 | ('a', '', b'Password is required.'), 26 | ('test', 'test', b'already registered'), 27 | )) 28 | def test_register_validate_input(client, username, password, message): 29 | response = client.post( 30 | '/auth/register', 31 | data={'username': username, 'password': password} 32 | ) 33 | assert message in response.data 34 | 35 | 36 | def test_login(client, auth): 37 | # test that viewing the page renders without template errors 38 | assert client.get('/auth/login').status_code == 200 39 | 40 | # test that successful login redirects to the index page 41 | response = auth.login() 42 | assert response.headers['Location'] == 'http://localhost/' 43 | 44 | # login request set the user_id in the session 45 | # check that the user is loaded from the session 46 | with client: 47 | client.get('/') 48 | assert session['user_id'] == 1 49 | assert g.user['username'] == 'test' 50 | 51 | 52 | @pytest.mark.parametrize(('username', 'password', 'message'), ( 53 | ('a', 'test', b'Incorrect username.'), 54 | ('test', 'a', b'Incorrect password.'), 55 | )) 56 | def test_login_validate_input(auth, username, password, message): 57 | response = auth.login(username, password) 58 | assert message in response.data 59 | 60 | 61 | def test_logout(client, auth): 62 | auth.login() 63 | 64 | with client: 65 | auth.logout() 66 | assert 'user_id' not in session 67 | -------------------------------------------------------------------------------- /examples/tutorial/tests/test_blog.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from flaskr.db import get_db 3 | 4 | 5 | def test_index(client, auth): 6 | response = client.get('/') 7 | assert b"Log In" in response.data 8 | assert b"Register" in response.data 9 | 10 | auth.login() 11 | response = client.get('/') 12 | assert b'test title' in response.data 13 | assert b'by test on 2018-01-01' in response.data 14 | assert b'test\nbody' in response.data 15 | assert b'href="/1/update"' in response.data 16 | 17 | 18 | @pytest.mark.parametrize('path', ( 19 | '/create', 20 | '/1/update', 21 | '/1/delete', 22 | )) 23 | def test_login_required(client, path): 24 | response = client.post(path) 25 | assert response.headers['Location'] == 'http://localhost/auth/login' 26 | 27 | 28 | def test_author_required(app, client, auth): 29 | # change the post author to another user 30 | with app.app_context(): 31 | db = get_db() 32 | db.execute('UPDATE post SET author_id = 2 WHERE id = 1') 33 | db.commit() 34 | 35 | auth.login() 36 | # current user can't modify other user's post 37 | assert client.post('/1/update').status_code == 403 38 | assert client.post('/1/delete').status_code == 403 39 | # current user doesn't see edit link 40 | assert b'href="/1/update"' not in client.get('/').data 41 | 42 | 43 | @pytest.mark.parametrize('path', ( 44 | '/2/update', 45 | '/2/delete', 46 | )) 47 | def test_exists_required(client, auth, path): 48 | auth.login() 49 | assert client.post(path).status_code == 404 50 | 51 | 52 | def test_create(client, auth, app): 53 | auth.login() 54 | assert client.get('/create').status_code == 200 55 | client.post('/create', data={'title': 'created', 'body': ''}) 56 | 57 | with app.app_context(): 58 | db = get_db() 59 | count = db.execute('SELECT COUNT(id) FROM post').fetchone()[0] 60 | assert count == 2 61 | 62 | 63 | def test_update(client, auth, app): 64 | auth.login() 65 | assert client.get('/1/update').status_code == 200 66 | client.post('/1/update', data={'title': 'updated', 'body': ''}) 67 | 68 | with app.app_context(): 69 | db = get_db() 70 | post = db.execute('SELECT * FROM post WHERE id = 1').fetchone() 71 | assert post['title'] == 'updated' 72 | 73 | 74 | @pytest.mark.parametrize('path', ( 75 | '/create', 76 | '/1/update', 77 | )) 78 | def test_create_update_validate(client, auth, path): 79 | auth.login() 80 | response = client.post(path, data={'title': '', 'body': ''}) 81 | assert b'Title is required.' in response.data 82 | 83 | 84 | def test_delete(client, auth, app): 85 | auth.login() 86 | response = client.post('/1/delete') 87 | assert response.headers['Location'] == 'http://localhost/' 88 | 89 | with app.app_context(): 90 | db = get_db() 91 | post = db.execute('SELECT * FROM post WHERE id = 1').fetchone() 92 | assert post is None 93 | -------------------------------------------------------------------------------- /examples/tutorial/tests/test_db.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | 3 | import pytest 4 | from flaskr.db import get_db 5 | 6 | 7 | def test_get_close_db(app): 8 | with app.app_context(): 9 | db = get_db() 10 | assert db is get_db() 11 | 12 | with pytest.raises(sqlite3.ProgrammingError) as e: 13 | db.execute('SELECT 1') 14 | 15 | assert 'closed' in str(e) 16 | 17 | 18 | def test_init_db_command(runner, monkeypatch): 19 | class Recorder(object): 20 | called = False 21 | 22 | def fake_init_db(): 23 | Recorder.called = True 24 | 25 | monkeypatch.setattr('flaskr.db.init_db', fake_init_db) 26 | result = runner.invoke(args=['init-db']) 27 | assert 'Initialized' in result.output 28 | assert Recorder.called 29 | -------------------------------------------------------------------------------- /examples/tutorial/tests/test_factory.py: -------------------------------------------------------------------------------- 1 | from flaskr import create_app 2 | 3 | 4 | def test_config(): 5 | """Test create_app without passing test config.""" 6 | assert not create_app().testing 7 | assert create_app({'TESTING': True}).testing 8 | 9 | 10 | def test_hello(client): 11 | response = client.get('/hello') 12 | assert response.data == b'Hello, World!' 13 | -------------------------------------------------------------------------------- /flask/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | flask 4 | ~~~~~ 5 | 6 | A microframework based on Werkzeug. It's extensively documented 7 | and follows best practice patterns. 8 | 9 | :copyright: © 2010 by the Pallets team. 10 | :license: BSD, see LICENSE for more details. 11 | """ 12 | 13 | __version__ = '1.1.dev' 14 | 15 | # utilities we import from Werkzeug and Jinja2 that are unused 16 | # in the module but are exported as public interface. 17 | from werkzeug.exceptions import abort 18 | from werkzeug.utils import redirect 19 | from jinja2 import Markup, escape 20 | 21 | from .app import Flask, Request, Response 22 | from .config import Config 23 | from .helpers import url_for, flash, send_file, send_from_directory, \ 24 | get_flashed_messages, get_template_attribute, make_response, safe_join, \ 25 | stream_with_context 26 | from .globals import current_app, g, request, session, _request_ctx_stack, \ 27 | _app_ctx_stack 28 | from .ctx import has_request_context, has_app_context, \ 29 | after_this_request, copy_current_request_context 30 | from .blueprints import Blueprint 31 | from .templating import render_template, render_template_string 32 | 33 | # the signals 34 | from .signals import signals_available, template_rendered, request_started, \ 35 | request_finished, got_request_exception, request_tearing_down, \ 36 | appcontext_tearing_down, appcontext_pushed, \ 37 | appcontext_popped, message_flashed, before_render_template 38 | 39 | # We're not exposing the actual json module but a convenient wrapper around 40 | # it. 41 | from . import json 42 | 43 | # This was the only thing that Flask used to export at one point and it had 44 | # a more generic name. 45 | jsonify = json.jsonify 46 | -------------------------------------------------------------------------------- /flask/__main__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | flask.__main__ 4 | ~~~~~~~~~~~~~~ 5 | 6 | Alias for flask.run for the command line. 7 | 8 | :copyright: © 2010 by the Pallets team. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | 12 | if __name__ == '__main__': 13 | from .cli import main 14 | main(as_module=True) 15 | -------------------------------------------------------------------------------- /flask/_compat.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | flask._compat 4 | ~~~~~~~~~~~~~ 5 | 6 | Some py2/py3 compatibility support based on a stripped down 7 | version of six so we don't have to depend on a specific version 8 | of it. 9 | 10 | :copyright: © 2010 by the Pallets team. 11 | :license: BSD, see LICENSE for more details. 12 | """ 13 | 14 | import sys 15 | 16 | PY2 = sys.version_info[0] == 2 17 | _identity = lambda x: x 18 | 19 | 20 | if not PY2: 21 | text_type = str 22 | string_types = (str,) 23 | integer_types = (int,) 24 | 25 | iterkeys = lambda d: iter(d.keys()) 26 | itervalues = lambda d: iter(d.values()) 27 | iteritems = lambda d: iter(d.items()) 28 | 29 | from inspect import getfullargspec as getargspec 30 | from io import StringIO 31 | 32 | def reraise(tp, value, tb=None): 33 | if value.__traceback__ is not tb: 34 | raise value.with_traceback(tb) 35 | raise value 36 | 37 | implements_to_string = _identity 38 | 39 | else: 40 | text_type = unicode 41 | string_types = (str, unicode) 42 | integer_types = (int, long) 43 | 44 | iterkeys = lambda d: d.iterkeys() 45 | itervalues = lambda d: d.itervalues() 46 | iteritems = lambda d: d.iteritems() 47 | 48 | from inspect import getargspec 49 | from cStringIO import StringIO 50 | 51 | exec('def reraise(tp, value, tb=None):\n raise tp, value, tb') 52 | 53 | def implements_to_string(cls): 54 | cls.__unicode__ = cls.__str__ 55 | cls.__str__ = lambda x: x.__unicode__().encode('utf-8') 56 | return cls 57 | 58 | 59 | def with_metaclass(meta, *bases): 60 | """Create a base class with a metaclass.""" 61 | # This requires a bit of explanation: the basic idea is to make a 62 | # dummy metaclass for one level of class instantiation that replaces 63 | # itself with the actual metaclass. 64 | class metaclass(type): 65 | def __new__(cls, name, this_bases, d): 66 | return meta(name, bases, d) 67 | return type.__new__(metaclass, 'temporary_class', (), {}) 68 | 69 | 70 | # Certain versions of pypy have a bug where clearing the exception stack 71 | # breaks the __exit__ function in a very peculiar way. The second level of 72 | # exception blocks is necessary because pypy seems to forget to check if an 73 | # exception happened until the next bytecode instruction? 74 | # 75 | # Relevant PyPy bugfix commit: 76 | # https://bitbucket.org/pypy/pypy/commits/77ecf91c635a287e88e60d8ddb0f4e9df4003301 77 | # According to ronan on #pypy IRC, it is released in PyPy2 2.3 and later 78 | # versions. 79 | # 80 | # Ubuntu 14.04 has PyPy 2.2.1, which does exhibit this bug. 81 | BROKEN_PYPY_CTXMGR_EXIT = False 82 | if hasattr(sys, 'pypy_version_info'): 83 | class _Mgr(object): 84 | def __enter__(self): 85 | return self 86 | def __exit__(self, *args): 87 | if hasattr(sys, 'exc_clear'): 88 | # Python 3 (PyPy3) doesn't have exc_clear 89 | sys.exc_clear() 90 | try: 91 | try: 92 | with _Mgr(): 93 | raise AssertionError() 94 | except: 95 | raise 96 | except TypeError: 97 | BROKEN_PYPY_CTXMGR_EXIT = True 98 | except AssertionError: 99 | pass 100 | -------------------------------------------------------------------------------- /flask/globals.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | flask.globals 4 | ~~~~~~~~~~~~~ 5 | 6 | Defines all the global objects that are proxies to the current 7 | active context. 8 | 9 | :copyright: © 2010 by the Pallets team. 10 | :license: BSD, see LICENSE for more details. 11 | """ 12 | 13 | from functools import partial 14 | from werkzeug.local import LocalStack, LocalProxy 15 | 16 | 17 | _request_ctx_err_msg = '''\ 18 | Working outside of request context. 19 | 20 | This typically means that you attempted to use functionality that needed 21 | an active HTTP request. Consult the documentation on testing for 22 | information about how to avoid this problem.\ 23 | ''' 24 | _app_ctx_err_msg = '''\ 25 | Working outside of application context. 26 | 27 | This typically means that you attempted to use functionality that needed 28 | to interface with the current application object in some way. To solve 29 | this, set up an application context with app.app_context(). See the 30 | documentation for more information.\ 31 | ''' 32 | 33 | 34 | def _lookup_req_object(name): 35 | top = _request_ctx_stack.top 36 | if top is None: 37 | raise RuntimeError(_request_ctx_err_msg) 38 | return getattr(top, name) 39 | 40 | 41 | def _lookup_app_object(name): 42 | top = _app_ctx_stack.top 43 | if top is None: 44 | raise RuntimeError(_app_ctx_err_msg) 45 | return getattr(top, name) 46 | 47 | 48 | def _find_app(): 49 | top = _app_ctx_stack.top 50 | if top is None: 51 | raise RuntimeError(_app_ctx_err_msg) 52 | return top.app 53 | 54 | 55 | # context locals 56 | _request_ctx_stack = LocalStack() 57 | _app_ctx_stack = LocalStack() 58 | current_app = LocalProxy(_find_app) 59 | request = LocalProxy(partial(_lookup_req_object, 'request')) 60 | session = LocalProxy(partial(_lookup_req_object, 'session')) 61 | g = LocalProxy(partial(_lookup_app_object, 'g')) 62 | -------------------------------------------------------------------------------- /flask/logging.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | flask.logging 4 | ~~~~~~~~~~~~~ 5 | 6 | :copyright: © 2010 by the Pallets team. 7 | :license: BSD, see LICENSE for more details. 8 | """ 9 | 10 | from __future__ import absolute_import 11 | 12 | import logging 13 | import sys 14 | 15 | from werkzeug.local import LocalProxy 16 | 17 | from .globals import request 18 | 19 | 20 | @LocalProxy 21 | def wsgi_errors_stream(): 22 | """Find the most appropriate error stream for the application. If a request 23 | is active, log to ``wsgi.errors``, otherwise use ``sys.stderr``. 24 | 25 | If you configure your own :class:`logging.StreamHandler`, you may want to 26 | use this for the stream. If you are using file or dict configuration and 27 | can't import this directly, you can refer to it as 28 | ``ext://flask.logging.wsgi_errors_stream``. 29 | """ 30 | return request.environ['wsgi.errors'] if request else sys.stderr 31 | 32 | 33 | def has_level_handler(logger): 34 | """Check if there is a handler in the logging chain that will handle the 35 | given logger's :meth:`effective level <~logging.Logger.getEffectiveLevel>`. 36 | """ 37 | level = logger.getEffectiveLevel() 38 | current = logger 39 | 40 | while current: 41 | if any(handler.level <= level for handler in current.handlers): 42 | return True 43 | 44 | if not current.propagate: 45 | break 46 | 47 | current = current.parent 48 | 49 | return False 50 | 51 | 52 | #: Log messages to :func:`~flask.logging.wsgi_errors_stream` with the format 53 | #: ``[%(asctime)s] %(levelname)s in %(module)s: %(message)s``. 54 | default_handler = logging.StreamHandler(wsgi_errors_stream) 55 | default_handler.setFormatter(logging.Formatter( 56 | '[%(asctime)s] %(levelname)s in %(module)s: %(message)s' 57 | )) 58 | 59 | 60 | def create_logger(app): 61 | """Get the ``'flask.app'`` logger and configure it if needed. 62 | 63 | When :attr:`~flask.Flask.debug` is enabled, set the logger level to 64 | :data:`logging.DEBUG` if it is not set. 65 | 66 | If there is no handler for the logger's effective level, add a 67 | :class:`~logging.StreamHandler` for 68 | :func:`~flask.logging.wsgi_errors_stream` with a basic format. 69 | """ 70 | logger = logging.getLogger('flask.app') 71 | 72 | if app.debug and logger.level == logging.NOTSET: 73 | logger.setLevel(logging.DEBUG) 74 | 75 | if not has_level_handler(logger): 76 | logger.addHandler(default_handler) 77 | 78 | return logger 79 | -------------------------------------------------------------------------------- /flask/signals.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | flask.signals 4 | ~~~~~~~~~~~~~ 5 | 6 | Implements signals based on blinker if available, otherwise 7 | falls silently back to a noop. 8 | 9 | :copyright: © 2010 by the Pallets team. 10 | :license: BSD, see LICENSE for more details. 11 | """ 12 | 13 | signals_available = False 14 | try: 15 | from blinker import Namespace 16 | signals_available = True 17 | except ImportError: 18 | class Namespace(object): 19 | def signal(self, name, doc=None): 20 | return _FakeSignal(name, doc) 21 | 22 | class _FakeSignal(object): 23 | """If blinker is unavailable, create a fake class with the same 24 | interface that allows sending of signals but will fail with an 25 | error on anything else. Instead of doing anything on send, it 26 | will just ignore the arguments and do nothing instead. 27 | """ 28 | 29 | def __init__(self, name, doc=None): 30 | self.name = name 31 | self.__doc__ = doc 32 | def _fail(self, *args, **kwargs): 33 | raise RuntimeError('signalling support is unavailable ' 34 | 'because the blinker library is ' 35 | 'not installed.') 36 | send = lambda *a, **kw: None 37 | connect = disconnect = has_receivers_for = receivers_for = \ 38 | temporarily_connected_to = connected_to = _fail 39 | del _fail 40 | 41 | # The namespace for code signals. If you are not Flask code, do 42 | # not put signals in here. Create your own namespace instead. 43 | _signals = Namespace() 44 | 45 | 46 | # Core signals. For usage examples grep the source code or consult 47 | # the API documentation in docs/api.rst as well as docs/signals.rst 48 | template_rendered = _signals.signal('template-rendered') 49 | before_render_template = _signals.signal('before-render-template') 50 | request_started = _signals.signal('request-started') 51 | request_finished = _signals.signal('request-finished') 52 | request_tearing_down = _signals.signal('request-tearing-down') 53 | got_request_exception = _signals.signal('got-request-exception') 54 | appcontext_tearing_down = _signals.signal('appcontext-tearing-down') 55 | appcontext_pushed = _signals.signal('appcontext-pushed') 56 | appcontext_popped = _signals.signal('appcontext-popped') 57 | message_flashed = _signals.signal('message-flashed') 58 | -------------------------------------------------------------------------------- /scripts/make-release.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from __future__ import print_function 3 | 4 | import os 5 | import re 6 | import sys 7 | from datetime import date, datetime 8 | from subprocess import PIPE, Popen 9 | 10 | _date_strip_re = re.compile(r'(?<=\d)(st|nd|rd|th)') 11 | 12 | 13 | def parse_changelog(): 14 | with open('CHANGES.rst') as f: 15 | lineiter = iter(f) 16 | for line in lineiter: 17 | match = re.search('^Version\s+(.*)', line.strip()) 18 | 19 | if match is None: 20 | continue 21 | 22 | version = match.group(1).strip() 23 | 24 | if next(lineiter).count('-') != len(match.group(0)): 25 | continue 26 | 27 | while 1: 28 | change_info = next(lineiter).strip() 29 | 30 | if change_info: 31 | break 32 | 33 | match = re.search( 34 | r'released on (\w+\s+\d+\w+\s+\d+)(?:, codename (.*))?', 35 | change_info, 36 | flags=re.IGNORECASE 37 | ) 38 | 39 | if match is None: 40 | continue 41 | 42 | datestr, codename = match.groups() 43 | return version, parse_date(datestr), codename 44 | 45 | 46 | def bump_version(version): 47 | try: 48 | parts = [int(i) for i in version.split('.')] 49 | except ValueError: 50 | fail('Current version is not numeric') 51 | 52 | parts[-1] += 1 53 | return '.'.join(map(str, parts)) 54 | 55 | 56 | def parse_date(string): 57 | string = _date_strip_re.sub('', string) 58 | return datetime.strptime(string, '%B %d %Y') 59 | 60 | 61 | def set_filename_version(filename, version_number, pattern): 62 | changed = [] 63 | 64 | def inject_version(match): 65 | before, old, after = match.groups() 66 | changed.append(True) 67 | return before + version_number + after 68 | 69 | with open(filename) as f: 70 | contents = re.sub( 71 | r"^(\s*%s\s*=\s*')(.+?)(')" % pattern, 72 | inject_version, f.read(), 73 | flags=re.DOTALL | re.MULTILINE 74 | ) 75 | 76 | if not changed: 77 | fail('Could not find %s in %s', pattern, filename) 78 | 79 | with open(filename, 'w') as f: 80 | f.write(contents) 81 | 82 | 83 | def set_init_version(version): 84 | info('Setting __init__.py version to %s', version) 85 | set_filename_version('flask/__init__.py', version, '__version__') 86 | 87 | 88 | def build(): 89 | cmd = [sys.executable, 'setup.py', 'sdist', 'bdist_wheel'] 90 | Popen(cmd).wait() 91 | 92 | 93 | def fail(message, *args): 94 | print('Error:', message % args, file=sys.stderr) 95 | sys.exit(1) 96 | 97 | 98 | def info(message, *args): 99 | print(message % args, file=sys.stderr) 100 | 101 | 102 | def get_git_tags(): 103 | return set( 104 | Popen(['git', 'tag'], stdout=PIPE).communicate()[0].splitlines() 105 | ) 106 | 107 | 108 | def git_is_clean(): 109 | return Popen(['git', 'diff', '--quiet']).wait() == 0 110 | 111 | 112 | def make_git_commit(message, *args): 113 | message = message % args 114 | Popen(['git', 'commit', '-am', message]).wait() 115 | 116 | 117 | def make_git_tag(tag): 118 | info('Tagging "%s"', tag) 119 | Popen(['git', 'tag', tag]).wait() 120 | 121 | 122 | def main(): 123 | os.chdir(os.path.join(os.path.dirname(__file__), '..')) 124 | 125 | rv = parse_changelog() 126 | 127 | if rv is None: 128 | fail('Could not parse changelog') 129 | 130 | version, release_date, codename = rv 131 | dev_version = bump_version(version) + '.dev' 132 | 133 | info( 134 | 'Releasing %s (codename %s, release date %s)', 135 | version, codename, release_date.strftime('%d/%m/%Y') 136 | ) 137 | tags = get_git_tags() 138 | 139 | if version in tags: 140 | fail('Version "%s" is already tagged', version) 141 | 142 | if release_date.date() != date.today(): 143 | fail( 144 | 'Release date is not today (%s != %s)', 145 | release_date.date(), date.today() 146 | ) 147 | 148 | if not git_is_clean(): 149 | fail('You have uncommitted changes in git') 150 | 151 | try: 152 | import wheel # noqa: F401 153 | except ImportError: 154 | fail('You need to install the wheel package.') 155 | 156 | set_init_version(version) 157 | make_git_commit('Bump version number to %s', version) 158 | make_git_tag(version) 159 | build() 160 | set_init_version(dev_version) 161 | 162 | 163 | if __name__ == '__main__': 164 | main() 165 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [aliases] 2 | release = egg_info -Db '' 3 | 4 | [bdist_wheel] 5 | universal = 1 6 | 7 | [metadata] 8 | license_file = LICENSE 9 | 10 | [tool:pytest] 11 | minversion = 3.0 12 | testpaths = tests 13 | 14 | [coverage:run] 15 | branch = True 16 | source = 17 | flask 18 | tests 19 | 20 | [coverage:paths] 21 | source = 22 | flask 23 | .tox/*/lib/python*/site-packages/flask 24 | .tox/pypy/site-packages/flask 25 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import io 4 | import re 5 | from collections import OrderedDict 6 | 7 | from setuptools import setup 8 | 9 | with io.open('README.rst', 'rt', encoding='utf8') as f: 10 | readme = f.read() 11 | 12 | with io.open('flask/__init__.py', 'rt', encoding='utf8') as f: 13 | version = re.search(r'__version__ = \'(.*?)\'', f.read()).group(1) 14 | 15 | setup( 16 | name='Flask', 17 | version=version, 18 | url='https://www.palletsprojects.com/p/flask/', 19 | project_urls=OrderedDict(( 20 | ('Documentation', 'http://flask.pocoo.org/docs/'), 21 | ('Code', 'https://github.com/pallets/flask'), 22 | ('Issue tracker', 'https://github.com/pallets/flask/issues'), 23 | )), 24 | license='BSD', 25 | author='Armin Ronacher', 26 | author_email='armin.ronacher@active-4.com', 27 | maintainer='Pallets team', 28 | maintainer_email='contact@palletsprojects.com', 29 | description='A simple framework for building complex web applications.', 30 | long_description=readme, 31 | packages=['flask', 'flask.json'], 32 | include_package_data=True, 33 | zip_safe=False, 34 | platforms='any', 35 | python_requires='>=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*', 36 | install_requires=[ 37 | 'Werkzeug>=0.14', 38 | 'Jinja2>=2.10', 39 | 'itsdangerous>=0.24', 40 | 'click>=5.1', 41 | ], 42 | extras_require={ 43 | 'dotenv': ['python-dotenv'], 44 | 'dev': [ 45 | 'pytest>=3', 46 | 'coverage', 47 | 'tox', 48 | 'sphinx', 49 | 'pallets-sphinx-themes', 50 | 'sphinxcontrib-log-cabinet', 51 | ], 52 | 'docs': [ 53 | 'sphinx', 54 | 'pallets-sphinx-themes', 55 | 'sphinxcontrib-log-cabinet', 56 | ] 57 | }, 58 | classifiers=[ 59 | 'Development Status :: 5 - Production/Stable', 60 | 'Environment :: Web Environment', 61 | 'Framework :: Flask', 62 | 'Intended Audience :: Developers', 63 | 'License :: OSI Approved :: BSD License', 64 | 'Operating System :: OS Independent', 65 | 'Programming Language :: Python', 66 | 'Programming Language :: Python :: 2', 67 | 'Programming Language :: Python :: 2.7', 68 | 'Programming Language :: Python :: 3', 69 | 'Programming Language :: Python :: 3.4', 70 | 'Programming Language :: Python :: 3.5', 71 | 'Programming Language :: Python :: 3.6', 72 | 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 73 | 'Topic :: Internet :: WWW/HTTP :: WSGI :: Application', 74 | 'Topic :: Software Development :: Libraries :: Application Frameworks', 75 | 'Topic :: Software Development :: Libraries :: Python Modules', 76 | ], 77 | entry_points={ 78 | 'console_scripts': [ 79 | 'flask = flask.cli:main', 80 | ], 81 | }, 82 | ) 83 | -------------------------------------------------------------------------------- /tests/static/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "TEST_KEY": "foo", 3 | "SECRET_KEY": "config" 4 | } 5 | -------------------------------------------------------------------------------- /tests/static/index.html: -------------------------------------------------------------------------------- 1 |

Hello World!

2 | -------------------------------------------------------------------------------- /tests/templates/_macro.html: -------------------------------------------------------------------------------- 1 | {% macro hello(name) %}Hello {{ name }}!{% endmacro %} 2 | -------------------------------------------------------------------------------- /tests/templates/context_template.html: -------------------------------------------------------------------------------- 1 |

{{ value }}|{{ injected_value }} 2 | -------------------------------------------------------------------------------- /tests/templates/escaping_template.html: -------------------------------------------------------------------------------- 1 | {{ text }} 2 | {{ html }} 3 | {% autoescape false %}{{ text }} 4 | {{ html }}{% endautoescape %} 5 | {% autoescape true %}{{ text }} 6 | {{ html }}{% endautoescape %} 7 | -------------------------------------------------------------------------------- /tests/templates/mail.txt: -------------------------------------------------------------------------------- 1 | {{ foo}} Mail 2 | -------------------------------------------------------------------------------- /tests/templates/nested/nested.txt: -------------------------------------------------------------------------------- 1 | I'm nested 2 | -------------------------------------------------------------------------------- /tests/templates/non_escaping_template.txt: -------------------------------------------------------------------------------- 1 | {{ text }} 2 | {{ html }} 3 | {% autoescape false %}{{ text }} 4 | {{ html }}{% endautoescape %} 5 | {% autoescape true %}{{ text }} 6 | {{ html }}{% endautoescape %} 7 | {{ text }} 8 | {{ html }} 9 | -------------------------------------------------------------------------------- /tests/templates/simple_template.html: -------------------------------------------------------------------------------- 1 |

{{ whiskey }}

2 | -------------------------------------------------------------------------------- /tests/templates/template_filter.html: -------------------------------------------------------------------------------- 1 | {{ value|super_reverse }} -------------------------------------------------------------------------------- /tests/templates/template_test.html: -------------------------------------------------------------------------------- 1 | {% if value is boolean %} 2 | Success! 3 | {% endif %} 4 | -------------------------------------------------------------------------------- /tests/test_apps/.env: -------------------------------------------------------------------------------- 1 | FOO=env 2 | SPAM=1 3 | EGGS=2 4 | -------------------------------------------------------------------------------- /tests/test_apps/.flaskenv: -------------------------------------------------------------------------------- 1 | FOO=flaskenv 2 | BAR=bar 3 | EGGS=0 4 | -------------------------------------------------------------------------------- /tests/test_apps/blueprintapp/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | 3 | app = Flask(__name__) 4 | app.config['DEBUG'] = True 5 | from blueprintapp.apps.admin import admin 6 | from blueprintapp.apps.frontend import frontend 7 | app.register_blueprint(admin) 8 | app.register_blueprint(frontend) 9 | -------------------------------------------------------------------------------- /tests/test_apps/blueprintapp/apps/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyup-bot/flask/b9b88b0cdf70190dee5c7d31a83efb1cae888a63/tests/test_apps/blueprintapp/apps/__init__.py -------------------------------------------------------------------------------- /tests/test_apps/blueprintapp/apps/admin/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint, render_template 2 | 3 | admin = Blueprint('admin', __name__, url_prefix='/admin', 4 | template_folder='templates', 5 | static_folder='static') 6 | 7 | 8 | @admin.route('/') 9 | def index(): 10 | return render_template('admin/index.html') 11 | 12 | 13 | @admin.route('/index2') 14 | def index2(): 15 | return render_template('./admin/index.html') 16 | -------------------------------------------------------------------------------- /tests/test_apps/blueprintapp/apps/admin/static/css/test.css: -------------------------------------------------------------------------------- 1 | /* nested file */ 2 | -------------------------------------------------------------------------------- /tests/test_apps/blueprintapp/apps/admin/static/test.txt: -------------------------------------------------------------------------------- 1 | Admin File 2 | -------------------------------------------------------------------------------- /tests/test_apps/blueprintapp/apps/admin/templates/admin/index.html: -------------------------------------------------------------------------------- 1 | Hello from the Admin 2 | -------------------------------------------------------------------------------- /tests/test_apps/blueprintapp/apps/frontend/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint, render_template 2 | 3 | frontend = Blueprint('frontend', __name__, template_folder='templates') 4 | 5 | 6 | @frontend.route('/') 7 | def index(): 8 | return render_template('frontend/index.html') 9 | 10 | 11 | @frontend.route('/missing') 12 | def missing_template(): 13 | return render_template('missing_template.html') 14 | -------------------------------------------------------------------------------- /tests/test_apps/blueprintapp/apps/frontend/templates/frontend/index.html: -------------------------------------------------------------------------------- 1 | Hello from the Frontend 2 | -------------------------------------------------------------------------------- /tests/test_apps/cliapp/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyup-bot/flask/b9b88b0cdf70190dee5c7d31a83efb1cae888a63/tests/test_apps/cliapp/__init__.py -------------------------------------------------------------------------------- /tests/test_apps/cliapp/app.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import, print_function 2 | 3 | from flask import Flask 4 | 5 | testapp = Flask('testapp') 6 | -------------------------------------------------------------------------------- /tests/test_apps/cliapp/factory.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import, print_function 2 | 3 | from flask import Flask 4 | 5 | 6 | def create_app(): 7 | return Flask('app') 8 | 9 | 10 | def create_app2(foo, bar): 11 | return Flask('_'.join(['app2', foo, bar])) 12 | 13 | 14 | def create_app3(foo, script_info): 15 | return Flask('_'.join(['app3', foo, script_info.data['test']])) 16 | 17 | 18 | def no_app(): 19 | pass 20 | -------------------------------------------------------------------------------- /tests/test_apps/cliapp/importerrorapp.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import, print_function 2 | 3 | from flask import Flask 4 | 5 | raise ImportError() 6 | 7 | testapp = Flask('testapp') 8 | -------------------------------------------------------------------------------- /tests/test_apps/cliapp/inner1/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | 3 | application = Flask(__name__) 4 | -------------------------------------------------------------------------------- /tests/test_apps/cliapp/inner1/inner2/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyup-bot/flask/b9b88b0cdf70190dee5c7d31a83efb1cae888a63/tests/test_apps/cliapp/inner1/inner2/__init__.py -------------------------------------------------------------------------------- /tests/test_apps/cliapp/inner1/inner2/flask.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | 3 | app = Flask(__name__) 4 | -------------------------------------------------------------------------------- /tests/test_apps/cliapp/message.txt: -------------------------------------------------------------------------------- 1 | So long, and thanks for all the fish. 2 | -------------------------------------------------------------------------------- /tests/test_apps/cliapp/multiapp.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import, print_function 2 | 3 | from flask import Flask 4 | 5 | app1 = Flask('app1') 6 | app2 = Flask('app2') 7 | -------------------------------------------------------------------------------- /tests/test_apps/helloworld/hello.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | app = Flask(__name__) 3 | 4 | @app.route("/") 5 | def hello(): 6 | return "Hello World!" 7 | -------------------------------------------------------------------------------- /tests/test_apps/helloworld/wsgi.py: -------------------------------------------------------------------------------- 1 | from hello import app 2 | -------------------------------------------------------------------------------- /tests/test_apps/subdomaintestmodule/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Module 2 | 3 | 4 | mod = Module(__name__, 'foo', subdomain='foo') 5 | -------------------------------------------------------------------------------- /tests/test_apps/subdomaintestmodule/static/hello.txt: -------------------------------------------------------------------------------- 1 | Hello Subdomain 2 | -------------------------------------------------------------------------------- /tests/test_instance_config.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | tests.test_instance 4 | ~~~~~~~~~~~~~~~~~~~ 5 | 6 | :copyright: © 2010 by the Pallets team. 7 | :license: BSD, see LICENSE for more details. 8 | """ 9 | 10 | import os 11 | import sys 12 | 13 | import pytest 14 | import flask 15 | from flask._compat import PY2 16 | 17 | 18 | def test_explicit_instance_paths(modules_tmpdir): 19 | with pytest.raises(ValueError) as excinfo: 20 | flask.Flask(__name__, instance_path='instance') 21 | assert 'must be absolute' in str(excinfo.value) 22 | 23 | app = flask.Flask(__name__, instance_path=str(modules_tmpdir)) 24 | assert app.instance_path == str(modules_tmpdir) 25 | 26 | 27 | def test_main_module_paths(modules_tmpdir, purge_module): 28 | app = modules_tmpdir.join('main_app.py') 29 | app.write('import flask\n\napp = flask.Flask("__main__")') 30 | purge_module('main_app') 31 | 32 | from main_app import app 33 | here = os.path.abspath(os.getcwd()) 34 | assert app.instance_path == os.path.join(here, 'instance') 35 | 36 | 37 | def test_uninstalled_module_paths(modules_tmpdir, purge_module): 38 | app = modules_tmpdir.join('config_module_app.py').write( 39 | 'import os\n' 40 | 'import flask\n' 41 | 'here = os.path.abspath(os.path.dirname(__file__))\n' 42 | 'app = flask.Flask(__name__)\n' 43 | ) 44 | purge_module('config_module_app') 45 | 46 | from config_module_app import app 47 | assert app.instance_path == str(modules_tmpdir.join('instance')) 48 | 49 | 50 | def test_uninstalled_package_paths(modules_tmpdir, purge_module): 51 | app = modules_tmpdir.mkdir('config_package_app') 52 | init = app.join('__init__.py') 53 | init.write( 54 | 'import os\n' 55 | 'import flask\n' 56 | 'here = os.path.abspath(os.path.dirname(__file__))\n' 57 | 'app = flask.Flask(__name__)\n' 58 | ) 59 | purge_module('config_package_app') 60 | 61 | from config_package_app import app 62 | assert app.instance_path == str(modules_tmpdir.join('instance')) 63 | 64 | 65 | def test_installed_module_paths(modules_tmpdir, modules_tmpdir_prefix, 66 | purge_module, site_packages, limit_loader): 67 | site_packages.join('site_app.py').write( 68 | 'import flask\n' 69 | 'app = flask.Flask(__name__)\n' 70 | ) 71 | purge_module('site_app') 72 | 73 | from site_app import app 74 | assert app.instance_path == \ 75 | modules_tmpdir.join('var').join('site_app-instance') 76 | 77 | 78 | def test_installed_package_paths(limit_loader, modules_tmpdir, 79 | modules_tmpdir_prefix, purge_module, 80 | monkeypatch): 81 | installed_path = modules_tmpdir.mkdir('path') 82 | monkeypatch.syspath_prepend(installed_path) 83 | 84 | app = installed_path.mkdir('installed_package') 85 | init = app.join('__init__.py') 86 | init.write('import flask\napp = flask.Flask(__name__)') 87 | purge_module('installed_package') 88 | 89 | from installed_package import app 90 | assert app.instance_path == \ 91 | modules_tmpdir.join('var').join('installed_package-instance') 92 | 93 | 94 | def test_prefix_package_paths(limit_loader, modules_tmpdir, 95 | modules_tmpdir_prefix, purge_module, 96 | site_packages): 97 | app = site_packages.mkdir('site_package') 98 | init = app.join('__init__.py') 99 | init.write('import flask\napp = flask.Flask(__name__)') 100 | purge_module('site_package') 101 | 102 | import site_package 103 | assert site_package.app.instance_path == \ 104 | modules_tmpdir.join('var').join('site_package-instance') 105 | 106 | 107 | def test_egg_installed_paths(install_egg, modules_tmpdir, 108 | modules_tmpdir_prefix): 109 | modules_tmpdir.mkdir('site_egg').join('__init__.py').write( 110 | 'import flask\n\napp = flask.Flask(__name__)' 111 | ) 112 | install_egg('site_egg') 113 | try: 114 | import site_egg 115 | assert site_egg.app.instance_path == \ 116 | str(modules_tmpdir.join('var/').join('site_egg-instance')) 117 | finally: 118 | if 'site_egg' in sys.modules: 119 | del sys.modules['site_egg'] 120 | 121 | 122 | @pytest.mark.skipif(not PY2, reason='This only works under Python 2.') 123 | def test_meta_path_loader_without_is_package(request, modules_tmpdir): 124 | app = modules_tmpdir.join('unimportable.py') 125 | app.write('import flask\napp = flask.Flask(__name__)') 126 | 127 | class Loader(object): 128 | def find_module(self, name, path=None): 129 | return self 130 | 131 | sys.meta_path.append(Loader()) 132 | request.addfinalizer(sys.meta_path.pop) 133 | 134 | with pytest.raises(AttributeError): 135 | import unimportable 136 | -------------------------------------------------------------------------------- /tests/test_json_tag.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | tests.test_json_tag 4 | ~~~~~~~~~~~~~~~~~~~ 5 | 6 | :copyright: © 2010 by the Pallets team. 7 | :license: BSD, see LICENSE for more details. 8 | """ 9 | 10 | from datetime import datetime 11 | from uuid import uuid4 12 | 13 | import pytest 14 | 15 | from flask import Markup 16 | from flask.json.tag import TaggedJSONSerializer, JSONTag 17 | 18 | 19 | @pytest.mark.parametrize("data", ( 20 | {' t': (1, 2, 3)}, 21 | {' t__': b'a'}, 22 | {' di': ' di'}, 23 | {'x': (1, 2, 3), 'y': 4}, 24 | (1, 2, 3), 25 | [(1, 2, 3)], 26 | b'\xff', 27 | Markup(''), 28 | uuid4(), 29 | datetime.utcnow().replace(microsecond=0), 30 | )) 31 | def test_dump_load_unchanged(data): 32 | s = TaggedJSONSerializer() 33 | assert s.loads(s.dumps(data)) == data 34 | 35 | 36 | def test_duplicate_tag(): 37 | class TagDict(JSONTag): 38 | key = ' d' 39 | 40 | s = TaggedJSONSerializer() 41 | pytest.raises(KeyError, s.register, TagDict) 42 | s.register(TagDict, force=True, index=0) 43 | assert isinstance(s.tags[' d'], TagDict) 44 | assert isinstance(s.order[0], TagDict) 45 | 46 | 47 | def test_custom_tag(): 48 | class Foo(object): 49 | def __init__(self, data): 50 | self.data = data 51 | 52 | class TagFoo(JSONTag): 53 | __slots__ = () 54 | key = ' f' 55 | 56 | def check(self, value): 57 | return isinstance(value, Foo) 58 | 59 | def to_json(self, value): 60 | return self.serializer.tag(value.data) 61 | 62 | def to_python(self, value): 63 | return Foo(value) 64 | 65 | s = TaggedJSONSerializer() 66 | s.register(TagFoo) 67 | assert s.loads(s.dumps(Foo('bar'))).data == 'bar' 68 | 69 | 70 | def test_tag_interface(): 71 | t = JSONTag(None) 72 | pytest.raises(NotImplementedError, t.check, None) 73 | pytest.raises(NotImplementedError, t.to_json, None) 74 | pytest.raises(NotImplementedError, t.to_python, None) 75 | 76 | 77 | def test_tag_order(): 78 | class Tag1(JSONTag): 79 | key = ' 1' 80 | 81 | class Tag2(JSONTag): 82 | key = ' 2' 83 | 84 | s = TaggedJSONSerializer() 85 | 86 | s.register(Tag1, index=-1) 87 | assert isinstance(s.order[-2], Tag1) 88 | 89 | s.register(Tag2, index=None) 90 | assert isinstance(s.order[-1], Tag2) 91 | -------------------------------------------------------------------------------- /tests/test_logging.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | tests.test_logging 4 | ~~~~~~~~~~~~~~~~~~~ 5 | 6 | :copyright: © 2010 by the Pallets team. 7 | :license: BSD, see LICENSE for more details. 8 | """ 9 | 10 | import logging 11 | import sys 12 | 13 | import pytest 14 | 15 | from flask._compat import StringIO 16 | from flask.logging import default_handler, has_level_handler, \ 17 | wsgi_errors_stream 18 | 19 | 20 | @pytest.fixture(autouse=True) 21 | def reset_logging(pytestconfig): 22 | root_handlers = logging.root.handlers[:] 23 | logging.root.handlers = [] 24 | root_level = logging.root.level 25 | 26 | logger = logging.getLogger('flask.app') 27 | logger.handlers = [] 28 | logger.setLevel(logging.NOTSET) 29 | 30 | logging_plugin = pytestconfig.pluginmanager.unregister( 31 | name='logging-plugin') 32 | 33 | yield 34 | 35 | logging.root.handlers[:] = root_handlers 36 | logging.root.setLevel(root_level) 37 | 38 | logger.handlers = [] 39 | logger.setLevel(logging.NOTSET) 40 | 41 | if logging_plugin: 42 | pytestconfig.pluginmanager.register(logging_plugin, 'logging-plugin') 43 | 44 | 45 | def test_logger(app): 46 | assert app.logger.name == 'flask.app' 47 | assert app.logger.level == logging.NOTSET 48 | assert app.logger.handlers == [default_handler] 49 | 50 | 51 | def test_logger_debug(app): 52 | app.debug = True 53 | assert app.logger.level == logging.DEBUG 54 | assert app.logger.handlers == [default_handler] 55 | 56 | 57 | def test_existing_handler(app): 58 | logging.root.addHandler(logging.StreamHandler()) 59 | assert app.logger.level == logging.NOTSET 60 | assert not app.logger.handlers 61 | 62 | 63 | def test_wsgi_errors_stream(app, client): 64 | @app.route('/') 65 | def index(): 66 | app.logger.error('test') 67 | return '' 68 | 69 | stream = StringIO() 70 | client.get('/', errors_stream=stream) 71 | assert 'ERROR in test_logging: test' in stream.getvalue() 72 | 73 | assert wsgi_errors_stream._get_current_object() is sys.stderr 74 | 75 | with app.test_request_context(errors_stream=stream): 76 | assert wsgi_errors_stream._get_current_object() is stream 77 | 78 | 79 | def test_has_level_handler(): 80 | logger = logging.getLogger('flask.app') 81 | assert not has_level_handler(logger) 82 | 83 | handler = logging.StreamHandler() 84 | logging.root.addHandler(handler) 85 | assert has_level_handler(logger) 86 | 87 | logger.propagate = False 88 | assert not has_level_handler(logger) 89 | logger.propagate = True 90 | 91 | handler.setLevel(logging.ERROR) 92 | assert not has_level_handler(logger) 93 | 94 | 95 | def test_log_view_exception(app, client): 96 | @app.route('/') 97 | def index(): 98 | raise Exception('test') 99 | 100 | app.testing = False 101 | stream = StringIO() 102 | rv = client.get('/', errors_stream=stream) 103 | assert rv.status_code == 500 104 | assert rv.data 105 | err = stream.getvalue() 106 | assert 'Exception on / [GET]' in err 107 | assert 'Exception: test' in err 108 | -------------------------------------------------------------------------------- /tests/test_regression.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | tests.regression 4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | Tests regressions. 7 | 8 | :copyright: © 2010 by the Pallets team. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | 12 | import gc 13 | import sys 14 | import threading 15 | 16 | import pytest 17 | from werkzeug.exceptions import NotFound 18 | 19 | import flask 20 | 21 | _gc_lock = threading.Lock() 22 | 23 | 24 | class assert_no_leak(object): 25 | 26 | def __enter__(self): 27 | gc.disable() 28 | _gc_lock.acquire() 29 | loc = flask._request_ctx_stack._local 30 | 31 | # Force Python to track this dictionary at all times. 32 | # This is necessary since Python only starts tracking 33 | # dicts if they contain mutable objects. It's a horrible, 34 | # horrible hack but makes this kinda testable. 35 | loc.__storage__['FOOO'] = [1, 2, 3] 36 | 37 | gc.collect() 38 | self.old_objects = len(gc.get_objects()) 39 | 40 | def __exit__(self, exc_type, exc_value, tb): 41 | gc.collect() 42 | new_objects = len(gc.get_objects()) 43 | if new_objects > self.old_objects: 44 | pytest.fail('Example code leaked') 45 | _gc_lock.release() 46 | gc.enable() 47 | 48 | 49 | def test_memory_consumption(): 50 | app = flask.Flask(__name__) 51 | 52 | @app.route('/') 53 | def index(): 54 | return flask.render_template('simple_template.html', whiskey=42) 55 | 56 | def fire(): 57 | with app.test_client() as c: 58 | rv = c.get('/') 59 | assert rv.status_code == 200 60 | assert rv.data == b'

42

' 61 | 62 | # Trigger caches 63 | fire() 64 | 65 | # This test only works on CPython 2.7. 66 | if sys.version_info >= (2, 7) and \ 67 | not hasattr(sys, 'pypy_translation_info'): 68 | with assert_no_leak(): 69 | for x in range(10): 70 | fire() 71 | 72 | 73 | def test_safe_join_toplevel_pardir(): 74 | from flask.helpers import safe_join 75 | with pytest.raises(NotFound): 76 | safe_join('/foo', '..') 77 | 78 | 79 | def test_aborting(app): 80 | class Foo(Exception): 81 | whatever = 42 82 | 83 | @app.errorhandler(Foo) 84 | def handle_foo(e): 85 | return str(e.whatever) 86 | 87 | @app.route('/') 88 | def index(): 89 | raise flask.abort(flask.redirect(flask.url_for('test'))) 90 | 91 | @app.route('/test') 92 | def test(): 93 | raise Foo() 94 | 95 | with app.test_client() as c: 96 | rv = c.get('/') 97 | assert rv.headers['Location'] == 'http://localhost/test' 98 | rv = c.get('/test') 99 | assert rv.data == b'42' 100 | -------------------------------------------------------------------------------- /tests/test_subclassing.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | tests.subclassing 4 | ~~~~~~~~~~~~~~~~~ 5 | 6 | Test that certain behavior of flask can be customized by 7 | subclasses. 8 | 9 | :copyright: © 2010 by the Pallets team. 10 | :license: BSD, see LICENSE for more details. 11 | """ 12 | 13 | import flask 14 | 15 | from flask._compat import StringIO 16 | 17 | 18 | def test_suppressed_exception_logging(): 19 | class SuppressedFlask(flask.Flask): 20 | def log_exception(self, exc_info): 21 | pass 22 | 23 | out = StringIO() 24 | app = SuppressedFlask(__name__) 25 | 26 | @app.route('/') 27 | def index(): 28 | raise Exception('test') 29 | 30 | rv = app.test_client().get('/', errors_stream=out) 31 | assert rv.status_code == 500 32 | assert b'Internal Server Error' in rv.data 33 | assert not out.getvalue() 34 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = 3 | py{36,35,34,27,py} 4 | py{36,27,py}-simplejson 5 | py{36,27,py}-devel 6 | py{36,27,py}-lowest 7 | docs-html 8 | coverage-report 9 | 10 | [testenv] 11 | passenv = LANG 12 | deps = 13 | pytest>=3 14 | coverage 15 | greenlet 16 | blinker 17 | python-dotenv 18 | 19 | lowest: Werkzeug==0.14 20 | lowest: Jinja2==2.10 21 | lowest: itsdangerous==0.24 22 | lowest: Click==5.1 23 | 24 | devel: https://github.com/pallets/werkzeug/archive/master.tar.gz 25 | devel: https://github.com/pallets/markupsafe/archive/master.tar.gz 26 | devel: https://github.com/pallets/jinja/archive/master.tar.gz 27 | devel: https://github.com/pallets/itsdangerous/archive/master.tar.gz 28 | devel: https://github.com/pallets/click/archive/master.tar.gz 29 | 30 | simplejson: simplejson 31 | 32 | commands = 33 | # the examples need to be installed to test successfully 34 | pip install -q -e examples/tutorial[test] 35 | pip install -q -e examples/javascript[test] 36 | 37 | # pytest-cov doesn't seem to play nice with -p 38 | coverage run -p -m pytest tests examples 39 | 40 | [testenv:docs-html] 41 | deps = 42 | sphinx 43 | pallets-sphinx-themes 44 | sphinxcontrib-log-cabinet 45 | commands = sphinx-build -W -b html -d {envtmpdir}/doctrees docs {envtmpdir}/html 46 | 47 | [testenv:docs-linkcheck] 48 | deps = 49 | sphinx 50 | pallets-sphinx-themes 51 | sphinxcontrib-log-cabinet 52 | commands = sphinx-build -W -b linkcheck -d {envtmpdir}/doctrees docs {envtmpdir}/linkcheck 53 | 54 | [testenv:coverage-report] 55 | deps = coverage 56 | skip_install = true 57 | commands = 58 | coverage combine 59 | coverage report 60 | coverage html 61 | 62 | [testenv:codecov] 63 | passenv = CI TRAVIS TRAVIS_* APPVEYOR APPVEYOR_* 64 | deps = codecov 65 | skip_install = true 66 | commands = 67 | coverage combine 68 | coverage report 69 | codecov 70 | --------------------------------------------------------------------------------