From c6c6408c3fb96245a2e2afc4b754cdf065fdad47 Mon Sep 17 00:00:00 2001 From: pgjones Date: Wed, 10 Feb 2021 21:14:58 +0000 Subject: [PATCH] Raise a runtime error if run_async is called without real ContextVars Werkzeug offers a ContextVar replacement for Python < 3.7, however it doesn't work across asyncio tasks, hence it makes sense to error out rather than find there are odd bugs. Note the docs build requires the latest (dev) Werkzeug due to this change (to import ContextVar from werkzeug.local). --- src/flask/helpers.py | 6 ++++++ tests/test_async.py | 9 +++++++++ tox.ini | 5 ++++- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/flask/helpers.py b/src/flask/helpers.py index 46244d29..5933f42a 100644 --- a/src/flask/helpers.py +++ b/src/flask/helpers.py @@ -7,6 +7,7 @@ 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 @@ -741,6 +742,11 @@ def run_async(func): "Install Flask with the 'async' extra in order to use async views." ) + if ContextVar.__module__ == "werkzeug.local": + raise RuntimeError( + "async cannot be used with this combination of Python & Greenlet versions" + ) + @wraps(func) def outer(*args, **kwargs): """This function grabs the current context for the inner function. diff --git a/tests/test_async.py b/tests/test_async.py index d47d36ce..12784c34 100644 --- a/tests/test_async.py +++ b/tests/test_async.py @@ -1,10 +1,12 @@ import asyncio +import sys import pytest from flask import abort from flask import Flask from flask import request +from flask.helpers import run_async @pytest.fixture(name="async_app") @@ -23,6 +25,7 @@ def _async_app(): return app +@pytest.mark.skipif(sys.version_info < (3, 7), reason="requires Python >= 3.7") def test_async_request_context(async_app): test_client = async_app.test_client() response = test_client.get("/") @@ -31,3 +34,9 @@ def test_async_request_context(async_app): assert b"POST" in response.get_data() response = test_client.get("/error") assert response.status_code == 412 + + +@pytest.mark.skipif(sys.version_info >= (3, 7), reason="should only raise Python < 3.7") +def test_async_runtime_error(): + with pytest.raises(RuntimeError): + run_async(None) diff --git a/tox.ini b/tox.ini index e0f666d8..cf12c0eb 100644 --- a/tox.ini +++ b/tox.ini @@ -25,5 +25,8 @@ skip_install = true commands = pre-commit run --all-files --show-diff-on-failure [testenv:docs] -deps = -r requirements/docs.txt +deps = + -r requirements/docs.txt + + https://github.com/pallets/werkzeug/archive/master.tar.gz commands = sphinx-build -W -b html -d {envtmpdir}/doctrees docs {envtmpdir}/html