Merge pull request #7144 from nicoddemus/async-testcase-7110
This commit is contained in:
commit
e3643751bc
|
@ -70,7 +70,7 @@ jobs:
|
|||
- name: "windows-py38"
|
||||
python: "3.8"
|
||||
os: windows-latest
|
||||
tox_env: "py38-twisted"
|
||||
tox_env: "py38-unittestextras"
|
||||
use_coverage: true
|
||||
|
||||
- name: "ubuntu-py35"
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Fixed regression: ``asyncbase.TestCase`` tests are executed correctly again.
|
|
@ -93,6 +93,13 @@ def iscoroutinefunction(func: object) -> bool:
|
|||
return inspect.iscoroutinefunction(func) or getattr(func, "_is_coroutine", False)
|
||||
|
||||
|
||||
def is_async_function(func: object) -> bool:
|
||||
"""Return True if the given function seems to be an async function or async generator"""
|
||||
return iscoroutinefunction(func) or (
|
||||
sys.version_info >= (3, 6) and inspect.isasyncgenfunction(func)
|
||||
)
|
||||
|
||||
|
||||
def getlocation(function, curdir=None) -> str:
|
||||
function = get_real_func(function)
|
||||
fn = py.path.local(inspect.getfile(function))
|
||||
|
|
|
@ -34,8 +34,8 @@ from _pytest.compat import get_default_arg_names
|
|||
from _pytest.compat import get_real_func
|
||||
from _pytest.compat import getimfunc
|
||||
from _pytest.compat import getlocation
|
||||
from _pytest.compat import is_async_function
|
||||
from _pytest.compat import is_generator
|
||||
from _pytest.compat import iscoroutinefunction
|
||||
from _pytest.compat import NOTSET
|
||||
from _pytest.compat import REGEX_TYPE
|
||||
from _pytest.compat import safe_getattr
|
||||
|
@ -159,7 +159,7 @@ def pytest_configure(config):
|
|||
)
|
||||
|
||||
|
||||
def async_warn(nodeid: str) -> None:
|
||||
def async_warn_and_skip(nodeid: str) -> None:
|
||||
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"
|
||||
|
@ -175,33 +175,13 @@ def async_warn(nodeid: str) -> None:
|
|||
@hookimpl(trylast=True)
|
||||
def pytest_pyfunc_call(pyfuncitem: "Function"):
|
||||
testfunction = pyfuncitem.obj
|
||||
|
||||
try:
|
||||
# ignoring type as the import is invalid in py37 and mypy thinks its a error
|
||||
from unittest import IsolatedAsyncioTestCase # type: ignore
|
||||
except ImportError:
|
||||
async_ok_in_stdlib = False
|
||||
else:
|
||||
async_ok_in_stdlib = isinstance(
|
||||
getattr(testfunction, "__self__", None), IsolatedAsyncioTestCase
|
||||
)
|
||||
|
||||
if (
|
||||
iscoroutinefunction(testfunction)
|
||||
or (sys.version_info >= (3, 6) and inspect.isasyncgenfunction(testfunction))
|
||||
) and not async_ok_in_stdlib:
|
||||
async_warn(pyfuncitem.nodeid)
|
||||
if is_async_function(testfunction):
|
||||
async_warn_and_skip(pyfuncitem.nodeid)
|
||||
funcargs = pyfuncitem.funcargs
|
||||
testargs = {arg: funcargs[arg] for arg in pyfuncitem._fixtureinfo.argnames}
|
||||
result = testfunction(**testargs)
|
||||
if hasattr(result, "__await__") or hasattr(result, "__aiter__"):
|
||||
if async_ok_in_stdlib:
|
||||
# todo: investigate moving this to the unittest plugin
|
||||
# by a test call result hook
|
||||
testcase = testfunction.__self__
|
||||
testcase._callMaybeAsync(lambda: result)
|
||||
else:
|
||||
async_warn(pyfuncitem.nodeid)
|
||||
async_warn_and_skip(pyfuncitem.nodeid)
|
||||
return True
|
||||
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import traceback
|
|||
import _pytest._code
|
||||
import pytest
|
||||
from _pytest.compat import getimfunc
|
||||
from _pytest.compat import is_async_function
|
||||
from _pytest.config import hookimpl
|
||||
from _pytest.outcomes import exit
|
||||
from _pytest.outcomes import fail
|
||||
|
@ -227,13 +228,17 @@ class TestCaseFunction(Function):
|
|||
self._needs_explicit_tearDown = True
|
||||
raise _GetOutOf_testPartExecutor(exc)
|
||||
|
||||
setattr(self._testcase, self._testcase._testMethodName, wrapped_testMethod)
|
||||
try:
|
||||
self._testcase(result=self)
|
||||
except _GetOutOf_testPartExecutor as exc:
|
||||
raise exc.args[0] from exc.args[0]
|
||||
finally:
|
||||
delattr(self._testcase, self._testcase._testMethodName)
|
||||
# let the unittest framework handle async functions
|
||||
if is_async_function(self.obj):
|
||||
self._testcase(self)
|
||||
else:
|
||||
setattr(self._testcase, self._testcase._testMethodName, wrapped_testMethod)
|
||||
try:
|
||||
self._testcase(result=self)
|
||||
except _GetOutOf_testPartExecutor as exc:
|
||||
raise exc.args[0] from exc.args[0]
|
||||
finally:
|
||||
delattr(self._testcase, self._testcase._testMethodName)
|
||||
|
||||
def _prunetraceback(self, excinfo):
|
||||
Function._prunetraceback(self, excinfo)
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
from unittest import IsolatedAsyncioTestCase # type: ignore
|
||||
|
||||
|
||||
teardowns = []
|
||||
|
||||
|
||||
class AsyncArguments(IsolatedAsyncioTestCase):
|
||||
async def asyncTearDown(self):
|
||||
teardowns.append(None)
|
||||
|
||||
async def test_something_async(self):
|
||||
async def addition(x, y):
|
||||
return x + y
|
||||
|
@ -13,3 +19,6 @@ class AsyncArguments(IsolatedAsyncioTestCase):
|
|||
return x + y
|
||||
|
||||
self.assertEqual(await addition(2, 2), 3)
|
||||
|
||||
def test_teardowns(self):
|
||||
assert len(teardowns) == 2
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
"""Issue #7110"""
|
||||
import asyncio
|
||||
|
||||
import asynctest
|
||||
|
||||
|
||||
teardowns = []
|
||||
|
||||
|
||||
class Test(asynctest.TestCase):
|
||||
async def tearDown(self):
|
||||
teardowns.append(None)
|
||||
|
||||
async def test_error(self):
|
||||
await asyncio.sleep(0)
|
||||
self.fail("failing on purpose")
|
||||
|
||||
async def test_ok(self):
|
||||
await asyncio.sleep(0)
|
||||
|
||||
def test_teardowns(self):
|
||||
assert len(teardowns) == 2
|
|
@ -1136,4 +1136,13 @@ def test_async_support(testdir):
|
|||
|
||||
testdir.copy_example("unittest/test_unittest_asyncio.py")
|
||||
reprec = testdir.inline_run()
|
||||
reprec.assertoutcome(failed=1, passed=1)
|
||||
reprec.assertoutcome(failed=1, passed=2)
|
||||
|
||||
|
||||
def test_asynctest_support(testdir):
|
||||
"""Check asynctest support (#7110)"""
|
||||
pytest.importorskip("asynctest")
|
||||
|
||||
testdir.copy_example("unittest/test_unittest_asynctest.py")
|
||||
reprec = testdir.inline_run()
|
||||
reprec.assertoutcome(failed=1, passed=2)
|
||||
|
|
5
tox.ini
5
tox.ini
|
@ -10,7 +10,7 @@ envlist =
|
|||
py37
|
||||
py38
|
||||
pypy3
|
||||
py37-{pexpect,xdist,twisted,numpy,pluggymaster}
|
||||
py37-{pexpect,xdist,unittestextras,numpy,pluggymaster}
|
||||
doctesting
|
||||
py37-freeze
|
||||
docs
|
||||
|
@ -49,7 +49,8 @@ deps =
|
|||
pexpect: pexpect
|
||||
pluggymaster: git+https://github.com/pytest-dev/pluggy.git@master
|
||||
pygments
|
||||
twisted: twisted
|
||||
unittestextras: twisted
|
||||
unittestextras: asynctest
|
||||
xdist: pytest-xdist>=1.13
|
||||
{env:_PYTEST_TOX_EXTRA_DEP:}
|
||||
|
||||
|
|
Loading…
Reference in New Issue