fixes #2208 by introducing a iteration limit

This commit is contained in:
Ronny Pfannschmidt 2017-01-19 11:38:15 +01:00
parent 6c011f43e9
commit 123289a88e
3 changed files with 44 additions and 6 deletions

View File

@ -41,6 +41,10 @@ Changes
* fix `#2013`_: turn RecordedWarning into namedtupe,
to give it a comprehensible repr while preventing unwarranted modification
* fix `#2208`_: ensure a iteration limit for _pytest.compat.get_real_func.
Thanks `@RonnyPfannschmidt`_ for the Report and PR
.. _@davidszotten: https://github.com/davidszotten
.. _@fushi: https://github.com/fushi
.. _@mattduck: https://github.com/mattduck
@ -57,6 +61,7 @@ Changes
.. _#2101: https://github.com/pytest-dev/pytest/pull/2101
.. _#2166: https://github.com/pytest-dev/pytest/pull/2166
.. _#2147: https://github.com/pytest-dev/pytest/issues/2147
.. _#2208: https://github.com/pytest-dev/pytest/issues/2208
3.0.6.dev0 (unreleased)
=======================

View File

@ -180,8 +180,16 @@ def get_real_func(obj):
""" gets the real function object of the (possibly) wrapped object by
functools.wraps or functools.partial.
"""
while hasattr(obj, "__wrapped__"):
obj = obj.__wrapped__
start_obj = obj
for i in range(100):
new_obj = getattr(obj, '__wrapped__', None)
if new_obj is None:
break
obj = new_obj
else:
raise ValueError(
("could not find real function of {start}"
"\nstopped at {current}").format(start=start_obj, current=obj))
if isinstance(obj, functools.partial):
obj = obj.func
return obj

View File

@ -1,7 +1,7 @@
import sys
import pytest
from _pytest.compat import is_generator
from _pytest.compat import is_generator, get_real_func
def test_is_generator():
@ -15,7 +15,30 @@ def test_is_generator():
assert not is_generator(foo)
@pytest.mark.skipif(sys.version_info < (3, 4), reason='asyncio available in Python 3.4+')
def test_real_func_loop_limit():
class Evil(object):
def __init__(self):
self.left = 1000
def __repr__(self):
return "<Evil left={left}>".format(left=self.left)
def __getattr__(self, attr):
if not self.left:
raise RuntimeError('its over')
self.left -= 1
return self
evil = Evil()
with pytest.raises(ValueError):
res = get_real_func(evil)
print(res)
@pytest.mark.skipif(sys.version_info < (3, 4),
reason='asyncio available in Python 3.4+')
def test_is_generator_asyncio(testdir):
testdir.makepyfile("""
from _pytest.compat import is_generator
@ -27,12 +50,14 @@ def test_is_generator_asyncio(testdir):
def test_is_generator_asyncio():
assert not is_generator(baz)
""")
# avoid importing asyncio into pytest's own process, which in turn imports logging (#8)
# avoid importing asyncio into pytest's own process,
# which in turn imports logging (#8)
result = testdir.runpytest_subprocess()
result.stdout.fnmatch_lines(['*1 passed*'])
@pytest.mark.skipif(sys.version_info < (3, 5), reason='async syntax available in Python 3.5+')
@pytest.mark.skipif(sys.version_info < (3, 5),
reason='async syntax available in Python 3.5+')
def test_is_generator_async_syntax(testdir):
testdir.makepyfile("""
from _pytest.compat import is_generator