From 3a59acf69f0121302bcddd34f71fcd74b966ce7c Mon Sep 17 00:00:00 2001 From: Dmitry Malinovsky Date: Sun, 11 Dec 2016 21:59:11 +0600 Subject: [PATCH] Use inspect to properly detect generators. Fixes #2129 --- _pytest/compat.py | 15 ++++++++++----- testing/conftest.py | 7 +++++++ testing/test_compat.py | 12 ++++++++++++ testing/test_compat_3.py | 15 +++++++++++++++ testing/test_compat_35.py | 12 ++++++++++++ 5 files changed, 56 insertions(+), 5 deletions(-) create mode 100644 testing/conftest.py create mode 100644 testing/test_compat.py create mode 100644 testing/test_compat_3.py create mode 100644 testing/test_compat_35.py diff --git a/_pytest/compat.py b/_pytest/compat.py index 51fc3bc5c..7a3d0af81 100644 --- a/_pytest/compat.py +++ b/_pytest/compat.py @@ -19,6 +19,12 @@ except ImportError: # pragma: no cover # Only available in Python 3.4+ or as a backport enum = None +try: + import asyncio +except ImportError: # pragma: no cover + # Only available in Python 3.4+ or as a backport + asyncio = None + _PY3 = sys.version_info > (3, 0) _PY2 = not _PY3 @@ -42,11 +48,10 @@ REGEX_TYPE = type(re.compile('')) def is_generator(func): - try: - return _pytest._code.getrawcode(func).co_flags & 32 # generator function - except AttributeError: # builtin functions have no bytecode - # assume them to not be generators - return False + genfunc = inspect.isgeneratorfunction(func) + if asyncio is not None: + return genfunc and not asyncio.iscoroutinefunction(func) + return genfunc def getlocation(function, curdir): diff --git a/testing/conftest.py b/testing/conftest.py new file mode 100644 index 000000000..76a314837 --- /dev/null +++ b/testing/conftest.py @@ -0,0 +1,7 @@ +import sys + +collect_ignore = [] +if sys.version_info[0] < 3: + collect_ignore.append("test_compat_3.py") +if sys.version_info < (3, 5): + collect_ignore.append("test_compat_35.py") diff --git a/testing/test_compat.py b/testing/test_compat.py new file mode 100644 index 000000000..185fc3bd6 --- /dev/null +++ b/testing/test_compat.py @@ -0,0 +1,12 @@ +from _pytest.compat import is_generator + + +def test_is_generator(): + def zap(): + yield + + def foo(): + pass + + assert is_generator(zap) + assert not is_generator(foo) diff --git a/testing/test_compat_3.py b/testing/test_compat_3.py new file mode 100644 index 000000000..a1ee0e6f1 --- /dev/null +++ b/testing/test_compat_3.py @@ -0,0 +1,15 @@ +import pytest +from _pytest.compat import is_generator +try: + import asyncio +except ImportError: + asyncio = None + + +@pytest.mark.skipif(asyncio is None, reason='asyncio is not installed') +def test_is_generator(): + @asyncio.coroutine + def baz(): + yield from [1,2,3] + + assert not is_generator(baz) diff --git a/testing/test_compat_35.py b/testing/test_compat_35.py new file mode 100644 index 000000000..93eecc920 --- /dev/null +++ b/testing/test_compat_35.py @@ -0,0 +1,12 @@ +from _pytest.compat import is_generator + + +def test_is_generator_py35(): + async def foo(): + await foo() + + async def bar(): + pass + + assert not is_generator(foo) + assert not is_generator(bar)