Agregar documentación con Sphinx
Signed-off-by: Edgar Alvarado Taleno <edgar.alvaradotaleno@ucr.ac.cr>
This commit is contained in:
parent
176b4fa719
commit
b3ae3117f9
159 changed files with 12034 additions and 13008 deletions
|
|
@ -6,7 +6,7 @@
|
|||
SPHINXOPTS ?=
|
||||
SPHINXBUILD ?= sphinx-build
|
||||
SOURCEDIR = source
|
||||
BUILDDIR = _build
|
||||
BUILDDIR = build
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
|
|
|
|||
717
docs/api.rst
717
docs/api.rst
|
|
@ -1,717 +0,0 @@
|
|||
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
|
||||
|
|
@ -1,147 +0,0 @@
|
|||
.. 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`.
|
||||
|
|
@ -1,125 +0,0 @@
|
|||
.. _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.
|
||||
|
|
@ -1,315 +0,0 @@
|
|||
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`.
|
||||
BIN
docs/build/doctrees/environment.pickle
vendored
Normal file
BIN
docs/build/doctrees/environment.pickle
vendored
Normal file
Binary file not shown.
BIN
docs/build/doctrees/flask.doctree
vendored
Normal file
BIN
docs/build/doctrees/flask.doctree
vendored
Normal file
Binary file not shown.
BIN
docs/build/doctrees/flask.json.doctree
vendored
Normal file
BIN
docs/build/doctrees/flask.json.doctree
vendored
Normal file
Binary file not shown.
BIN
docs/build/doctrees/index.doctree
vendored
Normal file
BIN
docs/build/doctrees/index.doctree
vendored
Normal file
Binary file not shown.
BIN
docs/build/doctrees/modules.doctree
vendored
Normal file
BIN
docs/build/doctrees/modules.doctree
vendored
Normal file
Binary file not shown.
4
docs/build/html/.buildinfo
vendored
Normal file
4
docs/build/html/.buildinfo
vendored
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: 21a39fdf72bffce870db8834b83d0887
|
||||
tags: 645f666f9bcd5a90fca523b33c5a78b7
|
||||
29
docs/build/html/_sources/flask.json.rst.txt
vendored
Normal file
29
docs/build/html/_sources/flask.json.rst.txt
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
flask.json package
|
||||
==================
|
||||
|
||||
Submodules
|
||||
----------
|
||||
|
||||
flask.json.provider module
|
||||
--------------------------
|
||||
|
||||
.. automodule:: flask.json.provider
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
flask.json.tag module
|
||||
---------------------
|
||||
|
||||
.. automodule:: flask.json.tag
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
Module contents
|
||||
---------------
|
||||
|
||||
.. automodule:: flask.json
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
149
docs/build/html/_sources/flask.rst.txt
vendored
Normal file
149
docs/build/html/_sources/flask.rst.txt
vendored
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
flask package
|
||||
=============
|
||||
|
||||
Subpackages
|
||||
-----------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 4
|
||||
|
||||
flask.json
|
||||
|
||||
Submodules
|
||||
----------
|
||||
|
||||
flask.app module
|
||||
----------------
|
||||
|
||||
.. automodule:: flask.app
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
flask.blueprints module
|
||||
-----------------------
|
||||
|
||||
.. automodule:: flask.blueprints
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
flask.cli module
|
||||
----------------
|
||||
|
||||
.. automodule:: flask.cli
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
flask.config module
|
||||
-------------------
|
||||
|
||||
.. automodule:: flask.config
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
flask.ctx module
|
||||
----------------
|
||||
|
||||
.. automodule:: flask.ctx
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
flask.debughelpers module
|
||||
-------------------------
|
||||
|
||||
.. automodule:: flask.debughelpers
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
flask.globals module
|
||||
--------------------
|
||||
|
||||
.. automodule:: flask.globals
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
flask.helpers module
|
||||
--------------------
|
||||
|
||||
.. automodule:: flask.helpers
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
flask.logging module
|
||||
--------------------
|
||||
|
||||
.. automodule:: flask.logging
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
flask.sessions module
|
||||
---------------------
|
||||
|
||||
.. automodule:: flask.sessions
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
flask.signals module
|
||||
--------------------
|
||||
|
||||
.. automodule:: flask.signals
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
flask.templating module
|
||||
-----------------------
|
||||
|
||||
.. automodule:: flask.templating
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
flask.testing module
|
||||
--------------------
|
||||
|
||||
.. automodule:: flask.testing
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
flask.typing module
|
||||
-------------------
|
||||
|
||||
.. automodule:: flask.typing
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
flask.views module
|
||||
------------------
|
||||
|
||||
.. automodule:: flask.views
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
flask.wrappers module
|
||||
---------------------
|
||||
|
||||
.. automodule:: flask.wrappers
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
Module contents
|
||||
---------------
|
||||
|
||||
.. automodule:: flask
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
16
docs/build/html/_sources/index.rst.txt
vendored
Normal file
16
docs/build/html/_sources/index.rst.txt
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
.. Flask documentation master file, created by
|
||||
sphinx-quickstart on Thu Apr 10 19:53:46 2025.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Flask - Documentación Personalizada
|
||||
====================================
|
||||
|
||||
Bienvenido a la documentación del proyecto Flask.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Contenido:
|
||||
|
||||
modules
|
||||
|
||||
7
docs/build/html/_sources/modules.rst.txt
vendored
Normal file
7
docs/build/html/_sources/modules.rst.txt
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
flask
|
||||
=====
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 4
|
||||
|
||||
flask
|
||||
123
docs/build/html/_static/_sphinx_javascript_frameworks_compat.js
vendored
Normal file
123
docs/build/html/_static/_sphinx_javascript_frameworks_compat.js
vendored
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
/* Compatability shim for jQuery and underscores.js.
|
||||
*
|
||||
* Copyright Sphinx contributors
|
||||
* Released under the two clause BSD licence
|
||||
*/
|
||||
|
||||
/**
|
||||
* small helper function to urldecode strings
|
||||
*
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL
|
||||
*/
|
||||
jQuery.urldecode = function(x) {
|
||||
if (!x) {
|
||||
return x
|
||||
}
|
||||
return decodeURIComponent(x.replace(/\+/g, ' '));
|
||||
};
|
||||
|
||||
/**
|
||||
* small helper function to urlencode strings
|
||||
*/
|
||||
jQuery.urlencode = encodeURIComponent;
|
||||
|
||||
/**
|
||||
* This function returns the parsed url parameters of the
|
||||
* current request. Multiple values per key are supported,
|
||||
* it will always return arrays of strings for the value parts.
|
||||
*/
|
||||
jQuery.getQueryParameters = function(s) {
|
||||
if (typeof s === 'undefined')
|
||||
s = document.location.search;
|
||||
var parts = s.substr(s.indexOf('?') + 1).split('&');
|
||||
var result = {};
|
||||
for (var i = 0; i < parts.length; i++) {
|
||||
var tmp = parts[i].split('=', 2);
|
||||
var key = jQuery.urldecode(tmp[0]);
|
||||
var value = jQuery.urldecode(tmp[1]);
|
||||
if (key in result)
|
||||
result[key].push(value);
|
||||
else
|
||||
result[key] = [value];
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* highlight a given string on a jquery object by wrapping it in
|
||||
* span elements with the given class name.
|
||||
*/
|
||||
jQuery.fn.highlightText = function(text, className) {
|
||||
function highlight(node, addItems) {
|
||||
if (node.nodeType === 3) {
|
||||
var val = node.nodeValue;
|
||||
var pos = val.toLowerCase().indexOf(text);
|
||||
if (pos >= 0 &&
|
||||
!jQuery(node.parentNode).hasClass(className) &&
|
||||
!jQuery(node.parentNode).hasClass("nohighlight")) {
|
||||
var span;
|
||||
var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg");
|
||||
if (isInSVG) {
|
||||
span = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
|
||||
} else {
|
||||
span = document.createElement("span");
|
||||
span.className = className;
|
||||
}
|
||||
span.appendChild(document.createTextNode(val.substr(pos, text.length)));
|
||||
node.parentNode.insertBefore(span, node.parentNode.insertBefore(
|
||||
document.createTextNode(val.substr(pos + text.length)),
|
||||
node.nextSibling));
|
||||
node.nodeValue = val.substr(0, pos);
|
||||
if (isInSVG) {
|
||||
var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
|
||||
var bbox = node.parentElement.getBBox();
|
||||
rect.x.baseVal.value = bbox.x;
|
||||
rect.y.baseVal.value = bbox.y;
|
||||
rect.width.baseVal.value = bbox.width;
|
||||
rect.height.baseVal.value = bbox.height;
|
||||
rect.setAttribute('class', className);
|
||||
addItems.push({
|
||||
"parent": node.parentNode,
|
||||
"target": rect});
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!jQuery(node).is("button, select, textarea")) {
|
||||
jQuery.each(node.childNodes, function() {
|
||||
highlight(this, addItems);
|
||||
});
|
||||
}
|
||||
}
|
||||
var addItems = [];
|
||||
var result = this.each(function() {
|
||||
highlight(this, addItems);
|
||||
});
|
||||
for (var i = 0; i < addItems.length; ++i) {
|
||||
jQuery(addItems[i].parent).before(addItems[i].target);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/*
|
||||
* backward compatibility for jQuery.browser
|
||||
* This will be supported until firefox bug is fixed.
|
||||
*/
|
||||
if (!jQuery.browser) {
|
||||
jQuery.uaMatch = function(ua) {
|
||||
ua = ua.toLowerCase();
|
||||
|
||||
var match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
|
||||
/(webkit)[ \/]([\w.]+)/.exec(ua) ||
|
||||
/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
|
||||
/(msie) ([\w.]+)/.exec(ua) ||
|
||||
ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) ||
|
||||
[];
|
||||
|
||||
return {
|
||||
browser: match[ 1 ] || "",
|
||||
version: match[ 2 ] || "0"
|
||||
};
|
||||
};
|
||||
jQuery.browser = {};
|
||||
jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true;
|
||||
}
|
||||
914
docs/build/html/_static/basic.css
vendored
Normal file
914
docs/build/html/_static/basic.css
vendored
Normal file
|
|
@ -0,0 +1,914 @@
|
|||
/*
|
||||
* Sphinx stylesheet -- basic theme.
|
||||
*/
|
||||
|
||||
/* -- main layout ----------------------------------------------------------- */
|
||||
|
||||
div.clearer {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
div.section::after {
|
||||
display: block;
|
||||
content: '';
|
||||
clear: left;
|
||||
}
|
||||
|
||||
/* -- relbar ---------------------------------------------------------------- */
|
||||
|
||||
div.related {
|
||||
width: 100%;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
div.related h3 {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.related ul {
|
||||
margin: 0;
|
||||
padding: 0 0 0 10px;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
div.related li {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
div.related li.right {
|
||||
float: right;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
/* -- sidebar --------------------------------------------------------------- */
|
||||
|
||||
div.sphinxsidebarwrapper {
|
||||
padding: 10px 5px 0 10px;
|
||||
}
|
||||
|
||||
div.sphinxsidebar {
|
||||
float: left;
|
||||
width: 230px;
|
||||
margin-left: -100%;
|
||||
font-size: 90%;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap : break-word;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul ul,
|
||||
div.sphinxsidebar ul.want-points {
|
||||
margin-left: 20px;
|
||||
list-style: square;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul ul {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
div.sphinxsidebar form {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
div.sphinxsidebar input {
|
||||
border: 1px solid #98dbcc;
|
||||
font-family: sans-serif;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
div.sphinxsidebar #searchbox form.search {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
div.sphinxsidebar #searchbox input[type="text"] {
|
||||
float: left;
|
||||
width: 80%;
|
||||
padding: 0.25em;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
div.sphinxsidebar #searchbox input[type="submit"] {
|
||||
float: left;
|
||||
width: 20%;
|
||||
border-left: none;
|
||||
padding: 0.25em;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
|
||||
img {
|
||||
border: 0;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/* -- search page ----------------------------------------------------------- */
|
||||
|
||||
ul.search {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
ul.search li {
|
||||
padding: 5px 0;
|
||||
}
|
||||
|
||||
ul.search li a {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
ul.search li p.context {
|
||||
color: #888;
|
||||
margin: 2px 0 0 30px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
ul.keywordmatches li.goodmatch a {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* -- index page ------------------------------------------------------------ */
|
||||
|
||||
table.contentstable {
|
||||
width: 90%;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
table.contentstable p.biglink {
|
||||
line-height: 150%;
|
||||
}
|
||||
|
||||
a.biglink {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
span.linkdescr {
|
||||
font-style: italic;
|
||||
padding-top: 5px;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
/* -- general index --------------------------------------------------------- */
|
||||
|
||||
table.indextable {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
table.indextable td {
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
table.indextable ul {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
table.indextable > tbody > tr > td > ul {
|
||||
padding-left: 0em;
|
||||
}
|
||||
|
||||
table.indextable tr.pcap {
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
table.indextable tr.cap {
|
||||
margin-top: 10px;
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
|
||||
img.toggler {
|
||||
margin-right: 3px;
|
||||
margin-top: 3px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
div.modindex-jumpbox {
|
||||
border-top: 1px solid #ddd;
|
||||
border-bottom: 1px solid #ddd;
|
||||
margin: 1em 0 1em 0;
|
||||
padding: 0.4em;
|
||||
}
|
||||
|
||||
div.genindex-jumpbox {
|
||||
border-top: 1px solid #ddd;
|
||||
border-bottom: 1px solid #ddd;
|
||||
margin: 1em 0 1em 0;
|
||||
padding: 0.4em;
|
||||
}
|
||||
|
||||
/* -- domain module index --------------------------------------------------- */
|
||||
|
||||
table.modindextable td {
|
||||
padding: 2px;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
/* -- general body styles --------------------------------------------------- */
|
||||
|
||||
div.body {
|
||||
min-width: 360px;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
div.body p, div.body dd, div.body li, div.body blockquote {
|
||||
-moz-hyphens: auto;
|
||||
-ms-hyphens: auto;
|
||||
-webkit-hyphens: auto;
|
||||
hyphens: auto;
|
||||
}
|
||||
|
||||
a.headerlink {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: #551A8B;
|
||||
}
|
||||
|
||||
h1:hover > a.headerlink,
|
||||
h2:hover > a.headerlink,
|
||||
h3:hover > a.headerlink,
|
||||
h4:hover > a.headerlink,
|
||||
h5:hover > a.headerlink,
|
||||
h6:hover > a.headerlink,
|
||||
dt:hover > a.headerlink,
|
||||
caption:hover > a.headerlink,
|
||||
p.caption:hover > a.headerlink,
|
||||
div.code-block-caption:hover > a.headerlink {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
div.body p.caption {
|
||||
text-align: inherit;
|
||||
}
|
||||
|
||||
div.body td {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.first {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
|
||||
p.rubric {
|
||||
margin-top: 30px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
img.align-left, figure.align-left, .figure.align-left, object.align-left {
|
||||
clear: left;
|
||||
float: left;
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
img.align-right, figure.align-right, .figure.align-right, object.align-right {
|
||||
clear: right;
|
||||
float: right;
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
img.align-center, figure.align-center, .figure.align-center, object.align-center {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
img.align-default, figure.align-default, .figure.align-default {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.align-left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.align-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.align-default {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.align-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* -- sidebars -------------------------------------------------------------- */
|
||||
|
||||
div.sidebar,
|
||||
aside.sidebar {
|
||||
margin: 0 0 0.5em 1em;
|
||||
border: 1px solid #ddb;
|
||||
padding: 7px;
|
||||
background-color: #ffe;
|
||||
width: 40%;
|
||||
float: right;
|
||||
clear: right;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
p.sidebar-title {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
nav.contents,
|
||||
aside.topic,
|
||||
div.admonition, div.topic, blockquote {
|
||||
clear: left;
|
||||
}
|
||||
|
||||
/* -- topics ---------------------------------------------------------------- */
|
||||
|
||||
nav.contents,
|
||||
aside.topic,
|
||||
div.topic {
|
||||
border: 1px solid #ccc;
|
||||
padding: 7px;
|
||||
margin: 10px 0 10px 0;
|
||||
}
|
||||
|
||||
p.topic-title {
|
||||
font-size: 1.1em;
|
||||
font-weight: bold;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/* -- admonitions ----------------------------------------------------------- */
|
||||
|
||||
div.admonition {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
padding: 7px;
|
||||
}
|
||||
|
||||
div.admonition dt {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
p.admonition-title {
|
||||
margin: 0px 10px 5px 0px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div.body p.centered {
|
||||
text-align: center;
|
||||
margin-top: 25px;
|
||||
}
|
||||
|
||||
/* -- content of sidebars/topics/admonitions -------------------------------- */
|
||||
|
||||
div.sidebar > :last-child,
|
||||
aside.sidebar > :last-child,
|
||||
nav.contents > :last-child,
|
||||
aside.topic > :last-child,
|
||||
div.topic > :last-child,
|
||||
div.admonition > :last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
div.sidebar::after,
|
||||
aside.sidebar::after,
|
||||
nav.contents::after,
|
||||
aside.topic::after,
|
||||
div.topic::after,
|
||||
div.admonition::after,
|
||||
blockquote::after {
|
||||
display: block;
|
||||
content: '';
|
||||
clear: both;
|
||||
}
|
||||
|
||||
/* -- tables ---------------------------------------------------------------- */
|
||||
|
||||
table.docutils {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
border: 0;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
table.align-center {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
table.align-default {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
table caption span.caption-number {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
table caption span.caption-text {
|
||||
}
|
||||
|
||||
table.docutils td, table.docutils th {
|
||||
padding: 1px 8px 1px 5px;
|
||||
border-top: 0;
|
||||
border-left: 0;
|
||||
border-right: 0;
|
||||
border-bottom: 1px solid #aaa;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
table.citation {
|
||||
border-left: solid 1px gray;
|
||||
margin-left: 1px;
|
||||
}
|
||||
|
||||
table.citation td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
th > :first-child,
|
||||
td > :first-child {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
th > :last-child,
|
||||
td > :last-child {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
/* -- figures --------------------------------------------------------------- */
|
||||
|
||||
div.figure, figure {
|
||||
margin: 0.5em;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
div.figure p.caption, figcaption {
|
||||
padding: 0.3em;
|
||||
}
|
||||
|
||||
div.figure p.caption span.caption-number,
|
||||
figcaption span.caption-number {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
div.figure p.caption span.caption-text,
|
||||
figcaption span.caption-text {
|
||||
}
|
||||
|
||||
/* -- field list styles ----------------------------------------------------- */
|
||||
|
||||
table.field-list td, table.field-list th {
|
||||
border: 0 !important;
|
||||
}
|
||||
|
||||
.field-list ul {
|
||||
margin: 0;
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
.field-list p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.field-name {
|
||||
-moz-hyphens: manual;
|
||||
-ms-hyphens: manual;
|
||||
-webkit-hyphens: manual;
|
||||
hyphens: manual;
|
||||
}
|
||||
|
||||
/* -- hlist styles ---------------------------------------------------------- */
|
||||
|
||||
table.hlist {
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
table.hlist td {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
/* -- object description styles --------------------------------------------- */
|
||||
|
||||
.sig {
|
||||
font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
|
||||
}
|
||||
|
||||
.sig-name, code.descname {
|
||||
background-color: transparent;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.sig-name {
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
code.descname {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.sig-prename, code.descclassname {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.optional {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
.sig-paren {
|
||||
font-size: larger;
|
||||
}
|
||||
|
||||
.sig-param.n {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* C++ specific styling */
|
||||
|
||||
.sig-inline.c-texpr,
|
||||
.sig-inline.cpp-texpr {
|
||||
font-family: unset;
|
||||
}
|
||||
|
||||
.sig.c .k, .sig.c .kt,
|
||||
.sig.cpp .k, .sig.cpp .kt {
|
||||
color: #0033B3;
|
||||
}
|
||||
|
||||
.sig.c .m,
|
||||
.sig.cpp .m {
|
||||
color: #1750EB;
|
||||
}
|
||||
|
||||
.sig.c .s, .sig.c .sc,
|
||||
.sig.cpp .s, .sig.cpp .sc {
|
||||
color: #067D17;
|
||||
}
|
||||
|
||||
|
||||
/* -- other body styles ----------------------------------------------------- */
|
||||
|
||||
ol.arabic {
|
||||
list-style: decimal;
|
||||
}
|
||||
|
||||
ol.loweralpha {
|
||||
list-style: lower-alpha;
|
||||
}
|
||||
|
||||
ol.upperalpha {
|
||||
list-style: upper-alpha;
|
||||
}
|
||||
|
||||
ol.lowerroman {
|
||||
list-style: lower-roman;
|
||||
}
|
||||
|
||||
ol.upperroman {
|
||||
list-style: upper-roman;
|
||||
}
|
||||
|
||||
:not(li) > ol > li:first-child > :first-child,
|
||||
:not(li) > ul > li:first-child > :first-child {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
:not(li) > ol > li:last-child > :last-child,
|
||||
:not(li) > ul > li:last-child > :last-child {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
ol.simple ol p,
|
||||
ol.simple ul p,
|
||||
ul.simple ol p,
|
||||
ul.simple ul p {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
ol.simple > li:not(:first-child) > p,
|
||||
ul.simple > li:not(:first-child) > p {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
ol.simple p,
|
||||
ul.simple p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
aside.footnote > span,
|
||||
div.citation > span {
|
||||
float: left;
|
||||
}
|
||||
aside.footnote > span:last-of-type,
|
||||
div.citation > span:last-of-type {
|
||||
padding-right: 0.5em;
|
||||
}
|
||||
aside.footnote > p {
|
||||
margin-left: 2em;
|
||||
}
|
||||
div.citation > p {
|
||||
margin-left: 4em;
|
||||
}
|
||||
aside.footnote > p:last-of-type,
|
||||
div.citation > p:last-of-type {
|
||||
margin-bottom: 0em;
|
||||
}
|
||||
aside.footnote > p:last-of-type:after,
|
||||
div.citation > p:last-of-type:after {
|
||||
content: "";
|
||||
clear: both;
|
||||
}
|
||||
|
||||
dl.field-list {
|
||||
display: grid;
|
||||
grid-template-columns: fit-content(30%) auto;
|
||||
}
|
||||
|
||||
dl.field-list > dt {
|
||||
font-weight: bold;
|
||||
word-break: break-word;
|
||||
padding-left: 0.5em;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
dl.field-list > dd {
|
||||
padding-left: 0.5em;
|
||||
margin-top: 0em;
|
||||
margin-left: 0em;
|
||||
margin-bottom: 0em;
|
||||
}
|
||||
|
||||
dl {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
dd > :first-child {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
dd ul, dd table {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-top: 3px;
|
||||
margin-bottom: 10px;
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
.sig dd {
|
||||
margin-top: 0px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.sig dl {
|
||||
margin-top: 0px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
dl > dd:last-child,
|
||||
dl > dd:last-child > :last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
dt:target, span.highlighted {
|
||||
background-color: #fbe54e;
|
||||
}
|
||||
|
||||
rect.highlighted {
|
||||
fill: #fbe54e;
|
||||
}
|
||||
|
||||
dl.glossary dt {
|
||||
font-weight: bold;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.versionmodified {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.system-message {
|
||||
background-color: #fda;
|
||||
padding: 5px;
|
||||
border: 3px solid red;
|
||||
}
|
||||
|
||||
.footnote:target {
|
||||
background-color: #ffa;
|
||||
}
|
||||
|
||||
.line-block {
|
||||
display: block;
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.line-block .line-block {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
margin-left: 1.5em;
|
||||
}
|
||||
|
||||
.guilabel, .menuselection {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
.accelerator {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.classifier {
|
||||
font-style: oblique;
|
||||
}
|
||||
|
||||
.classifier:before {
|
||||
font-style: normal;
|
||||
margin: 0 0.5em;
|
||||
content: ":";
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
abbr, acronym {
|
||||
border-bottom: dotted 1px;
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
.translated {
|
||||
background-color: rgba(207, 255, 207, 0.2)
|
||||
}
|
||||
|
||||
.untranslated {
|
||||
background-color: rgba(255, 207, 207, 0.2)
|
||||
}
|
||||
|
||||
/* -- code displays --------------------------------------------------------- */
|
||||
|
||||
pre {
|
||||
overflow: auto;
|
||||
overflow-y: hidden; /* fixes display issues on Chrome browsers */
|
||||
}
|
||||
|
||||
pre, div[class*="highlight-"] {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
span.pre {
|
||||
-moz-hyphens: none;
|
||||
-ms-hyphens: none;
|
||||
-webkit-hyphens: none;
|
||||
hyphens: none;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
div[class*="highlight-"] {
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
td.linenos pre {
|
||||
border: 0;
|
||||
background-color: transparent;
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
table.highlighttable {
|
||||
display: block;
|
||||
}
|
||||
|
||||
table.highlighttable tbody {
|
||||
display: block;
|
||||
}
|
||||
|
||||
table.highlighttable tr {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
table.highlighttable td {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
table.highlighttable td.linenos {
|
||||
padding-right: 0.5em;
|
||||
}
|
||||
|
||||
table.highlighttable td.code {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.highlight .hll {
|
||||
display: block;
|
||||
}
|
||||
|
||||
div.highlight pre,
|
||||
table.highlighttable pre {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
div.code-block-caption + div {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
div.code-block-caption {
|
||||
margin-top: 1em;
|
||||
padding: 2px 5px;
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
div.code-block-caption code {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
table.highlighttable td.linenos,
|
||||
span.linenos,
|
||||
div.highlight span.gp { /* gp: Generic.Prompt */
|
||||
user-select: none;
|
||||
-webkit-user-select: text; /* Safari fallback only */
|
||||
-webkit-user-select: none; /* Chrome/Safari */
|
||||
-moz-user-select: none; /* Firefox */
|
||||
-ms-user-select: none; /* IE10+ */
|
||||
}
|
||||
|
||||
div.code-block-caption span.caption-number {
|
||||
padding: 0.1em 0.3em;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
div.code-block-caption span.caption-text {
|
||||
}
|
||||
|
||||
div.literal-block-wrapper {
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
code.xref, a code {
|
||||
background-color: transparent;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h1 code, h2 code, h3 code, h4 code, h5 code, h6 code {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.viewcode-link {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.viewcode-back {
|
||||
float: right;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
div.viewcode-block:target {
|
||||
margin: -1px -10px;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
/* -- math display ---------------------------------------------------------- */
|
||||
|
||||
img.math {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
div.body div.math p {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
span.eqno {
|
||||
float: right;
|
||||
}
|
||||
|
||||
span.eqno a.headerlink {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
div.math:hover a.headerlink {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
/* -- printout stylesheet --------------------------------------------------- */
|
||||
|
||||
@media print {
|
||||
div.document,
|
||||
div.documentwrapper,
|
||||
div.bodywrapper {
|
||||
margin: 0 !important;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div.sphinxsidebar,
|
||||
div.related,
|
||||
div.footer,
|
||||
#top-link {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
1
docs/build/html/_static/css/badge_only.css
vendored
Normal file
1
docs/build/html/_static/css/badge_only.css
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-style:normal;font-weight:400;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#FontAwesome) format("svg")}.fa:before{font-family:FontAwesome;font-style:normal;font-weight:400;line-height:1}.fa:before,a .fa{text-decoration:inherit}.fa:before,a .fa,li .fa{display:inline-block}li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-caret-down:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before,.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before,.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before,.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60}.rst-versions .rst-current-version:after{clear:both;content:"";display:block}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions .rst-other-versions .rtd-current-item{font-weight:700}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}}#flyout-search-form{padding:6px}
|
||||
BIN
docs/build/html/_static/css/fonts/Roboto-Slab-Bold.woff
vendored
Normal file
BIN
docs/build/html/_static/css/fonts/Roboto-Slab-Bold.woff
vendored
Normal file
Binary file not shown.
BIN
docs/build/html/_static/css/fonts/Roboto-Slab-Bold.woff2
vendored
Normal file
BIN
docs/build/html/_static/css/fonts/Roboto-Slab-Bold.woff2
vendored
Normal file
Binary file not shown.
BIN
docs/build/html/_static/css/fonts/Roboto-Slab-Regular.woff
vendored
Normal file
BIN
docs/build/html/_static/css/fonts/Roboto-Slab-Regular.woff
vendored
Normal file
Binary file not shown.
BIN
docs/build/html/_static/css/fonts/Roboto-Slab-Regular.woff2
vendored
Normal file
BIN
docs/build/html/_static/css/fonts/Roboto-Slab-Regular.woff2
vendored
Normal file
Binary file not shown.
BIN
docs/build/html/_static/css/fonts/fontawesome-webfont.eot
vendored
Normal file
BIN
docs/build/html/_static/css/fonts/fontawesome-webfont.eot
vendored
Normal file
Binary file not shown.
2671
docs/build/html/_static/css/fonts/fontawesome-webfont.svg
vendored
Normal file
2671
docs/build/html/_static/css/fonts/fontawesome-webfont.svg
vendored
Normal file
File diff suppressed because it is too large
Load diff
|
After Width: | Height: | Size: 434 KiB |
BIN
docs/build/html/_static/css/fonts/fontawesome-webfont.ttf
vendored
Normal file
BIN
docs/build/html/_static/css/fonts/fontawesome-webfont.ttf
vendored
Normal file
Binary file not shown.
BIN
docs/build/html/_static/css/fonts/fontawesome-webfont.woff
vendored
Normal file
BIN
docs/build/html/_static/css/fonts/fontawesome-webfont.woff
vendored
Normal file
Binary file not shown.
BIN
docs/build/html/_static/css/fonts/fontawesome-webfont.woff2
vendored
Normal file
BIN
docs/build/html/_static/css/fonts/fontawesome-webfont.woff2
vendored
Normal file
Binary file not shown.
BIN
docs/build/html/_static/css/fonts/lato-bold-italic.woff
vendored
Normal file
BIN
docs/build/html/_static/css/fonts/lato-bold-italic.woff
vendored
Normal file
Binary file not shown.
BIN
docs/build/html/_static/css/fonts/lato-bold-italic.woff2
vendored
Normal file
BIN
docs/build/html/_static/css/fonts/lato-bold-italic.woff2
vendored
Normal file
Binary file not shown.
BIN
docs/build/html/_static/css/fonts/lato-bold.woff
vendored
Normal file
BIN
docs/build/html/_static/css/fonts/lato-bold.woff
vendored
Normal file
Binary file not shown.
BIN
docs/build/html/_static/css/fonts/lato-bold.woff2
vendored
Normal file
BIN
docs/build/html/_static/css/fonts/lato-bold.woff2
vendored
Normal file
Binary file not shown.
BIN
docs/build/html/_static/css/fonts/lato-normal-italic.woff
vendored
Normal file
BIN
docs/build/html/_static/css/fonts/lato-normal-italic.woff
vendored
Normal file
Binary file not shown.
BIN
docs/build/html/_static/css/fonts/lato-normal-italic.woff2
vendored
Normal file
BIN
docs/build/html/_static/css/fonts/lato-normal-italic.woff2
vendored
Normal file
Binary file not shown.
BIN
docs/build/html/_static/css/fonts/lato-normal.woff
vendored
Normal file
BIN
docs/build/html/_static/css/fonts/lato-normal.woff
vendored
Normal file
Binary file not shown.
BIN
docs/build/html/_static/css/fonts/lato-normal.woff2
vendored
Normal file
BIN
docs/build/html/_static/css/fonts/lato-normal.woff2
vendored
Normal file
Binary file not shown.
4
docs/build/html/_static/css/theme.css
vendored
Normal file
4
docs/build/html/_static/css/theme.css
vendored
Normal file
File diff suppressed because one or more lines are too long
149
docs/build/html/_static/doctools.js
vendored
Normal file
149
docs/build/html/_static/doctools.js
vendored
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
* Base JavaScript utilities for all Sphinx HTML documentation.
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([
|
||||
"TEXTAREA",
|
||||
"INPUT",
|
||||
"SELECT",
|
||||
"BUTTON",
|
||||
]);
|
||||
|
||||
const _ready = (callback) => {
|
||||
if (document.readyState !== "loading") {
|
||||
callback();
|
||||
} else {
|
||||
document.addEventListener("DOMContentLoaded", callback);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Small JavaScript module for the documentation.
|
||||
*/
|
||||
const Documentation = {
|
||||
init: () => {
|
||||
Documentation.initDomainIndexTable();
|
||||
Documentation.initOnKeyListeners();
|
||||
},
|
||||
|
||||
/**
|
||||
* i18n support
|
||||
*/
|
||||
TRANSLATIONS: {},
|
||||
PLURAL_EXPR: (n) => (n === 1 ? 0 : 1),
|
||||
LOCALE: "unknown",
|
||||
|
||||
// gettext and ngettext don't access this so that the functions
|
||||
// can safely bound to a different name (_ = Documentation.gettext)
|
||||
gettext: (string) => {
|
||||
const translated = Documentation.TRANSLATIONS[string];
|
||||
switch (typeof translated) {
|
||||
case "undefined":
|
||||
return string; // no translation
|
||||
case "string":
|
||||
return translated; // translation exists
|
||||
default:
|
||||
return translated[0]; // (singular, plural) translation tuple exists
|
||||
}
|
||||
},
|
||||
|
||||
ngettext: (singular, plural, n) => {
|
||||
const translated = Documentation.TRANSLATIONS[singular];
|
||||
if (typeof translated !== "undefined")
|
||||
return translated[Documentation.PLURAL_EXPR(n)];
|
||||
return n === 1 ? singular : plural;
|
||||
},
|
||||
|
||||
addTranslations: (catalog) => {
|
||||
Object.assign(Documentation.TRANSLATIONS, catalog.messages);
|
||||
Documentation.PLURAL_EXPR = new Function(
|
||||
"n",
|
||||
`return (${catalog.plural_expr})`
|
||||
);
|
||||
Documentation.LOCALE = catalog.locale;
|
||||
},
|
||||
|
||||
/**
|
||||
* helper function to focus on search bar
|
||||
*/
|
||||
focusSearchBar: () => {
|
||||
document.querySelectorAll("input[name=q]")[0]?.focus();
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialise the domain index toggle buttons
|
||||
*/
|
||||
initDomainIndexTable: () => {
|
||||
const toggler = (el) => {
|
||||
const idNumber = el.id.substr(7);
|
||||
const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`);
|
||||
if (el.src.substr(-9) === "minus.png") {
|
||||
el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`;
|
||||
toggledRows.forEach((el) => (el.style.display = "none"));
|
||||
} else {
|
||||
el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`;
|
||||
toggledRows.forEach((el) => (el.style.display = ""));
|
||||
}
|
||||
};
|
||||
|
||||
const togglerElements = document.querySelectorAll("img.toggler");
|
||||
togglerElements.forEach((el) =>
|
||||
el.addEventListener("click", (event) => toggler(event.currentTarget))
|
||||
);
|
||||
togglerElements.forEach((el) => (el.style.display = ""));
|
||||
if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler);
|
||||
},
|
||||
|
||||
initOnKeyListeners: () => {
|
||||
// only install a listener if it is really needed
|
||||
if (
|
||||
!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS &&
|
||||
!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS
|
||||
)
|
||||
return;
|
||||
|
||||
document.addEventListener("keydown", (event) => {
|
||||
// bail for input elements
|
||||
if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return;
|
||||
// bail with special keys
|
||||
if (event.altKey || event.ctrlKey || event.metaKey) return;
|
||||
|
||||
if (!event.shiftKey) {
|
||||
switch (event.key) {
|
||||
case "ArrowLeft":
|
||||
if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break;
|
||||
|
||||
const prevLink = document.querySelector('link[rel="prev"]');
|
||||
if (prevLink && prevLink.href) {
|
||||
window.location.href = prevLink.href;
|
||||
event.preventDefault();
|
||||
}
|
||||
break;
|
||||
case "ArrowRight":
|
||||
if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break;
|
||||
|
||||
const nextLink = document.querySelector('link[rel="next"]');
|
||||
if (nextLink && nextLink.href) {
|
||||
window.location.href = nextLink.href;
|
||||
event.preventDefault();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// some keyboard layouts may need Shift to get /
|
||||
switch (event.key) {
|
||||
case "/":
|
||||
if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break;
|
||||
Documentation.focusSearchBar();
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
// quick alias for translations
|
||||
const _ = Documentation.gettext;
|
||||
|
||||
_ready(Documentation.init);
|
||||
13
docs/build/html/_static/documentation_options.js
vendored
Normal file
13
docs/build/html/_static/documentation_options.js
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
const DOCUMENTATION_OPTIONS = {
|
||||
VERSION: '',
|
||||
LANGUAGE: 'en',
|
||||
COLLAPSE_INDEX: false,
|
||||
BUILDER: 'html',
|
||||
FILE_SUFFIX: '.html',
|
||||
LINK_SUFFIX: '.html',
|
||||
HAS_SOURCE: true,
|
||||
SOURCELINK_SUFFIX: '.txt',
|
||||
NAVIGATION_WITH_KEYS: false,
|
||||
SHOW_SEARCH_SUMMARY: true,
|
||||
ENABLE_SEARCH_SHORTCUTS: true,
|
||||
};
|
||||
BIN
docs/build/html/_static/file.png
vendored
Normal file
BIN
docs/build/html/_static/file.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 286 B |
BIN
docs/build/html/_static/fonts/Lato/lato-bold.eot
vendored
Normal file
BIN
docs/build/html/_static/fonts/Lato/lato-bold.eot
vendored
Normal file
Binary file not shown.
BIN
docs/build/html/_static/fonts/Lato/lato-bold.ttf
vendored
Normal file
BIN
docs/build/html/_static/fonts/Lato/lato-bold.ttf
vendored
Normal file
Binary file not shown.
BIN
docs/build/html/_static/fonts/Lato/lato-bold.woff
vendored
Normal file
BIN
docs/build/html/_static/fonts/Lato/lato-bold.woff
vendored
Normal file
Binary file not shown.
BIN
docs/build/html/_static/fonts/Lato/lato-bold.woff2
vendored
Normal file
BIN
docs/build/html/_static/fonts/Lato/lato-bold.woff2
vendored
Normal file
Binary file not shown.
BIN
docs/build/html/_static/fonts/Lato/lato-bolditalic.eot
vendored
Normal file
BIN
docs/build/html/_static/fonts/Lato/lato-bolditalic.eot
vendored
Normal file
Binary file not shown.
BIN
docs/build/html/_static/fonts/Lato/lato-bolditalic.ttf
vendored
Normal file
BIN
docs/build/html/_static/fonts/Lato/lato-bolditalic.ttf
vendored
Normal file
Binary file not shown.
BIN
docs/build/html/_static/fonts/Lato/lato-bolditalic.woff
vendored
Normal file
BIN
docs/build/html/_static/fonts/Lato/lato-bolditalic.woff
vendored
Normal file
Binary file not shown.
BIN
docs/build/html/_static/fonts/Lato/lato-bolditalic.woff2
vendored
Normal file
BIN
docs/build/html/_static/fonts/Lato/lato-bolditalic.woff2
vendored
Normal file
Binary file not shown.
BIN
docs/build/html/_static/fonts/Lato/lato-italic.eot
vendored
Normal file
BIN
docs/build/html/_static/fonts/Lato/lato-italic.eot
vendored
Normal file
Binary file not shown.
BIN
docs/build/html/_static/fonts/Lato/lato-italic.ttf
vendored
Normal file
BIN
docs/build/html/_static/fonts/Lato/lato-italic.ttf
vendored
Normal file
Binary file not shown.
BIN
docs/build/html/_static/fonts/Lato/lato-italic.woff
vendored
Normal file
BIN
docs/build/html/_static/fonts/Lato/lato-italic.woff
vendored
Normal file
Binary file not shown.
BIN
docs/build/html/_static/fonts/Lato/lato-italic.woff2
vendored
Normal file
BIN
docs/build/html/_static/fonts/Lato/lato-italic.woff2
vendored
Normal file
Binary file not shown.
BIN
docs/build/html/_static/fonts/Lato/lato-regular.eot
vendored
Normal file
BIN
docs/build/html/_static/fonts/Lato/lato-regular.eot
vendored
Normal file
Binary file not shown.
BIN
docs/build/html/_static/fonts/Lato/lato-regular.ttf
vendored
Normal file
BIN
docs/build/html/_static/fonts/Lato/lato-regular.ttf
vendored
Normal file
Binary file not shown.
BIN
docs/build/html/_static/fonts/Lato/lato-regular.woff
vendored
Normal file
BIN
docs/build/html/_static/fonts/Lato/lato-regular.woff
vendored
Normal file
Binary file not shown.
BIN
docs/build/html/_static/fonts/Lato/lato-regular.woff2
vendored
Normal file
BIN
docs/build/html/_static/fonts/Lato/lato-regular.woff2
vendored
Normal file
Binary file not shown.
BIN
docs/build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.eot
vendored
Normal file
BIN
docs/build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.eot
vendored
Normal file
Binary file not shown.
BIN
docs/build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.ttf
vendored
Normal file
BIN
docs/build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.ttf
vendored
Normal file
Binary file not shown.
BIN
docs/build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff
vendored
Normal file
BIN
docs/build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff
vendored
Normal file
Binary file not shown.
BIN
docs/build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff2
vendored
Normal file
BIN
docs/build/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff2
vendored
Normal file
Binary file not shown.
BIN
docs/build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.eot
vendored
Normal file
BIN
docs/build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.eot
vendored
Normal file
Binary file not shown.
BIN
docs/build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.ttf
vendored
Normal file
BIN
docs/build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.ttf
vendored
Normal file
Binary file not shown.
BIN
docs/build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff
vendored
Normal file
BIN
docs/build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff
vendored
Normal file
Binary file not shown.
BIN
docs/build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff2
vendored
Normal file
BIN
docs/build/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff2
vendored
Normal file
Binary file not shown.
2
docs/build/html/_static/jquery.js
vendored
Normal file
2
docs/build/html/_static/jquery.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
docs/build/html/_static/js/badge_only.js
vendored
Normal file
1
docs/build/html/_static/js/badge_only.js
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
!function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=4)}({4:function(e,t,r){}});
|
||||
1
docs/build/html/_static/js/theme.js
vendored
Normal file
1
docs/build/html/_static/js/theme.js
vendored
Normal file
File diff suppressed because one or more lines are too long
228
docs/build/html/_static/js/versions.js
vendored
Normal file
228
docs/build/html/_static/js/versions.js
vendored
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
const themeFlyoutDisplay = "hidden";
|
||||
const themeVersionSelector = true;
|
||||
const themeLanguageSelector = true;
|
||||
|
||||
if (themeFlyoutDisplay === "attached") {
|
||||
function renderLanguages(config) {
|
||||
if (!config.projects.translations.length) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Insert the current language to the options on the selector
|
||||
let languages = config.projects.translations.concat(config.projects.current);
|
||||
languages = languages.sort((a, b) => a.language.name.localeCompare(b.language.name));
|
||||
|
||||
const languagesHTML = `
|
||||
<dl>
|
||||
<dt>Languages</dt>
|
||||
${languages
|
||||
.map(
|
||||
(translation) => `
|
||||
<dd ${translation.slug == config.projects.current.slug ? 'class="rtd-current-item"' : ""}>
|
||||
<a href="${translation.urls.documentation}">${translation.language.code}</a>
|
||||
</dd>
|
||||
`,
|
||||
)
|
||||
.join("\n")}
|
||||
</dl>
|
||||
`;
|
||||
return languagesHTML;
|
||||
}
|
||||
|
||||
function renderVersions(config) {
|
||||
if (!config.versions.active.length) {
|
||||
return "";
|
||||
}
|
||||
const versionsHTML = `
|
||||
<dl>
|
||||
<dt>Versions</dt>
|
||||
${config.versions.active
|
||||
.map(
|
||||
(version) => `
|
||||
<dd ${version.slug === config.versions.current.slug ? 'class="rtd-current-item"' : ""}>
|
||||
<a href="${version.urls.documentation}">${version.slug}</a>
|
||||
</dd>
|
||||
`,
|
||||
)
|
||||
.join("\n")}
|
||||
</dl>
|
||||
`;
|
||||
return versionsHTML;
|
||||
}
|
||||
|
||||
function renderDownloads(config) {
|
||||
if (!Object.keys(config.versions.current.downloads).length) {
|
||||
return "";
|
||||
}
|
||||
const downloadsNameDisplay = {
|
||||
pdf: "PDF",
|
||||
epub: "Epub",
|
||||
htmlzip: "HTML",
|
||||
};
|
||||
|
||||
const downloadsHTML = `
|
||||
<dl>
|
||||
<dt>Downloads</dt>
|
||||
${Object.entries(config.versions.current.downloads)
|
||||
.map(
|
||||
([name, url]) => `
|
||||
<dd>
|
||||
<a href="${url}">${downloadsNameDisplay[name]}</a>
|
||||
</dd>
|
||||
`,
|
||||
)
|
||||
.join("\n")}
|
||||
</dl>
|
||||
`;
|
||||
return downloadsHTML;
|
||||
}
|
||||
|
||||
document.addEventListener("readthedocs-addons-data-ready", function (event) {
|
||||
const config = event.detail.data();
|
||||
|
||||
const flyout = `
|
||||
<div class="rst-versions" data-toggle="rst-versions" role="note">
|
||||
<span class="rst-current-version" data-toggle="rst-current-version">
|
||||
<span class="fa fa-book"> Read the Docs</span>
|
||||
v: ${config.versions.current.slug}
|
||||
<span class="fa fa-caret-down"></span>
|
||||
</span>
|
||||
<div class="rst-other-versions">
|
||||
<div class="injected">
|
||||
${renderLanguages(config)}
|
||||
${renderVersions(config)}
|
||||
${renderDownloads(config)}
|
||||
<dl>
|
||||
<dt>On Read the Docs</dt>
|
||||
<dd>
|
||||
<a href="${config.projects.current.urls.home}">Project Home</a>
|
||||
</dd>
|
||||
<dd>
|
||||
<a href="${config.projects.current.urls.builds}">Builds</a>
|
||||
</dd>
|
||||
<dd>
|
||||
<a href="${config.projects.current.urls.downloads}">Downloads</a>
|
||||
</dd>
|
||||
</dl>
|
||||
<dl>
|
||||
<dt>Search</dt>
|
||||
<dd>
|
||||
<form id="flyout-search-form">
|
||||
<input
|
||||
class="wy-form"
|
||||
type="text"
|
||||
name="q"
|
||||
aria-label="Search docs"
|
||||
placeholder="Search docs"
|
||||
/>
|
||||
</form>
|
||||
</dd>
|
||||
</dl>
|
||||
<hr />
|
||||
<small>
|
||||
<span>Hosted by <a href="https://about.readthedocs.org/?utm_source=&utm_content=flyout">Read the Docs</a></span>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Inject the generated flyout into the body HTML element.
|
||||
document.body.insertAdjacentHTML("beforeend", flyout);
|
||||
|
||||
// Trigger the Read the Docs Addons Search modal when clicking on the "Search docs" input from inside the flyout.
|
||||
document
|
||||
.querySelector("#flyout-search-form")
|
||||
.addEventListener("focusin", () => {
|
||||
const event = new CustomEvent("readthedocs-search-show");
|
||||
document.dispatchEvent(event);
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
if (themeLanguageSelector || themeVersionSelector) {
|
||||
function onSelectorSwitch(event) {
|
||||
const option = event.target.selectedIndex;
|
||||
const item = event.target.options[option];
|
||||
window.location.href = item.dataset.url;
|
||||
}
|
||||
|
||||
document.addEventListener("readthedocs-addons-data-ready", function (event) {
|
||||
const config = event.detail.data();
|
||||
|
||||
const versionSwitch = document.querySelector(
|
||||
"div.switch-menus > div.version-switch",
|
||||
);
|
||||
if (themeVersionSelector) {
|
||||
let versions = config.versions.active;
|
||||
if (config.versions.current.hidden || config.versions.current.type === "external") {
|
||||
versions.unshift(config.versions.current);
|
||||
}
|
||||
const versionSelect = `
|
||||
<select>
|
||||
${versions
|
||||
.map(
|
||||
(version) => `
|
||||
<option
|
||||
value="${version.slug}"
|
||||
${config.versions.current.slug === version.slug ? 'selected="selected"' : ""}
|
||||
data-url="${version.urls.documentation}">
|
||||
${version.slug}
|
||||
</option>`,
|
||||
)
|
||||
.join("\n")}
|
||||
</select>
|
||||
`;
|
||||
|
||||
versionSwitch.innerHTML = versionSelect;
|
||||
versionSwitch.firstElementChild.addEventListener("change", onSelectorSwitch);
|
||||
}
|
||||
|
||||
const languageSwitch = document.querySelector(
|
||||
"div.switch-menus > div.language-switch",
|
||||
);
|
||||
|
||||
if (themeLanguageSelector) {
|
||||
if (config.projects.translations.length) {
|
||||
// Add the current language to the options on the selector
|
||||
let languages = config.projects.translations.concat(
|
||||
config.projects.current,
|
||||
);
|
||||
languages = languages.sort((a, b) =>
|
||||
a.language.name.localeCompare(b.language.name),
|
||||
);
|
||||
|
||||
const languageSelect = `
|
||||
<select>
|
||||
${languages
|
||||
.map(
|
||||
(language) => `
|
||||
<option
|
||||
value="${language.language.code}"
|
||||
${config.projects.current.slug === language.slug ? 'selected="selected"' : ""}
|
||||
data-url="${language.urls.documentation}">
|
||||
${language.language.name}
|
||||
</option>`,
|
||||
)
|
||||
.join("\n")}
|
||||
</select>
|
||||
`;
|
||||
|
||||
languageSwitch.innerHTML = languageSelect;
|
||||
languageSwitch.firstElementChild.addEventListener("change", onSelectorSwitch);
|
||||
}
|
||||
else {
|
||||
languageSwitch.remove();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener("readthedocs-addons-data-ready", function (event) {
|
||||
// Trigger the Read the Docs Addons Search modal when clicking on "Search docs" input from the topnav.
|
||||
document
|
||||
.querySelector("[role='search'] input")
|
||||
.addEventListener("focusin", () => {
|
||||
const event = new CustomEvent("readthedocs-search-show");
|
||||
document.dispatchEvent(event);
|
||||
});
|
||||
});
|
||||
192
docs/build/html/_static/language_data.js
vendored
Normal file
192
docs/build/html/_static/language_data.js
vendored
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
/*
|
||||
* This script contains the language-specific data used by searchtools.js,
|
||||
* namely the list of stopwords, stemmer, scorer and splitter.
|
||||
*/
|
||||
|
||||
var stopwords = ["a", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", "near", "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", "they", "this", "to", "was", "will", "with"];
|
||||
|
||||
|
||||
/* Non-minified version is copied as a separate JS file, if available */
|
||||
|
||||
/**
|
||||
* Porter Stemmer
|
||||
*/
|
||||
var Stemmer = function() {
|
||||
|
||||
var step2list = {
|
||||
ational: 'ate',
|
||||
tional: 'tion',
|
||||
enci: 'ence',
|
||||
anci: 'ance',
|
||||
izer: 'ize',
|
||||
bli: 'ble',
|
||||
alli: 'al',
|
||||
entli: 'ent',
|
||||
eli: 'e',
|
||||
ousli: 'ous',
|
||||
ization: 'ize',
|
||||
ation: 'ate',
|
||||
ator: 'ate',
|
||||
alism: 'al',
|
||||
iveness: 'ive',
|
||||
fulness: 'ful',
|
||||
ousness: 'ous',
|
||||
aliti: 'al',
|
||||
iviti: 'ive',
|
||||
biliti: 'ble',
|
||||
logi: 'log'
|
||||
};
|
||||
|
||||
var step3list = {
|
||||
icate: 'ic',
|
||||
ative: '',
|
||||
alize: 'al',
|
||||
iciti: 'ic',
|
||||
ical: 'ic',
|
||||
ful: '',
|
||||
ness: ''
|
||||
};
|
||||
|
||||
var c = "[^aeiou]"; // consonant
|
||||
var v = "[aeiouy]"; // vowel
|
||||
var C = c + "[^aeiouy]*"; // consonant sequence
|
||||
var V = v + "[aeiou]*"; // vowel sequence
|
||||
|
||||
var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0
|
||||
var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1
|
||||
var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1
|
||||
var s_v = "^(" + C + ")?" + v; // vowel in stem
|
||||
|
||||
this.stemWord = function (w) {
|
||||
var stem;
|
||||
var suffix;
|
||||
var firstch;
|
||||
var origword = w;
|
||||
|
||||
if (w.length < 3)
|
||||
return w;
|
||||
|
||||
var re;
|
||||
var re2;
|
||||
var re3;
|
||||
var re4;
|
||||
|
||||
firstch = w.substr(0,1);
|
||||
if (firstch == "y")
|
||||
w = firstch.toUpperCase() + w.substr(1);
|
||||
|
||||
// Step 1a
|
||||
re = /^(.+?)(ss|i)es$/;
|
||||
re2 = /^(.+?)([^s])s$/;
|
||||
|
||||
if (re.test(w))
|
||||
w = w.replace(re,"$1$2");
|
||||
else if (re2.test(w))
|
||||
w = w.replace(re2,"$1$2");
|
||||
|
||||
// Step 1b
|
||||
re = /^(.+?)eed$/;
|
||||
re2 = /^(.+?)(ed|ing)$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
re = new RegExp(mgr0);
|
||||
if (re.test(fp[1])) {
|
||||
re = /.$/;
|
||||
w = w.replace(re,"");
|
||||
}
|
||||
}
|
||||
else if (re2.test(w)) {
|
||||
var fp = re2.exec(w);
|
||||
stem = fp[1];
|
||||
re2 = new RegExp(s_v);
|
||||
if (re2.test(stem)) {
|
||||
w = stem;
|
||||
re2 = /(at|bl|iz)$/;
|
||||
re3 = new RegExp("([^aeiouylsz])\\1$");
|
||||
re4 = new RegExp("^" + C + v + "[^aeiouwxy]$");
|
||||
if (re2.test(w))
|
||||
w = w + "e";
|
||||
else if (re3.test(w)) {
|
||||
re = /.$/;
|
||||
w = w.replace(re,"");
|
||||
}
|
||||
else if (re4.test(w))
|
||||
w = w + "e";
|
||||
}
|
||||
}
|
||||
|
||||
// Step 1c
|
||||
re = /^(.+?)y$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
re = new RegExp(s_v);
|
||||
if (re.test(stem))
|
||||
w = stem + "i";
|
||||
}
|
||||
|
||||
// Step 2
|
||||
re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
suffix = fp[2];
|
||||
re = new RegExp(mgr0);
|
||||
if (re.test(stem))
|
||||
w = stem + step2list[suffix];
|
||||
}
|
||||
|
||||
// Step 3
|
||||
re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
suffix = fp[2];
|
||||
re = new RegExp(mgr0);
|
||||
if (re.test(stem))
|
||||
w = stem + step3list[suffix];
|
||||
}
|
||||
|
||||
// Step 4
|
||||
re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;
|
||||
re2 = /^(.+?)(s|t)(ion)$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
re = new RegExp(mgr1);
|
||||
if (re.test(stem))
|
||||
w = stem;
|
||||
}
|
||||
else if (re2.test(w)) {
|
||||
var fp = re2.exec(w);
|
||||
stem = fp[1] + fp[2];
|
||||
re2 = new RegExp(mgr1);
|
||||
if (re2.test(stem))
|
||||
w = stem;
|
||||
}
|
||||
|
||||
// Step 5
|
||||
re = /^(.+?)e$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
re = new RegExp(mgr1);
|
||||
re2 = new RegExp(meq1);
|
||||
re3 = new RegExp("^" + C + v + "[^aeiouwxy]$");
|
||||
if (re.test(stem) || (re2.test(stem) && !(re3.test(stem))))
|
||||
w = stem;
|
||||
}
|
||||
re = /ll$/;
|
||||
re2 = new RegExp(mgr1);
|
||||
if (re.test(w) && re2.test(w)) {
|
||||
re = /.$/;
|
||||
w = w.replace(re,"");
|
||||
}
|
||||
|
||||
// and turn initial Y back to y
|
||||
if (firstch == "y")
|
||||
w = firstch.toLowerCase() + w.substr(1);
|
||||
return w;
|
||||
}
|
||||
}
|
||||
|
||||
BIN
docs/build/html/_static/minus.png
vendored
Normal file
BIN
docs/build/html/_static/minus.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 90 B |
BIN
docs/build/html/_static/plus.png
vendored
Normal file
BIN
docs/build/html/_static/plus.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 90 B |
75
docs/build/html/_static/pygments.css
vendored
Normal file
75
docs/build/html/_static/pygments.css
vendored
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
pre { line-height: 125%; }
|
||||
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
|
||||
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
|
||||
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
|
||||
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
|
||||
.highlight .hll { background-color: #ffffcc }
|
||||
.highlight { background: #f8f8f8; }
|
||||
.highlight .c { color: #3D7B7B; font-style: italic } /* Comment */
|
||||
.highlight .err { border: 1px solid #F00 } /* Error */
|
||||
.highlight .k { color: #008000; font-weight: bold } /* Keyword */
|
||||
.highlight .o { color: #666 } /* Operator */
|
||||
.highlight .ch { color: #3D7B7B; font-style: italic } /* Comment.Hashbang */
|
||||
.highlight .cm { color: #3D7B7B; font-style: italic } /* Comment.Multiline */
|
||||
.highlight .cp { color: #9C6500 } /* Comment.Preproc */
|
||||
.highlight .cpf { color: #3D7B7B; font-style: italic } /* Comment.PreprocFile */
|
||||
.highlight .c1 { color: #3D7B7B; font-style: italic } /* Comment.Single */
|
||||
.highlight .cs { color: #3D7B7B; font-style: italic } /* Comment.Special */
|
||||
.highlight .gd { color: #A00000 } /* Generic.Deleted */
|
||||
.highlight .ge { font-style: italic } /* Generic.Emph */
|
||||
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
|
||||
.highlight .gr { color: #E40000 } /* Generic.Error */
|
||||
.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
|
||||
.highlight .gi { color: #008400 } /* Generic.Inserted */
|
||||
.highlight .go { color: #717171 } /* Generic.Output */
|
||||
.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
|
||||
.highlight .gs { font-weight: bold } /* Generic.Strong */
|
||||
.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
|
||||
.highlight .gt { color: #04D } /* Generic.Traceback */
|
||||
.highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
|
||||
.highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
|
||||
.highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
|
||||
.highlight .kp { color: #008000 } /* Keyword.Pseudo */
|
||||
.highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
|
||||
.highlight .kt { color: #B00040 } /* Keyword.Type */
|
||||
.highlight .m { color: #666 } /* Literal.Number */
|
||||
.highlight .s { color: #BA2121 } /* Literal.String */
|
||||
.highlight .na { color: #687822 } /* Name.Attribute */
|
||||
.highlight .nb { color: #008000 } /* Name.Builtin */
|
||||
.highlight .nc { color: #00F; font-weight: bold } /* Name.Class */
|
||||
.highlight .no { color: #800 } /* Name.Constant */
|
||||
.highlight .nd { color: #A2F } /* Name.Decorator */
|
||||
.highlight .ni { color: #717171; font-weight: bold } /* Name.Entity */
|
||||
.highlight .ne { color: #CB3F38; font-weight: bold } /* Name.Exception */
|
||||
.highlight .nf { color: #00F } /* Name.Function */
|
||||
.highlight .nl { color: #767600 } /* Name.Label */
|
||||
.highlight .nn { color: #00F; font-weight: bold } /* Name.Namespace */
|
||||
.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */
|
||||
.highlight .nv { color: #19177C } /* Name.Variable */
|
||||
.highlight .ow { color: #A2F; font-weight: bold } /* Operator.Word */
|
||||
.highlight .w { color: #BBB } /* Text.Whitespace */
|
||||
.highlight .mb { color: #666 } /* Literal.Number.Bin */
|
||||
.highlight .mf { color: #666 } /* Literal.Number.Float */
|
||||
.highlight .mh { color: #666 } /* Literal.Number.Hex */
|
||||
.highlight .mi { color: #666 } /* Literal.Number.Integer */
|
||||
.highlight .mo { color: #666 } /* Literal.Number.Oct */
|
||||
.highlight .sa { color: #BA2121 } /* Literal.String.Affix */
|
||||
.highlight .sb { color: #BA2121 } /* Literal.String.Backtick */
|
||||
.highlight .sc { color: #BA2121 } /* Literal.String.Char */
|
||||
.highlight .dl { color: #BA2121 } /* Literal.String.Delimiter */
|
||||
.highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
|
||||
.highlight .s2 { color: #BA2121 } /* Literal.String.Double */
|
||||
.highlight .se { color: #AA5D1F; font-weight: bold } /* Literal.String.Escape */
|
||||
.highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */
|
||||
.highlight .si { color: #A45A77; font-weight: bold } /* Literal.String.Interpol */
|
||||
.highlight .sx { color: #008000 } /* Literal.String.Other */
|
||||
.highlight .sr { color: #A45A77 } /* Literal.String.Regex */
|
||||
.highlight .s1 { color: #BA2121 } /* Literal.String.Single */
|
||||
.highlight .ss { color: #19177C } /* Literal.String.Symbol */
|
||||
.highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */
|
||||
.highlight .fm { color: #00F } /* Name.Function.Magic */
|
||||
.highlight .vc { color: #19177C } /* Name.Variable.Class */
|
||||
.highlight .vg { color: #19177C } /* Name.Variable.Global */
|
||||
.highlight .vi { color: #19177C } /* Name.Variable.Instance */
|
||||
.highlight .vm { color: #19177C } /* Name.Variable.Magic */
|
||||
.highlight .il { color: #666 } /* Literal.Number.Integer.Long */
|
||||
632
docs/build/html/_static/searchtools.js
vendored
Normal file
632
docs/build/html/_static/searchtools.js
vendored
Normal file
|
|
@ -0,0 +1,632 @@
|
|||
/*
|
||||
* Sphinx JavaScript utilities for the full-text search.
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Simple result scoring code.
|
||||
*/
|
||||
if (typeof Scorer === "undefined") {
|
||||
var Scorer = {
|
||||
// Implement the following function to further tweak the score for each result
|
||||
// The function takes a result array [docname, title, anchor, descr, score, filename]
|
||||
// and returns the new score.
|
||||
/*
|
||||
score: result => {
|
||||
const [docname, title, anchor, descr, score, filename, kind] = result
|
||||
return score
|
||||
},
|
||||
*/
|
||||
|
||||
// query matches the full name of an object
|
||||
objNameMatch: 11,
|
||||
// or matches in the last dotted part of the object name
|
||||
objPartialMatch: 6,
|
||||
// Additive scores depending on the priority of the object
|
||||
objPrio: {
|
||||
0: 15, // used to be importantResults
|
||||
1: 5, // used to be objectResults
|
||||
2: -5, // used to be unimportantResults
|
||||
},
|
||||
// Used when the priority is not in the mapping.
|
||||
objPrioDefault: 0,
|
||||
|
||||
// query found in title
|
||||
title: 15,
|
||||
partialTitle: 7,
|
||||
// query found in terms
|
||||
term: 5,
|
||||
partialTerm: 2,
|
||||
};
|
||||
}
|
||||
|
||||
// Global search result kind enum, used by themes to style search results.
|
||||
class SearchResultKind {
|
||||
static get index() { return "index"; }
|
||||
static get object() { return "object"; }
|
||||
static get text() { return "text"; }
|
||||
static get title() { return "title"; }
|
||||
}
|
||||
|
||||
const _removeChildren = (element) => {
|
||||
while (element && element.lastChild) element.removeChild(element.lastChild);
|
||||
};
|
||||
|
||||
/**
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping
|
||||
*/
|
||||
const _escapeRegExp = (string) =>
|
||||
string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
|
||||
|
||||
const _displayItem = (item, searchTerms, highlightTerms) => {
|
||||
const docBuilder = DOCUMENTATION_OPTIONS.BUILDER;
|
||||
const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX;
|
||||
const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX;
|
||||
const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY;
|
||||
const contentRoot = document.documentElement.dataset.content_root;
|
||||
|
||||
const [docName, title, anchor, descr, score, _filename, kind] = item;
|
||||
|
||||
let listItem = document.createElement("li");
|
||||
// Add a class representing the item's type:
|
||||
// can be used by a theme's CSS selector for styling
|
||||
// See SearchResultKind for the class names.
|
||||
listItem.classList.add(`kind-${kind}`);
|
||||
let requestUrl;
|
||||
let linkUrl;
|
||||
if (docBuilder === "dirhtml") {
|
||||
// dirhtml builder
|
||||
let dirname = docName + "/";
|
||||
if (dirname.match(/\/index\/$/))
|
||||
dirname = dirname.substring(0, dirname.length - 6);
|
||||
else if (dirname === "index/") dirname = "";
|
||||
requestUrl = contentRoot + dirname;
|
||||
linkUrl = requestUrl;
|
||||
} else {
|
||||
// normal html builders
|
||||
requestUrl = contentRoot + docName + docFileSuffix;
|
||||
linkUrl = docName + docLinkSuffix;
|
||||
}
|
||||
let linkEl = listItem.appendChild(document.createElement("a"));
|
||||
linkEl.href = linkUrl + anchor;
|
||||
linkEl.dataset.score = score;
|
||||
linkEl.innerHTML = title;
|
||||
if (descr) {
|
||||
listItem.appendChild(document.createElement("span")).innerHTML =
|
||||
" (" + descr + ")";
|
||||
// highlight search terms in the description
|
||||
if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js
|
||||
highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted"));
|
||||
}
|
||||
else if (showSearchSummary)
|
||||
fetch(requestUrl)
|
||||
.then((responseData) => responseData.text())
|
||||
.then((data) => {
|
||||
if (data)
|
||||
listItem.appendChild(
|
||||
Search.makeSearchSummary(data, searchTerms, anchor)
|
||||
);
|
||||
// highlight search terms in the summary
|
||||
if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js
|
||||
highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted"));
|
||||
});
|
||||
Search.output.appendChild(listItem);
|
||||
};
|
||||
const _finishSearch = (resultCount) => {
|
||||
Search.stopPulse();
|
||||
Search.title.innerText = _("Search Results");
|
||||
if (!resultCount)
|
||||
Search.status.innerText = Documentation.gettext(
|
||||
"Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories."
|
||||
);
|
||||
else
|
||||
Search.status.innerText = Documentation.ngettext(
|
||||
"Search finished, found one page matching the search query.",
|
||||
"Search finished, found ${resultCount} pages matching the search query.",
|
||||
resultCount,
|
||||
).replace('${resultCount}', resultCount);
|
||||
};
|
||||
const _displayNextItem = (
|
||||
results,
|
||||
resultCount,
|
||||
searchTerms,
|
||||
highlightTerms,
|
||||
) => {
|
||||
// results left, load the summary and display it
|
||||
// this is intended to be dynamic (don't sub resultsCount)
|
||||
if (results.length) {
|
||||
_displayItem(results.pop(), searchTerms, highlightTerms);
|
||||
setTimeout(
|
||||
() => _displayNextItem(results, resultCount, searchTerms, highlightTerms),
|
||||
5
|
||||
);
|
||||
}
|
||||
// search finished, update title and status message
|
||||
else _finishSearch(resultCount);
|
||||
};
|
||||
// Helper function used by query() to order search results.
|
||||
// Each input is an array of [docname, title, anchor, descr, score, filename, kind].
|
||||
// Order the results by score (in opposite order of appearance, since the
|
||||
// `_displayNextItem` function uses pop() to retrieve items) and then alphabetically.
|
||||
const _orderResultsByScoreThenName = (a, b) => {
|
||||
const leftScore = a[4];
|
||||
const rightScore = b[4];
|
||||
if (leftScore === rightScore) {
|
||||
// same score: sort alphabetically
|
||||
const leftTitle = a[1].toLowerCase();
|
||||
const rightTitle = b[1].toLowerCase();
|
||||
if (leftTitle === rightTitle) return 0;
|
||||
return leftTitle > rightTitle ? -1 : 1; // inverted is intentional
|
||||
}
|
||||
return leftScore > rightScore ? 1 : -1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Default splitQuery function. Can be overridden in ``sphinx.search`` with a
|
||||
* custom function per language.
|
||||
*
|
||||
* The regular expression works by splitting the string on consecutive characters
|
||||
* that are not Unicode letters, numbers, underscores, or emoji characters.
|
||||
* This is the same as ``\W+`` in Python, preserving the surrogate pair area.
|
||||
*/
|
||||
if (typeof splitQuery === "undefined") {
|
||||
var splitQuery = (query) => query
|
||||
.split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu)
|
||||
.filter(term => term) // remove remaining empty strings
|
||||
}
|
||||
|
||||
/**
|
||||
* Search Module
|
||||
*/
|
||||
const Search = {
|
||||
_index: null,
|
||||
_queued_query: null,
|
||||
_pulse_status: -1,
|
||||
|
||||
htmlToText: (htmlString, anchor) => {
|
||||
const htmlElement = new DOMParser().parseFromString(htmlString, 'text/html');
|
||||
for (const removalQuery of [".headerlink", "script", "style"]) {
|
||||
htmlElement.querySelectorAll(removalQuery).forEach((el) => { el.remove() });
|
||||
}
|
||||
if (anchor) {
|
||||
const anchorContent = htmlElement.querySelector(`[role="main"] ${anchor}`);
|
||||
if (anchorContent) return anchorContent.textContent;
|
||||
|
||||
console.warn(
|
||||
`Anchored content block not found. Sphinx search tries to obtain it via DOM query '[role=main] ${anchor}'. Check your theme or template.`
|
||||
);
|
||||
}
|
||||
|
||||
// if anchor not specified or not found, fall back to main content
|
||||
const docContent = htmlElement.querySelector('[role="main"]');
|
||||
if (docContent) return docContent.textContent;
|
||||
|
||||
console.warn(
|
||||
"Content block not found. Sphinx search tries to obtain it via DOM query '[role=main]'. Check your theme or template."
|
||||
);
|
||||
return "";
|
||||
},
|
||||
|
||||
init: () => {
|
||||
const query = new URLSearchParams(window.location.search).get("q");
|
||||
document
|
||||
.querySelectorAll('input[name="q"]')
|
||||
.forEach((el) => (el.value = query));
|
||||
if (query) Search.performSearch(query);
|
||||
},
|
||||
|
||||
loadIndex: (url) =>
|
||||
(document.body.appendChild(document.createElement("script")).src = url),
|
||||
|
||||
setIndex: (index) => {
|
||||
Search._index = index;
|
||||
if (Search._queued_query !== null) {
|
||||
const query = Search._queued_query;
|
||||
Search._queued_query = null;
|
||||
Search.query(query);
|
||||
}
|
||||
},
|
||||
|
||||
hasIndex: () => Search._index !== null,
|
||||
|
||||
deferQuery: (query) => (Search._queued_query = query),
|
||||
|
||||
stopPulse: () => (Search._pulse_status = -1),
|
||||
|
||||
startPulse: () => {
|
||||
if (Search._pulse_status >= 0) return;
|
||||
|
||||
const pulse = () => {
|
||||
Search._pulse_status = (Search._pulse_status + 1) % 4;
|
||||
Search.dots.innerText = ".".repeat(Search._pulse_status);
|
||||
if (Search._pulse_status >= 0) window.setTimeout(pulse, 500);
|
||||
};
|
||||
pulse();
|
||||
},
|
||||
|
||||
/**
|
||||
* perform a search for something (or wait until index is loaded)
|
||||
*/
|
||||
performSearch: (query) => {
|
||||
// create the required interface elements
|
||||
const searchText = document.createElement("h2");
|
||||
searchText.textContent = _("Searching");
|
||||
const searchSummary = document.createElement("p");
|
||||
searchSummary.classList.add("search-summary");
|
||||
searchSummary.innerText = "";
|
||||
const searchList = document.createElement("ul");
|
||||
searchList.setAttribute("role", "list");
|
||||
searchList.classList.add("search");
|
||||
|
||||
const out = document.getElementById("search-results");
|
||||
Search.title = out.appendChild(searchText);
|
||||
Search.dots = Search.title.appendChild(document.createElement("span"));
|
||||
Search.status = out.appendChild(searchSummary);
|
||||
Search.output = out.appendChild(searchList);
|
||||
|
||||
const searchProgress = document.getElementById("search-progress");
|
||||
// Some themes don't use the search progress node
|
||||
if (searchProgress) {
|
||||
searchProgress.innerText = _("Preparing search...");
|
||||
}
|
||||
Search.startPulse();
|
||||
|
||||
// index already loaded, the browser was quick!
|
||||
if (Search.hasIndex()) Search.query(query);
|
||||
else Search.deferQuery(query);
|
||||
},
|
||||
|
||||
_parseQuery: (query) => {
|
||||
// stem the search terms and add them to the correct list
|
||||
const stemmer = new Stemmer();
|
||||
const searchTerms = new Set();
|
||||
const excludedTerms = new Set();
|
||||
const highlightTerms = new Set();
|
||||
const objectTerms = new Set(splitQuery(query.toLowerCase().trim()));
|
||||
splitQuery(query.trim()).forEach((queryTerm) => {
|
||||
const queryTermLower = queryTerm.toLowerCase();
|
||||
|
||||
// maybe skip this "word"
|
||||
// stopwords array is from language_data.js
|
||||
if (
|
||||
stopwords.indexOf(queryTermLower) !== -1 ||
|
||||
queryTerm.match(/^\d+$/)
|
||||
)
|
||||
return;
|
||||
|
||||
// stem the word
|
||||
let word = stemmer.stemWord(queryTermLower);
|
||||
// select the correct list
|
||||
if (word[0] === "-") excludedTerms.add(word.substr(1));
|
||||
else {
|
||||
searchTerms.add(word);
|
||||
highlightTerms.add(queryTermLower);
|
||||
}
|
||||
});
|
||||
|
||||
if (SPHINX_HIGHLIGHT_ENABLED) { // set in sphinx_highlight.js
|
||||
localStorage.setItem("sphinx_highlight_terms", [...highlightTerms].join(" "))
|
||||
}
|
||||
|
||||
// console.debug("SEARCH: searching for:");
|
||||
// console.info("required: ", [...searchTerms]);
|
||||
// console.info("excluded: ", [...excludedTerms]);
|
||||
|
||||
return [query, searchTerms, excludedTerms, highlightTerms, objectTerms];
|
||||
},
|
||||
|
||||
/**
|
||||
* execute search (requires search index to be loaded)
|
||||
*/
|
||||
_performSearch: (query, searchTerms, excludedTerms, highlightTerms, objectTerms) => {
|
||||
const filenames = Search._index.filenames;
|
||||
const docNames = Search._index.docnames;
|
||||
const titles = Search._index.titles;
|
||||
const allTitles = Search._index.alltitles;
|
||||
const indexEntries = Search._index.indexentries;
|
||||
|
||||
// Collect multiple result groups to be sorted separately and then ordered.
|
||||
// Each is an array of [docname, title, anchor, descr, score, filename, kind].
|
||||
const normalResults = [];
|
||||
const nonMainIndexResults = [];
|
||||
|
||||
_removeChildren(document.getElementById("search-progress"));
|
||||
|
||||
const queryLower = query.toLowerCase().trim();
|
||||
for (const [title, foundTitles] of Object.entries(allTitles)) {
|
||||
if (title.toLowerCase().trim().includes(queryLower) && (queryLower.length >= title.length/2)) {
|
||||
for (const [file, id] of foundTitles) {
|
||||
const score = Math.round(Scorer.title * queryLower.length / title.length);
|
||||
const boost = titles[file] === title ? 1 : 0; // add a boost for document titles
|
||||
normalResults.push([
|
||||
docNames[file],
|
||||
titles[file] !== title ? `${titles[file]} > ${title}` : title,
|
||||
id !== null ? "#" + id : "",
|
||||
null,
|
||||
score + boost,
|
||||
filenames[file],
|
||||
SearchResultKind.title,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// search for explicit entries in index directives
|
||||
for (const [entry, foundEntries] of Object.entries(indexEntries)) {
|
||||
if (entry.includes(queryLower) && (queryLower.length >= entry.length/2)) {
|
||||
for (const [file, id, isMain] of foundEntries) {
|
||||
const score = Math.round(100 * queryLower.length / entry.length);
|
||||
const result = [
|
||||
docNames[file],
|
||||
titles[file],
|
||||
id ? "#" + id : "",
|
||||
null,
|
||||
score,
|
||||
filenames[file],
|
||||
SearchResultKind.index,
|
||||
];
|
||||
if (isMain) {
|
||||
normalResults.push(result);
|
||||
} else {
|
||||
nonMainIndexResults.push(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// lookup as object
|
||||
objectTerms.forEach((term) =>
|
||||
normalResults.push(...Search.performObjectSearch(term, objectTerms))
|
||||
);
|
||||
|
||||
// lookup as search terms in fulltext
|
||||
normalResults.push(...Search.performTermsSearch(searchTerms, excludedTerms));
|
||||
|
||||
// let the scorer override scores with a custom scoring function
|
||||
if (Scorer.score) {
|
||||
normalResults.forEach((item) => (item[4] = Scorer.score(item)));
|
||||
nonMainIndexResults.forEach((item) => (item[4] = Scorer.score(item)));
|
||||
}
|
||||
|
||||
// Sort each group of results by score and then alphabetically by name.
|
||||
normalResults.sort(_orderResultsByScoreThenName);
|
||||
nonMainIndexResults.sort(_orderResultsByScoreThenName);
|
||||
|
||||
// Combine the result groups in (reverse) order.
|
||||
// Non-main index entries are typically arbitrary cross-references,
|
||||
// so display them after other results.
|
||||
let results = [...nonMainIndexResults, ...normalResults];
|
||||
|
||||
// remove duplicate search results
|
||||
// note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept
|
||||
let seen = new Set();
|
||||
results = results.reverse().reduce((acc, result) => {
|
||||
let resultStr = result.slice(0, 4).concat([result[5]]).map(v => String(v)).join(',');
|
||||
if (!seen.has(resultStr)) {
|
||||
acc.push(result);
|
||||
seen.add(resultStr);
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
return results.reverse();
|
||||
},
|
||||
|
||||
query: (query) => {
|
||||
const [searchQuery, searchTerms, excludedTerms, highlightTerms, objectTerms] = Search._parseQuery(query);
|
||||
const results = Search._performSearch(searchQuery, searchTerms, excludedTerms, highlightTerms, objectTerms);
|
||||
|
||||
// for debugging
|
||||
//Search.lastresults = results.slice(); // a copy
|
||||
// console.info("search results:", Search.lastresults);
|
||||
|
||||
// print the results
|
||||
_displayNextItem(results, results.length, searchTerms, highlightTerms);
|
||||
},
|
||||
|
||||
/**
|
||||
* search for object names
|
||||
*/
|
||||
performObjectSearch: (object, objectTerms) => {
|
||||
const filenames = Search._index.filenames;
|
||||
const docNames = Search._index.docnames;
|
||||
const objects = Search._index.objects;
|
||||
const objNames = Search._index.objnames;
|
||||
const titles = Search._index.titles;
|
||||
|
||||
const results = [];
|
||||
|
||||
const objectSearchCallback = (prefix, match) => {
|
||||
const name = match[4]
|
||||
const fullname = (prefix ? prefix + "." : "") + name;
|
||||
const fullnameLower = fullname.toLowerCase();
|
||||
if (fullnameLower.indexOf(object) < 0) return;
|
||||
|
||||
let score = 0;
|
||||
const parts = fullnameLower.split(".");
|
||||
|
||||
// check for different match types: exact matches of full name or
|
||||
// "last name" (i.e. last dotted part)
|
||||
if (fullnameLower === object || parts.slice(-1)[0] === object)
|
||||
score += Scorer.objNameMatch;
|
||||
else if (parts.slice(-1)[0].indexOf(object) > -1)
|
||||
score += Scorer.objPartialMatch; // matches in last name
|
||||
|
||||
const objName = objNames[match[1]][2];
|
||||
const title = titles[match[0]];
|
||||
|
||||
// If more than one term searched for, we require other words to be
|
||||
// found in the name/title/description
|
||||
const otherTerms = new Set(objectTerms);
|
||||
otherTerms.delete(object);
|
||||
if (otherTerms.size > 0) {
|
||||
const haystack = `${prefix} ${name} ${objName} ${title}`.toLowerCase();
|
||||
if (
|
||||
[...otherTerms].some((otherTerm) => haystack.indexOf(otherTerm) < 0)
|
||||
)
|
||||
return;
|
||||
}
|
||||
|
||||
let anchor = match[3];
|
||||
if (anchor === "") anchor = fullname;
|
||||
else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname;
|
||||
|
||||
const descr = objName + _(", in ") + title;
|
||||
|
||||
// add custom score for some objects according to scorer
|
||||
if (Scorer.objPrio.hasOwnProperty(match[2]))
|
||||
score += Scorer.objPrio[match[2]];
|
||||
else score += Scorer.objPrioDefault;
|
||||
|
||||
results.push([
|
||||
docNames[match[0]],
|
||||
fullname,
|
||||
"#" + anchor,
|
||||
descr,
|
||||
score,
|
||||
filenames[match[0]],
|
||||
SearchResultKind.object,
|
||||
]);
|
||||
};
|
||||
Object.keys(objects).forEach((prefix) =>
|
||||
objects[prefix].forEach((array) =>
|
||||
objectSearchCallback(prefix, array)
|
||||
)
|
||||
);
|
||||
return results;
|
||||
},
|
||||
|
||||
/**
|
||||
* search for full-text terms in the index
|
||||
*/
|
||||
performTermsSearch: (searchTerms, excludedTerms) => {
|
||||
// prepare search
|
||||
const terms = Search._index.terms;
|
||||
const titleTerms = Search._index.titleterms;
|
||||
const filenames = Search._index.filenames;
|
||||
const docNames = Search._index.docnames;
|
||||
const titles = Search._index.titles;
|
||||
|
||||
const scoreMap = new Map();
|
||||
const fileMap = new Map();
|
||||
|
||||
// perform the search on the required terms
|
||||
searchTerms.forEach((word) => {
|
||||
const files = [];
|
||||
const arr = [
|
||||
{ files: terms[word], score: Scorer.term },
|
||||
{ files: titleTerms[word], score: Scorer.title },
|
||||
];
|
||||
// add support for partial matches
|
||||
if (word.length > 2) {
|
||||
const escapedWord = _escapeRegExp(word);
|
||||
if (!terms.hasOwnProperty(word)) {
|
||||
Object.keys(terms).forEach((term) => {
|
||||
if (term.match(escapedWord))
|
||||
arr.push({ files: terms[term], score: Scorer.partialTerm });
|
||||
});
|
||||
}
|
||||
if (!titleTerms.hasOwnProperty(word)) {
|
||||
Object.keys(titleTerms).forEach((term) => {
|
||||
if (term.match(escapedWord))
|
||||
arr.push({ files: titleTerms[term], score: Scorer.partialTitle });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// no match but word was a required one
|
||||
if (arr.every((record) => record.files === undefined)) return;
|
||||
|
||||
// found search word in contents
|
||||
arr.forEach((record) => {
|
||||
if (record.files === undefined) return;
|
||||
|
||||
let recordFiles = record.files;
|
||||
if (recordFiles.length === undefined) recordFiles = [recordFiles];
|
||||
files.push(...recordFiles);
|
||||
|
||||
// set score for the word in each file
|
||||
recordFiles.forEach((file) => {
|
||||
if (!scoreMap.has(file)) scoreMap.set(file, {});
|
||||
scoreMap.get(file)[word] = record.score;
|
||||
});
|
||||
});
|
||||
|
||||
// create the mapping
|
||||
files.forEach((file) => {
|
||||
if (!fileMap.has(file)) fileMap.set(file, [word]);
|
||||
else if (fileMap.get(file).indexOf(word) === -1) fileMap.get(file).push(word);
|
||||
});
|
||||
});
|
||||
|
||||
// now check if the files don't contain excluded terms
|
||||
const results = [];
|
||||
for (const [file, wordList] of fileMap) {
|
||||
// check if all requirements are matched
|
||||
|
||||
// as search terms with length < 3 are discarded
|
||||
const filteredTermCount = [...searchTerms].filter(
|
||||
(term) => term.length > 2
|
||||
).length;
|
||||
if (
|
||||
wordList.length !== searchTerms.size &&
|
||||
wordList.length !== filteredTermCount
|
||||
)
|
||||
continue;
|
||||
|
||||
// ensure that none of the excluded terms is in the search result
|
||||
if (
|
||||
[...excludedTerms].some(
|
||||
(term) =>
|
||||
terms[term] === file ||
|
||||
titleTerms[term] === file ||
|
||||
(terms[term] || []).includes(file) ||
|
||||
(titleTerms[term] || []).includes(file)
|
||||
)
|
||||
)
|
||||
break;
|
||||
|
||||
// select one (max) score for the file.
|
||||
const score = Math.max(...wordList.map((w) => scoreMap.get(file)[w]));
|
||||
// add result to the result list
|
||||
results.push([
|
||||
docNames[file],
|
||||
titles[file],
|
||||
"",
|
||||
null,
|
||||
score,
|
||||
filenames[file],
|
||||
SearchResultKind.text,
|
||||
]);
|
||||
}
|
||||
return results;
|
||||
},
|
||||
|
||||
/**
|
||||
* helper function to return a node containing the
|
||||
* search summary for a given text. keywords is a list
|
||||
* of stemmed words.
|
||||
*/
|
||||
makeSearchSummary: (htmlText, keywords, anchor) => {
|
||||
const text = Search.htmlToText(htmlText, anchor);
|
||||
if (text === "") return null;
|
||||
|
||||
const textLower = text.toLowerCase();
|
||||
const actualStartPosition = [...keywords]
|
||||
.map((k) => textLower.indexOf(k.toLowerCase()))
|
||||
.filter((i) => i > -1)
|
||||
.slice(-1)[0];
|
||||
const startWithContext = Math.max(actualStartPosition - 120, 0);
|
||||
|
||||
const top = startWithContext === 0 ? "" : "...";
|
||||
const tail = startWithContext + 240 < text.length ? "..." : "";
|
||||
|
||||
let summary = document.createElement("p");
|
||||
summary.classList.add("context");
|
||||
summary.textContent = top + text.substr(startWithContext, 240).trim() + tail;
|
||||
|
||||
return summary;
|
||||
},
|
||||
};
|
||||
|
||||
_ready(Search.init);
|
||||
154
docs/build/html/_static/sphinx_highlight.js
vendored
Normal file
154
docs/build/html/_static/sphinx_highlight.js
vendored
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
/* Highlighting utilities for Sphinx HTML documentation. */
|
||||
"use strict";
|
||||
|
||||
const SPHINX_HIGHLIGHT_ENABLED = true
|
||||
|
||||
/**
|
||||
* highlight a given string on a node by wrapping it in
|
||||
* span elements with the given class name.
|
||||
*/
|
||||
const _highlight = (node, addItems, text, className) => {
|
||||
if (node.nodeType === Node.TEXT_NODE) {
|
||||
const val = node.nodeValue;
|
||||
const parent = node.parentNode;
|
||||
const pos = val.toLowerCase().indexOf(text);
|
||||
if (
|
||||
pos >= 0 &&
|
||||
!parent.classList.contains(className) &&
|
||||
!parent.classList.contains("nohighlight")
|
||||
) {
|
||||
let span;
|
||||
|
||||
const closestNode = parent.closest("body, svg, foreignObject");
|
||||
const isInSVG = closestNode && closestNode.matches("svg");
|
||||
if (isInSVG) {
|
||||
span = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
|
||||
} else {
|
||||
span = document.createElement("span");
|
||||
span.classList.add(className);
|
||||
}
|
||||
|
||||
span.appendChild(document.createTextNode(val.substr(pos, text.length)));
|
||||
const rest = document.createTextNode(val.substr(pos + text.length));
|
||||
parent.insertBefore(
|
||||
span,
|
||||
parent.insertBefore(
|
||||
rest,
|
||||
node.nextSibling
|
||||
)
|
||||
);
|
||||
node.nodeValue = val.substr(0, pos);
|
||||
/* There may be more occurrences of search term in this node. So call this
|
||||
* function recursively on the remaining fragment.
|
||||
*/
|
||||
_highlight(rest, addItems, text, className);
|
||||
|
||||
if (isInSVG) {
|
||||
const rect = document.createElementNS(
|
||||
"http://www.w3.org/2000/svg",
|
||||
"rect"
|
||||
);
|
||||
const bbox = parent.getBBox();
|
||||
rect.x.baseVal.value = bbox.x;
|
||||
rect.y.baseVal.value = bbox.y;
|
||||
rect.width.baseVal.value = bbox.width;
|
||||
rect.height.baseVal.value = bbox.height;
|
||||
rect.setAttribute("class", className);
|
||||
addItems.push({ parent: parent, target: rect });
|
||||
}
|
||||
}
|
||||
} else if (node.matches && !node.matches("button, select, textarea")) {
|
||||
node.childNodes.forEach((el) => _highlight(el, addItems, text, className));
|
||||
}
|
||||
};
|
||||
const _highlightText = (thisNode, text, className) => {
|
||||
let addItems = [];
|
||||
_highlight(thisNode, addItems, text, className);
|
||||
addItems.forEach((obj) =>
|
||||
obj.parent.insertAdjacentElement("beforebegin", obj.target)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Small JavaScript module for the documentation.
|
||||
*/
|
||||
const SphinxHighlight = {
|
||||
|
||||
/**
|
||||
* highlight the search words provided in localstorage in the text
|
||||
*/
|
||||
highlightSearchWords: () => {
|
||||
if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight
|
||||
|
||||
// get and clear terms from localstorage
|
||||
const url = new URL(window.location);
|
||||
const highlight =
|
||||
localStorage.getItem("sphinx_highlight_terms")
|
||||
|| url.searchParams.get("highlight")
|
||||
|| "";
|
||||
localStorage.removeItem("sphinx_highlight_terms")
|
||||
url.searchParams.delete("highlight");
|
||||
window.history.replaceState({}, "", url);
|
||||
|
||||
// get individual terms from highlight string
|
||||
const terms = highlight.toLowerCase().split(/\s+/).filter(x => x);
|
||||
if (terms.length === 0) return; // nothing to do
|
||||
|
||||
// There should never be more than one element matching "div.body"
|
||||
const divBody = document.querySelectorAll("div.body");
|
||||
const body = divBody.length ? divBody[0] : document.querySelector("body");
|
||||
window.setTimeout(() => {
|
||||
terms.forEach((term) => _highlightText(body, term, "highlighted"));
|
||||
}, 10);
|
||||
|
||||
const searchBox = document.getElementById("searchbox");
|
||||
if (searchBox === null) return;
|
||||
searchBox.appendChild(
|
||||
document
|
||||
.createRange()
|
||||
.createContextualFragment(
|
||||
'<p class="highlight-link">' +
|
||||
'<a href="javascript:SphinxHighlight.hideSearchWords()">' +
|
||||
_("Hide Search Matches") +
|
||||
"</a></p>"
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* helper function to hide the search marks again
|
||||
*/
|
||||
hideSearchWords: () => {
|
||||
document
|
||||
.querySelectorAll("#searchbox .highlight-link")
|
||||
.forEach((el) => el.remove());
|
||||
document
|
||||
.querySelectorAll("span.highlighted")
|
||||
.forEach((el) => el.classList.remove("highlighted"));
|
||||
localStorage.removeItem("sphinx_highlight_terms")
|
||||
},
|
||||
|
||||
initEscapeListener: () => {
|
||||
// only install a listener if it is really needed
|
||||
if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return;
|
||||
|
||||
document.addEventListener("keydown", (event) => {
|
||||
// bail for input elements
|
||||
if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return;
|
||||
// bail with special keys
|
||||
if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return;
|
||||
if (DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && (event.key === "Escape")) {
|
||||
SphinxHighlight.hideSearchWords();
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
_ready(() => {
|
||||
/* Do not call highlightSearchWords() when we are on the search page.
|
||||
* It will highlight words from the *previous* search query.
|
||||
*/
|
||||
if (typeof Search === "undefined") SphinxHighlight.highlightSearchWords();
|
||||
SphinxHighlight.initEscapeListener();
|
||||
});
|
||||
3605
docs/build/html/flask.html
vendored
Normal file
3605
docs/build/html/flask.html
vendored
Normal file
File diff suppressed because one or more lines are too long
1053
docs/build/html/flask.json.html
vendored
Normal file
1053
docs/build/html/flask.json.html
vendored
Normal file
File diff suppressed because it is too large
Load diff
1098
docs/build/html/genindex.html
vendored
Normal file
1098
docs/build/html/genindex.html
vendored
Normal file
File diff suppressed because it is too large
Load diff
117
docs/build/html/index.html
vendored
Normal file
117
docs/build/html/index.html
vendored
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html class="writer-html5" lang="en" data-content_root="./">
|
||||
<head>
|
||||
<meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Flask - Documentación Personalizada — Flask documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=b86133f3" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/css/theme.css?v=e59714d7" />
|
||||
|
||||
|
||||
<script src="_static/jquery.js?v=5d32c60e"></script>
|
||||
<script src="_static/_sphinx_javascript_frameworks_compat.js?v=2cd50e6c"></script>
|
||||
<script src="_static/documentation_options.js?v=5929fcd5"></script>
|
||||
<script src="_static/doctools.js?v=9bcbadda"></script>
|
||||
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
|
||||
<script src="_static/js/theme.js"></script>
|
||||
<link rel="index" title="Index" href="genindex.html" />
|
||||
<link rel="search" title="Search" href="search.html" />
|
||||
<link rel="next" title="flask" href="modules.html" />
|
||||
</head>
|
||||
|
||||
<body class="wy-body-for-nav">
|
||||
<div class="wy-grid-for-nav">
|
||||
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
|
||||
<div class="wy-side-scroll">
|
||||
<div class="wy-side-nav-search" >
|
||||
|
||||
|
||||
|
||||
<a href="#" class="icon icon-home">
|
||||
Flask
|
||||
</a>
|
||||
<div role="search">
|
||||
<form id="rtd-search-form" class="wy-form" action="search.html" method="get">
|
||||
<input type="text" name="q" placeholder="Search docs" aria-label="Search docs" />
|
||||
<input type="hidden" name="check_keywords" value="yes" />
|
||||
<input type="hidden" name="area" value="default" />
|
||||
</form>
|
||||
</div>
|
||||
</div><div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="Navigation menu">
|
||||
<p class="caption" role="heading"><span class="caption-text">Contenido:</span></p>
|
||||
<ul>
|
||||
<li class="toctree-l1"><a class="reference internal" href="modules.html">flask</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap"><nav class="wy-nav-top" aria-label="Mobile navigation menu" >
|
||||
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
|
||||
<a href="#">Flask</a>
|
||||
</nav>
|
||||
|
||||
<div class="wy-nav-content">
|
||||
<div class="rst-content">
|
||||
<div role="navigation" aria-label="Page navigation">
|
||||
<ul class="wy-breadcrumbs">
|
||||
<li><a href="#" class="icon icon-home" aria-label="Home"></a></li>
|
||||
<li class="breadcrumb-item active">Flask - Documentación Personalizada</li>
|
||||
<li class="wy-breadcrumbs-aside">
|
||||
<a href="_sources/index.rst.txt" rel="nofollow"> View page source</a>
|
||||
</li>
|
||||
</ul>
|
||||
<hr/>
|
||||
</div>
|
||||
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
|
||||
<div itemprop="articleBody">
|
||||
|
||||
<section id="flask-documentacion-personalizada">
|
||||
<h1>Flask - Documentación Personalizada<a class="headerlink" href="#flask-documentacion-personalizada" title="Link to this heading"></a></h1>
|
||||
<p>Bienvenido a la documentación del proyecto Flask.</p>
|
||||
<div class="toctree-wrapper compound">
|
||||
<p class="caption" role="heading"><span class="caption-text">Contenido:</span></p>
|
||||
<ul>
|
||||
<li class="toctree-l1"><a class="reference internal" href="modules.html">flask</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="flask.html">flask package</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<footer><div class="rst-footer-buttons" role="navigation" aria-label="Footer">
|
||||
<a href="modules.html" class="btn btn-neutral float-right" title="flask" accesskey="n" rel="next">Next <span class="fa fa-arrow-circle-right" aria-hidden="true"></span></a>
|
||||
</div>
|
||||
|
||||
<hr/>
|
||||
|
||||
<div role="contentinfo">
|
||||
<p>© Copyright 2025, Pallets Team.</p>
|
||||
</div>
|
||||
|
||||
Built with <a href="https://www.sphinx-doc.org/">Sphinx</a> using a
|
||||
<a href="https://github.com/readthedocs/sphinx_rtd_theme">theme</a>
|
||||
provided by <a href="https://readthedocs.org">Read the Docs</a>.
|
||||
|
||||
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<script>
|
||||
jQuery(function () {
|
||||
SphinxRtdTheme.Navigation.enable(true);
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
413
docs/build/html/modules.html
vendored
Normal file
413
docs/build/html/modules.html
vendored
Normal file
|
|
@ -0,0 +1,413 @@
|
|||
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html class="writer-html5" lang="en" data-content_root="./">
|
||||
<head>
|
||||
<meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>flask — Flask documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=b86133f3" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/css/theme.css?v=e59714d7" />
|
||||
|
||||
|
||||
<script src="_static/jquery.js?v=5d32c60e"></script>
|
||||
<script src="_static/_sphinx_javascript_frameworks_compat.js?v=2cd50e6c"></script>
|
||||
<script src="_static/documentation_options.js?v=5929fcd5"></script>
|
||||
<script src="_static/doctools.js?v=9bcbadda"></script>
|
||||
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
|
||||
<script src="_static/js/theme.js"></script>
|
||||
<link rel="index" title="Index" href="genindex.html" />
|
||||
<link rel="search" title="Search" href="search.html" />
|
||||
<link rel="next" title="flask package" href="flask.html" />
|
||||
<link rel="prev" title="Flask - Documentación Personalizada" href="index.html" />
|
||||
</head>
|
||||
|
||||
<body class="wy-body-for-nav">
|
||||
<div class="wy-grid-for-nav">
|
||||
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
|
||||
<div class="wy-side-scroll">
|
||||
<div class="wy-side-nav-search" >
|
||||
|
||||
|
||||
|
||||
<a href="index.html" class="icon icon-home">
|
||||
Flask
|
||||
</a>
|
||||
<div role="search">
|
||||
<form id="rtd-search-form" class="wy-form" action="search.html" method="get">
|
||||
<input type="text" name="q" placeholder="Search docs" aria-label="Search docs" />
|
||||
<input type="hidden" name="check_keywords" value="yes" />
|
||||
<input type="hidden" name="area" value="default" />
|
||||
</form>
|
||||
</div>
|
||||
</div><div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="Navigation menu">
|
||||
<p class="caption" role="heading"><span class="caption-text">Contenido:</span></p>
|
||||
<ul class="current">
|
||||
<li class="toctree-l1 current"><a class="current reference internal" href="#">flask</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="flask.html">flask package</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap"><nav class="wy-nav-top" aria-label="Mobile navigation menu" >
|
||||
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
|
||||
<a href="index.html">Flask</a>
|
||||
</nav>
|
||||
|
||||
<div class="wy-nav-content">
|
||||
<div class="rst-content">
|
||||
<div role="navigation" aria-label="Page navigation">
|
||||
<ul class="wy-breadcrumbs">
|
||||
<li><a href="index.html" class="icon icon-home" aria-label="Home"></a></li>
|
||||
<li class="breadcrumb-item active">flask</li>
|
||||
<li class="wy-breadcrumbs-aside">
|
||||
<a href="_sources/modules.rst.txt" rel="nofollow"> View page source</a>
|
||||
</li>
|
||||
</ul>
|
||||
<hr/>
|
||||
</div>
|
||||
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
|
||||
<div itemprop="articleBody">
|
||||
|
||||
<section id="flask">
|
||||
<h1>flask<a class="headerlink" href="#flask" title="Link to this heading"></a></h1>
|
||||
<div class="toctree-wrapper compound">
|
||||
<ul>
|
||||
<li class="toctree-l1"><a class="reference internal" href="flask.html">flask package</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="flask.html#subpackages">Subpackages</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.json.html">flask.json package</a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.json.html#submodules">Submodules</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.json.html#module-flask.json.provider">flask.json.provider module</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.json.html#module-flask.json.tag">flask.json.tag module</a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.json.html#module-flask.json">Module contents</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="flask.html#submodules">Submodules</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="flask.html#module-flask.app">flask.app module</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.app.Flask"><code class="docutils literal notranslate"><span class="pre">Flask</span></code></a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.app.Flask.app_context"><code class="docutils literal notranslate"><span class="pre">Flask.app_context()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.app.Flask.async_to_sync"><code class="docutils literal notranslate"><span class="pre">Flask.async_to_sync()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.app.Flask.cli"><code class="docutils literal notranslate"><span class="pre">Flask.cli</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.app.Flask.create_jinja_environment"><code class="docutils literal notranslate"><span class="pre">Flask.create_jinja_environment()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.app.Flask.create_url_adapter"><code class="docutils literal notranslate"><span class="pre">Flask.create_url_adapter()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.app.Flask.default_config"><code class="docutils literal notranslate"><span class="pre">Flask.default_config</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.app.Flask.dispatch_request"><code class="docutils literal notranslate"><span class="pre">Flask.dispatch_request()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.app.Flask.do_teardown_appcontext"><code class="docutils literal notranslate"><span class="pre">Flask.do_teardown_appcontext()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.app.Flask.do_teardown_request"><code class="docutils literal notranslate"><span class="pre">Flask.do_teardown_request()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.app.Flask.ensure_sync"><code class="docutils literal notranslate"><span class="pre">Flask.ensure_sync()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.app.Flask.finalize_request"><code class="docutils literal notranslate"><span class="pre">Flask.finalize_request()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.app.Flask.full_dispatch_request"><code class="docutils literal notranslate"><span class="pre">Flask.full_dispatch_request()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.app.Flask.get_send_file_max_age"><code class="docutils literal notranslate"><span class="pre">Flask.get_send_file_max_age()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.app.Flask.handle_exception"><code class="docutils literal notranslate"><span class="pre">Flask.handle_exception()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.app.Flask.handle_http_exception"><code class="docutils literal notranslate"><span class="pre">Flask.handle_http_exception()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.app.Flask.handle_user_exception"><code class="docutils literal notranslate"><span class="pre">Flask.handle_user_exception()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.app.Flask.log_exception"><code class="docutils literal notranslate"><span class="pre">Flask.log_exception()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.app.Flask.make_default_options_response"><code class="docutils literal notranslate"><span class="pre">Flask.make_default_options_response()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.app.Flask.make_response"><code class="docutils literal notranslate"><span class="pre">Flask.make_response()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.app.Flask.make_shell_context"><code class="docutils literal notranslate"><span class="pre">Flask.make_shell_context()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.app.Flask.open_instance_resource"><code class="docutils literal notranslate"><span class="pre">Flask.open_instance_resource()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.app.Flask.open_resource"><code class="docutils literal notranslate"><span class="pre">Flask.open_resource()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.app.Flask.preprocess_request"><code class="docutils literal notranslate"><span class="pre">Flask.preprocess_request()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.app.Flask.process_response"><code class="docutils literal notranslate"><span class="pre">Flask.process_response()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.app.Flask.request_class"><code class="docutils literal notranslate"><span class="pre">Flask.request_class</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.app.Flask.request_context"><code class="docutils literal notranslate"><span class="pre">Flask.request_context()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.app.Flask.response_class"><code class="docutils literal notranslate"><span class="pre">Flask.response_class</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.app.Flask.run"><code class="docutils literal notranslate"><span class="pre">Flask.run()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.app.Flask.send_static_file"><code class="docutils literal notranslate"><span class="pre">Flask.send_static_file()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.app.Flask.session_interface"><code class="docutils literal notranslate"><span class="pre">Flask.session_interface</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.app.Flask.test_cli_runner"><code class="docutils literal notranslate"><span class="pre">Flask.test_cli_runner()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.app.Flask.test_client"><code class="docutils literal notranslate"><span class="pre">Flask.test_client()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.app.Flask.test_request_context"><code class="docutils literal notranslate"><span class="pre">Flask.test_request_context()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.app.Flask.update_template_context"><code class="docutils literal notranslate"><span class="pre">Flask.update_template_context()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.app.Flask.url_for"><code class="docutils literal notranslate"><span class="pre">Flask.url_for()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.app.Flask.wsgi_app"><code class="docutils literal notranslate"><span class="pre">Flask.wsgi_app()</span></code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="flask.html#module-flask.blueprints">flask.blueprints module</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.blueprints.Blueprint"><code class="docutils literal notranslate"><span class="pre">Blueprint</span></code></a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.blueprints.Blueprint.cli"><code class="docutils literal notranslate"><span class="pre">Blueprint.cli</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.blueprints.Blueprint.get_send_file_max_age"><code class="docutils literal notranslate"><span class="pre">Blueprint.get_send_file_max_age()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.blueprints.Blueprint.open_resource"><code class="docutils literal notranslate"><span class="pre">Blueprint.open_resource()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.blueprints.Blueprint.send_static_file"><code class="docutils literal notranslate"><span class="pre">Blueprint.send_static_file()</span></code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="flask.html#module-flask.cli">flask.cli module</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.cli.AppGroup"><code class="docutils literal notranslate"><span class="pre">AppGroup</span></code></a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.cli.AppGroup.command"><code class="docutils literal notranslate"><span class="pre">AppGroup.command()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.cli.AppGroup.group"><code class="docutils literal notranslate"><span class="pre">AppGroup.group()</span></code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.cli.CertParamType"><code class="docutils literal notranslate"><span class="pre">CertParamType</span></code></a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.cli.CertParamType.convert"><code class="docutils literal notranslate"><span class="pre">CertParamType.convert()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.cli.CertParamType.name"><code class="docutils literal notranslate"><span class="pre">CertParamType.name</span></code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.cli.FlaskGroup"><code class="docutils literal notranslate"><span class="pre">FlaskGroup</span></code></a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.cli.FlaskGroup.get_command"><code class="docutils literal notranslate"><span class="pre">FlaskGroup.get_command()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.cli.FlaskGroup.list_commands"><code class="docutils literal notranslate"><span class="pre">FlaskGroup.list_commands()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.cli.FlaskGroup.make_context"><code class="docutils literal notranslate"><span class="pre">FlaskGroup.make_context()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.cli.FlaskGroup.parse_args"><code class="docutils literal notranslate"><span class="pre">FlaskGroup.parse_args()</span></code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.cli.NoAppException"><code class="docutils literal notranslate"><span class="pre">NoAppException</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.cli.ScriptInfo"><code class="docutils literal notranslate"><span class="pre">ScriptInfo</span></code></a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.cli.ScriptInfo.app_import_path"><code class="docutils literal notranslate"><span class="pre">ScriptInfo.app_import_path</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.cli.ScriptInfo.create_app"><code class="docutils literal notranslate"><span class="pre">ScriptInfo.create_app</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.cli.ScriptInfo.data"><code class="docutils literal notranslate"><span class="pre">ScriptInfo.data</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.cli.ScriptInfo.load_app"><code class="docutils literal notranslate"><span class="pre">ScriptInfo.load_app()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.cli.ScriptInfo.load_dotenv_defaults"><code class="docutils literal notranslate"><span class="pre">ScriptInfo.load_dotenv_defaults</span></code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.cli.SeparatedPathType"><code class="docutils literal notranslate"><span class="pre">SeparatedPathType</span></code></a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.cli.SeparatedPathType.convert"><code class="docutils literal notranslate"><span class="pre">SeparatedPathType.convert()</span></code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.cli.find_app_by_string"><code class="docutils literal notranslate"><span class="pre">find_app_by_string()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.cli.find_best_app"><code class="docutils literal notranslate"><span class="pre">find_best_app()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.cli.get_version"><code class="docutils literal notranslate"><span class="pre">get_version()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.cli.load_dotenv"><code class="docutils literal notranslate"><span class="pre">load_dotenv()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.cli.locate_app"><code class="docutils literal notranslate"><span class="pre">locate_app()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.cli.main"><code class="docutils literal notranslate"><span class="pre">main()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.cli.prepare_import"><code class="docutils literal notranslate"><span class="pre">prepare_import()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.cli.show_server_banner"><code class="docutils literal notranslate"><span class="pre">show_server_banner()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.cli.with_appcontext"><code class="docutils literal notranslate"><span class="pre">with_appcontext()</span></code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="flask.html#module-flask.config">flask.config module</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.config.Config"><code class="docutils literal notranslate"><span class="pre">Config</span></code></a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.config.Config.from_envvar"><code class="docutils literal notranslate"><span class="pre">Config.from_envvar()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.config.Config.from_file"><code class="docutils literal notranslate"><span class="pre">Config.from_file()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.config.Config.from_mapping"><code class="docutils literal notranslate"><span class="pre">Config.from_mapping()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.config.Config.from_object"><code class="docutils literal notranslate"><span class="pre">Config.from_object()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.config.Config.from_prefixed_env"><code class="docutils literal notranslate"><span class="pre">Config.from_prefixed_env()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.config.Config.from_pyfile"><code class="docutils literal notranslate"><span class="pre">Config.from_pyfile()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.config.Config.get_namespace"><code class="docutils literal notranslate"><span class="pre">Config.get_namespace()</span></code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.config.ConfigAttribute"><code class="docutils literal notranslate"><span class="pre">ConfigAttribute</span></code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="flask.html#module-flask.ctx">flask.ctx module</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.ctx.AppContext"><code class="docutils literal notranslate"><span class="pre">AppContext</span></code></a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.ctx.AppContext.pop"><code class="docutils literal notranslate"><span class="pre">AppContext.pop()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.ctx.AppContext.push"><code class="docutils literal notranslate"><span class="pre">AppContext.push()</span></code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.ctx.RequestContext"><code class="docutils literal notranslate"><span class="pre">RequestContext</span></code></a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.ctx.RequestContext.copy"><code class="docutils literal notranslate"><span class="pre">RequestContext.copy()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.ctx.RequestContext.match_request"><code class="docutils literal notranslate"><span class="pre">RequestContext.match_request()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.ctx.RequestContext.pop"><code class="docutils literal notranslate"><span class="pre">RequestContext.pop()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.ctx.RequestContext.push"><code class="docutils literal notranslate"><span class="pre">RequestContext.push()</span></code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.ctx.after_this_request"><code class="docutils literal notranslate"><span class="pre">after_this_request()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.ctx.copy_current_request_context"><code class="docutils literal notranslate"><span class="pre">copy_current_request_context()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.ctx.has_app_context"><code class="docutils literal notranslate"><span class="pre">has_app_context()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.ctx.has_request_context"><code class="docutils literal notranslate"><span class="pre">has_request_context()</span></code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="flask.html#module-flask.debughelpers">flask.debughelpers module</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.debughelpers.DebugFilesKeyError"><code class="docutils literal notranslate"><span class="pre">DebugFilesKeyError</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.debughelpers.FormDataRoutingRedirect"><code class="docutils literal notranslate"><span class="pre">FormDataRoutingRedirect</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.debughelpers.UnexpectedUnicodeError"><code class="docutils literal notranslate"><span class="pre">UnexpectedUnicodeError</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.debughelpers.explain_template_loading_attempts"><code class="docutils literal notranslate"><span class="pre">explain_template_loading_attempts()</span></code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="flask.html#module-flask.globals">flask.globals module</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="flask.html#module-flask.helpers">flask.helpers module</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.helpers.abort"><code class="docutils literal notranslate"><span class="pre">abort()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.helpers.flash"><code class="docutils literal notranslate"><span class="pre">flash()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.helpers.get_debug_flag"><code class="docutils literal notranslate"><span class="pre">get_debug_flag()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.helpers.get_flashed_messages"><code class="docutils literal notranslate"><span class="pre">get_flashed_messages()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.helpers.get_load_dotenv"><code class="docutils literal notranslate"><span class="pre">get_load_dotenv()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.helpers.get_template_attribute"><code class="docutils literal notranslate"><span class="pre">get_template_attribute()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.helpers.make_response"><code class="docutils literal notranslate"><span class="pre">make_response()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.helpers.redirect"><code class="docutils literal notranslate"><span class="pre">redirect()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.helpers.send_file"><code class="docutils literal notranslate"><span class="pre">send_file()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.helpers.send_from_directory"><code class="docutils literal notranslate"><span class="pre">send_from_directory()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.helpers.stream_with_context"><code class="docutils literal notranslate"><span class="pre">stream_with_context()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.helpers.url_for"><code class="docutils literal notranslate"><span class="pre">url_for()</span></code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="flask.html#module-flask.logging">flask.logging module</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.logging.create_logger"><code class="docutils literal notranslate"><span class="pre">create_logger()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.logging.default_handler"><code class="docutils literal notranslate"><span class="pre">default_handler</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.logging.has_level_handler"><code class="docutils literal notranslate"><span class="pre">has_level_handler()</span></code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="flask.html#module-flask.sessions">flask.sessions module</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.sessions.NullSession"><code class="docutils literal notranslate"><span class="pre">NullSession</span></code></a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.sessions.NullSession.clear"><code class="docutils literal notranslate"><span class="pre">NullSession.clear()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.sessions.NullSession.pop"><code class="docutils literal notranslate"><span class="pre">NullSession.pop()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.sessions.NullSession.popitem"><code class="docutils literal notranslate"><span class="pre">NullSession.popitem()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.sessions.NullSession.setdefault"><code class="docutils literal notranslate"><span class="pre">NullSession.setdefault()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.sessions.NullSession.update"><code class="docutils literal notranslate"><span class="pre">NullSession.update()</span></code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.sessions.SecureCookieSession"><code class="docutils literal notranslate"><span class="pre">SecureCookieSession</span></code></a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.sessions.SecureCookieSession.accessed"><code class="docutils literal notranslate"><span class="pre">SecureCookieSession.accessed</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.sessions.SecureCookieSession.get"><code class="docutils literal notranslate"><span class="pre">SecureCookieSession.get()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.sessions.SecureCookieSession.modified"><code class="docutils literal notranslate"><span class="pre">SecureCookieSession.modified</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.sessions.SecureCookieSession.setdefault"><code class="docutils literal notranslate"><span class="pre">SecureCookieSession.setdefault()</span></code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.sessions.SecureCookieSessionInterface"><code class="docutils literal notranslate"><span class="pre">SecureCookieSessionInterface</span></code></a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.sessions.SecureCookieSessionInterface.digest_method"><code class="docutils literal notranslate"><span class="pre">SecureCookieSessionInterface.digest_method()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.sessions.SecureCookieSessionInterface.get_signing_serializer"><code class="docutils literal notranslate"><span class="pre">SecureCookieSessionInterface.get_signing_serializer()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.sessions.SecureCookieSessionInterface.key_derivation"><code class="docutils literal notranslate"><span class="pre">SecureCookieSessionInterface.key_derivation</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.sessions.SecureCookieSessionInterface.open_session"><code class="docutils literal notranslate"><span class="pre">SecureCookieSessionInterface.open_session()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.sessions.SecureCookieSessionInterface.salt"><code class="docutils literal notranslate"><span class="pre">SecureCookieSessionInterface.salt</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.sessions.SecureCookieSessionInterface.save_session"><code class="docutils literal notranslate"><span class="pre">SecureCookieSessionInterface.save_session()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.sessions.SecureCookieSessionInterface.serializer"><code class="docutils literal notranslate"><span class="pre">SecureCookieSessionInterface.serializer</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.sessions.SecureCookieSessionInterface.session_class"><code class="docutils literal notranslate"><span class="pre">SecureCookieSessionInterface.session_class</span></code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.sessions.SessionInterface"><code class="docutils literal notranslate"><span class="pre">SessionInterface</span></code></a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.sessions.SessionInterface.get_cookie_domain"><code class="docutils literal notranslate"><span class="pre">SessionInterface.get_cookie_domain()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.sessions.SessionInterface.get_cookie_httponly"><code class="docutils literal notranslate"><span class="pre">SessionInterface.get_cookie_httponly()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.sessions.SessionInterface.get_cookie_name"><code class="docutils literal notranslate"><span class="pre">SessionInterface.get_cookie_name()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.sessions.SessionInterface.get_cookie_partitioned"><code class="docutils literal notranslate"><span class="pre">SessionInterface.get_cookie_partitioned()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.sessions.SessionInterface.get_cookie_path"><code class="docutils literal notranslate"><span class="pre">SessionInterface.get_cookie_path()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.sessions.SessionInterface.get_cookie_samesite"><code class="docutils literal notranslate"><span class="pre">SessionInterface.get_cookie_samesite()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.sessions.SessionInterface.get_cookie_secure"><code class="docutils literal notranslate"><span class="pre">SessionInterface.get_cookie_secure()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.sessions.SessionInterface.get_expiration_time"><code class="docutils literal notranslate"><span class="pre">SessionInterface.get_expiration_time()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.sessions.SessionInterface.is_null_session"><code class="docutils literal notranslate"><span class="pre">SessionInterface.is_null_session()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.sessions.SessionInterface.make_null_session"><code class="docutils literal notranslate"><span class="pre">SessionInterface.make_null_session()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.sessions.SessionInterface.null_session_class"><code class="docutils literal notranslate"><span class="pre">SessionInterface.null_session_class</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.sessions.SessionInterface.open_session"><code class="docutils literal notranslate"><span class="pre">SessionInterface.open_session()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.sessions.SessionInterface.pickle_based"><code class="docutils literal notranslate"><span class="pre">SessionInterface.pickle_based</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.sessions.SessionInterface.save_session"><code class="docutils literal notranslate"><span class="pre">SessionInterface.save_session()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.sessions.SessionInterface.should_set_cookie"><code class="docutils literal notranslate"><span class="pre">SessionInterface.should_set_cookie()</span></code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.sessions.SessionMixin"><code class="docutils literal notranslate"><span class="pre">SessionMixin</span></code></a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.sessions.SessionMixin.accessed"><code class="docutils literal notranslate"><span class="pre">SessionMixin.accessed</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.sessions.SessionMixin.modified"><code class="docutils literal notranslate"><span class="pre">SessionMixin.modified</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.sessions.SessionMixin.new"><code class="docutils literal notranslate"><span class="pre">SessionMixin.new</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.sessions.SessionMixin.permanent"><code class="docutils literal notranslate"><span class="pre">SessionMixin.permanent</span></code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="flask.html#module-flask.signals">flask.signals module</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="flask.html#module-flask.templating">flask.templating module</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.templating.DispatchingJinjaLoader"><code class="docutils literal notranslate"><span class="pre">DispatchingJinjaLoader</span></code></a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.templating.DispatchingJinjaLoader.get_source"><code class="docutils literal notranslate"><span class="pre">DispatchingJinjaLoader.get_source()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.templating.DispatchingJinjaLoader.list_templates"><code class="docutils literal notranslate"><span class="pre">DispatchingJinjaLoader.list_templates()</span></code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.templating.Environment"><code class="docutils literal notranslate"><span class="pre">Environment</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.templating.render_template"><code class="docutils literal notranslate"><span class="pre">render_template()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.templating.render_template_string"><code class="docutils literal notranslate"><span class="pre">render_template_string()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.templating.stream_template"><code class="docutils literal notranslate"><span class="pre">stream_template()</span></code></a></li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.templating.stream_template_string"><code class="docutils literal notranslate"><span class="pre">stream_template_string()</span></code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="flask.html#module-flask.testing">flask.testing module</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.testing.EnvironBuilder"><code class="docutils literal notranslate"><span class="pre">EnvironBuilder</span></code></a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.testing.EnvironBuilder.json_dumps"><code class="docutils literal notranslate"><span class="pre">EnvironBuilder.json_dumps()</span></code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.testing.FlaskCliRunner"><code class="docutils literal notranslate"><span class="pre">FlaskCliRunner</span></code></a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.testing.FlaskCliRunner.invoke"><code class="docutils literal notranslate"><span class="pre">FlaskCliRunner.invoke()</span></code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.testing.FlaskClient"><code class="docutils literal notranslate"><span class="pre">FlaskClient</span></code></a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.testing.FlaskClient.application"><code class="docutils literal notranslate"><span class="pre">FlaskClient.application</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.testing.FlaskClient.open"><code class="docutils literal notranslate"><span class="pre">FlaskClient.open()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.testing.FlaskClient.session_transaction"><code class="docutils literal notranslate"><span class="pre">FlaskClient.session_transaction()</span></code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="flask.html#module-flask.typing">flask.typing module</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="flask.html#module-flask.views">flask.views module</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.views.MethodView"><code class="docutils literal notranslate"><span class="pre">MethodView</span></code></a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.views.MethodView.dispatch_request"><code class="docutils literal notranslate"><span class="pre">MethodView.dispatch_request()</span></code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.views.View"><code class="docutils literal notranslate"><span class="pre">View</span></code></a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.views.View.as_view"><code class="docutils literal notranslate"><span class="pre">View.as_view()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.views.View.decorators"><code class="docutils literal notranslate"><span class="pre">View.decorators</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.views.View.dispatch_request"><code class="docutils literal notranslate"><span class="pre">View.dispatch_request()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.views.View.init_every_request"><code class="docutils literal notranslate"><span class="pre">View.init_every_request</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.views.View.methods"><code class="docutils literal notranslate"><span class="pre">View.methods</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.views.View.provide_automatic_options"><code class="docutils literal notranslate"><span class="pre">View.provide_automatic_options</span></code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="flask.html#module-flask.wrappers">flask.wrappers module</a><ul>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.wrappers.Request"><code class="docutils literal notranslate"><span class="pre">Request</span></code></a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.wrappers.Request.blueprint"><code class="docutils literal notranslate"><span class="pre">Request.blueprint</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.wrappers.Request.blueprints"><code class="docutils literal notranslate"><span class="pre">Request.blueprints</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.wrappers.Request.endpoint"><code class="docutils literal notranslate"><span class="pre">Request.endpoint</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.wrappers.Request.json_module"><code class="docutils literal notranslate"><span class="pre">Request.json_module</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.wrappers.Request.max_content_length"><code class="docutils literal notranslate"><span class="pre">Request.max_content_length</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.wrappers.Request.max_form_memory_size"><code class="docutils literal notranslate"><span class="pre">Request.max_form_memory_size</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.wrappers.Request.max_form_parts"><code class="docutils literal notranslate"><span class="pre">Request.max_form_parts</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.wrappers.Request.on_json_loading_failed"><code class="docutils literal notranslate"><span class="pre">Request.on_json_loading_failed()</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.wrappers.Request.routing_exception"><code class="docutils literal notranslate"><span class="pre">Request.routing_exception</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.wrappers.Request.url_rule"><code class="docutils literal notranslate"><span class="pre">Request.url_rule</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.wrappers.Request.view_args"><code class="docutils literal notranslate"><span class="pre">Request.view_args</span></code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l3"><a class="reference internal" href="flask.html#flask.wrappers.Response"><code class="docutils literal notranslate"><span class="pre">Response</span></code></a><ul>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.wrappers.Response.autocorrect_location_header"><code class="docutils literal notranslate"><span class="pre">Response.autocorrect_location_header</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.wrappers.Response.default_mimetype"><code class="docutils literal notranslate"><span class="pre">Response.default_mimetype</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.wrappers.Response.json_module"><code class="docutils literal notranslate"><span class="pre">Response.json_module</span></code></a></li>
|
||||
<li class="toctree-l4"><a class="reference internal" href="flask.html#flask.wrappers.Response.max_cookie_size"><code class="docutils literal notranslate"><span class="pre">Response.max_cookie_size</span></code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="flask.html#module-flask">Module contents</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<footer><div class="rst-footer-buttons" role="navigation" aria-label="Footer">
|
||||
<a href="index.html" class="btn btn-neutral float-left" title="Flask - Documentación Personalizada" accesskey="p" rel="prev"><span class="fa fa-arrow-circle-left" aria-hidden="true"></span> Previous</a>
|
||||
<a href="flask.html" class="btn btn-neutral float-right" title="flask package" accesskey="n" rel="next">Next <span class="fa fa-arrow-circle-right" aria-hidden="true"></span></a>
|
||||
</div>
|
||||
|
||||
<hr/>
|
||||
|
||||
<div role="contentinfo">
|
||||
<p>© Copyright 2025, Pallets Team.</p>
|
||||
</div>
|
||||
|
||||
Built with <a href="https://www.sphinx-doc.org/">Sphinx</a> using a
|
||||
<a href="https://github.com/readthedocs/sphinx_rtd_theme">theme</a>
|
||||
provided by <a href="https://readthedocs.org">Read the Docs</a>.
|
||||
|
||||
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<script>
|
||||
jQuery(function () {
|
||||
SphinxRtdTheme.Navigation.enable(true);
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
BIN
docs/build/html/objects.inv
vendored
Normal file
BIN
docs/build/html/objects.inv
vendored
Normal file
Binary file not shown.
215
docs/build/html/py-modindex.html
vendored
Normal file
215
docs/build/html/py-modindex.html
vendored
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html class="writer-html5" lang="en" data-content_root="./">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Python Module Index — Flask documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=b86133f3" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/css/theme.css?v=e59714d7" />
|
||||
|
||||
|
||||
<script src="_static/jquery.js?v=5d32c60e"></script>
|
||||
<script src="_static/_sphinx_javascript_frameworks_compat.js?v=2cd50e6c"></script>
|
||||
<script src="_static/documentation_options.js?v=5929fcd5"></script>
|
||||
<script src="_static/doctools.js?v=9bcbadda"></script>
|
||||
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
|
||||
<script src="_static/js/theme.js"></script>
|
||||
<link rel="index" title="Index" href="genindex.html" />
|
||||
<link rel="search" title="Search" href="search.html" />
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
<body class="wy-body-for-nav">
|
||||
<div class="wy-grid-for-nav">
|
||||
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
|
||||
<div class="wy-side-scroll">
|
||||
<div class="wy-side-nav-search" >
|
||||
|
||||
|
||||
|
||||
<a href="index.html" class="icon icon-home">
|
||||
Flask
|
||||
</a>
|
||||
<div role="search">
|
||||
<form id="rtd-search-form" class="wy-form" action="search.html" method="get">
|
||||
<input type="text" name="q" placeholder="Search docs" aria-label="Search docs" />
|
||||
<input type="hidden" name="check_keywords" value="yes" />
|
||||
<input type="hidden" name="area" value="default" />
|
||||
</form>
|
||||
</div>
|
||||
</div><div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="Navigation menu">
|
||||
<p class="caption" role="heading"><span class="caption-text">Contenido:</span></p>
|
||||
<ul>
|
||||
<li class="toctree-l1"><a class="reference internal" href="modules.html">flask</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap"><nav class="wy-nav-top" aria-label="Mobile navigation menu" >
|
||||
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
|
||||
<a href="index.html">Flask</a>
|
||||
</nav>
|
||||
|
||||
<div class="wy-nav-content">
|
||||
<div class="rst-content">
|
||||
<div role="navigation" aria-label="Page navigation">
|
||||
<ul class="wy-breadcrumbs">
|
||||
<li><a href="index.html" class="icon icon-home" aria-label="Home"></a></li>
|
||||
<li class="breadcrumb-item active">Python Module Index</li>
|
||||
<li class="wy-breadcrumbs-aside">
|
||||
</li>
|
||||
</ul>
|
||||
<hr/>
|
||||
</div>
|
||||
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
|
||||
<div itemprop="articleBody">
|
||||
|
||||
|
||||
<h1>Python Module Index</h1>
|
||||
|
||||
<div class="modindex-jumpbox">
|
||||
<a href="#cap-f"><strong>f</strong></a>
|
||||
</div>
|
||||
|
||||
<table class="indextable modindextable">
|
||||
<tr class="pcap"><td></td><td> </td><td></td></tr>
|
||||
<tr class="cap" id="cap-f"><td></td><td>
|
||||
<strong>f</strong></td><td></td></tr>
|
||||
<tr>
|
||||
<td><img src="_static/minus.png" class="toggler"
|
||||
id="toggle-1" style="display: none" alt="-" /></td>
|
||||
<td>
|
||||
<a href="flask.html#module-flask"><code class="xref">flask</code></a></td><td>
|
||||
<em></em></td></tr>
|
||||
<tr class="cg-1">
|
||||
<td></td>
|
||||
<td>   
|
||||
<a href="flask.html#module-flask.app"><code class="xref">flask.app</code></a></td><td>
|
||||
<em></em></td></tr>
|
||||
<tr class="cg-1">
|
||||
<td></td>
|
||||
<td>   
|
||||
<a href="flask.html#module-flask.blueprints"><code class="xref">flask.blueprints</code></a></td><td>
|
||||
<em></em></td></tr>
|
||||
<tr class="cg-1">
|
||||
<td></td>
|
||||
<td>   
|
||||
<a href="flask.html#module-flask.cli"><code class="xref">flask.cli</code></a></td><td>
|
||||
<em></em></td></tr>
|
||||
<tr class="cg-1">
|
||||
<td></td>
|
||||
<td>   
|
||||
<a href="flask.html#module-flask.config"><code class="xref">flask.config</code></a></td><td>
|
||||
<em></em></td></tr>
|
||||
<tr class="cg-1">
|
||||
<td></td>
|
||||
<td>   
|
||||
<a href="flask.html#module-flask.ctx"><code class="xref">flask.ctx</code></a></td><td>
|
||||
<em></em></td></tr>
|
||||
<tr class="cg-1">
|
||||
<td></td>
|
||||
<td>   
|
||||
<a href="flask.html#module-flask.debughelpers"><code class="xref">flask.debughelpers</code></a></td><td>
|
||||
<em></em></td></tr>
|
||||
<tr class="cg-1">
|
||||
<td></td>
|
||||
<td>   
|
||||
<a href="flask.html#module-flask.globals"><code class="xref">flask.globals</code></a></td><td>
|
||||
<em></em></td></tr>
|
||||
<tr class="cg-1">
|
||||
<td></td>
|
||||
<td>   
|
||||
<a href="flask.html#module-flask.helpers"><code class="xref">flask.helpers</code></a></td><td>
|
||||
<em></em></td></tr>
|
||||
<tr class="cg-1">
|
||||
<td></td>
|
||||
<td>   
|
||||
<a href="flask.json.html#module-flask.json"><code class="xref">flask.json</code></a></td><td>
|
||||
<em></em></td></tr>
|
||||
<tr class="cg-1">
|
||||
<td></td>
|
||||
<td>   
|
||||
<a href="flask.json.html#module-flask.json.provider"><code class="xref">flask.json.provider</code></a></td><td>
|
||||
<em></em></td></tr>
|
||||
<tr class="cg-1">
|
||||
<td></td>
|
||||
<td>   
|
||||
<a href="flask.json.html#module-flask.json.tag"><code class="xref">flask.json.tag</code></a></td><td>
|
||||
<em></em></td></tr>
|
||||
<tr class="cg-1">
|
||||
<td></td>
|
||||
<td>   
|
||||
<a href="flask.html#module-flask.logging"><code class="xref">flask.logging</code></a></td><td>
|
||||
<em></em></td></tr>
|
||||
<tr class="cg-1">
|
||||
<td></td>
|
||||
<td>   
|
||||
<a href="flask.html#module-flask.sessions"><code class="xref">flask.sessions</code></a></td><td>
|
||||
<em></em></td></tr>
|
||||
<tr class="cg-1">
|
||||
<td></td>
|
||||
<td>   
|
||||
<a href="flask.html#module-flask.signals"><code class="xref">flask.signals</code></a></td><td>
|
||||
<em></em></td></tr>
|
||||
<tr class="cg-1">
|
||||
<td></td>
|
||||
<td>   
|
||||
<a href="flask.html#module-flask.templating"><code class="xref">flask.templating</code></a></td><td>
|
||||
<em></em></td></tr>
|
||||
<tr class="cg-1">
|
||||
<td></td>
|
||||
<td>   
|
||||
<a href="flask.html#module-flask.testing"><code class="xref">flask.testing</code></a></td><td>
|
||||
<em></em></td></tr>
|
||||
<tr class="cg-1">
|
||||
<td></td>
|
||||
<td>   
|
||||
<a href="flask.html#module-flask.typing"><code class="xref">flask.typing</code></a></td><td>
|
||||
<em></em></td></tr>
|
||||
<tr class="cg-1">
|
||||
<td></td>
|
||||
<td>   
|
||||
<a href="flask.html#module-flask.views"><code class="xref">flask.views</code></a></td><td>
|
||||
<em></em></td></tr>
|
||||
<tr class="cg-1">
|
||||
<td></td>
|
||||
<td>   
|
||||
<a href="flask.html#module-flask.wrappers"><code class="xref">flask.wrappers</code></a></td><td>
|
||||
<em></em></td></tr>
|
||||
</table>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<footer>
|
||||
|
||||
<hr/>
|
||||
|
||||
<div role="contentinfo">
|
||||
<p>© Copyright 2025, Pallets Team.</p>
|
||||
</div>
|
||||
|
||||
Built with <a href="https://www.sphinx-doc.org/">Sphinx</a> using a
|
||||
<a href="https://github.com/readthedocs/sphinx_rtd_theme">theme</a>
|
||||
provided by <a href="https://readthedocs.org">Read the Docs</a>.
|
||||
|
||||
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<script>
|
||||
jQuery(function () {
|
||||
SphinxRtdTheme.Navigation.enable(true);
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
120
docs/build/html/search.html
vendored
Normal file
120
docs/build/html/search.html
vendored
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html class="writer-html5" lang="en" data-content_root="./">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Search — Flask documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=b86133f3" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/css/theme.css?v=e59714d7" />
|
||||
|
||||
|
||||
|
||||
<script src="_static/jquery.js?v=5d32c60e"></script>
|
||||
<script src="_static/_sphinx_javascript_frameworks_compat.js?v=2cd50e6c"></script>
|
||||
<script src="_static/documentation_options.js?v=5929fcd5"></script>
|
||||
<script src="_static/doctools.js?v=9bcbadda"></script>
|
||||
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
|
||||
<script src="_static/js/theme.js"></script>
|
||||
<script src="_static/searchtools.js"></script>
|
||||
<script src="_static/language_data.js"></script>
|
||||
<link rel="index" title="Index" href="genindex.html" />
|
||||
<link rel="search" title="Search" href="#" />
|
||||
</head>
|
||||
|
||||
<body class="wy-body-for-nav">
|
||||
<div class="wy-grid-for-nav">
|
||||
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
|
||||
<div class="wy-side-scroll">
|
||||
<div class="wy-side-nav-search" >
|
||||
|
||||
|
||||
|
||||
<a href="index.html" class="icon icon-home">
|
||||
Flask
|
||||
</a>
|
||||
<div role="search">
|
||||
<form id="rtd-search-form" class="wy-form" action="#" method="get">
|
||||
<input type="text" name="q" placeholder="Search docs" aria-label="Search docs" />
|
||||
<input type="hidden" name="check_keywords" value="yes" />
|
||||
<input type="hidden" name="area" value="default" />
|
||||
</form>
|
||||
</div>
|
||||
</div><div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="Navigation menu">
|
||||
<p class="caption" role="heading"><span class="caption-text">Contenido:</span></p>
|
||||
<ul>
|
||||
<li class="toctree-l1"><a class="reference internal" href="modules.html">flask</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap"><nav class="wy-nav-top" aria-label="Mobile navigation menu" >
|
||||
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
|
||||
<a href="index.html">Flask</a>
|
||||
</nav>
|
||||
|
||||
<div class="wy-nav-content">
|
||||
<div class="rst-content">
|
||||
<div role="navigation" aria-label="Page navigation">
|
||||
<ul class="wy-breadcrumbs">
|
||||
<li><a href="index.html" class="icon icon-home" aria-label="Home"></a></li>
|
||||
<li class="breadcrumb-item active">Search</li>
|
||||
<li class="wy-breadcrumbs-aside">
|
||||
</li>
|
||||
</ul>
|
||||
<hr/>
|
||||
</div>
|
||||
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
|
||||
<div itemprop="articleBody">
|
||||
|
||||
<noscript>
|
||||
<div id="fallback" class="admonition warning">
|
||||
<p class="last">
|
||||
Please activate JavaScript to enable the search functionality.
|
||||
</p>
|
||||
</div>
|
||||
</noscript>
|
||||
|
||||
|
||||
<div id="search-results">
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<footer>
|
||||
|
||||
<hr/>
|
||||
|
||||
<div role="contentinfo">
|
||||
<p>© Copyright 2025, Pallets Team.</p>
|
||||
</div>
|
||||
|
||||
Built with <a href="https://www.sphinx-doc.org/">Sphinx</a> using a
|
||||
<a href="https://github.com/readthedocs/sphinx_rtd_theme">theme</a>
|
||||
provided by <a href="https://readthedocs.org">Read the Docs</a>.
|
||||
|
||||
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<script>
|
||||
jQuery(function () {
|
||||
SphinxRtdTheme.Navigation.enable(true);
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
jQuery(function() { Search.loadIndex("searchindex.js"); });
|
||||
</script>
|
||||
|
||||
<script id="searchindexloader"></script>
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
1
docs/build/html/searchindex.js
vendored
Normal file
1
docs/build/html/searchindex.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -1,4 +0,0 @@
|
|||
Changes
|
||||
=======
|
||||
|
||||
.. include:: ../CHANGES.rst
|
||||
556
docs/cli.rst
556
docs/cli.rst
|
|
@ -1,556 +0,0 @@
|
|||
.. 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
docs/config.rst
836
docs/config.rst
|
|
@ -1,836 +0,0 @@
|
|||
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()
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
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/
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
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
|
||||
)
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
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``.
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
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
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
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.
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
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.
|
||||
|
|
@ -1,130 +0,0 @@
|
|||
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/
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
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.
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
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
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
nginx
|
||||
=====
|
||||
|
||||
`nginx`_ 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.
|
||||
|
||||
Nginx can be installed using your system package manager, or a pre-built
|
||||
executable for Windows. Installing and running Nginx itself is outside
|
||||
the scope of this doc. This page outlines the basics of configuring
|
||||
Nginx to proxy your application. Be sure to read its documentation to
|
||||
understand what features are available.
|
||||
|
||||
.. _nginx: https://nginx.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 nginx configuration is located at ``/etc/nginx/nginx.conf`` on
|
||||
Linux. It may be different depending on your operating system. Check the
|
||||
docs and look for ``nginx.conf``.
|
||||
|
||||
Remove or comment out any existing ``server`` section. Add a ``server``
|
||||
section and use the ``proxy_pass`` directive to point to the address the
|
||||
WSGI server is listening on. We'll assume the WSGI server is listening
|
||||
locally at ``http://127.0.0.1:8000``.
|
||||
|
||||
.. code-block:: nginx
|
||||
:caption: ``/etc/nginx.conf``
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8000/;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_set_header X-Forwarded-Prefix /;
|
||||
}
|
||||
}
|
||||
|
||||
Then :doc:`proxy_fix` so that your application uses these headers.
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
Tell Flask it is Behind a Proxy
|
||||
===============================
|
||||
|
||||
When using a reverse proxy, or many Python hosting platforms, the proxy
|
||||
will intercept and forward all external requests to the local WSGI
|
||||
server.
|
||||
|
||||
From the WSGI server and Flask application's perspectives, requests are
|
||||
now coming from the HTTP server to the local address, rather than from
|
||||
the remote address to the external server address.
|
||||
|
||||
HTTP servers should set ``X-Forwarded-`` headers to pass on the real
|
||||
values to the application. The application can then be told to trust and
|
||||
use those values by wrapping it with the
|
||||
:doc:`werkzeug:middleware/proxy_fix` middleware provided by Werkzeug.
|
||||
|
||||
This middleware should only be used if the application is actually
|
||||
behind a proxy, and should be configured with the number of proxies that
|
||||
are chained in front of it. Not all proxies set all the headers. Since
|
||||
incoming headers can be faked, you must set how many proxies are setting
|
||||
each header so the middleware knows what to trust.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from werkzeug.middleware.proxy_fix import ProxyFix
|
||||
|
||||
app.wsgi_app = ProxyFix(
|
||||
app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_prefix=1
|
||||
)
|
||||
|
||||
Remember, only apply this middleware if you are behind a proxy, and set
|
||||
the correct number of proxies that set each header. It can be a security
|
||||
issue if you get this configuration wrong.
|
||||
|
|
@ -1,145 +0,0 @@
|
|||
uWSGI
|
||||
=====
|
||||
|
||||
`uWSGI`_ is a fast, compiled server suite with extensive configuration
|
||||
and capabilities beyond a basic server.
|
||||
|
||||
* It can be very performant due to being a compiled program.
|
||||
* It is complex to configure beyond the basic application, and has so
|
||||
many options that it can be difficult for beginners to understand.
|
||||
* It does not support Windows (but does run on WSL).
|
||||
* It requires a compiler to install in some cases.
|
||||
|
||||
This page outlines the basics of running uWSGI. Be sure to read its
|
||||
documentation to understand what features are available.
|
||||
|
||||
.. _uWSGI: https://uwsgi-docs.readthedocs.io/en/latest/
|
||||
|
||||
|
||||
Installing
|
||||
----------
|
||||
|
||||
uWSGI has multiple ways to install it. The most straightforward is to
|
||||
install the ``pyuwsgi`` package, which provides precompiled wheels for
|
||||
common platforms. However, it does not provide SSL support, which can be
|
||||
provided with a reverse proxy instead.
|
||||
|
||||
Create a virtualenv, install your application, then install ``pyuwsgi``.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ cd hello-app
|
||||
$ python -m venv .venv
|
||||
$ . .venv/bin/activate
|
||||
$ pip install . # install your application
|
||||
$ pip install pyuwsgi
|
||||
|
||||
If you have a compiler available, you can install the ``uwsgi`` package
|
||||
instead. Or install the ``pyuwsgi`` package from sdist instead of wheel.
|
||||
Either method will include SSL support.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ pip install uwsgi
|
||||
|
||||
# or
|
||||
$ pip install --no-binary pyuwsgi pyuwsgi
|
||||
|
||||
|
||||
Running
|
||||
-------
|
||||
|
||||
The most basic way to run uWSGI is to tell it to start an HTTP server
|
||||
and import your application.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ uwsgi --http 127.0.0.1:8000 --master -p 4 -w hello:app
|
||||
|
||||
*** Starting uWSGI 2.0.20 (64bit) on [x] ***
|
||||
*** Operational MODE: preforking ***
|
||||
mounting hello:app on /
|
||||
spawned uWSGI master process (pid: x)
|
||||
spawned uWSGI worker 1 (pid: x, cores: 1)
|
||||
spawned uWSGI worker 2 (pid: x, cores: 1)
|
||||
spawned uWSGI worker 3 (pid: x, cores: 1)
|
||||
spawned uWSGI worker 4 (pid: x, cores: 1)
|
||||
spawned uWSGI http 1 (pid: x)
|
||||
|
||||
If you're using the app factory pattern, you'll need to create a small
|
||||
Python file to create the app, then point uWSGI at that.
|
||||
|
||||
.. code-block:: python
|
||||
:caption: ``wsgi.py``
|
||||
|
||||
from hello import create_app
|
||||
|
||||
app = create_app()
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ uwsgi --http 127.0.0.1:8000 --master -p 4 -w wsgi:app
|
||||
|
||||
The ``--http`` option starts an HTTP server at 127.0.0.1 port 8000. The
|
||||
``--master`` option specifies the standard worker manager. The ``-p``
|
||||
option starts 4 worker processes; a starting value could be ``CPU * 2``.
|
||||
The ``-w`` option tells uWSGI how to import your application
|
||||
|
||||
|
||||
Binding Externally
|
||||
------------------
|
||||
|
||||
uWSGI should not be run as root with the configuration shown in this doc
|
||||
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 uWSGI. It is possible to
|
||||
run uWSGI as root securely, but that is beyond the scope of this doc.
|
||||
|
||||
uWSGI has optimized integration with `Nginx uWSGI`_ and
|
||||
`Apache mod_proxy_uwsgi`_, and possibly other servers, instead of using
|
||||
a standard HTTP proxy. That configuration is beyond the scope of this
|
||||
doc, see the links for more information.
|
||||
|
||||
.. _Nginx uWSGI: https://uwsgi-docs.readthedocs.io/en/latest/Nginx.html
|
||||
.. _Apache mod_proxy_uwsgi: https://uwsgi-docs.readthedocs.io/en/latest/Apache.html#mod-proxy-uwsgi
|
||||
|
||||
You can bind to all external IPs on a non-privileged port using the
|
||||
``--http 0.0.0.0:8000`` option. Don't do this when using a reverse proxy
|
||||
setup, otherwise it will be possible to bypass the proxy.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ uwsgi --http 0.0.0.0:8000 --master -p 4 -w wsgi:app
|
||||
|
||||
``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
|
||||
-----------------
|
||||
|
||||
The default sync worker is appropriate for many use cases. If you need
|
||||
asynchronous support, uWSGI provides a `gevent`_ worker. This is not the
|
||||
same as Python's ``async/await``, or the ASGI server spec. You must
|
||||
actually use gevent in your own code to see any benefit to using the
|
||||
worker.
|
||||
|
||||
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.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ uwsgi --http 127.0.0.1:8000 --master --gevent 100 -w wsgi:app
|
||||
|
||||
*** Starting uWSGI 2.0.20 (64bit) on [x] ***
|
||||
*** Operational MODE: async ***
|
||||
mounting hello:app on /
|
||||
spawned uWSGI master process (pid: x)
|
||||
spawned uWSGI worker 1 (pid: x, cores: 100)
|
||||
spawned uWSGI http 1 (pid: x)
|
||||
*** running gevent loop engine [addr:x] ***
|
||||
|
||||
|
||||
.. _gevent: https://www.gevent.org/
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
Waitress
|
||||
========
|
||||
|
||||
`Waitress`_ is a pure Python WSGI server.
|
||||
|
||||
* It is easy to configure.
|
||||
* It supports Windows directly.
|
||||
* It is easy to install as it does not require additional dependencies
|
||||
or compilation.
|
||||
* It does not support streaming requests, full request data is always
|
||||
buffered.
|
||||
* It uses a single process with multiple thread workers.
|
||||
|
||||
This page outlines the basics of running Waitress. Be sure to read its
|
||||
documentation and ``waitress-serve --help`` to understand what features
|
||||
are available.
|
||||
|
||||
.. _Waitress: https://docs.pylonsproject.org/projects/waitress/
|
||||
|
||||
|
||||
Installing
|
||||
----------
|
||||
|
||||
Create a virtualenv, install your application, then install
|
||||
``waitress``.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ cd hello-app
|
||||
$ python -m venv .venv
|
||||
$ . .venv/bin/activate
|
||||
$ pip install . # install your application
|
||||
$ pip install waitress
|
||||
|
||||
|
||||
Running
|
||||
-------
|
||||
|
||||
The only required argument to ``waitress-serve`` tells it how to load
|
||||
your Flask application. The syntax is ``{module}:{app}``. ``module`` is
|
||||
the dotted import name to the module with your application. ``app`` is
|
||||
the variable with the application. If you're using the app factory
|
||||
pattern, use ``--call {module}:{factory}`` instead.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
# equivalent to 'from hello import app'
|
||||
$ waitress-serve --host 127.0.0.1 hello:app
|
||||
|
||||
# equivalent to 'from hello import create_app; create_app()'
|
||||
$ waitress-serve --host 127.0.0.1 --call hello:create_app
|
||||
|
||||
Serving on http://127.0.0.1:8080
|
||||
|
||||
The ``--host`` option binds the server to local ``127.0.0.1`` only.
|
||||
|
||||
Logs for each request aren't shown, only errors are shown. Logging can
|
||||
be configured through the Python interface instead of the command line.
|
||||
|
||||
|
||||
Binding Externally
|
||||
------------------
|
||||
|
||||
Waitress 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 Waitress.
|
||||
|
||||
You can bind to all external IPs on a non-privileged port by not
|
||||
specifying the ``--host`` option. 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.
|
||||
228
docs/design.rst
228
docs/design.rst
|
|
@ -1,228 +0,0 @@
|
|||
Design Decisions in Flask
|
||||
=========================
|
||||
|
||||
If you are curious why Flask does certain things the way it does and not
|
||||
differently, this section is for you. This should give you an idea about
|
||||
some of the design decisions that may appear arbitrary and surprising at
|
||||
first, especially in direct comparison with other frameworks.
|
||||
|
||||
|
||||
The Explicit Application Object
|
||||
-------------------------------
|
||||
|
||||
A Python web application based on WSGI has to have one central callable
|
||||
object that implements the actual application. In Flask this is an
|
||||
instance of the :class:`~flask.Flask` class. Each Flask application has
|
||||
to create an instance of this class itself and pass it the name of the
|
||||
module, but why can't Flask do that itself?
|
||||
|
||||
Without such an explicit application object the following code::
|
||||
|
||||
from flask import Flask
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
return 'Hello World!'
|
||||
|
||||
Would look like this instead::
|
||||
|
||||
from hypothetical_flask import route
|
||||
|
||||
@route('/')
|
||||
def index():
|
||||
return 'Hello World!'
|
||||
|
||||
There are three major reasons for this. The most important one is that
|
||||
implicit application objects require that there may only be one instance at
|
||||
the time. There are ways to fake multiple applications with a single
|
||||
application object, like maintaining a stack of applications, but this
|
||||
causes some problems I won't outline here in detail. Now the question is:
|
||||
when does a microframework need more than one application at the same
|
||||
time? A good example for this is unit testing. When you want to test
|
||||
something it can be very helpful to create a minimal application to test
|
||||
specific behavior. When the application object is deleted everything it
|
||||
allocated will be freed again.
|
||||
|
||||
Another thing that becomes possible when you have an explicit object lying
|
||||
around in your code is that you can subclass the base class
|
||||
(:class:`~flask.Flask`) to alter specific behavior. This would not be
|
||||
possible without hacks if the object were created ahead of time for you
|
||||
based on a class that is not exposed to you.
|
||||
|
||||
But there is another very important reason why Flask depends on an
|
||||
explicit instantiation of that class: the package name. Whenever you
|
||||
create a Flask instance you usually pass it `__name__` as package name.
|
||||
Flask depends on that information to properly load resources relative
|
||||
to your module. With Python's outstanding support for reflection it can
|
||||
then access the package to figure out where the templates and static files
|
||||
are stored (see :meth:`~flask.Flask.open_resource`). Now obviously there
|
||||
are frameworks around that do not need any configuration and will still be
|
||||
able to load templates relative to your application module. But they have
|
||||
to use the current working directory for that, which is a very unreliable
|
||||
way to determine where the application is. The current working directory
|
||||
is process-wide and if you are running multiple applications in one
|
||||
process (which could happen in a webserver without you knowing) the paths
|
||||
will be off. Worse: many webservers do not set the working directory to
|
||||
the directory of your application but to the document root which does not
|
||||
have to be the same folder.
|
||||
|
||||
The third reason is "explicit is better than implicit". That object is
|
||||
your WSGI application, you don't have to remember anything else. If you
|
||||
want to apply a WSGI middleware, just wrap it and you're done (though
|
||||
there are better ways to do that so that you do not lose the reference
|
||||
to the application object :meth:`~flask.Flask.wsgi_app`).
|
||||
|
||||
Furthermore this design makes it possible to use a factory function to
|
||||
create the application which is very helpful for unit testing and similar
|
||||
things (:doc:`/patterns/appfactories`).
|
||||
|
||||
The Routing System
|
||||
------------------
|
||||
|
||||
Flask uses the Werkzeug routing system which was designed to
|
||||
automatically order routes by complexity. This means that you can declare
|
||||
routes in arbitrary order and they will still work as expected. This is a
|
||||
requirement if you want to properly implement decorator based routing
|
||||
since decorators could be fired in undefined order when the application is
|
||||
split into multiple modules.
|
||||
|
||||
Another design decision with the Werkzeug routing system is that routes
|
||||
in Werkzeug try to ensure that URLs are unique. Werkzeug will go quite far
|
||||
with that in that it will automatically redirect to a canonical URL if a route
|
||||
is ambiguous.
|
||||
|
||||
|
||||
One Template Engine
|
||||
-------------------
|
||||
|
||||
Flask decides on one template engine: Jinja2. Why doesn't Flask have a
|
||||
pluggable template engine interface? You can obviously use a different
|
||||
template engine, but Flask will still configure Jinja2 for you. While
|
||||
that limitation that Jinja2 is *always* configured will probably go away,
|
||||
the decision to bundle one template engine and use that will not.
|
||||
|
||||
Template engines are like programming languages and each of those engines
|
||||
has a certain understanding about how things work. On the surface they
|
||||
all work the same: you tell the engine to evaluate a template with a set
|
||||
of variables and take the return value as string.
|
||||
|
||||
But that's about where similarities end. Jinja2 for example has an
|
||||
extensive filter system, a certain way to do template inheritance,
|
||||
support for reusable blocks (macros) that can be used from inside
|
||||
templates and also from Python code, supports iterative template
|
||||
rendering, configurable syntax and more. On the other hand an engine
|
||||
like Genshi is based on XML stream evaluation, template inheritance by
|
||||
taking the availability of XPath into account and more. Mako on the
|
||||
other hand treats templates similar to Python modules.
|
||||
|
||||
When it comes to connecting a template engine with an application or
|
||||
framework there is more than just rendering templates. For instance,
|
||||
Flask uses Jinja2's extensive autoescaping support. Also it provides
|
||||
ways to access macros from Jinja2 templates.
|
||||
|
||||
A template abstraction layer that would not take the unique features of
|
||||
the template engines away is a science on its own and a too large
|
||||
undertaking for a microframework like Flask.
|
||||
|
||||
Furthermore extensions can then easily depend on one template language
|
||||
being present. You can easily use your own templating language, but an
|
||||
extension could still depend on Jinja itself.
|
||||
|
||||
|
||||
What does "micro" mean?
|
||||
-----------------------
|
||||
|
||||
“Micro” does not mean that your whole web application has to fit into a single
|
||||
Python file (although it certainly can), nor does it mean that Flask is lacking
|
||||
in functionality. The "micro" in microframework means Flask aims to keep the
|
||||
core simple but extensible. Flask won't make many decisions for you, such as
|
||||
what database to use. Those decisions that it does make, such as what
|
||||
templating engine to use, are easy to change. Everything else is up to you, so
|
||||
that Flask can be everything you need and nothing you don't.
|
||||
|
||||
By default, Flask does not include a database abstraction layer, form
|
||||
validation or anything else where different libraries already exist that can
|
||||
handle that. Instead, Flask supports extensions to add such functionality to
|
||||
your application as if it was implemented in Flask itself. Numerous extensions
|
||||
provide database integration, form validation, upload handling, various open
|
||||
authentication technologies, and more. Flask may be "micro", but it's ready for
|
||||
production use on a variety of needs.
|
||||
|
||||
Why does Flask call itself a microframework and yet it depends on two
|
||||
libraries (namely Werkzeug and Jinja2). Why shouldn't it? If we look
|
||||
over to the Ruby side of web development there we have a protocol very
|
||||
similar to WSGI. Just that it's called Rack there, but besides that it
|
||||
looks very much like a WSGI rendition for Ruby. But nearly all
|
||||
applications in Ruby land do not work with Rack directly, but on top of a
|
||||
library with the same name. This Rack library has two equivalents in
|
||||
Python: WebOb (formerly Paste) and Werkzeug. Paste is still around but
|
||||
from my understanding it's sort of deprecated in favour of WebOb. The
|
||||
development of WebOb and Werkzeug started side by side with similar ideas
|
||||
in mind: be a good implementation of WSGI for other applications to take
|
||||
advantage.
|
||||
|
||||
Flask is a framework that takes advantage of the work already done by
|
||||
Werkzeug to properly interface WSGI (which can be a complex task at
|
||||
times). Thanks to recent developments in the Python package
|
||||
infrastructure, packages with dependencies are no longer an issue and
|
||||
there are very few reasons against having libraries that depend on others.
|
||||
|
||||
|
||||
Thread Locals
|
||||
-------------
|
||||
|
||||
Flask uses thread local objects (context local objects in fact, they
|
||||
support greenlet contexts as well) for request, session and an extra
|
||||
object you can put your own things on (:data:`~flask.g`). Why is that and
|
||||
isn't that a bad idea?
|
||||
|
||||
Yes it is usually not such a bright idea to use thread locals. They cause
|
||||
troubles for servers that are not based on the concept of threads and make
|
||||
large applications harder to maintain. However Flask is just not designed
|
||||
for large applications or asynchronous servers. Flask wants to make it
|
||||
quick and easy to write a traditional web application.
|
||||
|
||||
|
||||
Async/await and ASGI support
|
||||
----------------------------
|
||||
|
||||
Flask supports ``async`` coroutines for view functions by executing the
|
||||
coroutine on a separate thread instead of using an event loop on the
|
||||
main thread as an async-first (ASGI) framework would. This is necessary
|
||||
for Flask to remain backwards compatible with extensions and code built
|
||||
before ``async`` was introduced into Python. This compromise introduces
|
||||
a performance cost compared with the ASGI frameworks, due to the
|
||||
overhead of the threads.
|
||||
|
||||
Due to how tied to WSGI Flask's code is, it's not clear if it's possible
|
||||
to make the ``Flask`` class support ASGI and WSGI at the same time. Work
|
||||
is currently being done in Werkzeug to work with ASGI, which may
|
||||
eventually enable support in Flask as well.
|
||||
|
||||
See :doc:`/async-await` for more discussion.
|
||||
|
||||
|
||||
What Flask is, What Flask is Not
|
||||
--------------------------------
|
||||
|
||||
Flask will never have a database layer. It will not have a form library
|
||||
or anything else in that direction. Flask itself just bridges to Werkzeug
|
||||
to implement a proper WSGI application and to Jinja2 to handle templating.
|
||||
It also binds to a few common standard library packages such as logging.
|
||||
Everything else is up for extensions.
|
||||
|
||||
Why is this the case? Because people have different preferences and
|
||||
requirements and Flask could not meet those if it would force any of this
|
||||
into the core. The majority of web applications will need a template
|
||||
engine in some sort. However not every application needs a SQL database.
|
||||
|
||||
As your codebase grows, you are free to make the design decisions appropriate
|
||||
for your project. Flask will continue to provide a very simple glue layer to
|
||||
the best that Python has to offer. You can implement advanced patterns in
|
||||
SQLAlchemy or another database tool, introduce non-relational data persistence
|
||||
as appropriate, and take advantage of framework-agnostic tools built for WSGI,
|
||||
the Python web interface.
|
||||
|
||||
The idea of Flask is to build a good foundation for all applications.
|
||||
Everything else is up to you or extensions.
|
||||
|
|
@ -1,523 +0,0 @@
|
|||
Handling Application Errors
|
||||
===========================
|
||||
|
||||
Applications fail, servers fail. Sooner or later you will see an exception
|
||||
in production. Even if your code is 100% correct, you will still see
|
||||
exceptions from time to time. Why? Because everything else involved will
|
||||
fail. Here are some situations where perfectly fine code can lead to server
|
||||
errors:
|
||||
|
||||
- the client terminated the request early and the application was still
|
||||
reading from the incoming data
|
||||
- the database server was overloaded and could not handle the query
|
||||
- a filesystem is full
|
||||
- a harddrive crashed
|
||||
- a backend server overloaded
|
||||
- a programming error in a library you are using
|
||||
- network connection of the server to another system failed
|
||||
|
||||
And that's just a small sample of issues you could be facing. So how do we
|
||||
deal with that sort of problem? By default if your application runs in
|
||||
production mode, and an exception is raised Flask will display a very simple
|
||||
page for you and log the exception to the :attr:`~flask.Flask.logger`.
|
||||
|
||||
But there is more you can do, and we will cover some better setups to deal
|
||||
with errors including custom exceptions and 3rd party tools.
|
||||
|
||||
|
||||
.. _error-logging-tools:
|
||||
|
||||
Error Logging Tools
|
||||
-------------------
|
||||
|
||||
Sending error mails, even if just for critical ones, can become
|
||||
overwhelming if enough users are hitting the error and log files are
|
||||
typically never looked at. This is why we recommend using `Sentry
|
||||
<https://sentry.io/>`_ for dealing with application errors. It's
|
||||
available as a source-available project `on GitHub
|
||||
<https://github.com/getsentry/sentry>`_ and is also available as a `hosted version
|
||||
<https://sentry.io/signup/>`_ which you can try for free. Sentry
|
||||
aggregates duplicate errors, captures the full stack trace and local
|
||||
variables for debugging, and sends you mails based on new errors or
|
||||
frequency thresholds.
|
||||
|
||||
To use Sentry you need to install the ``sentry-sdk`` client with extra
|
||||
``flask`` dependencies.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ pip install sentry-sdk[flask]
|
||||
|
||||
And then add this to your Flask app:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import sentry_sdk
|
||||
from sentry_sdk.integrations.flask import FlaskIntegration
|
||||
|
||||
sentry_sdk.init('YOUR_DSN_HERE', integrations=[FlaskIntegration()])
|
||||
|
||||
The ``YOUR_DSN_HERE`` value needs to be replaced with the DSN value you
|
||||
get from your Sentry installation.
|
||||
|
||||
After installation, failures leading to an Internal Server Error
|
||||
are automatically reported to Sentry and from there you can
|
||||
receive error notifications.
|
||||
|
||||
See also:
|
||||
|
||||
- Sentry also supports catching errors from a worker queue
|
||||
(RQ, Celery, etc.) in a similar fashion. See the `Python SDK docs
|
||||
<https://docs.sentry.io/platforms/python/>`__ for more information.
|
||||
- `Flask-specific documentation <https://docs.sentry.io/platforms/python/guides/flask/>`__
|
||||
|
||||
|
||||
Error Handlers
|
||||
--------------
|
||||
|
||||
When an error occurs in Flask, an appropriate `HTTP status code
|
||||
<https://developer.mozilla.org/en-US/docs/Web/HTTP/Status>`__ will be
|
||||
returned. 400-499 indicate errors with the client's request data, or
|
||||
about the data requested. 500-599 indicate errors with the server or
|
||||
application itself.
|
||||
|
||||
You might want to show custom error pages to the user when an error occurs.
|
||||
This can be done by registering error handlers.
|
||||
|
||||
An error handler is a function that returns a response when a type of error is
|
||||
raised, similar to how a view is a function that returns a response when a
|
||||
request URL is matched. It is passed the instance of the error being handled,
|
||||
which is most likely a :exc:`~werkzeug.exceptions.HTTPException`.
|
||||
|
||||
The status code of the response will not be set to the handler's code. Make
|
||||
sure to provide the appropriate HTTP status code when returning a response from
|
||||
a handler.
|
||||
|
||||
|
||||
Registering
|
||||
```````````
|
||||
|
||||
Register handlers by decorating a function with
|
||||
:meth:`~flask.Flask.errorhandler`. Or use
|
||||
:meth:`~flask.Flask.register_error_handler` to register the function later.
|
||||
Remember to set the error code when returning the response.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@app.errorhandler(werkzeug.exceptions.BadRequest)
|
||||
def handle_bad_request(e):
|
||||
return 'bad request!', 400
|
||||
|
||||
# or, without the decorator
|
||||
app.register_error_handler(400, handle_bad_request)
|
||||
|
||||
:exc:`werkzeug.exceptions.HTTPException` subclasses like
|
||||
:exc:`~werkzeug.exceptions.BadRequest` and their HTTP codes are interchangeable
|
||||
when registering handlers. (``BadRequest.code == 400``)
|
||||
|
||||
Non-standard HTTP codes cannot be registered by code because they are not known
|
||||
by Werkzeug. Instead, define a subclass of
|
||||
:class:`~werkzeug.exceptions.HTTPException` with the appropriate code and
|
||||
register and raise that exception class.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class InsufficientStorage(werkzeug.exceptions.HTTPException):
|
||||
code = 507
|
||||
description = 'Not enough storage space.'
|
||||
|
||||
app.register_error_handler(InsufficientStorage, handle_507)
|
||||
|
||||
raise InsufficientStorage()
|
||||
|
||||
Handlers can be registered for any exception class, not just
|
||||
:exc:`~werkzeug.exceptions.HTTPException` subclasses or HTTP status
|
||||
codes. Handlers can be registered for a specific class, or for all subclasses
|
||||
of a parent class.
|
||||
|
||||
|
||||
Handling
|
||||
````````
|
||||
|
||||
When building a Flask application you *will* run into exceptions. If some part
|
||||
of your code breaks while handling a request (and you have no error handlers
|
||||
registered), a "500 Internal Server Error"
|
||||
(:exc:`~werkzeug.exceptions.InternalServerError`) will be returned by default.
|
||||
Similarly, "404 Not Found"
|
||||
(:exc:`~werkzeug.exceptions.NotFound`) error will occur if a request is sent to an unregistered route.
|
||||
If a route receives an unallowed request method, a "405 Method Not Allowed"
|
||||
(:exc:`~werkzeug.exceptions.MethodNotAllowed`) will be raised. These are all
|
||||
subclasses of :class:`~werkzeug.exceptions.HTTPException` and are provided by
|
||||
default in Flask.
|
||||
|
||||
Flask gives you the ability to raise any HTTP exception registered by
|
||||
Werkzeug. However, the default HTTP exceptions return simple exception
|
||||
pages. You might want to show custom error pages to the user when an error occurs.
|
||||
This can be done by registering error handlers.
|
||||
|
||||
When Flask catches an exception while handling a request, it is first looked up by code.
|
||||
If no handler is registered for the code, Flask looks up the error by its class hierarchy; the most specific handler is chosen.
|
||||
If no handler is registered, :class:`~werkzeug.exceptions.HTTPException` subclasses show a
|
||||
generic message about their code, while other exceptions are converted to a
|
||||
generic "500 Internal Server Error".
|
||||
|
||||
For example, if an instance of :exc:`ConnectionRefusedError` is raised,
|
||||
and a handler is registered for :exc:`ConnectionError` and
|
||||
:exc:`ConnectionRefusedError`, the more specific :exc:`ConnectionRefusedError`
|
||||
handler is called with the exception instance to generate the response.
|
||||
|
||||
Handlers registered on the blueprint take precedence over those registered
|
||||
globally on the application, assuming a blueprint is handling the request that
|
||||
raises the exception. However, the blueprint cannot handle 404 routing errors
|
||||
because the 404 occurs at the routing level before the blueprint can be
|
||||
determined.
|
||||
|
||||
|
||||
Generic Exception Handlers
|
||||
``````````````````````````
|
||||
|
||||
It is possible to register error handlers for very generic base classes
|
||||
such as ``HTTPException`` or even ``Exception``. However, be aware that
|
||||
these will catch more than you might expect.
|
||||
|
||||
For example, an error handler for ``HTTPException`` might be useful for turning
|
||||
the default HTML errors pages into JSON. However, this
|
||||
handler will trigger for things you don't cause directly, such as 404
|
||||
and 405 errors during routing. Be sure to craft your handler carefully
|
||||
so you don't lose information about the HTTP error.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import json
|
||||
from werkzeug.exceptions import HTTPException
|
||||
|
||||
@app.errorhandler(HTTPException)
|
||||
def handle_exception(e):
|
||||
"""Return JSON instead of HTML for HTTP errors."""
|
||||
# start with the correct headers and status code from the error
|
||||
response = e.get_response()
|
||||
# replace the body with JSON
|
||||
response.data = json.dumps({
|
||||
"code": e.code,
|
||||
"name": e.name,
|
||||
"description": e.description,
|
||||
})
|
||||
response.content_type = "application/json"
|
||||
return response
|
||||
|
||||
An error handler for ``Exception`` might seem useful for changing how
|
||||
all errors, even unhandled ones, are presented to the user. However,
|
||||
this is similar to doing ``except Exception:`` in Python, it will
|
||||
capture *all* otherwise unhandled errors, including all HTTP status
|
||||
codes.
|
||||
|
||||
In most cases it will be safer to register handlers for more
|
||||
specific exceptions. Since ``HTTPException`` instances are valid WSGI
|
||||
responses, you could also pass them through directly.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from werkzeug.exceptions import HTTPException
|
||||
|
||||
@app.errorhandler(Exception)
|
||||
def handle_exception(e):
|
||||
# pass through HTTP errors
|
||||
if isinstance(e, HTTPException):
|
||||
return e
|
||||
|
||||
# now you're handling non-HTTP exceptions only
|
||||
return render_template("500_generic.html", e=e), 500
|
||||
|
||||
Error handlers still respect the exception class hierarchy. If you
|
||||
register handlers for both ``HTTPException`` and ``Exception``, the
|
||||
``Exception`` handler will not handle ``HTTPException`` subclasses
|
||||
because the ``HTTPException`` handler is more specific.
|
||||
|
||||
|
||||
Unhandled Exceptions
|
||||
````````````````````
|
||||
|
||||
When there is no error handler registered for an exception, a 500
|
||||
Internal Server Error will be returned instead. See
|
||||
:meth:`flask.Flask.handle_exception` for information about this
|
||||
behavior.
|
||||
|
||||
If there is an error handler registered for ``InternalServerError``,
|
||||
this will be invoked. As of Flask 1.1.0, this error handler will always
|
||||
be passed an instance of ``InternalServerError``, not the original
|
||||
unhandled error.
|
||||
|
||||
The original error is available as ``e.original_exception``.
|
||||
|
||||
An error handler for "500 Internal Server Error" will be passed uncaught
|
||||
exceptions in addition to explicit 500 errors. In debug mode, a handler
|
||||
for "500 Internal Server Error" will not be used. Instead, the
|
||||
interactive debugger will be shown.
|
||||
|
||||
|
||||
Custom Error Pages
|
||||
------------------
|
||||
|
||||
Sometimes when building a Flask application, you might want to raise a
|
||||
:exc:`~werkzeug.exceptions.HTTPException` to signal to the user that
|
||||
something is wrong with the request. Fortunately, Flask comes with a handy
|
||||
:func:`~flask.abort` function that aborts a request with a HTTP error from
|
||||
werkzeug as desired. It will also provide a plain black and white error page
|
||||
for you with a basic description, but nothing fancy.
|
||||
|
||||
Depending on the error code it is less or more likely for the user to
|
||||
actually see such an error.
|
||||
|
||||
Consider the code below, we might have a user profile route, and if the user
|
||||
fails to pass a username we can raise a "400 Bad Request". If the user passes a
|
||||
username and we can't find it, we raise a "404 Not Found".
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import abort, render_template, request
|
||||
|
||||
# a username needs to be supplied in the query args
|
||||
# a successful request would be like /profile?username=jack
|
||||
@app.route("/profile")
|
||||
def user_profile():
|
||||
username = request.arg.get("username")
|
||||
# if a username isn't supplied in the request, return a 400 bad request
|
||||
if username is None:
|
||||
abort(400)
|
||||
|
||||
user = get_user(username=username)
|
||||
# if a user can't be found by their username, return 404 not found
|
||||
if user is None:
|
||||
abort(404)
|
||||
|
||||
return render_template("profile.html", user=user)
|
||||
|
||||
Here is another example implementation for a "404 Page Not Found" exception:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import render_template
|
||||
|
||||
@app.errorhandler(404)
|
||||
def page_not_found(e):
|
||||
# note that we set the 404 status explicitly
|
||||
return render_template('404.html'), 404
|
||||
|
||||
When using :doc:`/patterns/appfactories`:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import Flask, render_template
|
||||
|
||||
def page_not_found(e):
|
||||
return render_template('404.html'), 404
|
||||
|
||||
def create_app(config_filename):
|
||||
app = Flask(__name__)
|
||||
app.register_error_handler(404, page_not_found)
|
||||
return app
|
||||
|
||||
An example template might be this:
|
||||
|
||||
.. code-block:: html+jinja
|
||||
|
||||
{% extends "layout.html" %}
|
||||
{% block title %}Page Not Found{% endblock %}
|
||||
{% block body %}
|
||||
<h1>Page Not Found</h1>
|
||||
<p>What you were looking for is just not there.
|
||||
<p><a href="{{ url_for('index') }}">go somewhere nice</a>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
Further Examples
|
||||
````````````````
|
||||
|
||||
The above examples wouldn't actually be an improvement on the default
|
||||
exception pages. We can create a custom 500.html template like this:
|
||||
|
||||
.. code-block:: html+jinja
|
||||
|
||||
{% extends "layout.html" %}
|
||||
{% block title %}Internal Server Error{% endblock %}
|
||||
{% block body %}
|
||||
<h1>Internal Server Error</h1>
|
||||
<p>Oops... we seem to have made a mistake, sorry!</p>
|
||||
<p><a href="{{ url_for('index') }}">Go somewhere nice instead</a>
|
||||
{% endblock %}
|
||||
|
||||
It can be implemented by rendering the template on "500 Internal Server Error":
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import render_template
|
||||
|
||||
@app.errorhandler(500)
|
||||
def internal_server_error(e):
|
||||
# note that we set the 500 status explicitly
|
||||
return render_template('500.html'), 500
|
||||
|
||||
When using :doc:`/patterns/appfactories`:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import Flask, render_template
|
||||
|
||||
def internal_server_error(e):
|
||||
return render_template('500.html'), 500
|
||||
|
||||
def create_app():
|
||||
app = Flask(__name__)
|
||||
app.register_error_handler(500, internal_server_error)
|
||||
return app
|
||||
|
||||
When using :doc:`/blueprints`:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import Blueprint
|
||||
|
||||
blog = Blueprint('blog', __name__)
|
||||
|
||||
# as a decorator
|
||||
@blog.errorhandler(500)
|
||||
def internal_server_error(e):
|
||||
return render_template('500.html'), 500
|
||||
|
||||
# or with register_error_handler
|
||||
blog.register_error_handler(500, internal_server_error)
|
||||
|
||||
|
||||
Blueprint Error Handlers
|
||||
------------------------
|
||||
|
||||
In :doc:`/blueprints`, most error handlers will work as expected.
|
||||
However, there is a caveat concerning handlers for 404 and 405
|
||||
exceptions. These error handlers 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.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import jsonify, render_template
|
||||
|
||||
# at the application level
|
||||
# not the blueprint level
|
||||
@app.errorhandler(404)
|
||||
def page_not_found(e):
|
||||
# if a request is in our blog URL space
|
||||
if request.path.startswith('/blog/'):
|
||||
# we return a custom blog 404 page
|
||||
return render_template("blog/404.html"), 404
|
||||
else:
|
||||
# otherwise we return our generic site-wide 404 page
|
||||
return render_template("404.html"), 404
|
||||
|
||||
@app.errorhandler(405)
|
||||
def method_not_allowed(e):
|
||||
# if a request has the wrong method to our API
|
||||
if request.path.startswith('/api/'):
|
||||
# we return a json saying so
|
||||
return jsonify(message="Method Not Allowed"), 405
|
||||
else:
|
||||
# otherwise we return a generic site-wide 405 page
|
||||
return render_template("405.html"), 405
|
||||
|
||||
|
||||
Returning API Errors as JSON
|
||||
----------------------------
|
||||
|
||||
When building APIs in Flask, some developers realise that the built-in
|
||||
exceptions are not expressive enough for APIs and that the content type of
|
||||
:mimetype:`text/html` they are emitting is not very useful for API consumers.
|
||||
|
||||
Using the same techniques as above and :func:`~flask.json.jsonify` we can return JSON
|
||||
responses to API errors. :func:`~flask.abort` is called
|
||||
with a ``description`` parameter. The error handler will
|
||||
use that as the JSON error message, and set the status code to 404.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import abort, jsonify
|
||||
|
||||
@app.errorhandler(404)
|
||||
def resource_not_found(e):
|
||||
return jsonify(error=str(e)), 404
|
||||
|
||||
@app.route("/cheese")
|
||||
def get_one_cheese():
|
||||
resource = get_resource()
|
||||
|
||||
if resource is None:
|
||||
abort(404, description="Resource not found")
|
||||
|
||||
return jsonify(resource)
|
||||
|
||||
We can also create custom exception classes. For instance, we can
|
||||
introduce a new custom exception for an API that can take a proper human readable message,
|
||||
a status code for the error and some optional payload to give more context
|
||||
for the error.
|
||||
|
||||
This is a simple example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import jsonify, request
|
||||
|
||||
class InvalidAPIUsage(Exception):
|
||||
status_code = 400
|
||||
|
||||
def __init__(self, message, status_code=None, payload=None):
|
||||
super().__init__()
|
||||
self.message = message
|
||||
if status_code is not None:
|
||||
self.status_code = status_code
|
||||
self.payload = payload
|
||||
|
||||
def to_dict(self):
|
||||
rv = dict(self.payload or ())
|
||||
rv['message'] = self.message
|
||||
return rv
|
||||
|
||||
@app.errorhandler(InvalidAPIUsage)
|
||||
def invalid_api_usage(e):
|
||||
return jsonify(e.to_dict()), e.status_code
|
||||
|
||||
# an API app route for getting user information
|
||||
# a correct request might be /api/user?user_id=420
|
||||
@app.route("/api/user")
|
||||
def user_api(user_id):
|
||||
user_id = request.arg.get("user_id")
|
||||
if not user_id:
|
||||
raise InvalidAPIUsage("No user id provided!")
|
||||
|
||||
user = get_user(user_id=user_id)
|
||||
if not user:
|
||||
raise InvalidAPIUsage("No such user!", status_code=404)
|
||||
|
||||
return jsonify(user.to_dict())
|
||||
|
||||
A view can now raise that exception with an error message. Additionally
|
||||
some extra payload can be provided as a dictionary through the `payload`
|
||||
parameter.
|
||||
|
||||
|
||||
Logging
|
||||
-------
|
||||
|
||||
See :doc:`/logging` for information about how to log exceptions, such as
|
||||
by emailing them to admins.
|
||||
|
||||
|
||||
Debugging
|
||||
---------
|
||||
|
||||
See :doc:`/debugging` for information about how to debug errors in
|
||||
development and production.
|
||||
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