Fix Read the Docs build failure
This commit is contained in:
parent
9ce6e72e2c
commit
c0bbfef68d
259 changed files with 48269 additions and 0 deletions
4
_build/.buildinfo
Normal file
4
_build/.buildinfo
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
# Sphinx build info version 1
|
||||||
|
# This file records the configuration used when building these files. When it is not found, a full rebuild will be done.
|
||||||
|
config: 600360abbe6c4808701ab5031e46e0b0
|
||||||
|
tags: d77d1c0d9ca2f4c8421862c7c5a0d620
|
||||||
BIN
_build/.doctrees/api.doctree
Normal file
BIN
_build/.doctrees/api.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/appcontext.doctree
Normal file
BIN
_build/.doctrees/appcontext.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/async-await.doctree
Normal file
BIN
_build/.doctrees/async-await.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/blueprints.doctree
Normal file
BIN
_build/.doctrees/blueprints.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/changes.doctree
Normal file
BIN
_build/.doctrees/changes.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/cli.doctree
Normal file
BIN
_build/.doctrees/cli.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/config.doctree
Normal file
BIN
_build/.doctrees/config.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/contributing.doctree
Normal file
BIN
_build/.doctrees/contributing.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/debugging.doctree
Normal file
BIN
_build/.doctrees/debugging.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/deploying/apache-httpd.doctree
Normal file
BIN
_build/.doctrees/deploying/apache-httpd.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/deploying/asgi.doctree
Normal file
BIN
_build/.doctrees/deploying/asgi.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/deploying/eventlet.doctree
Normal file
BIN
_build/.doctrees/deploying/eventlet.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/deploying/gevent.doctree
Normal file
BIN
_build/.doctrees/deploying/gevent.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/deploying/gunicorn.doctree
Normal file
BIN
_build/.doctrees/deploying/gunicorn.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/deploying/index.doctree
Normal file
BIN
_build/.doctrees/deploying/index.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/deploying/mod_wsgi.doctree
Normal file
BIN
_build/.doctrees/deploying/mod_wsgi.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/deploying/nginx.doctree
Normal file
BIN
_build/.doctrees/deploying/nginx.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/deploying/proxy_fix.doctree
Normal file
BIN
_build/.doctrees/deploying/proxy_fix.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/deploying/uwsgi.doctree
Normal file
BIN
_build/.doctrees/deploying/uwsgi.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/deploying/waitress.doctree
Normal file
BIN
_build/.doctrees/deploying/waitress.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/design.doctree
Normal file
BIN
_build/.doctrees/design.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/environment.pickle
Normal file
BIN
_build/.doctrees/environment.pickle
Normal file
Binary file not shown.
BIN
_build/.doctrees/errorhandling.doctree
Normal file
BIN
_build/.doctrees/errorhandling.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/extensiondev.doctree
Normal file
BIN
_build/.doctrees/extensiondev.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/extensions.doctree
Normal file
BIN
_build/.doctrees/extensions.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/index.doctree
Normal file
BIN
_build/.doctrees/index.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/installation.doctree
Normal file
BIN
_build/.doctrees/installation.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/license.doctree
Normal file
BIN
_build/.doctrees/license.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/lifecycle.doctree
Normal file
BIN
_build/.doctrees/lifecycle.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/logging.doctree
Normal file
BIN
_build/.doctrees/logging.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/patterns/appdispatch.doctree
Normal file
BIN
_build/.doctrees/patterns/appdispatch.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/patterns/appfactories.doctree
Normal file
BIN
_build/.doctrees/patterns/appfactories.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/patterns/caching.doctree
Normal file
BIN
_build/.doctrees/patterns/caching.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/patterns/celery.doctree
Normal file
BIN
_build/.doctrees/patterns/celery.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/patterns/deferredcallbacks.doctree
Normal file
BIN
_build/.doctrees/patterns/deferredcallbacks.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/patterns/favicon.doctree
Normal file
BIN
_build/.doctrees/patterns/favicon.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/patterns/fileuploads.doctree
Normal file
BIN
_build/.doctrees/patterns/fileuploads.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/patterns/flashing.doctree
Normal file
BIN
_build/.doctrees/patterns/flashing.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/patterns/index.doctree
Normal file
BIN
_build/.doctrees/patterns/index.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/patterns/javascript.doctree
Normal file
BIN
_build/.doctrees/patterns/javascript.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/patterns/jquery.doctree
Normal file
BIN
_build/.doctrees/patterns/jquery.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/patterns/lazyloading.doctree
Normal file
BIN
_build/.doctrees/patterns/lazyloading.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/patterns/methodoverrides.doctree
Normal file
BIN
_build/.doctrees/patterns/methodoverrides.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/patterns/mongoengine.doctree
Normal file
BIN
_build/.doctrees/patterns/mongoengine.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/patterns/packages.doctree
Normal file
BIN
_build/.doctrees/patterns/packages.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/patterns/requestchecksum.doctree
Normal file
BIN
_build/.doctrees/patterns/requestchecksum.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/patterns/singlepageapplications.doctree
Normal file
BIN
_build/.doctrees/patterns/singlepageapplications.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/patterns/sqlalchemy.doctree
Normal file
BIN
_build/.doctrees/patterns/sqlalchemy.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/patterns/sqlite3.doctree
Normal file
BIN
_build/.doctrees/patterns/sqlite3.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/patterns/streaming.doctree
Normal file
BIN
_build/.doctrees/patterns/streaming.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/patterns/subclassing.doctree
Normal file
BIN
_build/.doctrees/patterns/subclassing.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/patterns/templateinheritance.doctree
Normal file
BIN
_build/.doctrees/patterns/templateinheritance.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/patterns/urlprocessors.doctree
Normal file
BIN
_build/.doctrees/patterns/urlprocessors.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/patterns/viewdecorators.doctree
Normal file
BIN
_build/.doctrees/patterns/viewdecorators.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/patterns/wtforms.doctree
Normal file
BIN
_build/.doctrees/patterns/wtforms.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/quickstart.doctree
Normal file
BIN
_build/.doctrees/quickstart.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/reqcontext.doctree
Normal file
BIN
_build/.doctrees/reqcontext.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/server.doctree
Normal file
BIN
_build/.doctrees/server.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/shell.doctree
Normal file
BIN
_build/.doctrees/shell.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/signals.doctree
Normal file
BIN
_build/.doctrees/signals.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/templating.doctree
Normal file
BIN
_build/.doctrees/templating.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/testing.doctree
Normal file
BIN
_build/.doctrees/testing.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/tutorial/blog.doctree
Normal file
BIN
_build/.doctrees/tutorial/blog.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/tutorial/database.doctree
Normal file
BIN
_build/.doctrees/tutorial/database.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/tutorial/deploy.doctree
Normal file
BIN
_build/.doctrees/tutorial/deploy.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/tutorial/factory.doctree
Normal file
BIN
_build/.doctrees/tutorial/factory.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/tutorial/index.doctree
Normal file
BIN
_build/.doctrees/tutorial/index.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/tutorial/install.doctree
Normal file
BIN
_build/.doctrees/tutorial/install.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/tutorial/layout.doctree
Normal file
BIN
_build/.doctrees/tutorial/layout.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/tutorial/next.doctree
Normal file
BIN
_build/.doctrees/tutorial/next.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/tutorial/static.doctree
Normal file
BIN
_build/.doctrees/tutorial/static.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/tutorial/templates.doctree
Normal file
BIN
_build/.doctrees/tutorial/templates.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/tutorial/tests.doctree
Normal file
BIN
_build/.doctrees/tutorial/tests.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/tutorial/views.doctree
Normal file
BIN
_build/.doctrees/tutorial/views.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/views.doctree
Normal file
BIN
_build/.doctrees/views.doctree
Normal file
Binary file not shown.
BIN
_build/.doctrees/web-security.doctree
Normal file
BIN
_build/.doctrees/web-security.doctree
Normal file
Binary file not shown.
82
_build/404/index.html
Normal file
82
_build/404/index.html
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
|
||||||
|
<html lang="en" data-content_root="../">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Page Not Found — Flask Documentation (3.1.x)</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/_static/pygments.css?v=4f649999" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="/_static/flask.css?v=b87c8d14" />
|
||||||
|
<script src="/_static/documentation_options.js?v=d71c4578"></script>
|
||||||
|
<script src="/_static/doctools.js?v=9bcbadda"></script>
|
||||||
|
<script src="/_static/sphinx_highlight.js?v=dc90522c"></script>
|
||||||
|
<script data-project="flask" data-version="3.1.x" src="/_static/describe_version.js?v=fa7f30d0"></script>
|
||||||
|
<link rel="icon" href="/_static/shortcut-icon.png"/>
|
||||||
|
<link rel="index" title="Index" href="/genindex/" />
|
||||||
|
<link rel="search" title="Search" href="/search/" />
|
||||||
|
</head><body>
|
||||||
|
<div class="related" role="navigation" aria-label="Related">
|
||||||
|
<h3>Navigation</h3>
|
||||||
|
<ul>
|
||||||
|
<li class="right" style="margin-right: 10px">
|
||||||
|
<a href="/genindex/" title="General Index"
|
||||||
|
accesskey="I">index</a></li>
|
||||||
|
<li class="right" >
|
||||||
|
<a href="/py-modindex/" title="Python Module Index"
|
||||||
|
>modules</a> |</li>
|
||||||
|
<li class="nav-item nav-item-0"><a href="#">Flask Documentation (3.1.x)</a> »</li>
|
||||||
|
<li class="nav-item nav-item-this"><a href="">Page Not Found</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="document">
|
||||||
|
<div class="documentwrapper">
|
||||||
|
<div class="bodywrapper">
|
||||||
|
<div class="body" role="main">
|
||||||
|
|
||||||
|
<h1>Page Not Found</h1>
|
||||||
|
<p>
|
||||||
|
The page you requested does not exist. You may have followed a bad
|
||||||
|
link, or the page may have been moved or removed.
|
||||||
|
|
||||||
|
|
||||||
|
<div class="clearer"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span id="sidebar-top"></span>
|
||||||
|
<div class="sphinxsidebar" role="navigation" aria-label="Main">
|
||||||
|
<div class="sphinxsidebarwrapper">
|
||||||
|
|
||||||
|
|
||||||
|
<p class="logo"><a href="#">
|
||||||
|
<img class="logo" src="/_static/flask-vertical.png" alt="Logo of Flask"/>
|
||||||
|
</a></p>
|
||||||
|
|
||||||
|
<h3>Navigation</h3>
|
||||||
|
<ul>
|
||||||
|
<li><a href="#">Overview</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<search id="searchbox" style="display: none" role="search">
|
||||||
|
<h3 id="searchlabel">Quick search</h3>
|
||||||
|
<div class="searchformwrapper">
|
||||||
|
<form class="search" action="/search/" method="get">
|
||||||
|
<input type="text" name="q" aria-labelledby="searchlabel" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"/>
|
||||||
|
<input type="submit" value="Go" />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</search>
|
||||||
|
<script>document.getElementById('searchbox').style.display = "block"</script><div id="ethical-ad-placement"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="clearer"></div>
|
||||||
|
</div>
|
||||||
|
<div class="footer" role="contentinfo">
|
||||||
|
© Copyright 2010 Pallets.
|
||||||
|
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 8.1.3.
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
BIN
_build/_images/debugger.png
Normal file
BIN
_build/_images/debugger.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 203 KiB |
BIN
_build/_images/flask-horizontal.png
Normal file
BIN
_build/_images/flask-horizontal.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
BIN
_build/_images/flaskr_edit.png
Normal file
BIN
_build/_images/flaskr_edit.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
BIN
_build/_images/flaskr_index.png
Normal file
BIN
_build/_images/flaskr_index.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
BIN
_build/_images/flaskr_login.png
Normal file
BIN
_build/_images/flaskr_login.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.3 KiB |
BIN
_build/_images/pycharm-run-config.png
Normal file
BIN
_build/_images/pycharm-run-config.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 97 KiB |
717
_build/_sources/api.rst.txt
Normal file
717
_build/_sources/api.rst.txt
Normal file
|
|
@ -0,0 +1,717 @@
|
||||||
|
API
|
||||||
|
===
|
||||||
|
|
||||||
|
.. module:: flask
|
||||||
|
|
||||||
|
This part of the documentation covers all the interfaces of Flask. For
|
||||||
|
parts where Flask depends on external libraries, we document the most
|
||||||
|
important right here and provide links to the canonical documentation.
|
||||||
|
|
||||||
|
|
||||||
|
Application Object
|
||||||
|
------------------
|
||||||
|
|
||||||
|
.. autoclass:: Flask
|
||||||
|
:members:
|
||||||
|
:inherited-members:
|
||||||
|
|
||||||
|
|
||||||
|
Blueprint Objects
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
.. autoclass:: Blueprint
|
||||||
|
:members:
|
||||||
|
:inherited-members:
|
||||||
|
|
||||||
|
Incoming Request Data
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
.. autoclass:: Request
|
||||||
|
:members:
|
||||||
|
:inherited-members:
|
||||||
|
:exclude-members: json_module
|
||||||
|
|
||||||
|
.. attribute:: request
|
||||||
|
|
||||||
|
To access incoming request data, you can use the global `request`
|
||||||
|
object. Flask parses incoming request data for you and gives you
|
||||||
|
access to it through that global object. Internally Flask makes
|
||||||
|
sure that you always get the correct data for the active thread if you
|
||||||
|
are in a multithreaded environment.
|
||||||
|
|
||||||
|
This is a proxy. See :ref:`notes-on-proxies` for more information.
|
||||||
|
|
||||||
|
The request object is an instance of a :class:`~flask.Request`.
|
||||||
|
|
||||||
|
|
||||||
|
Response Objects
|
||||||
|
----------------
|
||||||
|
|
||||||
|
.. autoclass:: flask.Response
|
||||||
|
:members:
|
||||||
|
:inherited-members:
|
||||||
|
:exclude-members: json_module
|
||||||
|
|
||||||
|
Sessions
|
||||||
|
--------
|
||||||
|
|
||||||
|
If you have set :attr:`Flask.secret_key` (or configured it from
|
||||||
|
:data:`SECRET_KEY`) you can use sessions in Flask applications. A session makes
|
||||||
|
it possible to remember information from one request to another. The way Flask
|
||||||
|
does this is by using a signed cookie. The user can look at the session
|
||||||
|
contents, but can't modify it unless they know the secret key, so make sure to
|
||||||
|
set that to something complex and unguessable.
|
||||||
|
|
||||||
|
To access the current session you can use the :class:`session` object:
|
||||||
|
|
||||||
|
.. class:: session
|
||||||
|
|
||||||
|
The session object works pretty much like an ordinary dict, with the
|
||||||
|
difference that it keeps track of modifications.
|
||||||
|
|
||||||
|
This is a proxy. See :ref:`notes-on-proxies` for more information.
|
||||||
|
|
||||||
|
The following attributes are interesting:
|
||||||
|
|
||||||
|
.. attribute:: new
|
||||||
|
|
||||||
|
``True`` if the session is new, ``False`` otherwise.
|
||||||
|
|
||||||
|
.. attribute:: modified
|
||||||
|
|
||||||
|
``True`` if the session object detected a modification. Be advised
|
||||||
|
that modifications on mutable structures are not picked up
|
||||||
|
automatically, in that situation you have to explicitly set the
|
||||||
|
attribute to ``True`` yourself. Here an example::
|
||||||
|
|
||||||
|
# this change is not picked up because a mutable object (here
|
||||||
|
# a list) is changed.
|
||||||
|
session['objects'].append(42)
|
||||||
|
# so mark it as modified yourself
|
||||||
|
session.modified = True
|
||||||
|
|
||||||
|
.. attribute:: permanent
|
||||||
|
|
||||||
|
If set to ``True`` the session lives for
|
||||||
|
:attr:`~flask.Flask.permanent_session_lifetime` seconds. The
|
||||||
|
default is 31 days. If set to ``False`` (which is the default) the
|
||||||
|
session will be deleted when the user closes the browser.
|
||||||
|
|
||||||
|
|
||||||
|
Session Interface
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
.. versionadded:: 0.8
|
||||||
|
|
||||||
|
The session interface provides a simple way to replace the session
|
||||||
|
implementation that Flask is using.
|
||||||
|
|
||||||
|
.. currentmodule:: flask.sessions
|
||||||
|
|
||||||
|
.. autoclass:: SessionInterface
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: SecureCookieSessionInterface
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: SecureCookieSession
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: NullSession
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: SessionMixin
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. admonition:: Notice
|
||||||
|
|
||||||
|
The :data:`PERMANENT_SESSION_LIFETIME` config can be an integer or ``timedelta``.
|
||||||
|
The :attr:`~flask.Flask.permanent_session_lifetime` attribute is always a
|
||||||
|
``timedelta``.
|
||||||
|
|
||||||
|
|
||||||
|
Test Client
|
||||||
|
-----------
|
||||||
|
|
||||||
|
.. currentmodule:: flask.testing
|
||||||
|
|
||||||
|
.. autoclass:: FlaskClient
|
||||||
|
:members:
|
||||||
|
|
||||||
|
|
||||||
|
Test CLI Runner
|
||||||
|
---------------
|
||||||
|
|
||||||
|
.. currentmodule:: flask.testing
|
||||||
|
|
||||||
|
.. autoclass:: FlaskCliRunner
|
||||||
|
:members:
|
||||||
|
|
||||||
|
|
||||||
|
Application Globals
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
.. currentmodule:: flask
|
||||||
|
|
||||||
|
To share data that is valid for one request only from one function to
|
||||||
|
another, a global variable is not good enough because it would break in
|
||||||
|
threaded environments. Flask provides you with a special object that
|
||||||
|
ensures it is only valid for the active request and that will return
|
||||||
|
different values for each request. In a nutshell: it does the right
|
||||||
|
thing, like it does for :class:`request` and :class:`session`.
|
||||||
|
|
||||||
|
.. data:: g
|
||||||
|
|
||||||
|
A namespace object that can store data during an
|
||||||
|
:doc:`application context </appcontext>`. This is an instance of
|
||||||
|
:attr:`Flask.app_ctx_globals_class`, which defaults to
|
||||||
|
:class:`ctx._AppCtxGlobals`.
|
||||||
|
|
||||||
|
This is a good place to store resources during a request. For
|
||||||
|
example, a ``before_request`` function could load a user object from
|
||||||
|
a session id, then set ``g.user`` to be used in the view function.
|
||||||
|
|
||||||
|
This is a proxy. See :ref:`notes-on-proxies` for more information.
|
||||||
|
|
||||||
|
.. versionchanged:: 0.10
|
||||||
|
Bound to the application context instead of the request context.
|
||||||
|
|
||||||
|
.. autoclass:: flask.ctx._AppCtxGlobals
|
||||||
|
:members:
|
||||||
|
|
||||||
|
|
||||||
|
Useful Functions and Classes
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
.. data:: current_app
|
||||||
|
|
||||||
|
A proxy to the application handling the current request. This is
|
||||||
|
useful to access the application without needing to import it, or if
|
||||||
|
it can't be imported, such as when using the application factory
|
||||||
|
pattern or in blueprints and extensions.
|
||||||
|
|
||||||
|
This is only available when an
|
||||||
|
:doc:`application context </appcontext>` is pushed. This happens
|
||||||
|
automatically during requests and CLI commands. It can be controlled
|
||||||
|
manually with :meth:`~flask.Flask.app_context`.
|
||||||
|
|
||||||
|
This is a proxy. See :ref:`notes-on-proxies` for more information.
|
||||||
|
|
||||||
|
.. autofunction:: has_request_context
|
||||||
|
|
||||||
|
.. autofunction:: copy_current_request_context
|
||||||
|
|
||||||
|
.. autofunction:: has_app_context
|
||||||
|
|
||||||
|
.. autofunction:: url_for
|
||||||
|
|
||||||
|
.. autofunction:: abort
|
||||||
|
|
||||||
|
.. autofunction:: redirect
|
||||||
|
|
||||||
|
.. autofunction:: make_response
|
||||||
|
|
||||||
|
.. autofunction:: after_this_request
|
||||||
|
|
||||||
|
.. autofunction:: send_file
|
||||||
|
|
||||||
|
.. autofunction:: send_from_directory
|
||||||
|
|
||||||
|
|
||||||
|
Message Flashing
|
||||||
|
----------------
|
||||||
|
|
||||||
|
.. autofunction:: flash
|
||||||
|
|
||||||
|
.. autofunction:: get_flashed_messages
|
||||||
|
|
||||||
|
|
||||||
|
JSON Support
|
||||||
|
------------
|
||||||
|
|
||||||
|
.. module:: flask.json
|
||||||
|
|
||||||
|
Flask uses Python's built-in :mod:`json` module for handling JSON by
|
||||||
|
default. The JSON implementation can be changed by assigning a different
|
||||||
|
provider to :attr:`flask.Flask.json_provider_class` or
|
||||||
|
:attr:`flask.Flask.json`. The functions provided by ``flask.json`` will
|
||||||
|
use methods on ``app.json`` if an app context is active.
|
||||||
|
|
||||||
|
Jinja's ``|tojson`` filter is configured to use the app's JSON provider.
|
||||||
|
The filter marks the output with ``|safe``. Use it to render data inside
|
||||||
|
HTML ``<script>`` tags.
|
||||||
|
|
||||||
|
.. sourcecode:: html+jinja
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const names = {{ names|tojson }};
|
||||||
|
renderChart(names, {{ axis_data|tojson }});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
.. autofunction:: jsonify
|
||||||
|
|
||||||
|
.. autofunction:: dumps
|
||||||
|
|
||||||
|
.. autofunction:: dump
|
||||||
|
|
||||||
|
.. autofunction:: loads
|
||||||
|
|
||||||
|
.. autofunction:: load
|
||||||
|
|
||||||
|
.. autoclass:: flask.json.provider.JSONProvider
|
||||||
|
:members:
|
||||||
|
:member-order: bysource
|
||||||
|
|
||||||
|
.. autoclass:: flask.json.provider.DefaultJSONProvider
|
||||||
|
:members:
|
||||||
|
:member-order: bysource
|
||||||
|
|
||||||
|
.. automodule:: flask.json.tag
|
||||||
|
|
||||||
|
|
||||||
|
Template Rendering
|
||||||
|
------------------
|
||||||
|
|
||||||
|
.. currentmodule:: flask
|
||||||
|
|
||||||
|
.. autofunction:: render_template
|
||||||
|
|
||||||
|
.. autofunction:: render_template_string
|
||||||
|
|
||||||
|
.. autofunction:: stream_template
|
||||||
|
|
||||||
|
.. autofunction:: stream_template_string
|
||||||
|
|
||||||
|
.. autofunction:: get_template_attribute
|
||||||
|
|
||||||
|
Configuration
|
||||||
|
-------------
|
||||||
|
|
||||||
|
.. autoclass:: Config
|
||||||
|
:members:
|
||||||
|
|
||||||
|
|
||||||
|
Stream Helpers
|
||||||
|
--------------
|
||||||
|
|
||||||
|
.. autofunction:: stream_with_context
|
||||||
|
|
||||||
|
Useful Internals
|
||||||
|
----------------
|
||||||
|
|
||||||
|
.. autoclass:: flask.ctx.RequestContext
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. data:: flask.globals.request_ctx
|
||||||
|
|
||||||
|
The current :class:`~flask.ctx.RequestContext`. If a request context
|
||||||
|
is not active, accessing attributes on this proxy will raise a
|
||||||
|
``RuntimeError``.
|
||||||
|
|
||||||
|
This is an internal object that is essential to how Flask handles
|
||||||
|
requests. Accessing this should not be needed in most cases. Most
|
||||||
|
likely you want :data:`request` and :data:`session` instead.
|
||||||
|
|
||||||
|
.. autoclass:: flask.ctx.AppContext
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. data:: flask.globals.app_ctx
|
||||||
|
|
||||||
|
The current :class:`~flask.ctx.AppContext`. If an app context is not
|
||||||
|
active, accessing attributes on this proxy will raise a
|
||||||
|
``RuntimeError``.
|
||||||
|
|
||||||
|
This is an internal object that is essential to how Flask handles
|
||||||
|
requests. Accessing this should not be needed in most cases. Most
|
||||||
|
likely you want :data:`current_app` and :data:`g` instead.
|
||||||
|
|
||||||
|
.. autoclass:: flask.blueprints.BlueprintSetupState
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. _core-signals-list:
|
||||||
|
|
||||||
|
Signals
|
||||||
|
-------
|
||||||
|
|
||||||
|
Signals are provided by the `Blinker`_ library. See :doc:`signals` for an introduction.
|
||||||
|
|
||||||
|
.. _blinker: https://blinker.readthedocs.io/
|
||||||
|
|
||||||
|
.. data:: template_rendered
|
||||||
|
|
||||||
|
This signal is sent when a template was successfully rendered. The
|
||||||
|
signal is invoked with the instance of the template as `template`
|
||||||
|
and the context as dictionary (named `context`).
|
||||||
|
|
||||||
|
Example subscriber::
|
||||||
|
|
||||||
|
def log_template_renders(sender, template, context, **extra):
|
||||||
|
sender.logger.debug('Rendering template "%s" with context %s',
|
||||||
|
template.name or 'string template',
|
||||||
|
context)
|
||||||
|
|
||||||
|
from flask import template_rendered
|
||||||
|
template_rendered.connect(log_template_renders, app)
|
||||||
|
|
||||||
|
.. data:: flask.before_render_template
|
||||||
|
:noindex:
|
||||||
|
|
||||||
|
This signal is sent before template rendering process. The
|
||||||
|
signal is invoked with the instance of the template as `template`
|
||||||
|
and the context as dictionary (named `context`).
|
||||||
|
|
||||||
|
Example subscriber::
|
||||||
|
|
||||||
|
def log_template_renders(sender, template, context, **extra):
|
||||||
|
sender.logger.debug('Rendering template "%s" with context %s',
|
||||||
|
template.name or 'string template',
|
||||||
|
context)
|
||||||
|
|
||||||
|
from flask import before_render_template
|
||||||
|
before_render_template.connect(log_template_renders, app)
|
||||||
|
|
||||||
|
.. data:: request_started
|
||||||
|
|
||||||
|
This signal is sent when the request context is set up, before
|
||||||
|
any request processing happens. Because the request context is already
|
||||||
|
bound, the subscriber can access the request with the standard global
|
||||||
|
proxies such as :class:`~flask.request`.
|
||||||
|
|
||||||
|
Example subscriber::
|
||||||
|
|
||||||
|
def log_request(sender, **extra):
|
||||||
|
sender.logger.debug('Request context is set up')
|
||||||
|
|
||||||
|
from flask import request_started
|
||||||
|
request_started.connect(log_request, app)
|
||||||
|
|
||||||
|
.. data:: request_finished
|
||||||
|
|
||||||
|
This signal is sent right before the response is sent to the client.
|
||||||
|
It is passed the response to be sent named `response`.
|
||||||
|
|
||||||
|
Example subscriber::
|
||||||
|
|
||||||
|
def log_response(sender, response, **extra):
|
||||||
|
sender.logger.debug('Request context is about to close down. '
|
||||||
|
'Response: %s', response)
|
||||||
|
|
||||||
|
from flask import request_finished
|
||||||
|
request_finished.connect(log_response, app)
|
||||||
|
|
||||||
|
.. data:: got_request_exception
|
||||||
|
|
||||||
|
This signal is sent when an unhandled exception happens during
|
||||||
|
request processing, including when debugging. The exception is
|
||||||
|
passed to the subscriber as ``exception``.
|
||||||
|
|
||||||
|
This signal is not sent for
|
||||||
|
:exc:`~werkzeug.exceptions.HTTPException`, or other exceptions that
|
||||||
|
have error handlers registered, unless the exception was raised from
|
||||||
|
an error handler.
|
||||||
|
|
||||||
|
This example shows how to do some extra logging if a theoretical
|
||||||
|
``SecurityException`` was raised:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from flask import got_request_exception
|
||||||
|
|
||||||
|
def log_security_exception(sender, exception, **extra):
|
||||||
|
if not isinstance(exception, SecurityException):
|
||||||
|
return
|
||||||
|
|
||||||
|
security_logger.exception(
|
||||||
|
f"SecurityException at {request.url!r}",
|
||||||
|
exc_info=exception,
|
||||||
|
)
|
||||||
|
|
||||||
|
got_request_exception.connect(log_security_exception, app)
|
||||||
|
|
||||||
|
.. data:: request_tearing_down
|
||||||
|
|
||||||
|
This signal is sent when the request is tearing down. This is always
|
||||||
|
called, even if an exception is caused. Currently functions listening
|
||||||
|
to this signal are called after the regular teardown handlers, but this
|
||||||
|
is not something you can rely on.
|
||||||
|
|
||||||
|
Example subscriber::
|
||||||
|
|
||||||
|
def close_db_connection(sender, **extra):
|
||||||
|
session.close()
|
||||||
|
|
||||||
|
from flask import request_tearing_down
|
||||||
|
request_tearing_down.connect(close_db_connection, app)
|
||||||
|
|
||||||
|
As of Flask 0.9, this will also be passed an `exc` keyword argument
|
||||||
|
that has a reference to the exception that caused the teardown if
|
||||||
|
there was one.
|
||||||
|
|
||||||
|
.. data:: appcontext_tearing_down
|
||||||
|
|
||||||
|
This signal is sent when the app context is tearing down. This is always
|
||||||
|
called, even if an exception is caused. Currently functions listening
|
||||||
|
to this signal are called after the regular teardown handlers, but this
|
||||||
|
is not something you can rely on.
|
||||||
|
|
||||||
|
Example subscriber::
|
||||||
|
|
||||||
|
def close_db_connection(sender, **extra):
|
||||||
|
session.close()
|
||||||
|
|
||||||
|
from flask import appcontext_tearing_down
|
||||||
|
appcontext_tearing_down.connect(close_db_connection, app)
|
||||||
|
|
||||||
|
This will also be passed an `exc` keyword argument that has a reference
|
||||||
|
to the exception that caused the teardown if there was one.
|
||||||
|
|
||||||
|
.. data:: appcontext_pushed
|
||||||
|
|
||||||
|
This signal is sent when an application context is pushed. The sender
|
||||||
|
is the application. This is usually useful for unittests in order to
|
||||||
|
temporarily hook in information. For instance it can be used to
|
||||||
|
set a resource early onto the `g` object.
|
||||||
|
|
||||||
|
Example usage::
|
||||||
|
|
||||||
|
from contextlib import contextmanager
|
||||||
|
from flask import appcontext_pushed
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def user_set(app, user):
|
||||||
|
def handler(sender, **kwargs):
|
||||||
|
g.user = user
|
||||||
|
with appcontext_pushed.connected_to(handler, app):
|
||||||
|
yield
|
||||||
|
|
||||||
|
And in the testcode::
|
||||||
|
|
||||||
|
def test_user_me(self):
|
||||||
|
with user_set(app, 'john'):
|
||||||
|
c = app.test_client()
|
||||||
|
resp = c.get('/users/me')
|
||||||
|
assert resp.data == 'username=john'
|
||||||
|
|
||||||
|
.. versionadded:: 0.10
|
||||||
|
|
||||||
|
.. data:: appcontext_popped
|
||||||
|
|
||||||
|
This signal is sent when an application context is popped. The sender
|
||||||
|
is the application. This usually falls in line with the
|
||||||
|
:data:`appcontext_tearing_down` signal.
|
||||||
|
|
||||||
|
.. versionadded:: 0.10
|
||||||
|
|
||||||
|
.. data:: message_flashed
|
||||||
|
|
||||||
|
This signal is sent when the application is flashing a message. The
|
||||||
|
messages is sent as `message` keyword argument and the category as
|
||||||
|
`category`.
|
||||||
|
|
||||||
|
Example subscriber::
|
||||||
|
|
||||||
|
recorded = []
|
||||||
|
def record(sender, message, category, **extra):
|
||||||
|
recorded.append((message, category))
|
||||||
|
|
||||||
|
from flask import message_flashed
|
||||||
|
message_flashed.connect(record, app)
|
||||||
|
|
||||||
|
.. versionadded:: 0.10
|
||||||
|
|
||||||
|
|
||||||
|
Class-Based Views
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
.. versionadded:: 0.7
|
||||||
|
|
||||||
|
.. currentmodule:: None
|
||||||
|
|
||||||
|
.. autoclass:: flask.views.View
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: flask.views.MethodView
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. _url-route-registrations:
|
||||||
|
|
||||||
|
URL Route Registrations
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
Generally there are three ways to define rules for the routing system:
|
||||||
|
|
||||||
|
1. You can use the :meth:`flask.Flask.route` decorator.
|
||||||
|
2. You can use the :meth:`flask.Flask.add_url_rule` function.
|
||||||
|
3. You can directly access the underlying Werkzeug routing system
|
||||||
|
which is exposed as :attr:`flask.Flask.url_map`.
|
||||||
|
|
||||||
|
Variable parts in the route can be specified with angular brackets
|
||||||
|
(``/user/<username>``). By default a variable part in the URL accepts any
|
||||||
|
string without a slash however a different converter can be specified as
|
||||||
|
well by using ``<converter:name>``.
|
||||||
|
|
||||||
|
Variable parts are passed to the view function as keyword arguments.
|
||||||
|
|
||||||
|
The following converters are available:
|
||||||
|
|
||||||
|
=========== ===============================================
|
||||||
|
`string` accepts any text without a slash (the default)
|
||||||
|
`int` accepts integers
|
||||||
|
`float` like `int` but for floating point values
|
||||||
|
`path` like the default but also accepts slashes
|
||||||
|
`any` matches one of the items provided
|
||||||
|
`uuid` accepts UUID strings
|
||||||
|
=========== ===============================================
|
||||||
|
|
||||||
|
Custom converters can be defined using :attr:`flask.Flask.url_map`.
|
||||||
|
|
||||||
|
Here are some examples::
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
def index():
|
||||||
|
pass
|
||||||
|
|
||||||
|
@app.route('/<username>')
|
||||||
|
def show_user(username):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@app.route('/post/<int:post_id>')
|
||||||
|
def show_post(post_id):
|
||||||
|
pass
|
||||||
|
|
||||||
|
An important detail to keep in mind is how Flask deals with trailing
|
||||||
|
slashes. The idea is to keep each URL unique so the following rules
|
||||||
|
apply:
|
||||||
|
|
||||||
|
1. If a rule ends with a slash and is requested without a slash by the
|
||||||
|
user, the user is automatically redirected to the same page with a
|
||||||
|
trailing slash attached.
|
||||||
|
2. If a rule does not end with a trailing slash and the user requests the
|
||||||
|
page with a trailing slash, a 404 not found is raised.
|
||||||
|
|
||||||
|
This is consistent with how web servers deal with static files. This
|
||||||
|
also makes it possible to use relative link targets safely.
|
||||||
|
|
||||||
|
You can also define multiple rules for the same function. They have to be
|
||||||
|
unique however. Defaults can also be specified. Here for example is a
|
||||||
|
definition for a URL that accepts an optional page::
|
||||||
|
|
||||||
|
@app.route('/users/', defaults={'page': 1})
|
||||||
|
@app.route('/users/page/<int:page>')
|
||||||
|
def show_users(page):
|
||||||
|
pass
|
||||||
|
|
||||||
|
This specifies that ``/users/`` will be the URL for page one and
|
||||||
|
``/users/page/N`` will be the URL for page ``N``.
|
||||||
|
|
||||||
|
If a URL contains a default value, it will be redirected to its simpler
|
||||||
|
form with a 301 redirect. In the above example, ``/users/page/1`` will
|
||||||
|
be redirected to ``/users/``. If your route handles ``GET`` and ``POST``
|
||||||
|
requests, make sure the default route only handles ``GET``, as redirects
|
||||||
|
can't preserve form data. ::
|
||||||
|
|
||||||
|
@app.route('/region/', defaults={'id': 1})
|
||||||
|
@app.route('/region/<int:id>', methods=['GET', 'POST'])
|
||||||
|
def region(id):
|
||||||
|
pass
|
||||||
|
|
||||||
|
Here are the parameters that :meth:`~flask.Flask.route` and
|
||||||
|
:meth:`~flask.Flask.add_url_rule` accept. The only difference is that
|
||||||
|
with the route parameter the view function is defined with the decorator
|
||||||
|
instead of the `view_func` parameter.
|
||||||
|
|
||||||
|
=============== ==========================================================
|
||||||
|
`rule` the URL rule as string
|
||||||
|
`endpoint` the endpoint for the registered URL rule. Flask itself
|
||||||
|
assumes that the name of the view function is the name
|
||||||
|
of the endpoint if not explicitly stated.
|
||||||
|
`view_func` the function to call when serving a request to the
|
||||||
|
provided endpoint. If this is not provided one can
|
||||||
|
specify the function later by storing it in the
|
||||||
|
:attr:`~flask.Flask.view_functions` dictionary with the
|
||||||
|
endpoint as key.
|
||||||
|
`defaults` A dictionary with defaults for this rule. See the
|
||||||
|
example above for how defaults work.
|
||||||
|
`subdomain` specifies the rule for the subdomain in case subdomain
|
||||||
|
matching is in use. If not specified the default
|
||||||
|
subdomain is assumed.
|
||||||
|
`**options` the options to be forwarded to the underlying
|
||||||
|
:class:`~werkzeug.routing.Rule` object. A change to
|
||||||
|
Werkzeug is handling of method options. methods is a list
|
||||||
|
of methods this rule should be limited to (``GET``, ``POST``
|
||||||
|
etc.). By default a rule just listens for ``GET`` (and
|
||||||
|
implicitly ``HEAD``). Starting with Flask 0.6, ``OPTIONS`` is
|
||||||
|
implicitly added and handled by the standard request
|
||||||
|
handling. They have to be specified as keyword arguments.
|
||||||
|
=============== ==========================================================
|
||||||
|
|
||||||
|
|
||||||
|
View Function Options
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
For internal usage the view functions can have some attributes attached to
|
||||||
|
customize behavior the view function would normally not have control over.
|
||||||
|
The following attributes can be provided optionally to either override
|
||||||
|
some defaults to :meth:`~flask.Flask.add_url_rule` or general behavior:
|
||||||
|
|
||||||
|
- `__name__`: The name of a function is by default used as endpoint. If
|
||||||
|
endpoint is provided explicitly this value is used. Additionally this
|
||||||
|
will be prefixed with the name of the blueprint by default which
|
||||||
|
cannot be customized from the function itself.
|
||||||
|
|
||||||
|
- `methods`: If methods are not provided when the URL rule is added,
|
||||||
|
Flask will look on the view function object itself if a `methods`
|
||||||
|
attribute exists. If it does, it will pull the information for the
|
||||||
|
methods from there.
|
||||||
|
|
||||||
|
- `provide_automatic_options`: if this attribute is set Flask will
|
||||||
|
either force enable or disable the automatic implementation of the
|
||||||
|
HTTP ``OPTIONS`` response. This can be useful when working with
|
||||||
|
decorators that want to customize the ``OPTIONS`` response on a per-view
|
||||||
|
basis.
|
||||||
|
|
||||||
|
- `required_methods`: if this attribute is set, Flask will always add
|
||||||
|
these methods when registering a URL rule even if the methods were
|
||||||
|
explicitly overridden in the ``route()`` call.
|
||||||
|
|
||||||
|
Full example::
|
||||||
|
|
||||||
|
def index():
|
||||||
|
if request.method == 'OPTIONS':
|
||||||
|
# custom options handling here
|
||||||
|
...
|
||||||
|
return 'Hello World!'
|
||||||
|
index.provide_automatic_options = False
|
||||||
|
index.methods = ['GET', 'OPTIONS']
|
||||||
|
|
||||||
|
app.add_url_rule('/', index)
|
||||||
|
|
||||||
|
.. versionadded:: 0.8
|
||||||
|
The `provide_automatic_options` functionality was added.
|
||||||
|
|
||||||
|
Command Line Interface
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
.. currentmodule:: flask.cli
|
||||||
|
|
||||||
|
.. autoclass:: FlaskGroup
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: AppGroup
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: ScriptInfo
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autofunction:: load_dotenv
|
||||||
|
|
||||||
|
.. autofunction:: with_appcontext
|
||||||
|
|
||||||
|
.. autofunction:: pass_script_info
|
||||||
|
|
||||||
|
Marks a function so that an instance of :class:`ScriptInfo` is passed
|
||||||
|
as first argument to the click callback.
|
||||||
|
|
||||||
|
.. autodata:: run_command
|
||||||
|
|
||||||
|
.. autodata:: shell_command
|
||||||
147
_build/_sources/appcontext.rst.txt
Normal file
147
_build/_sources/appcontext.rst.txt
Normal file
|
|
@ -0,0 +1,147 @@
|
||||||
|
.. currentmodule:: flask
|
||||||
|
|
||||||
|
The Application Context
|
||||||
|
=======================
|
||||||
|
|
||||||
|
The application context keeps track of the application-level data during
|
||||||
|
a request, CLI command, or other activity. Rather than passing the
|
||||||
|
application around to each function, the :data:`current_app` and
|
||||||
|
:data:`g` proxies are accessed instead.
|
||||||
|
|
||||||
|
This is similar to :doc:`/reqcontext`, which keeps track of
|
||||||
|
request-level data during a request. A corresponding application context
|
||||||
|
is pushed when a request context is pushed.
|
||||||
|
|
||||||
|
Purpose of the Context
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
The :class:`Flask` application object has attributes, such as
|
||||||
|
:attr:`~Flask.config`, that are useful to access within views and
|
||||||
|
:doc:`CLI commands </cli>`. However, importing the ``app`` instance
|
||||||
|
within the modules in your project is prone to circular import issues.
|
||||||
|
When using the :doc:`app factory pattern </patterns/appfactories>` or
|
||||||
|
writing reusable :doc:`blueprints </blueprints>` or
|
||||||
|
:doc:`extensions </extensions>` there won't be an ``app`` instance to
|
||||||
|
import at all.
|
||||||
|
|
||||||
|
Flask solves this issue with the *application context*. Rather than
|
||||||
|
referring to an ``app`` directly, you use the :data:`current_app`
|
||||||
|
proxy, which points to the application handling the current activity.
|
||||||
|
|
||||||
|
Flask automatically *pushes* an application context when handling a
|
||||||
|
request. View functions, error handlers, and other functions that run
|
||||||
|
during a request will have access to :data:`current_app`.
|
||||||
|
|
||||||
|
Flask will also automatically push an app context when running CLI
|
||||||
|
commands registered with :attr:`Flask.cli` using ``@app.cli.command()``.
|
||||||
|
|
||||||
|
|
||||||
|
Lifetime of the Context
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
The application context is created and destroyed as necessary. When a
|
||||||
|
Flask application begins handling a request, it pushes an application
|
||||||
|
context and a :doc:`request context </reqcontext>`. When the request
|
||||||
|
ends it pops the request context then the application context.
|
||||||
|
Typically, an application context will have the same lifetime as a
|
||||||
|
request.
|
||||||
|
|
||||||
|
See :doc:`/reqcontext` for more information about how the contexts work
|
||||||
|
and the full life cycle of a request.
|
||||||
|
|
||||||
|
|
||||||
|
Manually Push a Context
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
If you try to access :data:`current_app`, or anything that uses it,
|
||||||
|
outside an application context, you'll get this error message:
|
||||||
|
|
||||||
|
.. code-block:: pytb
|
||||||
|
|
||||||
|
RuntimeError: Working outside of application context.
|
||||||
|
|
||||||
|
This typically means that you attempted to use functionality that
|
||||||
|
needed to interface with the current application object in some way.
|
||||||
|
To solve this, set up an application context with app.app_context().
|
||||||
|
|
||||||
|
If you see that error while configuring your application, such as when
|
||||||
|
initializing an extension, you can push a context manually since you
|
||||||
|
have direct access to the ``app``. Use :meth:`~Flask.app_context` in a
|
||||||
|
``with`` block, and everything that runs in the block will have access
|
||||||
|
to :data:`current_app`. ::
|
||||||
|
|
||||||
|
def create_app():
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
with app.app_context():
|
||||||
|
init_db()
|
||||||
|
|
||||||
|
return app
|
||||||
|
|
||||||
|
If you see that error somewhere else in your code not related to
|
||||||
|
configuring the application, it most likely indicates that you should
|
||||||
|
move that code into a view function or CLI command.
|
||||||
|
|
||||||
|
|
||||||
|
Storing Data
|
||||||
|
------------
|
||||||
|
|
||||||
|
The application context is a good place to store common data during a
|
||||||
|
request or CLI command. Flask provides the :data:`g object <g>` for this
|
||||||
|
purpose. It is a simple namespace object that has the same lifetime as
|
||||||
|
an application context.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
The ``g`` name stands for "global", but that is referring to the
|
||||||
|
data being global *within a context*. The data on ``g`` is lost
|
||||||
|
after the context ends, and it is not an appropriate place to store
|
||||||
|
data between requests. Use the :data:`session` or a database to
|
||||||
|
store data across requests.
|
||||||
|
|
||||||
|
A common use for :data:`g` is to manage resources during a request.
|
||||||
|
|
||||||
|
1. ``get_X()`` creates resource ``X`` if it does not exist, caching it
|
||||||
|
as ``g.X``.
|
||||||
|
2. ``teardown_X()`` closes or otherwise deallocates the resource if it
|
||||||
|
exists. It is registered as a :meth:`~Flask.teardown_appcontext`
|
||||||
|
handler.
|
||||||
|
|
||||||
|
For example, you can manage a database connection using this pattern::
|
||||||
|
|
||||||
|
from flask import g
|
||||||
|
|
||||||
|
def get_db():
|
||||||
|
if 'db' not in g:
|
||||||
|
g.db = connect_to_database()
|
||||||
|
|
||||||
|
return g.db
|
||||||
|
|
||||||
|
@app.teardown_appcontext
|
||||||
|
def teardown_db(exception):
|
||||||
|
db = g.pop('db', None)
|
||||||
|
|
||||||
|
if db is not None:
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
During a request, every call to ``get_db()`` will return the same
|
||||||
|
connection, and it will be closed automatically at the end of the
|
||||||
|
request.
|
||||||
|
|
||||||
|
You can use :class:`~werkzeug.local.LocalProxy` to make a new context
|
||||||
|
local from ``get_db()``::
|
||||||
|
|
||||||
|
from werkzeug.local import LocalProxy
|
||||||
|
db = LocalProxy(get_db)
|
||||||
|
|
||||||
|
Accessing ``db`` will call ``get_db`` internally, in the same way that
|
||||||
|
:data:`current_app` works.
|
||||||
|
|
||||||
|
|
||||||
|
Events and Signals
|
||||||
|
------------------
|
||||||
|
|
||||||
|
The application will call functions registered with :meth:`~Flask.teardown_appcontext`
|
||||||
|
when the application context is popped.
|
||||||
|
|
||||||
|
The following signals are sent: :data:`appcontext_pushed`,
|
||||||
|
:data:`appcontext_tearing_down`, and :data:`appcontext_popped`.
|
||||||
125
_build/_sources/async-await.rst.txt
Normal file
125
_build/_sources/async-await.rst.txt
Normal file
|
|
@ -0,0 +1,125 @@
|
||||||
|
.. _async_await:
|
||||||
|
|
||||||
|
Using ``async`` and ``await``
|
||||||
|
=============================
|
||||||
|
|
||||||
|
.. versionadded:: 2.0
|
||||||
|
|
||||||
|
Routes, error handlers, before request, after request, and teardown
|
||||||
|
functions can all be coroutine functions if Flask is installed with the
|
||||||
|
``async`` extra (``pip install flask[async]``). This allows views to be
|
||||||
|
defined with ``async def`` and use ``await``.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
@app.route("/get-data")
|
||||||
|
async def get_data():
|
||||||
|
data = await async_db_query(...)
|
||||||
|
return jsonify(data)
|
||||||
|
|
||||||
|
Pluggable class-based views also support handlers that are implemented as
|
||||||
|
coroutines. This applies to the :meth:`~flask.views.View.dispatch_request`
|
||||||
|
method in views that inherit from the :class:`flask.views.View` class, as
|
||||||
|
well as all the HTTP method handlers in views that inherit from the
|
||||||
|
:class:`flask.views.MethodView` class.
|
||||||
|
|
||||||
|
.. admonition:: Using ``async`` with greenlet
|
||||||
|
|
||||||
|
When using gevent or eventlet to serve an application or patch the
|
||||||
|
runtime, greenlet>=1.0 is required. When using PyPy, PyPy>=7.3.7 is
|
||||||
|
required.
|
||||||
|
|
||||||
|
|
||||||
|
Performance
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Async functions require an event loop to run. Flask, as a WSGI
|
||||||
|
application, uses one worker to handle one request/response cycle.
|
||||||
|
When a request comes in to an async view, Flask will start an event loop
|
||||||
|
in a thread, run the view function there, then return the result.
|
||||||
|
|
||||||
|
Each request still ties up one worker, even for async views. The upside
|
||||||
|
is that you can run async code within a view, for example to make
|
||||||
|
multiple concurrent database queries, HTTP requests to an external API,
|
||||||
|
etc. However, the number of requests your application can handle at one
|
||||||
|
time will remain the same.
|
||||||
|
|
||||||
|
**Async is not inherently faster than sync code.** Async is beneficial
|
||||||
|
when performing concurrent IO-bound tasks, but will probably not improve
|
||||||
|
CPU-bound tasks. Traditional Flask views will still be appropriate for
|
||||||
|
most use cases, but Flask's async support enables writing and using
|
||||||
|
code that wasn't possible natively before.
|
||||||
|
|
||||||
|
|
||||||
|
Background tasks
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Async functions will run in an event loop until they complete, at
|
||||||
|
which stage the event loop will stop. This means any additional
|
||||||
|
spawned tasks that haven't completed when the async function completes
|
||||||
|
will be cancelled. Therefore you cannot spawn background tasks, for
|
||||||
|
example via ``asyncio.create_task``.
|
||||||
|
|
||||||
|
If you wish to use background tasks it is best to use a task queue to
|
||||||
|
trigger background work, rather than spawn tasks in a view
|
||||||
|
function. With that in mind you can spawn asyncio tasks by serving
|
||||||
|
Flask with an ASGI server and utilising the asgiref WsgiToAsgi adapter
|
||||||
|
as described in :doc:`deploying/asgi`. This works as the adapter creates
|
||||||
|
an event loop that runs continually.
|
||||||
|
|
||||||
|
|
||||||
|
When to use Quart instead
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
Flask's async support is less performant than async-first frameworks due
|
||||||
|
to the way it is implemented. If you have a mainly async codebase it
|
||||||
|
would make sense to consider `Quart`_. Quart is a reimplementation of
|
||||||
|
Flask based on the `ASGI`_ standard instead of WSGI. This allows it to
|
||||||
|
handle many concurrent requests, long running requests, and websockets
|
||||||
|
without requiring multiple worker processes or threads.
|
||||||
|
|
||||||
|
It has also already been possible to run Flask with Gevent or Eventlet
|
||||||
|
to get many of the benefits of async request handling. These libraries
|
||||||
|
patch low-level Python functions to accomplish this, whereas ``async``/
|
||||||
|
``await`` and ASGI use standard, modern Python capabilities. Deciding
|
||||||
|
whether you should use Flask, Quart, or something else is ultimately up
|
||||||
|
to understanding the specific needs of your project.
|
||||||
|
|
||||||
|
.. _Quart: https://github.com/pallets/quart
|
||||||
|
.. _ASGI: https://asgi.readthedocs.io/en/latest/
|
||||||
|
|
||||||
|
|
||||||
|
Extensions
|
||||||
|
----------
|
||||||
|
|
||||||
|
Flask extensions predating Flask's async support do not expect async views.
|
||||||
|
If they provide decorators to add functionality to views, those will probably
|
||||||
|
not work with async views because they will not await the function or be
|
||||||
|
awaitable. Other functions they provide will not be awaitable either and
|
||||||
|
will probably be blocking if called within an async view.
|
||||||
|
|
||||||
|
Extension authors can support async functions by utilising the
|
||||||
|
:meth:`flask.Flask.ensure_sync` method. For example, if the extension
|
||||||
|
provides a view function decorator add ``ensure_sync`` before calling
|
||||||
|
the decorated function,
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
def extension(func):
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
... # Extension logic
|
||||||
|
return current_app.ensure_sync(func)(*args, **kwargs)
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
Check the changelog of the extension you want to use to see if they've
|
||||||
|
implemented async support, or make a feature request or PR to them.
|
||||||
|
|
||||||
|
|
||||||
|
Other event loops
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
At the moment Flask only supports :mod:`asyncio`. It's possible to
|
||||||
|
override :meth:`flask.Flask.ensure_sync` to change how async functions
|
||||||
|
are wrapped to use a different library.
|
||||||
315
_build/_sources/blueprints.rst.txt
Normal file
315
_build/_sources/blueprints.rst.txt
Normal file
|
|
@ -0,0 +1,315 @@
|
||||||
|
Modular Applications with Blueprints
|
||||||
|
====================================
|
||||||
|
|
||||||
|
.. currentmodule:: flask
|
||||||
|
|
||||||
|
.. versionadded:: 0.7
|
||||||
|
|
||||||
|
Flask uses a concept of *blueprints* for making application components and
|
||||||
|
supporting common patterns within an application or across applications.
|
||||||
|
Blueprints can greatly simplify how large applications work and provide a
|
||||||
|
central means for Flask extensions to register operations on applications.
|
||||||
|
A :class:`Blueprint` object works similarly to a :class:`Flask`
|
||||||
|
application object, but it is not actually an application. Rather it is a
|
||||||
|
*blueprint* of how to construct or extend an application.
|
||||||
|
|
||||||
|
Why Blueprints?
|
||||||
|
---------------
|
||||||
|
|
||||||
|
Blueprints in Flask are intended for these cases:
|
||||||
|
|
||||||
|
* Factor an application into a set of blueprints. This is ideal for
|
||||||
|
larger applications; a project could instantiate an application object,
|
||||||
|
initialize several extensions, and register a collection of blueprints.
|
||||||
|
* Register a blueprint on an application at a URL prefix and/or subdomain.
|
||||||
|
Parameters in the URL prefix/subdomain become common view arguments
|
||||||
|
(with defaults) across all view functions in the blueprint.
|
||||||
|
* Register a blueprint multiple times on an application with different URL
|
||||||
|
rules.
|
||||||
|
* Provide template filters, static files, templates, and other utilities
|
||||||
|
through blueprints. A blueprint does not have to implement applications
|
||||||
|
or view functions.
|
||||||
|
* Register a blueprint on an application for any of these cases when
|
||||||
|
initializing a Flask extension.
|
||||||
|
|
||||||
|
A blueprint in Flask is not a pluggable app because it is not actually an
|
||||||
|
application -- it's a set of operations which can be registered on an
|
||||||
|
application, even multiple times. Why not have multiple application
|
||||||
|
objects? You can do that (see :doc:`/patterns/appdispatch`), but your
|
||||||
|
applications will have separate configs and will be managed at the WSGI
|
||||||
|
layer.
|
||||||
|
|
||||||
|
Blueprints instead provide separation at the Flask level, share
|
||||||
|
application config, and can change an application object as necessary with
|
||||||
|
being registered. The downside is that you cannot unregister a blueprint
|
||||||
|
once an application was created without having to destroy the whole
|
||||||
|
application object.
|
||||||
|
|
||||||
|
The Concept of Blueprints
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
The basic concept of blueprints is that they record operations to execute
|
||||||
|
when registered on an application. Flask associates view functions with
|
||||||
|
blueprints when dispatching requests and generating URLs from one endpoint
|
||||||
|
to another.
|
||||||
|
|
||||||
|
My First Blueprint
|
||||||
|
------------------
|
||||||
|
|
||||||
|
This is what a very basic blueprint looks like. In this case we want to
|
||||||
|
implement a blueprint that does simple rendering of static templates::
|
||||||
|
|
||||||
|
from flask import Blueprint, render_template, abort
|
||||||
|
from jinja2 import TemplateNotFound
|
||||||
|
|
||||||
|
simple_page = Blueprint('simple_page', __name__,
|
||||||
|
template_folder='templates')
|
||||||
|
|
||||||
|
@simple_page.route('/', defaults={'page': 'index'})
|
||||||
|
@simple_page.route('/<page>')
|
||||||
|
def show(page):
|
||||||
|
try:
|
||||||
|
return render_template(f'pages/{page}.html')
|
||||||
|
except TemplateNotFound:
|
||||||
|
abort(404)
|
||||||
|
|
||||||
|
When you bind a function with the help of the ``@simple_page.route``
|
||||||
|
decorator, the blueprint will record the intention of registering the
|
||||||
|
function ``show`` on the application when it's later registered.
|
||||||
|
Additionally it will prefix the endpoint of the function with the
|
||||||
|
name of the blueprint which was given to the :class:`Blueprint`
|
||||||
|
constructor (in this case also ``simple_page``). The blueprint's name
|
||||||
|
does not modify the URL, only the endpoint.
|
||||||
|
|
||||||
|
Registering Blueprints
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
So how do you register that blueprint? Like this::
|
||||||
|
|
||||||
|
from flask import Flask
|
||||||
|
from yourapplication.simple_page import simple_page
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
app.register_blueprint(simple_page)
|
||||||
|
|
||||||
|
If you check the rules registered on the application, you will find
|
||||||
|
these::
|
||||||
|
|
||||||
|
>>> app.url_map
|
||||||
|
Map([<Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>,
|
||||||
|
<Rule '/<page>' (HEAD, OPTIONS, GET) -> simple_page.show>,
|
||||||
|
<Rule '/' (HEAD, OPTIONS, GET) -> simple_page.show>])
|
||||||
|
|
||||||
|
The first one is obviously from the application itself for the static
|
||||||
|
files. The other two are for the `show` function of the ``simple_page``
|
||||||
|
blueprint. As you can see, they are also prefixed with the name of the
|
||||||
|
blueprint and separated by a dot (``.``).
|
||||||
|
|
||||||
|
Blueprints however can also be mounted at different locations::
|
||||||
|
|
||||||
|
app.register_blueprint(simple_page, url_prefix='/pages')
|
||||||
|
|
||||||
|
And sure enough, these are the generated rules::
|
||||||
|
|
||||||
|
>>> app.url_map
|
||||||
|
Map([<Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>,
|
||||||
|
<Rule '/pages/<page>' (HEAD, OPTIONS, GET) -> simple_page.show>,
|
||||||
|
<Rule '/pages/' (HEAD, OPTIONS, GET) -> simple_page.show>])
|
||||||
|
|
||||||
|
On top of that you can register blueprints multiple times though not every
|
||||||
|
blueprint might respond properly to that. In fact it depends on how the
|
||||||
|
blueprint is implemented if it can be mounted more than once.
|
||||||
|
|
||||||
|
Nesting Blueprints
|
||||||
|
------------------
|
||||||
|
|
||||||
|
It is possible to register a blueprint on another blueprint.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
parent = Blueprint('parent', __name__, url_prefix='/parent')
|
||||||
|
child = Blueprint('child', __name__, url_prefix='/child')
|
||||||
|
parent.register_blueprint(child)
|
||||||
|
app.register_blueprint(parent)
|
||||||
|
|
||||||
|
The child blueprint will gain the parent's name as a prefix to its
|
||||||
|
name, and child URLs will be prefixed with the parent's URL prefix.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
url_for('parent.child.create')
|
||||||
|
/parent/child/create
|
||||||
|
|
||||||
|
In addition a child blueprint's will gain their parent's subdomain,
|
||||||
|
with their subdomain as prefix if present i.e.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
parent = Blueprint('parent', __name__, subdomain='parent')
|
||||||
|
child = Blueprint('child', __name__, subdomain='child')
|
||||||
|
parent.register_blueprint(child)
|
||||||
|
app.register_blueprint(parent)
|
||||||
|
|
||||||
|
url_for('parent.child.create', _external=True)
|
||||||
|
"child.parent.domain.tld"
|
||||||
|
|
||||||
|
Blueprint-specific before request functions, etc. registered with the
|
||||||
|
parent will trigger for the child. If a child does not have an error
|
||||||
|
handler that can handle a given exception, the parent's will be tried.
|
||||||
|
|
||||||
|
|
||||||
|
Blueprint Resources
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
Blueprints can provide resources as well. Sometimes you might want to
|
||||||
|
introduce a blueprint only for the resources it provides.
|
||||||
|
|
||||||
|
Blueprint Resource Folder
|
||||||
|
`````````````````````````
|
||||||
|
|
||||||
|
Like for regular applications, blueprints are considered to be contained
|
||||||
|
in a folder. While multiple blueprints can originate from the same folder,
|
||||||
|
it does not have to be the case and it's usually not recommended.
|
||||||
|
|
||||||
|
The folder is inferred from the second argument to :class:`Blueprint` which
|
||||||
|
is usually `__name__`. This argument specifies what logical Python
|
||||||
|
module or package corresponds to the blueprint. If it points to an actual
|
||||||
|
Python package that package (which is a folder on the filesystem) is the
|
||||||
|
resource folder. If it's a module, the package the module is contained in
|
||||||
|
will be the resource folder. You can access the
|
||||||
|
:attr:`Blueprint.root_path` property to see what the resource folder is::
|
||||||
|
|
||||||
|
>>> simple_page.root_path
|
||||||
|
'/Users/username/TestProject/yourapplication'
|
||||||
|
|
||||||
|
To quickly open sources from this folder you can use the
|
||||||
|
:meth:`~Blueprint.open_resource` function::
|
||||||
|
|
||||||
|
with simple_page.open_resource('static/style.css') as f:
|
||||||
|
code = f.read()
|
||||||
|
|
||||||
|
Static Files
|
||||||
|
````````````
|
||||||
|
|
||||||
|
A blueprint can expose a folder with static files by providing the path
|
||||||
|
to the folder on the filesystem with the ``static_folder`` argument.
|
||||||
|
It is either an absolute path or relative to the blueprint's location::
|
||||||
|
|
||||||
|
admin = Blueprint('admin', __name__, static_folder='static')
|
||||||
|
|
||||||
|
By default the rightmost part of the path is where it is exposed on the
|
||||||
|
web. This can be changed with the ``static_url_path`` argument. Because the
|
||||||
|
folder is called ``static`` here it will be available at the
|
||||||
|
``url_prefix`` of the blueprint + ``/static``. If the blueprint
|
||||||
|
has the prefix ``/admin``, the static URL will be ``/admin/static``.
|
||||||
|
|
||||||
|
The endpoint is named ``blueprint_name.static``. You can generate URLs
|
||||||
|
to it with :func:`url_for` like you would with the static folder of the
|
||||||
|
application::
|
||||||
|
|
||||||
|
url_for('admin.static', filename='style.css')
|
||||||
|
|
||||||
|
However, if the blueprint does not have a ``url_prefix``, it is not
|
||||||
|
possible to access the blueprint's static folder. This is because the
|
||||||
|
URL would be ``/static`` in this case, and the application's ``/static``
|
||||||
|
route takes precedence. Unlike template folders, blueprint static
|
||||||
|
folders are not searched if the file does not exist in the application
|
||||||
|
static folder.
|
||||||
|
|
||||||
|
Templates
|
||||||
|
`````````
|
||||||
|
|
||||||
|
If you want the blueprint to expose templates you can do that by providing
|
||||||
|
the `template_folder` parameter to the :class:`Blueprint` constructor::
|
||||||
|
|
||||||
|
admin = Blueprint('admin', __name__, template_folder='templates')
|
||||||
|
|
||||||
|
For static files, the path can be absolute or relative to the blueprint
|
||||||
|
resource folder.
|
||||||
|
|
||||||
|
The template folder is added to the search path of templates but with a lower
|
||||||
|
priority than the actual application's template folder. That way you can
|
||||||
|
easily override templates that a blueprint provides in the actual application.
|
||||||
|
This also means that if you don't want a blueprint template to be accidentally
|
||||||
|
overridden, make sure that no other blueprint or actual application template
|
||||||
|
has the same relative path. When multiple blueprints provide the same relative
|
||||||
|
template path the first blueprint registered takes precedence over the others.
|
||||||
|
|
||||||
|
|
||||||
|
So if you have a blueprint in the folder ``yourapplication/admin`` and you
|
||||||
|
want to render the template ``'admin/index.html'`` and you have provided
|
||||||
|
``templates`` as a `template_folder` you will have to create a file like
|
||||||
|
this: :file:`yourapplication/admin/templates/admin/index.html`. The reason
|
||||||
|
for the extra ``admin`` folder is to avoid getting our template overridden
|
||||||
|
by a template named ``index.html`` in the actual application template
|
||||||
|
folder.
|
||||||
|
|
||||||
|
To further reiterate this: if you have a blueprint named ``admin`` and you
|
||||||
|
want to render a template called :file:`index.html` which is specific to this
|
||||||
|
blueprint, the best idea is to lay out your templates like this::
|
||||||
|
|
||||||
|
yourpackage/
|
||||||
|
blueprints/
|
||||||
|
admin/
|
||||||
|
templates/
|
||||||
|
admin/
|
||||||
|
index.html
|
||||||
|
__init__.py
|
||||||
|
|
||||||
|
And then when you want to render the template, use :file:`admin/index.html` as
|
||||||
|
the name to look up the template by. If you encounter problems loading
|
||||||
|
the correct templates enable the ``EXPLAIN_TEMPLATE_LOADING`` config
|
||||||
|
variable which will instruct Flask to print out the steps it goes through
|
||||||
|
to locate templates on every ``render_template`` call.
|
||||||
|
|
||||||
|
Building URLs
|
||||||
|
-------------
|
||||||
|
|
||||||
|
If you want to link from one page to another you can use the
|
||||||
|
:func:`url_for` function just like you normally would do just that you
|
||||||
|
prefix the URL endpoint with the name of the blueprint and a dot (``.``)::
|
||||||
|
|
||||||
|
url_for('admin.index')
|
||||||
|
|
||||||
|
Additionally if you are in a view function of a blueprint or a rendered
|
||||||
|
template and you want to link to another endpoint of the same blueprint,
|
||||||
|
you can use relative redirects by prefixing the endpoint with a dot only::
|
||||||
|
|
||||||
|
url_for('.index')
|
||||||
|
|
||||||
|
This will link to ``admin.index`` for instance in case the current request
|
||||||
|
was dispatched to any other admin blueprint endpoint.
|
||||||
|
|
||||||
|
|
||||||
|
Blueprint Error Handlers
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
Blueprints support the ``errorhandler`` decorator just like the :class:`Flask`
|
||||||
|
application object, so it is easy to make Blueprint-specific custom error
|
||||||
|
pages.
|
||||||
|
|
||||||
|
Here is an example for a "404 Page Not Found" exception::
|
||||||
|
|
||||||
|
@simple_page.errorhandler(404)
|
||||||
|
def page_not_found(e):
|
||||||
|
return render_template('pages/404.html')
|
||||||
|
|
||||||
|
Most errorhandlers will simply work as expected; however, there is a caveat
|
||||||
|
concerning handlers for 404 and 405 exceptions. These errorhandlers are only
|
||||||
|
invoked from an appropriate ``raise`` statement or a call to ``abort`` in another
|
||||||
|
of the blueprint's view functions; they are not invoked by, e.g., an invalid URL
|
||||||
|
access. This is because the blueprint does not "own" a certain URL space, so
|
||||||
|
the application instance has no way of knowing which blueprint error handler it
|
||||||
|
should run if given an invalid URL. If you would like to execute different
|
||||||
|
handling strategies for these errors based on URL prefixes, they may be defined
|
||||||
|
at the application level using the ``request`` proxy object::
|
||||||
|
|
||||||
|
@app.errorhandler(404)
|
||||||
|
@app.errorhandler(405)
|
||||||
|
def _handle_api_error(ex):
|
||||||
|
if request.path.startswith('/api/'):
|
||||||
|
return jsonify(error=str(ex)), ex.code
|
||||||
|
else:
|
||||||
|
return ex
|
||||||
|
|
||||||
|
See :doc:`/errorhandling`.
|
||||||
4
_build/_sources/changes.rst.txt
Normal file
4
_build/_sources/changes.rst.txt
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
Changes
|
||||||
|
=======
|
||||||
|
|
||||||
|
.. include:: ../CHANGES.rst
|
||||||
556
_build/_sources/cli.rst.txt
Normal file
556
_build/_sources/cli.rst.txt
Normal file
|
|
@ -0,0 +1,556 @@
|
||||||
|
.. currentmodule:: flask
|
||||||
|
|
||||||
|
Command Line Interface
|
||||||
|
======================
|
||||||
|
|
||||||
|
Installing Flask installs the ``flask`` script, a `Click`_ command line
|
||||||
|
interface, in your virtualenv. Executed from the terminal, this script gives
|
||||||
|
access to built-in, extension, and application-defined commands. The ``--help``
|
||||||
|
option will give more information about any commands and options.
|
||||||
|
|
||||||
|
.. _Click: https://click.palletsprojects.com/
|
||||||
|
|
||||||
|
|
||||||
|
Application Discovery
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
The ``flask`` command is installed by Flask, not your application; it must be
|
||||||
|
told where to find your application in order to use it. The ``--app``
|
||||||
|
option is used to specify how to load the application.
|
||||||
|
|
||||||
|
While ``--app`` supports a variety of options for specifying your
|
||||||
|
application, most use cases should be simple. Here are the typical values:
|
||||||
|
|
||||||
|
(nothing)
|
||||||
|
The name "app" or "wsgi" is imported (as a ".py" file, or package),
|
||||||
|
automatically detecting an app (``app`` or ``application``) or
|
||||||
|
factory (``create_app`` or ``make_app``).
|
||||||
|
|
||||||
|
``--app hello``
|
||||||
|
The given name is imported, automatically detecting an app (``app``
|
||||||
|
or ``application``) or factory (``create_app`` or ``make_app``).
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
``--app`` has three parts: an optional path that sets the current working
|
||||||
|
directory, a Python file or dotted import path, and an optional variable
|
||||||
|
name of the instance or factory. If the name is a factory, it can optionally
|
||||||
|
be followed by arguments in parentheses. The following values demonstrate these
|
||||||
|
parts:
|
||||||
|
|
||||||
|
``--app src/hello``
|
||||||
|
Sets the current working directory to ``src`` then imports ``hello``.
|
||||||
|
|
||||||
|
``--app hello.web``
|
||||||
|
Imports the path ``hello.web``.
|
||||||
|
|
||||||
|
``--app hello:app2``
|
||||||
|
Uses the ``app2`` Flask instance in ``hello``.
|
||||||
|
|
||||||
|
``--app 'hello:create_app("dev")'``
|
||||||
|
The ``create_app`` factory in ``hello`` is called with the string ``'dev'``
|
||||||
|
as the argument.
|
||||||
|
|
||||||
|
If ``--app`` is not set, the command will try to import "app" or
|
||||||
|
"wsgi" (as a ".py" file, or package) and try to detect an application
|
||||||
|
instance or factory.
|
||||||
|
|
||||||
|
Within the given import, the command looks for an application instance named
|
||||||
|
``app`` or ``application``, then any application instance. If no instance is
|
||||||
|
found, the command looks for a factory function named ``create_app`` or
|
||||||
|
``make_app`` that returns an instance.
|
||||||
|
|
||||||
|
If parentheses follow the factory name, their contents are parsed as
|
||||||
|
Python literals and passed as arguments and keyword arguments to the
|
||||||
|
function. This means that strings must still be in quotes.
|
||||||
|
|
||||||
|
|
||||||
|
Run the Development Server
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
The :func:`run <cli.run_command>` command will start the development server. It
|
||||||
|
replaces the :meth:`Flask.run` method in most cases. ::
|
||||||
|
|
||||||
|
$ flask --app hello run
|
||||||
|
* Serving Flask app "hello"
|
||||||
|
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
|
||||||
|
|
||||||
|
.. warning:: Do not use this command to run your application in production.
|
||||||
|
Only use the development server during development. The development server
|
||||||
|
is provided for convenience, but is not designed to be particularly secure,
|
||||||
|
stable, or efficient. See :doc:`/deploying/index` for how to run in production.
|
||||||
|
|
||||||
|
If another program is already using port 5000, you'll see
|
||||||
|
``OSError: [Errno 98]`` or ``OSError: [WinError 10013]`` when the
|
||||||
|
server tries to start. See :ref:`address-already-in-use` for how to
|
||||||
|
handle that.
|
||||||
|
|
||||||
|
|
||||||
|
Debug Mode
|
||||||
|
~~~~~~~~~~
|
||||||
|
|
||||||
|
In debug mode, the ``flask run`` command will enable the interactive debugger and the
|
||||||
|
reloader by default, and make errors easier to see and debug. To enable debug mode, use
|
||||||
|
the ``--debug`` option.
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ flask --app hello run --debug
|
||||||
|
* Serving Flask app "hello"
|
||||||
|
* Debug mode: on
|
||||||
|
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
|
||||||
|
* Restarting with inotify reloader
|
||||||
|
* Debugger is active!
|
||||||
|
* Debugger PIN: 223-456-919
|
||||||
|
|
||||||
|
The ``--debug`` option can also be passed to the top level ``flask`` command to enable
|
||||||
|
debug mode for any command. The following two ``run`` calls are equivalent.
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ flask --app hello --debug run
|
||||||
|
$ flask --app hello run --debug
|
||||||
|
|
||||||
|
|
||||||
|
Watch and Ignore Files with the Reloader
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
When using debug mode, the reloader will trigger whenever your Python code or imported
|
||||||
|
modules change. The reloader can watch additional files with the ``--extra-files``
|
||||||
|
option. Multiple paths are separated with ``:``, or ``;`` on Windows.
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
$ flask run --extra-files file1:dirA/file2:dirB/
|
||||||
|
* Running on http://127.0.0.1:8000/
|
||||||
|
* Detected change in '/path/to/file1', reloading
|
||||||
|
|
||||||
|
The reloader can also ignore files using :mod:`fnmatch` patterns with the
|
||||||
|
``--exclude-patterns`` option. Multiple patterns are separated with ``:``, or ``;`` on
|
||||||
|
Windows.
|
||||||
|
|
||||||
|
|
||||||
|
Open a Shell
|
||||||
|
------------
|
||||||
|
|
||||||
|
To explore the data in your application, you can start an interactive Python
|
||||||
|
shell with the :func:`shell <cli.shell_command>` command. An application
|
||||||
|
context will be active, and the app instance will be imported. ::
|
||||||
|
|
||||||
|
$ flask shell
|
||||||
|
Python 3.10.0 (default, Oct 27 2021, 06:59:51) [GCC 11.1.0] on linux
|
||||||
|
App: example [production]
|
||||||
|
Instance: /home/david/Projects/pallets/flask/instance
|
||||||
|
>>>
|
||||||
|
|
||||||
|
Use :meth:`~Flask.shell_context_processor` to add other automatic imports.
|
||||||
|
|
||||||
|
|
||||||
|
.. _dotenv:
|
||||||
|
|
||||||
|
Environment Variables From dotenv
|
||||||
|
---------------------------------
|
||||||
|
|
||||||
|
The ``flask`` command supports setting any option for any command with
|
||||||
|
environment variables. The variables are named like ``FLASK_OPTION`` or
|
||||||
|
``FLASK_COMMAND_OPTION``, for example ``FLASK_APP`` or
|
||||||
|
``FLASK_RUN_PORT``.
|
||||||
|
|
||||||
|
Rather than passing options every time you run a command, or environment
|
||||||
|
variables every time you open a new terminal, you can use Flask's dotenv
|
||||||
|
support to set environment variables automatically.
|
||||||
|
|
||||||
|
If `python-dotenv`_ is installed, running the ``flask`` command will set
|
||||||
|
environment variables defined in the files ``.env`` and ``.flaskenv``.
|
||||||
|
You can also specify an extra file to load with the ``--env-file``
|
||||||
|
option. Dotenv files can be used to avoid having to set ``--app`` or
|
||||||
|
``FLASK_APP`` manually, and to set configuration using environment
|
||||||
|
variables similar to how some deployment services work.
|
||||||
|
|
||||||
|
Variables set on the command line are used over those set in :file:`.env`,
|
||||||
|
which are used over those set in :file:`.flaskenv`. :file:`.flaskenv` should be
|
||||||
|
used for public variables, such as ``FLASK_APP``, while :file:`.env` should not
|
||||||
|
be committed to your repository so that it can set private variables.
|
||||||
|
|
||||||
|
Directories are scanned upwards from the directory you call ``flask``
|
||||||
|
from to locate the files.
|
||||||
|
|
||||||
|
The files are only loaded by the ``flask`` command or calling
|
||||||
|
:meth:`~Flask.run`. If you would like to load these files when running in
|
||||||
|
production, you should call :func:`~cli.load_dotenv` manually.
|
||||||
|
|
||||||
|
.. _python-dotenv: https://github.com/theskumar/python-dotenv#readme
|
||||||
|
|
||||||
|
|
||||||
|
Setting Command Options
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Click is configured to load default values for command options from
|
||||||
|
environment variables. The variables use the pattern
|
||||||
|
``FLASK_COMMAND_OPTION``. For example, to set the port for the run
|
||||||
|
command, instead of ``flask run --port 8000``:
|
||||||
|
|
||||||
|
.. tabs::
|
||||||
|
|
||||||
|
.. group-tab:: Bash
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
$ export FLASK_RUN_PORT=8000
|
||||||
|
$ flask run
|
||||||
|
* Running on http://127.0.0.1:8000/
|
||||||
|
|
||||||
|
.. group-tab:: Fish
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
$ set -x FLASK_RUN_PORT 8000
|
||||||
|
$ flask run
|
||||||
|
* Running on http://127.0.0.1:8000/
|
||||||
|
|
||||||
|
.. group-tab:: CMD
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
> set FLASK_RUN_PORT=8000
|
||||||
|
> flask run
|
||||||
|
* Running on http://127.0.0.1:8000/
|
||||||
|
|
||||||
|
.. group-tab:: Powershell
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
> $env:FLASK_RUN_PORT = 8000
|
||||||
|
> flask run
|
||||||
|
* Running on http://127.0.0.1:8000/
|
||||||
|
|
||||||
|
These can be added to the ``.flaskenv`` file just like ``FLASK_APP`` to
|
||||||
|
control default command options.
|
||||||
|
|
||||||
|
|
||||||
|
Disable dotenv
|
||||||
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The ``flask`` command will show a message if it detects dotenv files but
|
||||||
|
python-dotenv is not installed.
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
$ flask run
|
||||||
|
* Tip: There are .env files present. Do "pip install python-dotenv" to use them.
|
||||||
|
|
||||||
|
You can tell Flask not to load dotenv files even when python-dotenv is
|
||||||
|
installed by setting the ``FLASK_SKIP_DOTENV`` environment variable.
|
||||||
|
This can be useful if you want to load them manually, or if you're using
|
||||||
|
a project runner that loads them already. Keep in mind that the
|
||||||
|
environment variables must be set before the app loads or it won't
|
||||||
|
configure as expected.
|
||||||
|
|
||||||
|
.. tabs::
|
||||||
|
|
||||||
|
.. group-tab:: Bash
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
$ export FLASK_SKIP_DOTENV=1
|
||||||
|
$ flask run
|
||||||
|
|
||||||
|
.. group-tab:: Fish
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
$ set -x FLASK_SKIP_DOTENV 1
|
||||||
|
$ flask run
|
||||||
|
|
||||||
|
.. group-tab:: CMD
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
> set FLASK_SKIP_DOTENV=1
|
||||||
|
> flask run
|
||||||
|
|
||||||
|
.. group-tab:: Powershell
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
> $env:FLASK_SKIP_DOTENV = 1
|
||||||
|
> flask run
|
||||||
|
|
||||||
|
|
||||||
|
Environment Variables From virtualenv
|
||||||
|
-------------------------------------
|
||||||
|
|
||||||
|
If you do not want to install dotenv support, you can still set environment
|
||||||
|
variables by adding them to the end of the virtualenv's :file:`activate`
|
||||||
|
script. Activating the virtualenv will set the variables.
|
||||||
|
|
||||||
|
.. tabs::
|
||||||
|
|
||||||
|
.. group-tab:: Bash
|
||||||
|
|
||||||
|
Unix Bash, :file:`.venv/bin/activate`::
|
||||||
|
|
||||||
|
$ export FLASK_APP=hello
|
||||||
|
|
||||||
|
.. group-tab:: Fish
|
||||||
|
|
||||||
|
Fish, :file:`.venv/bin/activate.fish`::
|
||||||
|
|
||||||
|
$ set -x FLASK_APP hello
|
||||||
|
|
||||||
|
.. group-tab:: CMD
|
||||||
|
|
||||||
|
Windows CMD, :file:`.venv\\Scripts\\activate.bat`::
|
||||||
|
|
||||||
|
> set FLASK_APP=hello
|
||||||
|
|
||||||
|
.. group-tab:: Powershell
|
||||||
|
|
||||||
|
Windows Powershell, :file:`.venv\\Scripts\\activate.ps1`::
|
||||||
|
|
||||||
|
> $env:FLASK_APP = "hello"
|
||||||
|
|
||||||
|
It is preferred to use dotenv support over this, since :file:`.flaskenv` can be
|
||||||
|
committed to the repository so that it works automatically wherever the project
|
||||||
|
is checked out.
|
||||||
|
|
||||||
|
|
||||||
|
Custom Commands
|
||||||
|
---------------
|
||||||
|
|
||||||
|
The ``flask`` command is implemented using `Click`_. See that project's
|
||||||
|
documentation for full information about writing commands.
|
||||||
|
|
||||||
|
This example adds the command ``create-user`` that takes the argument
|
||||||
|
``name``. ::
|
||||||
|
|
||||||
|
import click
|
||||||
|
from flask import Flask
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
@app.cli.command("create-user")
|
||||||
|
@click.argument("name")
|
||||||
|
def create_user(name):
|
||||||
|
...
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
$ flask create-user admin
|
||||||
|
|
||||||
|
This example adds the same command, but as ``user create``, a command in a
|
||||||
|
group. This is useful if you want to organize multiple related commands. ::
|
||||||
|
|
||||||
|
import click
|
||||||
|
from flask import Flask
|
||||||
|
from flask.cli import AppGroup
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
user_cli = AppGroup('user')
|
||||||
|
|
||||||
|
@user_cli.command('create')
|
||||||
|
@click.argument('name')
|
||||||
|
def create_user(name):
|
||||||
|
...
|
||||||
|
|
||||||
|
app.cli.add_command(user_cli)
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
$ flask user create demo
|
||||||
|
|
||||||
|
See :ref:`testing-cli` for an overview of how to test your custom
|
||||||
|
commands.
|
||||||
|
|
||||||
|
|
||||||
|
Registering Commands with Blueprints
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
If your application uses blueprints, you can optionally register CLI
|
||||||
|
commands directly onto them. When your blueprint is registered onto your
|
||||||
|
application, the associated commands will be available to the ``flask``
|
||||||
|
command. By default, those commands will be nested in a group matching
|
||||||
|
the name of the blueprint.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from flask import Blueprint
|
||||||
|
|
||||||
|
bp = Blueprint('students', __name__)
|
||||||
|
|
||||||
|
@bp.cli.command('create')
|
||||||
|
@click.argument('name')
|
||||||
|
def create(name):
|
||||||
|
...
|
||||||
|
|
||||||
|
app.register_blueprint(bp)
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
$ flask students create alice
|
||||||
|
|
||||||
|
You can alter the group name by specifying the ``cli_group`` parameter
|
||||||
|
when creating the :class:`Blueprint` object, or later with
|
||||||
|
:meth:`app.register_blueprint(bp, cli_group='...') <Flask.register_blueprint>`.
|
||||||
|
The following are equivalent:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
bp = Blueprint('students', __name__, cli_group='other')
|
||||||
|
# or
|
||||||
|
app.register_blueprint(bp, cli_group='other')
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
$ flask other create alice
|
||||||
|
|
||||||
|
Specifying ``cli_group=None`` will remove the nesting and merge the
|
||||||
|
commands directly to the application's level:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
bp = Blueprint('students', __name__, cli_group=None)
|
||||||
|
# or
|
||||||
|
app.register_blueprint(bp, cli_group=None)
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
$ flask create alice
|
||||||
|
|
||||||
|
|
||||||
|
Application Context
|
||||||
|
~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Commands added using the Flask app's :attr:`~Flask.cli` or
|
||||||
|
:class:`~flask.cli.FlaskGroup` :meth:`~cli.AppGroup.command` decorator
|
||||||
|
will be executed with an application context pushed, so your custom
|
||||||
|
commands and parameters have access to the app and its configuration. The
|
||||||
|
:func:`~cli.with_appcontext` decorator can be used to get the same
|
||||||
|
behavior, but is not needed in most cases.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
import click
|
||||||
|
from flask.cli import with_appcontext
|
||||||
|
|
||||||
|
@click.command()
|
||||||
|
@with_appcontext
|
||||||
|
def do_work():
|
||||||
|
...
|
||||||
|
|
||||||
|
app.cli.add_command(do_work)
|
||||||
|
|
||||||
|
|
||||||
|
Plugins
|
||||||
|
-------
|
||||||
|
|
||||||
|
Flask will automatically load commands specified in the ``flask.commands``
|
||||||
|
`entry point`_. This is useful for extensions that want to add commands when
|
||||||
|
they are installed. Entry points are specified in :file:`pyproject.toml`:
|
||||||
|
|
||||||
|
.. code-block:: toml
|
||||||
|
|
||||||
|
[project.entry-points."flask.commands"]
|
||||||
|
my-command = "my_extension.commands:cli"
|
||||||
|
|
||||||
|
.. _entry point: https://packaging.python.org/tutorials/packaging-projects/#entry-points
|
||||||
|
|
||||||
|
Inside :file:`my_extension/commands.py` you can then export a Click
|
||||||
|
object::
|
||||||
|
|
||||||
|
import click
|
||||||
|
|
||||||
|
@click.command()
|
||||||
|
def cli():
|
||||||
|
...
|
||||||
|
|
||||||
|
Once that package is installed in the same virtualenv as your Flask project,
|
||||||
|
you can run ``flask my-command`` to invoke the command.
|
||||||
|
|
||||||
|
|
||||||
|
.. _custom-scripts:
|
||||||
|
|
||||||
|
Custom Scripts
|
||||||
|
--------------
|
||||||
|
|
||||||
|
When you are using the app factory pattern, it may be more convenient to define
|
||||||
|
your own Click script. Instead of using ``--app`` and letting Flask load
|
||||||
|
your application, you can create your own Click object and export it as a
|
||||||
|
`console script`_ entry point.
|
||||||
|
|
||||||
|
Create an instance of :class:`~cli.FlaskGroup` and pass it the factory::
|
||||||
|
|
||||||
|
import click
|
||||||
|
from flask import Flask
|
||||||
|
from flask.cli import FlaskGroup
|
||||||
|
|
||||||
|
def create_app():
|
||||||
|
app = Flask('wiki')
|
||||||
|
# other setup
|
||||||
|
return app
|
||||||
|
|
||||||
|
@click.group(cls=FlaskGroup, create_app=create_app)
|
||||||
|
def cli():
|
||||||
|
"""Management script for the Wiki application."""
|
||||||
|
|
||||||
|
Define the entry point in :file:`pyproject.toml`:
|
||||||
|
|
||||||
|
.. code-block:: toml
|
||||||
|
|
||||||
|
[project.scripts]
|
||||||
|
wiki = "wiki:cli"
|
||||||
|
|
||||||
|
Install the application in the virtualenv in editable mode and the custom
|
||||||
|
script is available. Note that you don't need to set ``--app``. ::
|
||||||
|
|
||||||
|
$ pip install -e .
|
||||||
|
$ wiki run
|
||||||
|
|
||||||
|
.. admonition:: Errors in Custom Scripts
|
||||||
|
|
||||||
|
When using a custom script, if you introduce an error in your
|
||||||
|
module-level code, the reloader will fail because it can no longer
|
||||||
|
load the entry point.
|
||||||
|
|
||||||
|
The ``flask`` command, being separate from your code, does not have
|
||||||
|
this issue and is recommended in most cases.
|
||||||
|
|
||||||
|
.. _console script: https://packaging.python.org/tutorials/packaging-projects/#console-scripts
|
||||||
|
|
||||||
|
|
||||||
|
PyCharm Integration
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
PyCharm Professional provides a special Flask run configuration to run the development
|
||||||
|
server. For the Community Edition, and for other commands besides ``run``, you need to
|
||||||
|
create a custom run configuration. These instructions should be similar for any other
|
||||||
|
IDE you use.
|
||||||
|
|
||||||
|
In PyCharm, with your project open, click on *Run* from the menu bar and go to *Edit
|
||||||
|
Configurations*. You'll see a screen similar to this:
|
||||||
|
|
||||||
|
.. image:: _static/pycharm-run-config.png
|
||||||
|
:align: center
|
||||||
|
:class: screenshot
|
||||||
|
:alt: Screenshot of PyCharm run configuration.
|
||||||
|
|
||||||
|
Once you create a configuration for the ``flask run``, you can copy and change it to
|
||||||
|
call any other command.
|
||||||
|
|
||||||
|
Click the *+ (Add New Configuration)* button and select *Python*. Give the configuration
|
||||||
|
a name such as "flask run".
|
||||||
|
|
||||||
|
Click the *Script path* dropdown and change it to *Module name*, then input ``flask``.
|
||||||
|
|
||||||
|
The *Parameters* field is set to the CLI command to execute along with any arguments.
|
||||||
|
This example uses ``--app hello run --debug``, which will run the development server in
|
||||||
|
debug mode. ``--app hello`` should be the import or file with your Flask app.
|
||||||
|
|
||||||
|
If you installed your project as a package in your virtualenv, you may uncheck the
|
||||||
|
*PYTHONPATH* options. This will more accurately match how you deploy later.
|
||||||
|
|
||||||
|
Click *OK* to save and close the configuration. Select the configuration in the main
|
||||||
|
PyCharm window and click the play button next to it to run the server.
|
||||||
|
|
||||||
|
Now that you have a configuration for ``flask run``, you can copy that configuration and
|
||||||
|
change the *Parameters* argument to run a different CLI command.
|
||||||
836
_build/_sources/config.rst.txt
Normal file
836
_build/_sources/config.rst.txt
Normal file
|
|
@ -0,0 +1,836 @@
|
||||||
|
Configuration Handling
|
||||||
|
======================
|
||||||
|
|
||||||
|
Applications need some kind of configuration. There are different settings
|
||||||
|
you might want to change depending on the application environment like
|
||||||
|
toggling the debug mode, setting the secret key, and other such
|
||||||
|
environment-specific things.
|
||||||
|
|
||||||
|
The way Flask is designed usually requires the configuration to be
|
||||||
|
available when the application starts up. You can hard code the
|
||||||
|
configuration in the code, which for many small applications is not
|
||||||
|
actually that bad, but there are better ways.
|
||||||
|
|
||||||
|
Independent of how you load your config, there is a config object
|
||||||
|
available which holds the loaded configuration values:
|
||||||
|
The :attr:`~flask.Flask.config` attribute of the :class:`~flask.Flask`
|
||||||
|
object. This is the place where Flask itself puts certain configuration
|
||||||
|
values and also where extensions can put their configuration values. But
|
||||||
|
this is also where you can have your own configuration.
|
||||||
|
|
||||||
|
|
||||||
|
Configuration Basics
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
The :attr:`~flask.Flask.config` is actually a subclass of a dictionary and
|
||||||
|
can be modified just like any dictionary::
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
app.config['TESTING'] = True
|
||||||
|
|
||||||
|
Certain configuration values are also forwarded to the
|
||||||
|
:attr:`~flask.Flask` object so you can read and write them from there::
|
||||||
|
|
||||||
|
app.testing = True
|
||||||
|
|
||||||
|
To update multiple keys at once you can use the :meth:`dict.update`
|
||||||
|
method::
|
||||||
|
|
||||||
|
app.config.update(
|
||||||
|
TESTING=True,
|
||||||
|
SECRET_KEY='192b9bdd22ab9ed4d12e236c78afcb9a393ec15f71bbf5dc987d54727823bcbf'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
Debug Mode
|
||||||
|
----------
|
||||||
|
|
||||||
|
The :data:`DEBUG` config value is special because it may behave inconsistently if
|
||||||
|
changed after the app has begun setting up. In order to set debug mode reliably, use the
|
||||||
|
``--debug`` option on the ``flask`` or ``flask run`` command. ``flask run`` will use the
|
||||||
|
interactive debugger and reloader by default in debug mode.
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
$ flask --app hello run --debug
|
||||||
|
|
||||||
|
Using the option is recommended. While it is possible to set :data:`DEBUG` in your
|
||||||
|
config or code, this is strongly discouraged. It can't be read early by the
|
||||||
|
``flask run`` command, and some systems or extensions may have already configured
|
||||||
|
themselves based on a previous value.
|
||||||
|
|
||||||
|
|
||||||
|
Builtin Configuration Values
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
The following configuration values are used internally by Flask:
|
||||||
|
|
||||||
|
.. py:data:: DEBUG
|
||||||
|
|
||||||
|
Whether debug mode is enabled. When using ``flask run`` to start the development
|
||||||
|
server, an interactive debugger will be shown for unhandled exceptions, and the
|
||||||
|
server will be reloaded when code changes. The :attr:`~flask.Flask.debug` attribute
|
||||||
|
maps to this config key. This is set with the ``FLASK_DEBUG`` environment variable.
|
||||||
|
It may not behave as expected if set in code.
|
||||||
|
|
||||||
|
**Do not enable debug mode when deploying in production.**
|
||||||
|
|
||||||
|
Default: ``False``
|
||||||
|
|
||||||
|
.. py:data:: TESTING
|
||||||
|
|
||||||
|
Enable testing mode. Exceptions are propagated rather than handled by the
|
||||||
|
the app's error handlers. Extensions may also change their behavior to
|
||||||
|
facilitate easier testing. You should enable this in your own tests.
|
||||||
|
|
||||||
|
Default: ``False``
|
||||||
|
|
||||||
|
.. py:data:: PROPAGATE_EXCEPTIONS
|
||||||
|
|
||||||
|
Exceptions are re-raised rather than being handled by the app's error
|
||||||
|
handlers. If not set, this is implicitly true if ``TESTING`` or ``DEBUG``
|
||||||
|
is enabled.
|
||||||
|
|
||||||
|
Default: ``None``
|
||||||
|
|
||||||
|
.. py:data:: TRAP_HTTP_EXCEPTIONS
|
||||||
|
|
||||||
|
If there is no handler for an ``HTTPException``-type exception, re-raise it
|
||||||
|
to be handled by the interactive debugger instead of returning it as a
|
||||||
|
simple error response.
|
||||||
|
|
||||||
|
Default: ``False``
|
||||||
|
|
||||||
|
.. py:data:: TRAP_BAD_REQUEST_ERRORS
|
||||||
|
|
||||||
|
Trying to access a key that doesn't exist from request dicts like ``args``
|
||||||
|
and ``form`` will return a 400 Bad Request error page. Enable this to treat
|
||||||
|
the error as an unhandled exception instead so that you get the interactive
|
||||||
|
debugger. This is a more specific version of ``TRAP_HTTP_EXCEPTIONS``. If
|
||||||
|
unset, it is enabled in debug mode.
|
||||||
|
|
||||||
|
Default: ``None``
|
||||||
|
|
||||||
|
.. py:data:: SECRET_KEY
|
||||||
|
|
||||||
|
A secret key that will be used for securely signing the session cookie
|
||||||
|
and can be used for any other security related needs by extensions or your
|
||||||
|
application. It should be a long random ``bytes`` or ``str``. For
|
||||||
|
example, copy the output of this to your config::
|
||||||
|
|
||||||
|
$ python -c 'import secrets; print(secrets.token_hex())'
|
||||||
|
'192b9bdd22ab9ed4d12e236c78afcb9a393ec15f71bbf5dc987d54727823bcbf'
|
||||||
|
|
||||||
|
**Do not reveal the secret key when posting questions or committing code.**
|
||||||
|
|
||||||
|
Default: ``None``
|
||||||
|
|
||||||
|
.. py:data:: SECRET_KEY_FALLBACKS
|
||||||
|
|
||||||
|
A list of old secret keys that can still be used for unsigning, most recent
|
||||||
|
first. This allows a project to implement key rotation without invalidating
|
||||||
|
active sessions or other recently-signed secrets.
|
||||||
|
|
||||||
|
Keys should be removed after an appropriate period of time, as checking each
|
||||||
|
additional key adds some overhead.
|
||||||
|
|
||||||
|
Flask's built-in secure cookie session supports this. Extensions that use
|
||||||
|
:data:`SECRET_KEY` may not support this yet.
|
||||||
|
|
||||||
|
Default: ``None``
|
||||||
|
|
||||||
|
.. versionadded:: 3.1
|
||||||
|
|
||||||
|
.. py:data:: SESSION_COOKIE_NAME
|
||||||
|
|
||||||
|
The name of the session cookie. Can be changed in case you already have a
|
||||||
|
cookie with the same name.
|
||||||
|
|
||||||
|
Default: ``'session'``
|
||||||
|
|
||||||
|
.. py:data:: SESSION_COOKIE_DOMAIN
|
||||||
|
|
||||||
|
The value of the ``Domain`` parameter on the session cookie. If not set, browsers
|
||||||
|
will only send the cookie to the exact domain it was set from. Otherwise, they
|
||||||
|
will send it to any subdomain of the given value as well.
|
||||||
|
|
||||||
|
Not setting this value is more restricted and secure than setting it.
|
||||||
|
|
||||||
|
Default: ``None``
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
If this is changed after the browser created a cookie is created with
|
||||||
|
one setting, it may result in another being created. Browsers may send
|
||||||
|
send both in an undefined order. In that case, you may want to change
|
||||||
|
:data:`SESSION_COOKIE_NAME` as well or otherwise invalidate old sessions.
|
||||||
|
|
||||||
|
.. versionchanged:: 2.3
|
||||||
|
Not set by default, does not fall back to ``SERVER_NAME``.
|
||||||
|
|
||||||
|
.. py:data:: SESSION_COOKIE_PATH
|
||||||
|
|
||||||
|
The path that the session cookie will be valid for. If not set, the cookie
|
||||||
|
will be valid underneath ``APPLICATION_ROOT`` or ``/`` if that is not set.
|
||||||
|
|
||||||
|
Default: ``None``
|
||||||
|
|
||||||
|
.. py:data:: SESSION_COOKIE_HTTPONLY
|
||||||
|
|
||||||
|
Browsers will not allow JavaScript access to cookies marked as "HTTP only"
|
||||||
|
for security.
|
||||||
|
|
||||||
|
Default: ``True``
|
||||||
|
|
||||||
|
.. py:data:: SESSION_COOKIE_SECURE
|
||||||
|
|
||||||
|
Browsers will only send cookies with requests over HTTPS if the cookie is
|
||||||
|
marked "secure". The application must be served over HTTPS for this to make
|
||||||
|
sense.
|
||||||
|
|
||||||
|
Default: ``False``
|
||||||
|
|
||||||
|
.. py:data:: SESSION_COOKIE_PARTITIONED
|
||||||
|
|
||||||
|
Browsers will send cookies based on the top-level document's domain, rather
|
||||||
|
than only the domain of the document setting the cookie. This prevents third
|
||||||
|
party cookies set in iframes from "leaking" between separate sites.
|
||||||
|
|
||||||
|
Browsers are beginning to disallow non-partitioned third party cookies, so
|
||||||
|
you need to mark your cookies partitioned if you expect them to work in such
|
||||||
|
embedded situations.
|
||||||
|
|
||||||
|
Enabling this implicitly enables :data:`SESSION_COOKIE_SECURE` as well, as
|
||||||
|
it is only valid when served over HTTPS.
|
||||||
|
|
||||||
|
Default: ``False``
|
||||||
|
|
||||||
|
.. versionadded:: 3.1
|
||||||
|
|
||||||
|
.. py:data:: SESSION_COOKIE_SAMESITE
|
||||||
|
|
||||||
|
Restrict how cookies are sent with requests from external sites. Can
|
||||||
|
be set to ``'Lax'`` (recommended) or ``'Strict'``.
|
||||||
|
See :ref:`security-cookie`.
|
||||||
|
|
||||||
|
Default: ``None``
|
||||||
|
|
||||||
|
.. versionadded:: 1.0
|
||||||
|
|
||||||
|
.. py:data:: PERMANENT_SESSION_LIFETIME
|
||||||
|
|
||||||
|
If ``session.permanent`` is true, the cookie's expiration will be set this
|
||||||
|
number of seconds in the future. Can either be a
|
||||||
|
:class:`datetime.timedelta` or an ``int``.
|
||||||
|
|
||||||
|
Flask's default cookie implementation validates that the cryptographic
|
||||||
|
signature is not older than this value.
|
||||||
|
|
||||||
|
Default: ``timedelta(days=31)`` (``2678400`` seconds)
|
||||||
|
|
||||||
|
.. py:data:: SESSION_REFRESH_EACH_REQUEST
|
||||||
|
|
||||||
|
Control whether the cookie is sent with every response when
|
||||||
|
``session.permanent`` is true. Sending the cookie every time (the default)
|
||||||
|
can more reliably keep the session from expiring, but uses more bandwidth.
|
||||||
|
Non-permanent sessions are not affected.
|
||||||
|
|
||||||
|
Default: ``True``
|
||||||
|
|
||||||
|
.. py:data:: USE_X_SENDFILE
|
||||||
|
|
||||||
|
When serving files, set the ``X-Sendfile`` header instead of serving the
|
||||||
|
data with Flask. Some web servers, such as Apache, recognize this and serve
|
||||||
|
the data more efficiently. This only makes sense when using such a server.
|
||||||
|
|
||||||
|
Default: ``False``
|
||||||
|
|
||||||
|
.. py:data:: SEND_FILE_MAX_AGE_DEFAULT
|
||||||
|
|
||||||
|
When serving files, set the cache control max age to this number of
|
||||||
|
seconds. Can be a :class:`datetime.timedelta` or an ``int``.
|
||||||
|
Override this value on a per-file basis using
|
||||||
|
:meth:`~flask.Flask.get_send_file_max_age` on the application or
|
||||||
|
blueprint.
|
||||||
|
|
||||||
|
If ``None``, ``send_file`` tells the browser to use conditional
|
||||||
|
requests will be used instead of a timed cache, which is usually
|
||||||
|
preferable.
|
||||||
|
|
||||||
|
Default: ``None``
|
||||||
|
|
||||||
|
.. py:data:: TRUSTED_HOSTS
|
||||||
|
|
||||||
|
Validate :attr:`.Request.host` and other attributes that use it against
|
||||||
|
these trusted values. Raise a :exc:`~werkzeug.exceptions.SecurityError` if
|
||||||
|
the host is invalid, which results in a 400 error. If it is ``None``, all
|
||||||
|
hosts are valid. Each value is either an exact match, or can start with
|
||||||
|
a dot ``.`` to match any subdomain.
|
||||||
|
|
||||||
|
Validation is done during routing against this value. ``before_request`` and
|
||||||
|
``after_request`` callbacks will still be called.
|
||||||
|
|
||||||
|
Default: ``None``
|
||||||
|
|
||||||
|
.. versionadded:: 3.1
|
||||||
|
|
||||||
|
.. py:data:: SERVER_NAME
|
||||||
|
|
||||||
|
Inform the application what host and port it is bound to.
|
||||||
|
|
||||||
|
Must be set if ``subdomain_matching`` is enabled, to be able to extract the
|
||||||
|
subdomain from the request.
|
||||||
|
|
||||||
|
Must be set for ``url_for`` to generate external URLs outside of a
|
||||||
|
request context.
|
||||||
|
|
||||||
|
Default: ``None``
|
||||||
|
|
||||||
|
.. versionchanged:: 3.1
|
||||||
|
Does not restrict requests to only this domain, for both
|
||||||
|
``subdomain_matching`` and ``host_matching``.
|
||||||
|
|
||||||
|
.. versionchanged:: 1.0
|
||||||
|
Does not implicitly enable ``subdomain_matching``.
|
||||||
|
|
||||||
|
.. versionchanged:: 2.3
|
||||||
|
Does not affect ``SESSION_COOKIE_DOMAIN``.
|
||||||
|
|
||||||
|
.. py:data:: APPLICATION_ROOT
|
||||||
|
|
||||||
|
Inform the application what path it is mounted under by the application /
|
||||||
|
web server. This is used for generating URLs outside the context of a
|
||||||
|
request (inside a request, the dispatcher is responsible for setting
|
||||||
|
``SCRIPT_NAME`` instead; see :doc:`/patterns/appdispatch`
|
||||||
|
for examples of dispatch configuration).
|
||||||
|
|
||||||
|
Will be used for the session cookie path if ``SESSION_COOKIE_PATH`` is not
|
||||||
|
set.
|
||||||
|
|
||||||
|
Default: ``'/'``
|
||||||
|
|
||||||
|
.. py:data:: PREFERRED_URL_SCHEME
|
||||||
|
|
||||||
|
Use this scheme for generating external URLs when not in a request context.
|
||||||
|
|
||||||
|
Default: ``'http'``
|
||||||
|
|
||||||
|
.. py:data:: MAX_CONTENT_LENGTH
|
||||||
|
|
||||||
|
The maximum number of bytes that will be read during this request. If
|
||||||
|
this limit is exceeded, a 413 :exc:`~werkzeug.exceptions.RequestEntityTooLarge`
|
||||||
|
error is raised. If it is set to ``None``, no limit is enforced at the
|
||||||
|
Flask application level. However, if it is ``None`` and the request has no
|
||||||
|
``Content-Length`` header and the WSGI server does not indicate that it
|
||||||
|
terminates the stream, then no data is read to avoid an infinite stream.
|
||||||
|
|
||||||
|
Each request defaults to this config. It can be set on a specific
|
||||||
|
:attr:`.Request.max_content_length` to apply the limit to that specific
|
||||||
|
view. This should be set appropriately based on an application's or view's
|
||||||
|
specific needs.
|
||||||
|
|
||||||
|
Default: ``None``
|
||||||
|
|
||||||
|
.. versionadded:: 0.6
|
||||||
|
|
||||||
|
.. py:data:: MAX_FORM_MEMORY_SIZE
|
||||||
|
|
||||||
|
The maximum size in bytes any non-file form field may be in a
|
||||||
|
``multipart/form-data`` body. If this limit is exceeded, a 413
|
||||||
|
:exc:`~werkzeug.exceptions.RequestEntityTooLarge` error is raised. If it is
|
||||||
|
set to ``None``, no limit is enforced at the Flask application level.
|
||||||
|
|
||||||
|
Each request defaults to this config. It can be set on a specific
|
||||||
|
:attr:`.Request.max_form_memory_parts` to apply the limit to that specific
|
||||||
|
view. This should be set appropriately based on an application's or view's
|
||||||
|
specific needs.
|
||||||
|
|
||||||
|
Default: ``500_000``
|
||||||
|
|
||||||
|
.. versionadded:: 3.1
|
||||||
|
|
||||||
|
.. py:data:: MAX_FORM_PARTS
|
||||||
|
|
||||||
|
The maximum number of fields that may be present in a
|
||||||
|
``multipart/form-data`` body. If this limit is exceeded, a 413
|
||||||
|
:exc:`~werkzeug.exceptions.RequestEntityTooLarge` error is raised. If it
|
||||||
|
is set to ``None``, no limit is enforced at the Flask application level.
|
||||||
|
|
||||||
|
Each request defaults to this config. It can be set on a specific
|
||||||
|
:attr:`.Request.max_form_parts` to apply the limit to that specific view.
|
||||||
|
This should be set appropriately based on an application's or view's
|
||||||
|
specific needs.
|
||||||
|
|
||||||
|
Default: ``1_000``
|
||||||
|
|
||||||
|
.. versionadded:: 3.1
|
||||||
|
|
||||||
|
.. py:data:: TEMPLATES_AUTO_RELOAD
|
||||||
|
|
||||||
|
Reload templates when they are changed. If not set, it will be enabled in
|
||||||
|
debug mode.
|
||||||
|
|
||||||
|
Default: ``None``
|
||||||
|
|
||||||
|
.. py:data:: EXPLAIN_TEMPLATE_LOADING
|
||||||
|
|
||||||
|
Log debugging information tracing how a template file was loaded. This can
|
||||||
|
be useful to figure out why a template was not loaded or the wrong file
|
||||||
|
appears to be loaded.
|
||||||
|
|
||||||
|
Default: ``False``
|
||||||
|
|
||||||
|
.. py:data:: MAX_COOKIE_SIZE
|
||||||
|
|
||||||
|
Warn if cookie headers are larger than this many bytes. Defaults to
|
||||||
|
``4093``. Larger cookies may be silently ignored by browsers. Set to
|
||||||
|
``0`` to disable the warning.
|
||||||
|
|
||||||
|
.. py:data:: PROVIDE_AUTOMATIC_OPTIONS
|
||||||
|
|
||||||
|
Set to ``False`` to disable the automatic addition of OPTIONS
|
||||||
|
responses. This can be overridden per route by altering the
|
||||||
|
``provide_automatic_options`` attribute.
|
||||||
|
|
||||||
|
.. versionadded:: 0.4
|
||||||
|
``LOGGER_NAME``
|
||||||
|
|
||||||
|
.. versionadded:: 0.5
|
||||||
|
``SERVER_NAME``
|
||||||
|
|
||||||
|
.. versionadded:: 0.6
|
||||||
|
``MAX_CONTENT_LENGTH``
|
||||||
|
|
||||||
|
.. versionadded:: 0.7
|
||||||
|
``PROPAGATE_EXCEPTIONS``, ``PRESERVE_CONTEXT_ON_EXCEPTION``
|
||||||
|
|
||||||
|
.. versionadded:: 0.8
|
||||||
|
``TRAP_BAD_REQUEST_ERRORS``, ``TRAP_HTTP_EXCEPTIONS``,
|
||||||
|
``APPLICATION_ROOT``, ``SESSION_COOKIE_DOMAIN``,
|
||||||
|
``SESSION_COOKIE_PATH``, ``SESSION_COOKIE_HTTPONLY``,
|
||||||
|
``SESSION_COOKIE_SECURE``
|
||||||
|
|
||||||
|
.. versionadded:: 0.9
|
||||||
|
``PREFERRED_URL_SCHEME``
|
||||||
|
|
||||||
|
.. versionadded:: 0.10
|
||||||
|
``JSON_AS_ASCII``, ``JSON_SORT_KEYS``, ``JSONIFY_PRETTYPRINT_REGULAR``
|
||||||
|
|
||||||
|
.. versionadded:: 0.11
|
||||||
|
``SESSION_REFRESH_EACH_REQUEST``, ``TEMPLATES_AUTO_RELOAD``,
|
||||||
|
``LOGGER_HANDLER_POLICY``, ``EXPLAIN_TEMPLATE_LOADING``
|
||||||
|
|
||||||
|
.. versionchanged:: 1.0
|
||||||
|
``LOGGER_NAME`` and ``LOGGER_HANDLER_POLICY`` were removed. See
|
||||||
|
:doc:`/logging` for information about configuration.
|
||||||
|
|
||||||
|
Added :data:`ENV` to reflect the :envvar:`FLASK_ENV` environment
|
||||||
|
variable.
|
||||||
|
|
||||||
|
Added :data:`SESSION_COOKIE_SAMESITE` to control the session
|
||||||
|
cookie's ``SameSite`` option.
|
||||||
|
|
||||||
|
Added :data:`MAX_COOKIE_SIZE` to control a warning from Werkzeug.
|
||||||
|
|
||||||
|
.. versionchanged:: 2.2
|
||||||
|
Removed ``PRESERVE_CONTEXT_ON_EXCEPTION``.
|
||||||
|
|
||||||
|
.. versionchanged:: 2.3
|
||||||
|
``JSON_AS_ASCII``, ``JSON_SORT_KEYS``, ``JSONIFY_MIMETYPE``, and
|
||||||
|
``JSONIFY_PRETTYPRINT_REGULAR`` were removed. The default ``app.json`` provider has
|
||||||
|
equivalent attributes instead.
|
||||||
|
|
||||||
|
.. versionchanged:: 2.3
|
||||||
|
``ENV`` was removed.
|
||||||
|
|
||||||
|
.. versionadded:: 3.10
|
||||||
|
Added :data:`PROVIDE_AUTOMATIC_OPTIONS` to control the default
|
||||||
|
addition of autogenerated OPTIONS responses.
|
||||||
|
|
||||||
|
|
||||||
|
Configuring from Python Files
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
Configuration becomes more useful if you can store it in a separate file, ideally
|
||||||
|
located outside the actual application package. You can deploy your application, then
|
||||||
|
separately configure it for the specific deployment.
|
||||||
|
|
||||||
|
A common pattern is this::
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
app.config.from_object('yourapplication.default_settings')
|
||||||
|
app.config.from_envvar('YOURAPPLICATION_SETTINGS')
|
||||||
|
|
||||||
|
This first loads the configuration from the
|
||||||
|
`yourapplication.default_settings` module and then overrides the values
|
||||||
|
with the contents of the file the :envvar:`YOURAPPLICATION_SETTINGS`
|
||||||
|
environment variable points to. This environment variable can be set
|
||||||
|
in the shell before starting the server:
|
||||||
|
|
||||||
|
.. tabs::
|
||||||
|
|
||||||
|
.. group-tab:: Bash
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
$ export YOURAPPLICATION_SETTINGS=/path/to/settings.cfg
|
||||||
|
$ flask run
|
||||||
|
* Running on http://127.0.0.1:5000/
|
||||||
|
|
||||||
|
.. group-tab:: Fish
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
$ set -x YOURAPPLICATION_SETTINGS /path/to/settings.cfg
|
||||||
|
$ flask run
|
||||||
|
* Running on http://127.0.0.1:5000/
|
||||||
|
|
||||||
|
.. group-tab:: CMD
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
> set YOURAPPLICATION_SETTINGS=\path\to\settings.cfg
|
||||||
|
> flask run
|
||||||
|
* Running on http://127.0.0.1:5000/
|
||||||
|
|
||||||
|
.. group-tab:: Powershell
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
> $env:YOURAPPLICATION_SETTINGS = "\path\to\settings.cfg"
|
||||||
|
> flask run
|
||||||
|
* Running on http://127.0.0.1:5000/
|
||||||
|
|
||||||
|
The configuration files themselves are actual Python files. Only values
|
||||||
|
in uppercase are actually stored in the config object later on. So make
|
||||||
|
sure to use uppercase letters for your config keys.
|
||||||
|
|
||||||
|
Here is an example of a configuration file::
|
||||||
|
|
||||||
|
# Example configuration
|
||||||
|
SECRET_KEY = '192b9bdd22ab9ed4d12e236c78afcb9a393ec15f71bbf5dc987d54727823bcbf'
|
||||||
|
|
||||||
|
Make sure to load the configuration very early on, so that extensions have
|
||||||
|
the ability to access the configuration when starting up. There are other
|
||||||
|
methods on the config object as well to load from individual files. For a
|
||||||
|
complete reference, read the :class:`~flask.Config` object's
|
||||||
|
documentation.
|
||||||
|
|
||||||
|
|
||||||
|
Configuring from Data Files
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
It is also possible to load configuration from a file in a format of
|
||||||
|
your choice using :meth:`~flask.Config.from_file`. For example to load
|
||||||
|
from a TOML file:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
import tomllib
|
||||||
|
app.config.from_file("config.toml", load=tomllib.load, text=False)
|
||||||
|
|
||||||
|
Or from a JSON file:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
import json
|
||||||
|
app.config.from_file("config.json", load=json.load)
|
||||||
|
|
||||||
|
|
||||||
|
Configuring from Environment Variables
|
||||||
|
--------------------------------------
|
||||||
|
|
||||||
|
In addition to pointing to configuration files using environment
|
||||||
|
variables, you may find it useful (or necessary) to control your
|
||||||
|
configuration values directly from the environment. Flask can be
|
||||||
|
instructed to load all environment variables starting with a specific
|
||||||
|
prefix into the config using :meth:`~flask.Config.from_prefixed_env`.
|
||||||
|
|
||||||
|
Environment variables can be set in the shell before starting the
|
||||||
|
server:
|
||||||
|
|
||||||
|
.. tabs::
|
||||||
|
|
||||||
|
.. group-tab:: Bash
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
$ export FLASK_SECRET_KEY="5f352379324c22463451387a0aec5d2f"
|
||||||
|
$ export FLASK_MAIL_ENABLED=false
|
||||||
|
$ flask run
|
||||||
|
* Running on http://127.0.0.1:5000/
|
||||||
|
|
||||||
|
.. group-tab:: Fish
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
$ set -x FLASK_SECRET_KEY "5f352379324c22463451387a0aec5d2f"
|
||||||
|
$ set -x FLASK_MAIL_ENABLED false
|
||||||
|
$ flask run
|
||||||
|
* Running on http://127.0.0.1:5000/
|
||||||
|
|
||||||
|
.. group-tab:: CMD
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
> set FLASK_SECRET_KEY="5f352379324c22463451387a0aec5d2f"
|
||||||
|
> set FLASK_MAIL_ENABLED=false
|
||||||
|
> flask run
|
||||||
|
* Running on http://127.0.0.1:5000/
|
||||||
|
|
||||||
|
.. group-tab:: Powershell
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
> $env:FLASK_SECRET_KEY = "5f352379324c22463451387a0aec5d2f"
|
||||||
|
> $env:FLASK_MAIL_ENABLED = "false"
|
||||||
|
> flask run
|
||||||
|
* Running on http://127.0.0.1:5000/
|
||||||
|
|
||||||
|
The variables can then be loaded and accessed via the config with a key
|
||||||
|
equal to the environment variable name without the prefix i.e.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
app.config.from_prefixed_env()
|
||||||
|
app.config["SECRET_KEY"] # Is "5f352379324c22463451387a0aec5d2f"
|
||||||
|
|
||||||
|
The prefix is ``FLASK_`` by default. This is configurable via the
|
||||||
|
``prefix`` argument of :meth:`~flask.Config.from_prefixed_env`.
|
||||||
|
|
||||||
|
Values will be parsed to attempt to convert them to a more specific type
|
||||||
|
than strings. By default :func:`json.loads` is used, so any valid JSON
|
||||||
|
value is possible, including lists and dicts. This is configurable via
|
||||||
|
the ``loads`` argument of :meth:`~flask.Config.from_prefixed_env`.
|
||||||
|
|
||||||
|
When adding a boolean value with the default JSON parsing, only "true"
|
||||||
|
and "false", lowercase, are valid values. Keep in mind that any
|
||||||
|
non-empty string is considered ``True`` by Python.
|
||||||
|
|
||||||
|
It is possible to set keys in nested dictionaries by separating the
|
||||||
|
keys with double underscore (``__``). Any intermediate keys that don't
|
||||||
|
exist on the parent dict will be initialized to an empty dict.
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
$ export FLASK_MYAPI__credentials__username=user123
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
app.config["MYAPI"]["credentials"]["username"] # Is "user123"
|
||||||
|
|
||||||
|
On Windows, environment variable keys are always uppercase, therefore
|
||||||
|
the above example would end up as ``MYAPI__CREDENTIALS__USERNAME``.
|
||||||
|
|
||||||
|
For even more config loading features, including merging and
|
||||||
|
case-insensitive Windows support, try a dedicated library such as
|
||||||
|
Dynaconf_, which includes integration with Flask.
|
||||||
|
|
||||||
|
.. _Dynaconf: https://www.dynaconf.com/
|
||||||
|
|
||||||
|
|
||||||
|
Configuration Best Practices
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
The downside with the approach mentioned earlier is that it makes testing
|
||||||
|
a little harder. There is no single 100% solution for this problem in
|
||||||
|
general, but there are a couple of things you can keep in mind to improve
|
||||||
|
that experience:
|
||||||
|
|
||||||
|
1. Create your application in a function and register blueprints on it.
|
||||||
|
That way you can create multiple instances of your application with
|
||||||
|
different configurations attached which makes unit testing a lot
|
||||||
|
easier. You can use this to pass in configuration as needed.
|
||||||
|
|
||||||
|
2. Do not write code that needs the configuration at import time. If you
|
||||||
|
limit yourself to request-only accesses to the configuration you can
|
||||||
|
reconfigure the object later on as needed.
|
||||||
|
|
||||||
|
3. Make sure to load the configuration very early on, so that
|
||||||
|
extensions can access the configuration when calling ``init_app``.
|
||||||
|
|
||||||
|
|
||||||
|
.. _config-dev-prod:
|
||||||
|
|
||||||
|
Development / Production
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
Most applications need more than one configuration. There should be at
|
||||||
|
least separate configurations for the production server and the one used
|
||||||
|
during development. The easiest way to handle this is to use a default
|
||||||
|
configuration that is always loaded and part of the version control, and a
|
||||||
|
separate configuration that overrides the values as necessary as mentioned
|
||||||
|
in the example above::
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
app.config.from_object('yourapplication.default_settings')
|
||||||
|
app.config.from_envvar('YOURAPPLICATION_SETTINGS')
|
||||||
|
|
||||||
|
Then you just have to add a separate :file:`config.py` file and export
|
||||||
|
``YOURAPPLICATION_SETTINGS=/path/to/config.py`` and you are done. However
|
||||||
|
there are alternative ways as well. For example you could use imports or
|
||||||
|
subclassing.
|
||||||
|
|
||||||
|
What is very popular in the Django world is to make the import explicit in
|
||||||
|
the config file by adding ``from yourapplication.default_settings
|
||||||
|
import *`` to the top of the file and then overriding the changes by hand.
|
||||||
|
You could also inspect an environment variable like
|
||||||
|
``YOURAPPLICATION_MODE`` and set that to `production`, `development` etc
|
||||||
|
and import different hard-coded files based on that.
|
||||||
|
|
||||||
|
An interesting pattern is also to use classes and inheritance for
|
||||||
|
configuration::
|
||||||
|
|
||||||
|
class Config(object):
|
||||||
|
TESTING = False
|
||||||
|
|
||||||
|
class ProductionConfig(Config):
|
||||||
|
DATABASE_URI = 'mysql://user@localhost/foo'
|
||||||
|
|
||||||
|
class DevelopmentConfig(Config):
|
||||||
|
DATABASE_URI = "sqlite:////tmp/foo.db"
|
||||||
|
|
||||||
|
class TestingConfig(Config):
|
||||||
|
DATABASE_URI = 'sqlite:///:memory:'
|
||||||
|
TESTING = True
|
||||||
|
|
||||||
|
To enable such a config you just have to call into
|
||||||
|
:meth:`~flask.Config.from_object`::
|
||||||
|
|
||||||
|
app.config.from_object('configmodule.ProductionConfig')
|
||||||
|
|
||||||
|
Note that :meth:`~flask.Config.from_object` does not instantiate the class
|
||||||
|
object. If you need to instantiate the class, such as to access a property,
|
||||||
|
then you must do so before calling :meth:`~flask.Config.from_object`::
|
||||||
|
|
||||||
|
from configmodule import ProductionConfig
|
||||||
|
app.config.from_object(ProductionConfig())
|
||||||
|
|
||||||
|
# Alternatively, import via string:
|
||||||
|
from werkzeug.utils import import_string
|
||||||
|
cfg = import_string('configmodule.ProductionConfig')()
|
||||||
|
app.config.from_object(cfg)
|
||||||
|
|
||||||
|
Instantiating the configuration object allows you to use ``@property`` in
|
||||||
|
your configuration classes::
|
||||||
|
|
||||||
|
class Config(object):
|
||||||
|
"""Base config, uses staging database server."""
|
||||||
|
TESTING = False
|
||||||
|
DB_SERVER = '192.168.1.56'
|
||||||
|
|
||||||
|
@property
|
||||||
|
def DATABASE_URI(self): # Note: all caps
|
||||||
|
return f"mysql://user@{self.DB_SERVER}/foo"
|
||||||
|
|
||||||
|
class ProductionConfig(Config):
|
||||||
|
"""Uses production database server."""
|
||||||
|
DB_SERVER = '192.168.19.32'
|
||||||
|
|
||||||
|
class DevelopmentConfig(Config):
|
||||||
|
DB_SERVER = 'localhost'
|
||||||
|
|
||||||
|
class TestingConfig(Config):
|
||||||
|
DB_SERVER = 'localhost'
|
||||||
|
DATABASE_URI = 'sqlite:///:memory:'
|
||||||
|
|
||||||
|
There are many different ways and it's up to you how you want to manage
|
||||||
|
your configuration files. However here a list of good recommendations:
|
||||||
|
|
||||||
|
- Keep a default configuration in version control. Either populate the
|
||||||
|
config with this default configuration or import it in your own
|
||||||
|
configuration files before overriding values.
|
||||||
|
- Use an environment variable to switch between the configurations.
|
||||||
|
This can be done from outside the Python interpreter and makes
|
||||||
|
development and deployment much easier because you can quickly and
|
||||||
|
easily switch between different configs without having to touch the
|
||||||
|
code at all. If you are working often on different projects you can
|
||||||
|
even create your own script for sourcing that activates a virtualenv
|
||||||
|
and exports the development configuration for you.
|
||||||
|
- Use a tool like `fabric`_ to push code and configuration separately
|
||||||
|
to the production server(s).
|
||||||
|
|
||||||
|
.. _fabric: https://www.fabfile.org/
|
||||||
|
|
||||||
|
|
||||||
|
.. _instance-folders:
|
||||||
|
|
||||||
|
Instance Folders
|
||||||
|
----------------
|
||||||
|
|
||||||
|
.. versionadded:: 0.8
|
||||||
|
|
||||||
|
Flask 0.8 introduces instance folders. Flask for a long time made it
|
||||||
|
possible to refer to paths relative to the application's folder directly
|
||||||
|
(via :attr:`Flask.root_path`). This was also how many developers loaded
|
||||||
|
configurations stored next to the application. Unfortunately however this
|
||||||
|
only works well if applications are not packages in which case the root
|
||||||
|
path refers to the contents of the package.
|
||||||
|
|
||||||
|
With Flask 0.8 a new attribute was introduced:
|
||||||
|
:attr:`Flask.instance_path`. It refers to a new concept called the
|
||||||
|
“instance folder”. The instance folder is designed to not be under
|
||||||
|
version control and be deployment specific. It's the perfect place to
|
||||||
|
drop things that either change at runtime or configuration files.
|
||||||
|
|
||||||
|
You can either explicitly provide the path of the instance folder when
|
||||||
|
creating the Flask application or you can let Flask autodetect the
|
||||||
|
instance folder. For explicit configuration use the `instance_path`
|
||||||
|
parameter::
|
||||||
|
|
||||||
|
app = Flask(__name__, instance_path='/path/to/instance/folder')
|
||||||
|
|
||||||
|
Please keep in mind that this path *must* be absolute when provided.
|
||||||
|
|
||||||
|
If the `instance_path` parameter is not provided the following default
|
||||||
|
locations are used:
|
||||||
|
|
||||||
|
- Uninstalled module::
|
||||||
|
|
||||||
|
/myapp.py
|
||||||
|
/instance
|
||||||
|
|
||||||
|
- Uninstalled package::
|
||||||
|
|
||||||
|
/myapp
|
||||||
|
/__init__.py
|
||||||
|
/instance
|
||||||
|
|
||||||
|
- Installed module or package::
|
||||||
|
|
||||||
|
$PREFIX/lib/pythonX.Y/site-packages/myapp
|
||||||
|
$PREFIX/var/myapp-instance
|
||||||
|
|
||||||
|
``$PREFIX`` is the prefix of your Python installation. This can be
|
||||||
|
``/usr`` or the path to your virtualenv. You can print the value of
|
||||||
|
``sys.prefix`` to see what the prefix is set to.
|
||||||
|
|
||||||
|
Since the config object provided loading of configuration files from
|
||||||
|
relative filenames we made it possible to change the loading via filenames
|
||||||
|
to be relative to the instance path if wanted. The behavior of relative
|
||||||
|
paths in config files can be flipped between “relative to the application
|
||||||
|
root” (the default) to “relative to instance folder” via the
|
||||||
|
`instance_relative_config` switch to the application constructor::
|
||||||
|
|
||||||
|
app = Flask(__name__, instance_relative_config=True)
|
||||||
|
|
||||||
|
Here is a full example of how to configure Flask to preload the config
|
||||||
|
from a module and then override the config from a file in the instance
|
||||||
|
folder if it exists::
|
||||||
|
|
||||||
|
app = Flask(__name__, instance_relative_config=True)
|
||||||
|
app.config.from_object('yourapplication.default_settings')
|
||||||
|
app.config.from_pyfile('application.cfg', silent=True)
|
||||||
|
|
||||||
|
The path to the instance folder can be found via the
|
||||||
|
:attr:`Flask.instance_path`. Flask also provides a shortcut to open a
|
||||||
|
file from the instance folder with :meth:`Flask.open_instance_resource`.
|
||||||
|
|
||||||
|
Example usage for both::
|
||||||
|
|
||||||
|
filename = os.path.join(app.instance_path, 'application.cfg')
|
||||||
|
with open(filename) as f:
|
||||||
|
config = f.read()
|
||||||
|
|
||||||
|
# or via open_instance_resource:
|
||||||
|
with app.open_instance_resource('application.cfg') as f:
|
||||||
|
config = f.read()
|
||||||
8
_build/_sources/contributing.rst.txt
Normal file
8
_build/_sources/contributing.rst.txt
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
Contributing
|
||||||
|
============
|
||||||
|
|
||||||
|
See the Pallets `detailed contributing documentation <_contrib>`_ for many ways
|
||||||
|
to contribute, including reporting issues, requesting features, asking or
|
||||||
|
answering questions, and making PRs.
|
||||||
|
|
||||||
|
.. _contrib: https://palletsprojects.com/contributing/
|
||||||
99
_build/_sources/debugging.rst.txt
Normal file
99
_build/_sources/debugging.rst.txt
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
Debugging Application Errors
|
||||||
|
============================
|
||||||
|
|
||||||
|
|
||||||
|
In Production
|
||||||
|
-------------
|
||||||
|
|
||||||
|
**Do not run the development server, or enable the built-in debugger, in
|
||||||
|
a production environment.** The debugger allows executing arbitrary
|
||||||
|
Python code from the browser. It's protected by a pin, but that should
|
||||||
|
not be relied on for security.
|
||||||
|
|
||||||
|
Use an error logging tool, such as Sentry, as described in
|
||||||
|
:ref:`error-logging-tools`, or enable logging and notifications as
|
||||||
|
described in :doc:`/logging`.
|
||||||
|
|
||||||
|
If you have access to the server, you could add some code to start an
|
||||||
|
external debugger if ``request.remote_addr`` matches your IP. Some IDE
|
||||||
|
debuggers also have a remote mode so breakpoints on the server can be
|
||||||
|
interacted with locally. Only enable a debugger temporarily.
|
||||||
|
|
||||||
|
|
||||||
|
The Built-In Debugger
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
The built-in Werkzeug development server provides a debugger which shows
|
||||||
|
an interactive traceback in the browser when an unhandled error occurs
|
||||||
|
during a request. This debugger should only be used during development.
|
||||||
|
|
||||||
|
.. image:: _static/debugger.png
|
||||||
|
:align: center
|
||||||
|
:class: screenshot
|
||||||
|
:alt: screenshot of debugger in action
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
The debugger allows executing arbitrary Python code from the
|
||||||
|
browser. It is protected by a pin, but still represents a major
|
||||||
|
security risk. Do not run the development server or debugger in a
|
||||||
|
production environment.
|
||||||
|
|
||||||
|
The debugger is enabled by default when the development server is run in debug mode.
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
$ flask --app hello run --debug
|
||||||
|
|
||||||
|
When running from Python code, passing ``debug=True`` enables debug mode, which is
|
||||||
|
mostly equivalent.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
app.run(debug=True)
|
||||||
|
|
||||||
|
:doc:`/server` and :doc:`/cli` have more information about running the debugger and
|
||||||
|
debug mode. More information about the debugger can be found in the `Werkzeug
|
||||||
|
documentation <https://werkzeug.palletsprojects.com/debug/>`__.
|
||||||
|
|
||||||
|
|
||||||
|
External Debuggers
|
||||||
|
------------------
|
||||||
|
|
||||||
|
External debuggers, such as those provided by IDEs, can offer a more
|
||||||
|
powerful debugging experience than the built-in debugger. They can also
|
||||||
|
be used to step through code during a request before an error is raised,
|
||||||
|
or if no error is raised. Some even have a remote mode so you can debug
|
||||||
|
code running on another machine.
|
||||||
|
|
||||||
|
When using an external debugger, the app should still be in debug mode, otherwise Flask
|
||||||
|
turns unhandled errors into generic 500 error pages. However, the built-in debugger and
|
||||||
|
reloader should be disabled so they don't interfere with the external debugger.
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
$ flask --app hello run --debug --no-debugger --no-reload
|
||||||
|
|
||||||
|
When running from Python:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
app.run(debug=True, use_debugger=False, use_reloader=False)
|
||||||
|
|
||||||
|
Disabling these isn't required, an external debugger will continue to work with the
|
||||||
|
following caveats.
|
||||||
|
|
||||||
|
- If the built-in debugger is not disabled, it will catch unhandled exceptions before
|
||||||
|
the external debugger can.
|
||||||
|
- If the reloader is not disabled, it could cause an unexpected reload if code changes
|
||||||
|
during a breakpoint.
|
||||||
|
- The development server will still catch unhandled exceptions if the built-in
|
||||||
|
debugger is disabled, otherwise it would crash on any error. If you want that (and
|
||||||
|
usually you don't) pass ``passthrough_errors=True`` to ``app.run``.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
app.run(
|
||||||
|
debug=True, passthrough_errors=True,
|
||||||
|
use_debugger=False, use_reloader=False
|
||||||
|
)
|
||||||
66
_build/_sources/deploying/apache-httpd.rst.txt
Normal file
66
_build/_sources/deploying/apache-httpd.rst.txt
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
Apache httpd
|
||||||
|
============
|
||||||
|
|
||||||
|
`Apache httpd`_ is a fast, production level HTTP server. When serving
|
||||||
|
your application with one of the WSGI servers listed in :doc:`index`, it
|
||||||
|
is often good or necessary to put a dedicated HTTP server in front of
|
||||||
|
it. This "reverse proxy" can handle incoming requests, TLS, and other
|
||||||
|
security and performance concerns better than the WSGI server.
|
||||||
|
|
||||||
|
httpd can be installed using your system package manager, or a pre-built
|
||||||
|
executable for Windows. Installing and running httpd itself is outside
|
||||||
|
the scope of this doc. This page outlines the basics of configuring
|
||||||
|
httpd to proxy your application. Be sure to read its documentation to
|
||||||
|
understand what features are available.
|
||||||
|
|
||||||
|
.. _Apache httpd: https://httpd.apache.org/
|
||||||
|
|
||||||
|
|
||||||
|
Domain Name
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Acquiring and configuring a domain name is outside the scope of this
|
||||||
|
doc. In general, you will buy a domain name from a registrar, pay for
|
||||||
|
server space with a hosting provider, and then point your registrar
|
||||||
|
at the hosting provider's name servers.
|
||||||
|
|
||||||
|
To simulate this, you can also edit your ``hosts`` file, located at
|
||||||
|
``/etc/hosts`` on Linux. Add a line that associates a name with the
|
||||||
|
local IP.
|
||||||
|
|
||||||
|
Modern Linux systems may be configured to treat any domain name that
|
||||||
|
ends with ``.localhost`` like this without adding it to the ``hosts``
|
||||||
|
file.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
:caption: ``/etc/hosts``
|
||||||
|
|
||||||
|
127.0.0.1 hello.localhost
|
||||||
|
|
||||||
|
|
||||||
|
Configuration
|
||||||
|
-------------
|
||||||
|
|
||||||
|
The httpd configuration is located at ``/etc/httpd/conf/httpd.conf`` on
|
||||||
|
Linux. It may be different depending on your operating system. Check the
|
||||||
|
docs and look for ``httpd.conf``.
|
||||||
|
|
||||||
|
Remove or comment out any existing ``DocumentRoot`` directive. Add the
|
||||||
|
config lines below. We'll assume the WSGI server is listening locally at
|
||||||
|
``http://127.0.0.1:8000``.
|
||||||
|
|
||||||
|
.. code-block:: apache
|
||||||
|
:caption: ``/etc/httpd/conf/httpd.conf``
|
||||||
|
|
||||||
|
LoadModule proxy_module modules/mod_proxy.so
|
||||||
|
LoadModule proxy_http_module modules/mod_proxy_http.so
|
||||||
|
ProxyPass / http://127.0.0.1:8000/
|
||||||
|
RequestHeader set X-Forwarded-Proto http
|
||||||
|
RequestHeader set X-Forwarded-Prefix /
|
||||||
|
|
||||||
|
The ``LoadModule`` lines might already exist. If so, make sure they are
|
||||||
|
uncommented instead of adding them manually.
|
||||||
|
|
||||||
|
Then :doc:`proxy_fix` so that your application uses the ``X-Forwarded``
|
||||||
|
headers. ``X-Forwarded-For`` and ``X-Forwarded-Host`` are automatically
|
||||||
|
set by ``ProxyPass``.
|
||||||
27
_build/_sources/deploying/asgi.rst.txt
Normal file
27
_build/_sources/deploying/asgi.rst.txt
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
ASGI
|
||||||
|
====
|
||||||
|
|
||||||
|
If you'd like to use an ASGI server you will need to utilise WSGI to
|
||||||
|
ASGI middleware. The asgiref
|
||||||
|
`WsgiToAsgi <https://github.com/django/asgiref#wsgi-to-asgi-adapter>`_
|
||||||
|
adapter is recommended as it integrates with the event loop used for
|
||||||
|
Flask's :ref:`async_await` support. You can use the adapter by
|
||||||
|
wrapping the Flask app,
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from asgiref.wsgi import WsgiToAsgi
|
||||||
|
from flask import Flask
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
asgi_app = WsgiToAsgi(app)
|
||||||
|
|
||||||
|
and then serving the ``asgi_app`` with the ASGI server, e.g. using
|
||||||
|
`Hypercorn <https://github.com/pgjones/hypercorn>`_,
|
||||||
|
|
||||||
|
.. sourcecode:: text
|
||||||
|
|
||||||
|
$ hypercorn module:asgi_app
|
||||||
80
_build/_sources/deploying/eventlet.rst.txt
Normal file
80
_build/_sources/deploying/eventlet.rst.txt
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
eventlet
|
||||||
|
========
|
||||||
|
|
||||||
|
Prefer using :doc:`gunicorn` with eventlet workers rather than using
|
||||||
|
`eventlet`_ directly. Gunicorn provides a much more configurable and
|
||||||
|
production-tested server.
|
||||||
|
|
||||||
|
`eventlet`_ allows writing asynchronous, coroutine-based code that looks
|
||||||
|
like standard synchronous Python. It uses `greenlet`_ to enable task
|
||||||
|
switching without writing ``async/await`` or using ``asyncio``.
|
||||||
|
|
||||||
|
:doc:`gevent` is another library that does the same thing. Certain
|
||||||
|
dependencies you have, or other considerations, may affect which of the
|
||||||
|
two you choose to use.
|
||||||
|
|
||||||
|
eventlet provides a WSGI server that can handle many connections at once
|
||||||
|
instead of one per worker process. You must actually use eventlet in
|
||||||
|
your own code to see any benefit to using the server.
|
||||||
|
|
||||||
|
.. _eventlet: https://eventlet.net/
|
||||||
|
.. _greenlet: https://greenlet.readthedocs.io/en/latest/
|
||||||
|
|
||||||
|
|
||||||
|
Installing
|
||||||
|
----------
|
||||||
|
|
||||||
|
When using eventlet, greenlet>=1.0 is required, otherwise context locals
|
||||||
|
such as ``request`` will not work as expected. When using PyPy,
|
||||||
|
PyPy>=7.3.7 is required.
|
||||||
|
|
||||||
|
Create a virtualenv, install your application, then install
|
||||||
|
``eventlet``.
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
$ cd hello-app
|
||||||
|
$ python -m venv .venv
|
||||||
|
$ . .venv/bin/activate
|
||||||
|
$ pip install . # install your application
|
||||||
|
$ pip install eventlet
|
||||||
|
|
||||||
|
|
||||||
|
Running
|
||||||
|
-------
|
||||||
|
|
||||||
|
To use eventlet to serve your application, write a script that imports
|
||||||
|
its ``wsgi.server``, as well as your app or app factory.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
:caption: ``wsgi.py``
|
||||||
|
|
||||||
|
import eventlet
|
||||||
|
from eventlet import wsgi
|
||||||
|
from hello import create_app
|
||||||
|
|
||||||
|
app = create_app()
|
||||||
|
wsgi.server(eventlet.listen(("127.0.0.1", 8000)), app)
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
$ python wsgi.py
|
||||||
|
(x) wsgi starting up on http://127.0.0.1:8000
|
||||||
|
|
||||||
|
|
||||||
|
Binding Externally
|
||||||
|
------------------
|
||||||
|
|
||||||
|
eventlet should not be run as root because it would cause your
|
||||||
|
application code to run as root, which is not secure. However, this
|
||||||
|
means it will not be possible to bind to port 80 or 443. Instead, a
|
||||||
|
reverse proxy such as :doc:`nginx` or :doc:`apache-httpd` should be used
|
||||||
|
in front of eventlet.
|
||||||
|
|
||||||
|
You can bind to all external IPs on a non-privileged port by using
|
||||||
|
``0.0.0.0`` in the server arguments shown in the previous section.
|
||||||
|
Don't do this when using a reverse proxy setup, otherwise it will be
|
||||||
|
possible to bypass the proxy.
|
||||||
|
|
||||||
|
``0.0.0.0`` is not a valid address to navigate to, you'd use a specific
|
||||||
|
IP address in your browser.
|
||||||
80
_build/_sources/deploying/gevent.rst.txt
Normal file
80
_build/_sources/deploying/gevent.rst.txt
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
gevent
|
||||||
|
======
|
||||||
|
|
||||||
|
Prefer using :doc:`gunicorn` or :doc:`uwsgi` with gevent workers rather
|
||||||
|
than using `gevent`_ directly. Gunicorn and uWSGI provide much more
|
||||||
|
configurable and production-tested servers.
|
||||||
|
|
||||||
|
`gevent`_ allows writing asynchronous, coroutine-based code that looks
|
||||||
|
like standard synchronous Python. It uses `greenlet`_ to enable task
|
||||||
|
switching without writing ``async/await`` or using ``asyncio``.
|
||||||
|
|
||||||
|
:doc:`eventlet` is another library that does the same thing. Certain
|
||||||
|
dependencies you have, or other considerations, may affect which of the
|
||||||
|
two you choose to use.
|
||||||
|
|
||||||
|
gevent provides a WSGI server that can handle many connections at once
|
||||||
|
instead of one per worker process. You must actually use gevent in your
|
||||||
|
own code to see any benefit to using the server.
|
||||||
|
|
||||||
|
.. _gevent: https://www.gevent.org/
|
||||||
|
.. _greenlet: https://greenlet.readthedocs.io/en/latest/
|
||||||
|
|
||||||
|
|
||||||
|
Installing
|
||||||
|
----------
|
||||||
|
|
||||||
|
When using gevent, greenlet>=1.0 is required, otherwise context locals
|
||||||
|
such as ``request`` will not work as expected. When using PyPy,
|
||||||
|
PyPy>=7.3.7 is required.
|
||||||
|
|
||||||
|
Create a virtualenv, install your application, then install ``gevent``.
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
$ cd hello-app
|
||||||
|
$ python -m venv .venv
|
||||||
|
$ . .venv/bin/activate
|
||||||
|
$ pip install . # install your application
|
||||||
|
$ pip install gevent
|
||||||
|
|
||||||
|
|
||||||
|
Running
|
||||||
|
-------
|
||||||
|
|
||||||
|
To use gevent to serve your application, write a script that imports its
|
||||||
|
``WSGIServer``, as well as your app or app factory.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
:caption: ``wsgi.py``
|
||||||
|
|
||||||
|
from gevent.pywsgi import WSGIServer
|
||||||
|
from hello import create_app
|
||||||
|
|
||||||
|
app = create_app()
|
||||||
|
http_server = WSGIServer(("127.0.0.1", 8000), app)
|
||||||
|
http_server.serve_forever()
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
$ python wsgi.py
|
||||||
|
|
||||||
|
No output is shown when the server starts.
|
||||||
|
|
||||||
|
|
||||||
|
Binding Externally
|
||||||
|
------------------
|
||||||
|
|
||||||
|
gevent should not be run as root because it would cause your
|
||||||
|
application code to run as root, which is not secure. However, this
|
||||||
|
means it will not be possible to bind to port 80 or 443. Instead, a
|
||||||
|
reverse proxy such as :doc:`nginx` or :doc:`apache-httpd` should be used
|
||||||
|
in front of gevent.
|
||||||
|
|
||||||
|
You can bind to all external IPs on a non-privileged port by using
|
||||||
|
``0.0.0.0`` in the server arguments shown in the previous section. Don't
|
||||||
|
do this when using a reverse proxy setup, otherwise it will be possible
|
||||||
|
to bypass the proxy.
|
||||||
|
|
||||||
|
``0.0.0.0`` is not a valid address to navigate to, you'd use a specific
|
||||||
|
IP address in your browser.
|
||||||
130
_build/_sources/deploying/gunicorn.rst.txt
Normal file
130
_build/_sources/deploying/gunicorn.rst.txt
Normal file
|
|
@ -0,0 +1,130 @@
|
||||||
|
Gunicorn
|
||||||
|
========
|
||||||
|
|
||||||
|
`Gunicorn`_ is a pure Python WSGI server with simple configuration and
|
||||||
|
multiple worker implementations for performance tuning.
|
||||||
|
|
||||||
|
* It tends to integrate easily with hosting platforms.
|
||||||
|
* It does not support Windows (but does run on WSL).
|
||||||
|
* It is easy to install as it does not require additional dependencies
|
||||||
|
or compilation.
|
||||||
|
* It has built-in async worker support using gevent or eventlet.
|
||||||
|
|
||||||
|
This page outlines the basics of running Gunicorn. Be sure to read its
|
||||||
|
`documentation`_ and use ``gunicorn --help`` to understand what features
|
||||||
|
are available.
|
||||||
|
|
||||||
|
.. _Gunicorn: https://gunicorn.org/
|
||||||
|
.. _documentation: https://docs.gunicorn.org/
|
||||||
|
|
||||||
|
|
||||||
|
Installing
|
||||||
|
----------
|
||||||
|
|
||||||
|
Gunicorn is easy to install, as it does not require external
|
||||||
|
dependencies or compilation. It runs on Windows only under WSL.
|
||||||
|
|
||||||
|
Create a virtualenv, install your application, then install
|
||||||
|
``gunicorn``.
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
$ cd hello-app
|
||||||
|
$ python -m venv .venv
|
||||||
|
$ . .venv/bin/activate
|
||||||
|
$ pip install . # install your application
|
||||||
|
$ pip install gunicorn
|
||||||
|
|
||||||
|
|
||||||
|
Running
|
||||||
|
-------
|
||||||
|
|
||||||
|
The only required argument to Gunicorn tells it how to load your Flask
|
||||||
|
application. The syntax is ``{module_import}:{app_variable}``.
|
||||||
|
``module_import`` is the dotted import name to the module with your
|
||||||
|
application. ``app_variable`` is the variable with the application. It
|
||||||
|
can also be a function call (with any arguments) if you're using the
|
||||||
|
app factory pattern.
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
# equivalent to 'from hello import app'
|
||||||
|
$ gunicorn -w 4 'hello:app'
|
||||||
|
|
||||||
|
# equivalent to 'from hello import create_app; create_app()'
|
||||||
|
$ gunicorn -w 4 'hello:create_app()'
|
||||||
|
|
||||||
|
Starting gunicorn 20.1.0
|
||||||
|
Listening at: http://127.0.0.1:8000 (x)
|
||||||
|
Using worker: sync
|
||||||
|
Booting worker with pid: x
|
||||||
|
Booting worker with pid: x
|
||||||
|
Booting worker with pid: x
|
||||||
|
Booting worker with pid: x
|
||||||
|
|
||||||
|
The ``-w`` option specifies the number of processes to run; a starting
|
||||||
|
value could be ``CPU * 2``. The default is only 1 worker, which is
|
||||||
|
probably not what you want for the default worker type.
|
||||||
|
|
||||||
|
Logs for each request aren't shown by default, only worker info and
|
||||||
|
errors are shown. To show access logs on stdout, use the
|
||||||
|
``--access-logfile=-`` option.
|
||||||
|
|
||||||
|
|
||||||
|
Binding Externally
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Gunicorn should not be run as root because it would cause your
|
||||||
|
application code to run as root, which is not secure. However, this
|
||||||
|
means it will not be possible to bind to port 80 or 443. Instead, a
|
||||||
|
reverse proxy such as :doc:`nginx` or :doc:`apache-httpd` should be used
|
||||||
|
in front of Gunicorn.
|
||||||
|
|
||||||
|
You can bind to all external IPs on a non-privileged port using the
|
||||||
|
``-b 0.0.0.0`` option. Don't do this when using a reverse proxy setup,
|
||||||
|
otherwise it will be possible to bypass the proxy.
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
$ gunicorn -w 4 -b 0.0.0.0 'hello:create_app()'
|
||||||
|
Listening at: http://0.0.0.0:8000 (x)
|
||||||
|
|
||||||
|
``0.0.0.0`` is not a valid address to navigate to, you'd use a specific
|
||||||
|
IP address in your browser.
|
||||||
|
|
||||||
|
|
||||||
|
Async with gevent or eventlet
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
The default sync worker is appropriate for many use cases. If you need
|
||||||
|
asynchronous support, Gunicorn provides workers using either `gevent`_
|
||||||
|
or `eventlet`_. This is not the same as Python's ``async/await``, or the
|
||||||
|
ASGI server spec. You must actually use gevent/eventlet in your own code
|
||||||
|
to see any benefit to using the workers.
|
||||||
|
|
||||||
|
When using either gevent or eventlet, greenlet>=1.0 is required,
|
||||||
|
otherwise context locals such as ``request`` will not work as expected.
|
||||||
|
When using PyPy, PyPy>=7.3.7 is required.
|
||||||
|
|
||||||
|
To use gevent:
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
$ gunicorn -k gevent 'hello:create_app()'
|
||||||
|
Starting gunicorn 20.1.0
|
||||||
|
Listening at: http://127.0.0.1:8000 (x)
|
||||||
|
Using worker: gevent
|
||||||
|
Booting worker with pid: x
|
||||||
|
|
||||||
|
To use eventlet:
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
$ gunicorn -k eventlet 'hello:create_app()'
|
||||||
|
Starting gunicorn 20.1.0
|
||||||
|
Listening at: http://127.0.0.1:8000 (x)
|
||||||
|
Using worker: eventlet
|
||||||
|
Booting worker with pid: x
|
||||||
|
|
||||||
|
.. _gevent: https://www.gevent.org/
|
||||||
|
.. _eventlet: https://eventlet.net/
|
||||||
79
_build/_sources/deploying/index.rst.txt
Normal file
79
_build/_sources/deploying/index.rst.txt
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
Deploying to Production
|
||||||
|
=======================
|
||||||
|
|
||||||
|
After developing your application, you'll want to make it available
|
||||||
|
publicly to other users. When you're developing locally, you're probably
|
||||||
|
using the built-in development server, debugger, and reloader. These
|
||||||
|
should not be used in production. Instead, you should use a dedicated
|
||||||
|
WSGI server or hosting platform, some of which will be described here.
|
||||||
|
|
||||||
|
"Production" means "not development", which applies whether you're
|
||||||
|
serving your application publicly to millions of users or privately /
|
||||||
|
locally to a single user. **Do not use the development server when
|
||||||
|
deploying to production. It is intended for use only during local
|
||||||
|
development. It is not designed to be particularly secure, stable, or
|
||||||
|
efficient.**
|
||||||
|
|
||||||
|
Self-Hosted Options
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
Flask is a WSGI *application*. A WSGI *server* is used to run the
|
||||||
|
application, converting incoming HTTP requests to the standard WSGI
|
||||||
|
environ, and converting outgoing WSGI responses to HTTP responses.
|
||||||
|
|
||||||
|
The primary goal of these docs is to familiarize you with the concepts
|
||||||
|
involved in running a WSGI application using a production WSGI server
|
||||||
|
and HTTP server. There are many WSGI servers and HTTP servers, with many
|
||||||
|
configuration possibilities. The pages below discuss the most common
|
||||||
|
servers, and show the basics of running each one. The next section
|
||||||
|
discusses platforms that can manage this for you.
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 1
|
||||||
|
|
||||||
|
gunicorn
|
||||||
|
waitress
|
||||||
|
mod_wsgi
|
||||||
|
uwsgi
|
||||||
|
gevent
|
||||||
|
eventlet
|
||||||
|
asgi
|
||||||
|
|
||||||
|
WSGI servers have HTTP servers built-in. However, a dedicated HTTP
|
||||||
|
server may be safer, more efficient, or more capable. Putting an HTTP
|
||||||
|
server in front of the WSGI server is called a "reverse proxy."
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 1
|
||||||
|
|
||||||
|
proxy_fix
|
||||||
|
nginx
|
||||||
|
apache-httpd
|
||||||
|
|
||||||
|
This list is not exhaustive, and you should evaluate these and other
|
||||||
|
servers based on your application's needs. Different servers will have
|
||||||
|
different capabilities, configuration, and support.
|
||||||
|
|
||||||
|
|
||||||
|
Hosting Platforms
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
There are many services available for hosting web applications without
|
||||||
|
needing to maintain your own server, networking, domain, etc. Some
|
||||||
|
services may have a free tier up to a certain time or bandwidth. Many of
|
||||||
|
these services use one of the WSGI servers described above, or a similar
|
||||||
|
interface. The links below are for some of the most common platforms,
|
||||||
|
which have instructions for Flask, WSGI, or Python.
|
||||||
|
|
||||||
|
- `PythonAnywhere <https://help.pythonanywhere.com/pages/Flask/>`_
|
||||||
|
- `Google App Engine <https://cloud.google.com/appengine/docs/standard/python3/building-app>`_
|
||||||
|
- `Google Cloud Run <https://cloud.google.com/run/docs/quickstarts/build-and-deploy/deploy-python-service>`_
|
||||||
|
- `AWS Elastic Beanstalk <https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/create-deploy-python-flask.html>`_
|
||||||
|
- `Microsoft Azure <https://docs.microsoft.com/en-us/azure/app-service/quickstart-python>`_
|
||||||
|
|
||||||
|
This list is not exhaustive, and you should evaluate these and other
|
||||||
|
services based on your application's needs. Different services will have
|
||||||
|
different capabilities, configuration, pricing, and support.
|
||||||
|
|
||||||
|
You'll probably need to :doc:`proxy_fix` when using most hosting
|
||||||
|
platforms.
|
||||||
94
_build/_sources/deploying/mod_wsgi.rst.txt
Normal file
94
_build/_sources/deploying/mod_wsgi.rst.txt
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
mod_wsgi
|
||||||
|
========
|
||||||
|
|
||||||
|
`mod_wsgi`_ is a WSGI server integrated with the `Apache httpd`_ server.
|
||||||
|
The modern `mod_wsgi-express`_ command makes it easy to configure and
|
||||||
|
start the server without needing to write Apache httpd configuration.
|
||||||
|
|
||||||
|
* Tightly integrated with Apache httpd.
|
||||||
|
* Supports Windows directly.
|
||||||
|
* Requires a compiler and the Apache development headers to install.
|
||||||
|
* Does not require a reverse proxy setup.
|
||||||
|
|
||||||
|
This page outlines the basics of running mod_wsgi-express, not the more
|
||||||
|
complex installation and configuration with httpd. Be sure to read the
|
||||||
|
`mod_wsgi-express`_, `mod_wsgi`_, and `Apache httpd`_ documentation to
|
||||||
|
understand what features are available.
|
||||||
|
|
||||||
|
.. _mod_wsgi-express: https://pypi.org/project/mod-wsgi/
|
||||||
|
.. _mod_wsgi: https://modwsgi.readthedocs.io/
|
||||||
|
.. _Apache httpd: https://httpd.apache.org/
|
||||||
|
|
||||||
|
|
||||||
|
Installing
|
||||||
|
----------
|
||||||
|
|
||||||
|
Installing mod_wsgi requires a compiler and the Apache server and
|
||||||
|
development headers installed. You will get an error if they are not.
|
||||||
|
How to install them depends on the OS and package manager that you use.
|
||||||
|
|
||||||
|
Create a virtualenv, install your application, then install
|
||||||
|
``mod_wsgi``.
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
$ cd hello-app
|
||||||
|
$ python -m venv .venv
|
||||||
|
$ . .venv/bin/activate
|
||||||
|
$ pip install . # install your application
|
||||||
|
$ pip install mod_wsgi
|
||||||
|
|
||||||
|
|
||||||
|
Running
|
||||||
|
-------
|
||||||
|
|
||||||
|
The only argument to ``mod_wsgi-express`` specifies a script containing
|
||||||
|
your Flask application, which must be called ``application``. You can
|
||||||
|
write a small script to import your app with this name, or to create it
|
||||||
|
if using the app factory pattern.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
:caption: ``wsgi.py``
|
||||||
|
|
||||||
|
from hello import app
|
||||||
|
|
||||||
|
application = app
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
:caption: ``wsgi.py``
|
||||||
|
|
||||||
|
from hello import create_app
|
||||||
|
|
||||||
|
application = create_app()
|
||||||
|
|
||||||
|
Now run the ``mod_wsgi-express start-server`` command.
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
$ mod_wsgi-express start-server wsgi.py --processes 4
|
||||||
|
|
||||||
|
The ``--processes`` option specifies the number of worker processes to
|
||||||
|
run; a starting value could be ``CPU * 2``.
|
||||||
|
|
||||||
|
Logs for each request aren't show in the terminal. If an error occurs,
|
||||||
|
its information is written to the error log file shown when starting the
|
||||||
|
server.
|
||||||
|
|
||||||
|
|
||||||
|
Binding Externally
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Unlike the other WSGI servers in these docs, mod_wsgi can be run as
|
||||||
|
root to bind to privileged ports like 80 and 443. However, it must be
|
||||||
|
configured to drop permissions to a different user and group for the
|
||||||
|
worker processes.
|
||||||
|
|
||||||
|
For example, if you created a ``hello`` user and group, you should
|
||||||
|
install your virtualenv and application as that user, then tell
|
||||||
|
mod_wsgi to drop to that user after starting.
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
$ sudo /home/hello/.venv/bin/mod_wsgi-express start-server \
|
||||||
|
/home/hello/wsgi.py \
|
||||||
|
--user hello --group hello --port 80 --processes 4
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue