From 2889da67cb15ac6d5d882781d54014286d9ae010 Mon Sep 17 00:00:00 2001 From: pgjones Date: Mon, 3 May 2021 10:59:28 +0100 Subject: [PATCH] Remove the async helper method It is better to encourage users to utilise the app ensure_sync method (or the newely added async_to_sync method) so that any extensions that alter these methods take affect throughout the users code. With the helper method users code fix parts of their code to the asgiref async_to_sync ignoring any extension changes. --- src/flask/app.py | 34 ++++++++++++++++++++++++++++++++-- src/flask/helpers.py | 24 ------------------------ tests/test_async.py | 4 ++-- 3 files changed, 34 insertions(+), 28 deletions(-) diff --git a/src/flask/app.py b/src/flask/app.py index 7bc952b1..85306d7c 100644 --- a/src/flask/app.py +++ b/src/flask/app.py @@ -16,6 +16,7 @@ from werkzeug.exceptions import BadRequest from werkzeug.exceptions import BadRequestKeyError from werkzeug.exceptions import HTTPException from werkzeug.exceptions import InternalServerError +from werkzeug.local import ContextVar from werkzeug.routing import BuildError from werkzeug.routing import Map from werkzeug.routing import MapAdapter @@ -35,7 +36,6 @@ from .globals import _request_ctx_stack from .globals import g from .globals import request from .globals import session -from .helpers import async_to_sync from .helpers import get_debug_flag from .helpers import get_env from .helpers import get_flashed_messages @@ -1579,10 +1579,40 @@ class Flask(Scaffold): .. versionadded:: 2.0 """ if iscoroutinefunction(func): - return async_to_sync(func) + return self.async_to_sync(func) return func + def async_to_sync( + self, func: t.Callable[..., t.Coroutine] + ) -> t.Callable[..., t.Any]: + """Return a sync function that will run the coroutine function. + + .. code-block:: python + + result = app.async_to_sync(func)(*args, **kwargs) + + Override this method to change how the app converts async code + to be synchronously callable. + + .. versionadded:: 2.0 + """ + try: + from asgiref.sync import async_to_sync as asgiref_async_to_sync + except ImportError: + raise RuntimeError( + "Install Flask with the 'async' extra in order to use async views." + ) + + # Check that Werkzeug isn't using its fallback ContextVar class. + if ContextVar.__module__ == "werkzeug.local": + raise RuntimeError( + "Async cannot be used with this combination of Python " + "and Greenlet versions." + ) + + return asgiref_async_to_sync(func) + def make_response(self, rv: ResponseReturnValue) -> Response: """Convert the return value from a view function to an instance of :attr:`response_class`. diff --git a/src/flask/helpers.py b/src/flask/helpers.py index 621d51e6..109f544f 100644 --- a/src/flask/helpers.py +++ b/src/flask/helpers.py @@ -10,7 +10,6 @@ from threading import RLock import werkzeug.utils from werkzeug.exceptions import NotFound -from werkzeug.local import ContextVar from werkzeug.routing import BuildError from werkzeug.urls import url_quote @@ -800,26 +799,3 @@ def is_ip(value: str) -> bool: return True return False - - -def async_to_sync(func: t.Callable[..., t.Coroutine]) -> t.Callable[..., t.Any]: - """Return a sync function that will run the coroutine function *func*. - - This can be used as so - - result = async_to_async(func)(*args, **kwargs) - """ - try: - from asgiref.sync import async_to_sync as asgiref_async_to_sync - except ImportError: - raise RuntimeError( - "Install Flask with the 'async' extra in order to use async views." - ) - - # Check that Werkzeug isn't using its fallback ContextVar class. - if ContextVar.__module__ == "werkzeug.local": - raise RuntimeError( - "Async cannot be used with this combination of Python & Greenlet versions." - ) - - return asgiref_async_to_sync(func) diff --git a/tests/test_async.py b/tests/test_async.py index 798eed85..26a91118 100644 --- a/tests/test_async.py +++ b/tests/test_async.py @@ -6,7 +6,6 @@ import pytest from flask import Blueprint from flask import Flask from flask import request -from flask.helpers import async_to_sync pytest.importorskip("asgiref") @@ -136,5 +135,6 @@ def test_async_before_after_request(): @pytest.mark.skipif(sys.version_info >= (3, 7), reason="should only raise Python < 3.7") def test_async_runtime_error(): + app = Flask(__name__) with pytest.raises(RuntimeError): - async_to_sync(None) + app.async_to_sync(None)