Merge branch 'main' into class-route
This commit is contained in:
commit
e5fb65b171
50 changed files with 902 additions and 534 deletions
|
|
@ -7,7 +7,8 @@ Using ``async`` and ``await``
|
|||
|
||||
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
|
||||
``async`` extra (``pip install flask[async]``). It requires Python 3.7+
|
||||
where ``contextvars.ContextVar`` is available. This allows views to be
|
||||
defined with ``async def`` and use ``await``.
|
||||
|
||||
.. code-block:: python
|
||||
|
|
@ -17,6 +18,18 @@ defined with ``async def`` and use ``await``.
|
|||
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`` on Windows on Python 3.8
|
||||
|
||||
Python 3.8 has a bug related to asyncio on Windows. If you encounter
|
||||
something like ``ValueError: set_wakeup_fd only works in main thread``,
|
||||
please upgrade to Python 3.9.
|
||||
|
||||
|
||||
Performance
|
||||
-----------
|
||||
|
|
@ -51,7 +64,7 @@ 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 a ASGI server and utilising the asgiref WsgiToAsgi adapter
|
||||
Flask with an ASGI server and utilising the asgiref WsgiToAsgi adapter
|
||||
as described in :ref:`asgi`. This works as the adapter creates an
|
||||
event loop that runs continually.
|
||||
|
||||
|
|
@ -64,7 +77,7 @@ 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 individual worker processes or threads.
|
||||
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
|
||||
|
|
@ -80,12 +93,27 @@ to understanding the specific needs of your project.
|
|||
Extensions
|
||||
----------
|
||||
|
||||
Existing Flask extensions only expect views to be synchronous. If they
|
||||
provide decorators to add functionality to views, those will probably
|
||||
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.
|
||||
|
||||
|
|
|
|||
|
|
@ -127,8 +127,8 @@ 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 = Blueprint('parent', __name__, url_prefix='/parent')
|
||||
child = Blueprint('child', __name__, url_prefix='/child')
|
||||
parent.register_blueprint(child)
|
||||
app.register_blueprint(parent)
|
||||
|
||||
|
|
|
|||
10
docs/cli.rst
10
docs/cli.rst
|
|
@ -45,13 +45,13 @@ While ``FLASK_APP`` supports a variety of options for specifying your
|
|||
application, most use cases should be simple. Here are the typical values:
|
||||
|
||||
(nothing)
|
||||
The file :file:`wsgi.py` is imported, automatically detecting an app
|
||||
(``app``). This provides an easy way to create an app from a factory with
|
||||
extra arguments.
|
||||
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``).
|
||||
|
||||
``FLASK_APP=hello``
|
||||
The name is imported, automatically detecting an app (``app``) or factory
|
||||
(``create_app``).
|
||||
The given name is imported, automatically detecting an app (``app``
|
||||
or ``application``) or factory (``create_app`` or ``make_app``).
|
||||
|
||||
----
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ extensions = [
|
|||
"sphinx_issues",
|
||||
"sphinx_tabs.tabs",
|
||||
]
|
||||
autodoc_typehints = "description"
|
||||
intersphinx_mapping = {
|
||||
"python": ("https://docs.python.org/3/", None),
|
||||
"werkzeug": ("https://werkzeug.palletsprojects.com/", None),
|
||||
|
|
@ -48,10 +49,10 @@ html_context = {
|
|||
]
|
||||
}
|
||||
html_sidebars = {
|
||||
"index": ["project.html", "localtoc.html", "searchbox.html"],
|
||||
"**": ["localtoc.html", "relations.html", "searchbox.html"],
|
||||
"index": ["project.html", "localtoc.html", "searchbox.html", "ethicalads.html"],
|
||||
"**": ["localtoc.html", "relations.html", "searchbox.html", "ethicalads.html"],
|
||||
}
|
||||
singlehtml_sidebars = {"index": ["project.html", "localtoc.html"]}
|
||||
singlehtml_sidebars = {"index": ["project.html", "localtoc.html", "ethicalads.html"]}
|
||||
html_static_path = ["_static"]
|
||||
html_favicon = "_static/flask-icon.png"
|
||||
html_logo = "_static/flask-icon.png"
|
||||
|
|
@ -77,7 +78,7 @@ def github_link(name, rawtext, text, lineno, inliner, options=None, content=None
|
|||
words = None
|
||||
|
||||
if packaging.version.parse(release).is_devrelease:
|
||||
url = f"{base_url}master/{text}"
|
||||
url = f"{base_url}main/{text}"
|
||||
else:
|
||||
url = f"{base_url}{release}/{text}"
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ Hosted options
|
|||
|
||||
- `Deploying Flask on Heroku <https://devcenter.heroku.com/articles/getting-started-with-python>`_
|
||||
- `Deploying Flask on Google App Engine <https://cloud.google.com/appengine/docs/standard/python3/runtime>`_
|
||||
- `Deploying Flask on Google Cloud Run <https://cloud.google.com/run/docs/quickstarts/build-and-deploy/python>`_
|
||||
- `Deploying Flask on AWS Elastic Beanstalk <https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/create-deploy-python-flask.html>`_
|
||||
- `Deploying on Azure (IIS) <https://docs.microsoft.com/en-us/azure/app-service/containers/how-to-configure-python>`_
|
||||
- `Deploying on PythonAnywhere <https://help.pythonanywhere.com/pages/Flask/>`_
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ Python Version
|
|||
We recommend using the latest version of Python. Flask supports Python
|
||||
3.6 and newer.
|
||||
|
||||
``async`` support in Flask requires Python 3.7+ for ``contextvars.ContextVar``.
|
||||
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ An example task
|
|||
|
||||
Let's write a task that adds two numbers together and returns the result. We
|
||||
configure Celery's broker and backend to use Redis, create a ``celery``
|
||||
application using the factor from above, and then use it to define the task. ::
|
||||
application using the factory from above, and then use it to define the task. ::
|
||||
|
||||
from flask import Flask
|
||||
|
||||
|
|
|
|||
|
|
@ -142,7 +142,7 @@ Here is the code for that decorator::
|
|||
def decorated_function(*args, **kwargs):
|
||||
template_name = template
|
||||
if template_name is None:
|
||||
template_name = f"'{request.endpoint.replace('.', '/')}.html'"
|
||||
template_name = f"{request.endpoint.replace('.', '/')}.html"
|
||||
ctx = f(*args, **kwargs)
|
||||
if ctx is None:
|
||||
ctx = {}
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ to tell your terminal the application to work with by exporting the
|
|||
|
||||
.. code-block:: text
|
||||
|
||||
$ export FLASK_APP=hello.py
|
||||
$ export FLASK_APP=hello
|
||||
$ flask run
|
||||
* Running on http://127.0.0.1:5000/
|
||||
|
||||
|
|
@ -58,7 +58,7 @@ to tell your terminal the application to work with by exporting the
|
|||
|
||||
.. code-block:: text
|
||||
|
||||
> set FLASK_APP=hello.py
|
||||
> set FLASK_APP=hello
|
||||
> flask run
|
||||
* Running on http://127.0.0.1:5000/
|
||||
|
||||
|
|
@ -66,10 +66,16 @@ to tell your terminal the application to work with by exporting the
|
|||
|
||||
.. code-block:: text
|
||||
|
||||
> $env:FLASK_APP = "hello.py"
|
||||
> $env:FLASK_APP = "hello"
|
||||
> flask run
|
||||
* Running on http://127.0.0.1:5000/
|
||||
|
||||
.. admonition:: Application Discovery Behavior
|
||||
|
||||
As a shortcut, if the file is named ``app.py`` or ``wsgi.py``, you
|
||||
don't have to set the ``FLASK_APP`` environment variable. See
|
||||
:doc:`/cli` for more details.
|
||||
|
||||
This launches a very simple builtin server, which is good enough for
|
||||
testing but probably not what you want to use in production. For
|
||||
deployment options see :doc:`deploying/index`.
|
||||
|
|
@ -240,7 +246,7 @@ of the argument like ``<converter:variable_name>``. ::
|
|||
@app.route('/user/<username>')
|
||||
def show_user_profile(username):
|
||||
# show the user profile for that user
|
||||
return f'User {username}'
|
||||
return f'User {escape(username)}'
|
||||
|
||||
@app.route('/post/<int:post_id>')
|
||||
def show_post(post_id):
|
||||
|
|
@ -250,7 +256,7 @@ of the argument like ``<converter:variable_name>``. ::
|
|||
@app.route('/path/<path:subpath>')
|
||||
def show_subpath(subpath):
|
||||
# show the subpath after /path/
|
||||
return f'Subpath {subpath}'
|
||||
return f'Subpath {escape(subpath)}'
|
||||
|
||||
Converter types:
|
||||
|
||||
|
|
@ -438,9 +444,9 @@ Here is an example template:
|
|||
<h1>Hello, World!</h1>
|
||||
{% endif %}
|
||||
|
||||
Inside templates you also have access to the :class:`~flask.request`,
|
||||
:class:`~flask.session` and :class:`~flask.g` [#]_ objects
|
||||
as well as the :func:`~flask.get_flashed_messages` function.
|
||||
Inside templates you also have access to the :data:`~flask.Flask.config`,
|
||||
:class:`~flask.request`, :class:`~flask.session` and :class:`~flask.g` [#]_ objects
|
||||
as well as the :func:`~flask.url_for` and :func:`~flask.get_flashed_messages` functions.
|
||||
|
||||
Templates are especially useful if inheritance is used. If you want to
|
||||
know how that works, see :doc:`patterns/templateinheritance`. Basically
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ by default:
|
|||
.. data:: config
|
||||
:noindex:
|
||||
|
||||
The current configuration object (:data:`flask.config`)
|
||||
The current configuration object (:data:`flask.Flask.config`)
|
||||
|
||||
.. versionadded:: 0.6
|
||||
|
||||
|
|
|
|||
|
|
@ -48,20 +48,21 @@ the application for testing and initializes a new database::
|
|||
import pytest
|
||||
|
||||
from flaskr import create_app
|
||||
from flaskr.db import init_db
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client():
|
||||
db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp()
|
||||
flaskr.app.config['TESTING'] = True
|
||||
db_fd, db_path = tempfile.mkstemp()
|
||||
app = create_app({'TESTING': True, 'DATABASE': db_path})
|
||||
|
||||
with flaskr.app.test_client() as client:
|
||||
with flaskr.app.app_context():
|
||||
flaskr.init_db()
|
||||
with app.test_client() as client:
|
||||
with app.app_context():
|
||||
init_db()
|
||||
yield client
|
||||
|
||||
os.close(db_fd)
|
||||
os.unlink(flaskr.app.config['DATABASE'])
|
||||
os.unlink(db_path)
|
||||
|
||||
This client fixture will be called by each individual test. It gives us a
|
||||
simple interface to the application, where we can trigger test requests to the
|
||||
|
|
@ -224,13 +225,13 @@ temporarily. With this you can access the :class:`~flask.request`,
|
|||
:class:`~flask.g` and :class:`~flask.session` objects like in view
|
||||
functions. Here is a full example that demonstrates this approach::
|
||||
|
||||
import flask
|
||||
from flask import Flask, request
|
||||
|
||||
app = flask.Flask(__name__)
|
||||
app = Flask(__name__)
|
||||
|
||||
with app.test_request_context('/?name=Peter'):
|
||||
assert flask.request.path == '/'
|
||||
assert flask.request.args['name'] == 'Peter'
|
||||
assert request.path == '/'
|
||||
assert request.args['name'] == 'Peter'
|
||||
|
||||
All the other objects that are context bound can be used in the same
|
||||
way.
|
||||
|
|
@ -247,7 +248,7 @@ the test request context leaves the ``with`` block. If you do want the
|
|||
:meth:`~flask.Flask.before_request` functions to be called as well, you
|
||||
need to call :meth:`~flask.Flask.preprocess_request` yourself::
|
||||
|
||||
app = flask.Flask(__name__)
|
||||
app = Flask(__name__)
|
||||
|
||||
with app.test_request_context('/?name=Peter'):
|
||||
app.preprocess_request()
|
||||
|
|
@ -260,7 +261,7 @@ If you want to call the :meth:`~flask.Flask.after_request` functions you
|
|||
need to call into :meth:`~flask.Flask.process_response` which however
|
||||
requires that you pass it a response object::
|
||||
|
||||
app = flask.Flask(__name__)
|
||||
app = Flask(__name__)
|
||||
|
||||
with app.test_request_context('/?name=Peter'):
|
||||
resp = Response('...')
|
||||
|
|
@ -329,7 +330,7 @@ context around for a little longer so that additional introspection can
|
|||
happen. With Flask 0.4 this is possible by using the
|
||||
:meth:`~flask.Flask.test_client` with a ``with`` block::
|
||||
|
||||
app = flask.Flask(__name__)
|
||||
app = Flask(__name__)
|
||||
|
||||
with app.test_client() as c:
|
||||
rv = c.get('/?tequila=42')
|
||||
|
|
@ -353,7 +354,7 @@ keep the context around and access :data:`flask.session`::
|
|||
|
||||
with app.test_client() as c:
|
||||
rv = c.get('/')
|
||||
assert flask.session['foo'] == 42
|
||||
assert session['foo'] == 42
|
||||
|
||||
This however does not make it possible to also modify the session or to
|
||||
access the session before a request was fired. Starting with Flask 0.8 we
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ this structure and take full advantage of Flask's flexibility.
|
|||
.. image:: flaskr_edit.png
|
||||
:align: center
|
||||
:class: screenshot
|
||||
:alt: screenshot of login page
|
||||
:alt: screenshot of edit page
|
||||
|
||||
:gh:`The tutorial project is available as an example in the Flask
|
||||
repository <examples/tutorial>`, if you want to compare your project
|
||||
|
|
|
|||
|
|
@ -113,8 +113,8 @@ Method Based Dispatching
|
|||
|
||||
For RESTful APIs it's especially helpful to execute a different function
|
||||
for each HTTP method. With the :class:`flask.views.MethodView` you can
|
||||
easily do that. Each HTTP method maps to a function with the same name
|
||||
(just in lowercase)::
|
||||
easily do that. Each HTTP method maps to a method of the class with the
|
||||
same name (just in lowercase)::
|
||||
|
||||
from flask.views import MethodView
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue