warn for async generator functions (#5734)
warn for async generator functions
This commit is contained in:
commit
0ba774a7c3
|
@ -0,0 +1 @@
|
||||||
|
Skip async generator test functions, and update the warning message to refer to ``async def`` functions.
|
|
@ -40,14 +40,16 @@ def is_generator(func):
|
||||||
|
|
||||||
|
|
||||||
def iscoroutinefunction(func):
|
def iscoroutinefunction(func):
|
||||||
"""Return True if func is a decorated coroutine function.
|
|
||||||
|
|
||||||
Note: copied and modified from Python 3.5's builtin couroutines.py to avoid import asyncio directly,
|
|
||||||
which in turns also initializes the "logging" module as side-effect (see issue #8).
|
|
||||||
"""
|
"""
|
||||||
return getattr(func, "_is_coroutine", False) or (
|
Return True if func is a coroutine function (a function defined with async
|
||||||
hasattr(inspect, "iscoroutinefunction") and inspect.iscoroutinefunction(func)
|
def syntax, and doesn't contain yield), or a function decorated with
|
||||||
)
|
@asyncio.coroutine.
|
||||||
|
|
||||||
|
Note: copied and modified from Python 3.5's builtin couroutines.py to avoid
|
||||||
|
importing asyncio directly, which in turns also initializes the "logging"
|
||||||
|
module as a side-effect (see issue #8).
|
||||||
|
"""
|
||||||
|
return inspect.iscoroutinefunction(func) or getattr(func, "_is_coroutine", False)
|
||||||
|
|
||||||
|
|
||||||
def getlocation(function, curdir):
|
def getlocation(function, curdir):
|
||||||
|
|
|
@ -23,6 +23,7 @@ from _pytest.compat import getfslineno
|
||||||
from _pytest.compat import getimfunc
|
from _pytest.compat import getimfunc
|
||||||
from _pytest.compat import getlocation
|
from _pytest.compat import getlocation
|
||||||
from _pytest.compat import is_generator
|
from _pytest.compat import is_generator
|
||||||
|
from _pytest.compat import iscoroutinefunction
|
||||||
from _pytest.compat import NOTSET
|
from _pytest.compat import NOTSET
|
||||||
from _pytest.compat import REGEX_TYPE
|
from _pytest.compat import REGEX_TYPE
|
||||||
from _pytest.compat import safe_getattr
|
from _pytest.compat import safe_getattr
|
||||||
|
@ -151,15 +152,16 @@ def pytest_configure(config):
|
||||||
@hookimpl(trylast=True)
|
@hookimpl(trylast=True)
|
||||||
def pytest_pyfunc_call(pyfuncitem):
|
def pytest_pyfunc_call(pyfuncitem):
|
||||||
testfunction = pyfuncitem.obj
|
testfunction = pyfuncitem.obj
|
||||||
iscoroutinefunction = getattr(inspect, "iscoroutinefunction", None)
|
if iscoroutinefunction(testfunction) or (
|
||||||
if iscoroutinefunction is not None and iscoroutinefunction(testfunction):
|
sys.version_info >= (3, 6) and inspect.isasyncgenfunction(testfunction)
|
||||||
msg = "Coroutine functions are not natively supported and have been skipped.\n"
|
):
|
||||||
|
msg = "async def functions are not natively supported and have been skipped.\n"
|
||||||
msg += "You need to install a suitable plugin for your async framework, for example:\n"
|
msg += "You need to install a suitable plugin for your async framework, for example:\n"
|
||||||
msg += " - pytest-asyncio\n"
|
msg += " - pytest-asyncio\n"
|
||||||
msg += " - pytest-trio\n"
|
msg += " - pytest-trio\n"
|
||||||
msg += " - pytest-tornasync"
|
msg += " - pytest-tornasync"
|
||||||
warnings.warn(PytestUnhandledCoroutineWarning(msg.format(pyfuncitem.nodeid)))
|
warnings.warn(PytestUnhandledCoroutineWarning(msg.format(pyfuncitem.nodeid)))
|
||||||
skip(msg="coroutine function and no async plugin installed (see warnings)")
|
skip(msg="async def function and no async plugin installed (see warnings)")
|
||||||
funcargs = pyfuncitem.funcargs
|
funcargs = pyfuncitem.funcargs
|
||||||
testargs = {arg: funcargs[arg] for arg in pyfuncitem._fixtureinfo.argnames}
|
testargs = {arg: funcargs[arg] for arg in pyfuncitem._fixtureinfo.argnames}
|
||||||
testfunction(**testargs)
|
testfunction(**testargs)
|
||||||
|
|
|
@ -1199,11 +1199,39 @@ def test_warn_on_async_function(testdir):
|
||||||
[
|
[
|
||||||
"test_async.py::test_1",
|
"test_async.py::test_1",
|
||||||
"test_async.py::test_2",
|
"test_async.py::test_2",
|
||||||
"*Coroutine functions are not natively supported*",
|
"*async def functions are not natively supported*",
|
||||||
"*2 skipped, 2 warnings in*",
|
"*2 skipped, 2 warnings in*",
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
# ensure our warning message appears only once
|
# ensure our warning message appears only once
|
||||||
assert (
|
assert (
|
||||||
result.stdout.str().count("Coroutine functions are not natively supported") == 1
|
result.stdout.str().count("async def functions are not natively supported") == 1
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.filterwarnings("default")
|
||||||
|
@pytest.mark.skipif(
|
||||||
|
sys.version_info < (3, 6), reason="async gen syntax available in Python 3.6+"
|
||||||
|
)
|
||||||
|
def test_warn_on_async_gen_function(testdir):
|
||||||
|
testdir.makepyfile(
|
||||||
|
test_async="""
|
||||||
|
async def test_1():
|
||||||
|
yield
|
||||||
|
async def test_2():
|
||||||
|
yield
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
result = testdir.runpytest()
|
||||||
|
result.stdout.fnmatch_lines(
|
||||||
|
[
|
||||||
|
"test_async.py::test_1",
|
||||||
|
"test_async.py::test_2",
|
||||||
|
"*async def functions are not natively supported*",
|
||||||
|
"*2 skipped, 2 warnings in*",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
# ensure our warning message appears only once
|
||||||
|
assert (
|
||||||
|
result.stdout.str().count("async def functions are not natively supported") == 1
|
||||||
)
|
)
|
||||||
|
|
|
@ -91,9 +91,6 @@ def test_is_generator_asyncio(testdir):
|
||||||
result.stdout.fnmatch_lines(["*1 passed*"])
|
result.stdout.fnmatch_lines(["*1 passed*"])
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(
|
|
||||||
sys.version_info < (3, 5), reason="async syntax available in Python 3.5+"
|
|
||||||
)
|
|
||||||
def test_is_generator_async_syntax(testdir):
|
def test_is_generator_async_syntax(testdir):
|
||||||
testdir.makepyfile(
|
testdir.makepyfile(
|
||||||
"""
|
"""
|
||||||
|
@ -113,6 +110,29 @@ def test_is_generator_async_syntax(testdir):
|
||||||
result.stdout.fnmatch_lines(["*1 passed*"])
|
result.stdout.fnmatch_lines(["*1 passed*"])
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(
|
||||||
|
sys.version_info < (3, 6), reason="async gen syntax available in Python 3.6+"
|
||||||
|
)
|
||||||
|
def test_is_generator_async_gen_syntax(testdir):
|
||||||
|
testdir.makepyfile(
|
||||||
|
"""
|
||||||
|
from _pytest.compat import is_generator
|
||||||
|
def test_is_generator_py36():
|
||||||
|
async def foo():
|
||||||
|
yield
|
||||||
|
await foo()
|
||||||
|
|
||||||
|
async def bar():
|
||||||
|
yield
|
||||||
|
|
||||||
|
assert not is_generator(foo)
|
||||||
|
assert not is_generator(bar)
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
result = testdir.runpytest()
|
||||||
|
result.stdout.fnmatch_lines(["*1 passed*"])
|
||||||
|
|
||||||
|
|
||||||
class ErrorsHelper:
|
class ErrorsHelper:
|
||||||
@property
|
@property
|
||||||
def raise_exception(self):
|
def raise_exception(self):
|
||||||
|
|
Loading…
Reference in New Issue