Merge pull request #7149 from nicoddemus/backport-7144
This commit is contained in:
commit
ba2c49e71e
|
@ -70,7 +70,7 @@ jobs:
|
||||||
- name: "windows-py38"
|
- name: "windows-py38"
|
||||||
python: "3.8"
|
python: "3.8"
|
||||||
os: windows-latest
|
os: windows-latest
|
||||||
tox_env: "py38-twisted"
|
tox_env: "py38-unittestextras"
|
||||||
use_coverage: true
|
use_coverage: true
|
||||||
|
|
||||||
- name: "ubuntu-py35"
|
- 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)
|
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:
|
def getlocation(function, curdir=None) -> str:
|
||||||
function = get_real_func(function)
|
function = get_real_func(function)
|
||||||
fn = py.path.local(inspect.getfile(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 get_real_func
|
||||||
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_async_function
|
||||||
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
|
||||||
|
@ -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 = "async def functions are not natively supported and have been skipped.\n"
|
||||||
msg += (
|
msg += (
|
||||||
"You need to install a suitable plugin for your async framework, for example:\n"
|
"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)
|
@hookimpl(trylast=True)
|
||||||
def pytest_pyfunc_call(pyfuncitem: "Function"):
|
def pytest_pyfunc_call(pyfuncitem: "Function"):
|
||||||
testfunction = pyfuncitem.obj
|
testfunction = pyfuncitem.obj
|
||||||
|
if is_async_function(testfunction):
|
||||||
try:
|
async_warn_and_skip(pyfuncitem.nodeid)
|
||||||
# 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)
|
|
||||||
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}
|
||||||
result = testfunction(**testargs)
|
result = testfunction(**testargs)
|
||||||
if hasattr(result, "__await__") or hasattr(result, "__aiter__"):
|
if hasattr(result, "__await__") or hasattr(result, "__aiter__"):
|
||||||
if async_ok_in_stdlib:
|
async_warn_and_skip(pyfuncitem.nodeid)
|
||||||
# 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)
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import traceback
|
||||||
import _pytest._code
|
import _pytest._code
|
||||||
import pytest
|
import pytest
|
||||||
from _pytest.compat import getimfunc
|
from _pytest.compat import getimfunc
|
||||||
|
from _pytest.compat import is_async_function
|
||||||
from _pytest.config import hookimpl
|
from _pytest.config import hookimpl
|
||||||
from _pytest.outcomes import exit
|
from _pytest.outcomes import exit
|
||||||
from _pytest.outcomes import fail
|
from _pytest.outcomes import fail
|
||||||
|
@ -227,13 +228,17 @@ class TestCaseFunction(Function):
|
||||||
self._needs_explicit_tearDown = True
|
self._needs_explicit_tearDown = True
|
||||||
raise _GetOutOf_testPartExecutor(exc)
|
raise _GetOutOf_testPartExecutor(exc)
|
||||||
|
|
||||||
setattr(self._testcase, self._testcase._testMethodName, wrapped_testMethod)
|
# let the unittest framework handle async functions
|
||||||
try:
|
if is_async_function(self.obj):
|
||||||
self._testcase(result=self)
|
self._testcase(self)
|
||||||
except _GetOutOf_testPartExecutor as exc:
|
else:
|
||||||
raise exc.args[0] from exc.args[0]
|
setattr(self._testcase, self._testcase._testMethodName, wrapped_testMethod)
|
||||||
finally:
|
try:
|
||||||
delattr(self._testcase, self._testcase._testMethodName)
|
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):
|
def _prunetraceback(self, excinfo):
|
||||||
Function._prunetraceback(self, excinfo)
|
Function._prunetraceback(self, excinfo)
|
||||||
|
|
|
@ -1,7 +1,13 @@
|
||||||
from unittest import IsolatedAsyncioTestCase # type: ignore
|
from unittest import IsolatedAsyncioTestCase # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
teardowns = []
|
||||||
|
|
||||||
|
|
||||||
class AsyncArguments(IsolatedAsyncioTestCase):
|
class AsyncArguments(IsolatedAsyncioTestCase):
|
||||||
|
async def asyncTearDown(self):
|
||||||
|
teardowns.append(None)
|
||||||
|
|
||||||
async def test_something_async(self):
|
async def test_something_async(self):
|
||||||
async def addition(x, y):
|
async def addition(x, y):
|
||||||
return x + y
|
return x + y
|
||||||
|
@ -13,3 +19,6 @@ class AsyncArguments(IsolatedAsyncioTestCase):
|
||||||
return x + y
|
return x + y
|
||||||
|
|
||||||
self.assertEqual(await addition(2, 2), 3)
|
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")
|
testdir.copy_example("unittest/test_unittest_asyncio.py")
|
||||||
reprec = testdir.inline_run()
|
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
|
@ -11,7 +11,7 @@ envlist =
|
||||||
py38
|
py38
|
||||||
pypy
|
pypy
|
||||||
pypy3
|
pypy3
|
||||||
py37-{pexpect,xdist,twisted,numpy,pluggymaster}
|
py37-{pexpect,xdist,unittestextras,numpy,pluggymaster}
|
||||||
doctesting
|
doctesting
|
||||||
py37-freeze
|
py37-freeze
|
||||||
docs
|
docs
|
||||||
|
@ -50,7 +50,8 @@ deps =
|
||||||
pexpect: pexpect
|
pexpect: pexpect
|
||||||
pluggymaster: git+https://github.com/pytest-dev/pluggy.git@master
|
pluggymaster: git+https://github.com/pytest-dev/pluggy.git@master
|
||||||
pygments
|
pygments
|
||||||
twisted: twisted
|
unittestextras: twisted
|
||||||
|
unittestextras: asynctest
|
||||||
xdist: pytest-xdist>=1.13
|
xdist: pytest-xdist>=1.13
|
||||||
{env:_PYTEST_TOX_EXTRA_DEP:}
|
{env:_PYTEST_TOX_EXTRA_DEP:}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue