forked from orbit-oss/flask
Support View and MethodView instances with async handlers
This commit is contained in:
parent
491ea32803
commit
270eb2df2a
4 changed files with 34 additions and 3 deletions
|
|
@ -10,6 +10,7 @@ Unreleased
|
||||||
decorators. :issue:`4104`
|
decorators. :issue:`4104`
|
||||||
- Fixed the issue where typing requires template global
|
- Fixed the issue where typing requires template global
|
||||||
decorators to accept functions with no arguments. :issue:`4098`
|
decorators to accept functions with no arguments. :issue:`4098`
|
||||||
|
- Support View and MethodView instances with async handlers. :issue:`4112`
|
||||||
|
|
||||||
|
|
||||||
Version 2.0.1
|
Version 2.0.1
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,12 @@ defined with ``async def`` and use ``await``.
|
||||||
data = await async_db_query(...)
|
data = await async_db_query(...)
|
||||||
return jsonify(data)
|
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
|
.. admonition:: Using ``async`` on Windows on Python 3.8
|
||||||
|
|
||||||
Python 3.8 has a bug related to asyncio on Windows. If you encounter
|
Python 3.8 has a bug related to asyncio on Windows. If you encounter
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import typing as t
|
import typing as t
|
||||||
|
|
||||||
|
from .globals import current_app
|
||||||
from .globals import request
|
from .globals import request
|
||||||
from .typing import ResponseReturnValue
|
from .typing import ResponseReturnValue
|
||||||
|
|
||||||
|
|
@ -80,7 +81,7 @@ class View:
|
||||||
|
|
||||||
def view(*args: t.Any, **kwargs: t.Any) -> ResponseReturnValue:
|
def view(*args: t.Any, **kwargs: t.Any) -> ResponseReturnValue:
|
||||||
self = view.view_class(*class_args, **class_kwargs) # type: ignore
|
self = view.view_class(*class_args, **class_kwargs) # type: ignore
|
||||||
return self.dispatch_request(*args, **kwargs)
|
return current_app.ensure_sync(self.dispatch_request)(*args, **kwargs)
|
||||||
|
|
||||||
if cls.decorators:
|
if cls.decorators:
|
||||||
view.__name__ = name
|
view.__name__ = name
|
||||||
|
|
@ -154,4 +155,4 @@ class MethodView(View, metaclass=MethodViewType):
|
||||||
meth = getattr(self, "get", None)
|
meth = getattr(self, "get", None)
|
||||||
|
|
||||||
assert meth is not None, f"Unimplemented method {request.method!r}"
|
assert meth is not None, f"Unimplemented method {request.method!r}"
|
||||||
return meth(*args, **kwargs)
|
return current_app.ensure_sync(meth)(*args, **kwargs)
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ import pytest
|
||||||
from flask import Blueprint
|
from flask import Blueprint
|
||||||
from flask import Flask
|
from flask import Flask
|
||||||
from flask import request
|
from flask import request
|
||||||
|
from flask.views import MethodView
|
||||||
|
from flask.views import View
|
||||||
|
|
||||||
pytest.importorskip("asgiref")
|
pytest.importorskip("asgiref")
|
||||||
|
|
||||||
|
|
@ -18,6 +20,24 @@ class BlueprintError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class AsyncView(View):
|
||||||
|
methods = ["GET", "POST"]
|
||||||
|
|
||||||
|
async def dispatch_request(self):
|
||||||
|
await asyncio.sleep(0)
|
||||||
|
return request.method
|
||||||
|
|
||||||
|
|
||||||
|
class AsyncMethodView(MethodView):
|
||||||
|
async def get(self):
|
||||||
|
await asyncio.sleep(0)
|
||||||
|
return 'GET'
|
||||||
|
|
||||||
|
async def post(self):
|
||||||
|
await asyncio.sleep(0)
|
||||||
|
return 'POST'
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="async_app")
|
@pytest.fixture(name="async_app")
|
||||||
def _async_app():
|
def _async_app():
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
@ -53,11 +73,14 @@ def _async_app():
|
||||||
|
|
||||||
app.register_blueprint(blueprint, url_prefix="/bp")
|
app.register_blueprint(blueprint, url_prefix="/bp")
|
||||||
|
|
||||||
|
app.add_url_rule('/view', view_func=AsyncView.as_view('view'))
|
||||||
|
app.add_url_rule('/methodview', view_func=AsyncMethodView.as_view('methodview'))
|
||||||
|
|
||||||
return app
|
return app
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(sys.version_info < (3, 7), reason="requires Python >= 3.7")
|
@pytest.mark.skipif(sys.version_info < (3, 7), reason="requires Python >= 3.7")
|
||||||
@pytest.mark.parametrize("path", ["/", "/home", "/bp/"])
|
@pytest.mark.parametrize("path", ["/", "/home", "/bp/", "/view", "/methodview"])
|
||||||
def test_async_route(path, async_app):
|
def test_async_route(path, async_app):
|
||||||
test_client = async_app.test_client()
|
test_client = async_app.test_client()
|
||||||
response = test_client.get(path)
|
response = test_client.get(path)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue