Skip to content

Commit

Permalink
Merge pull request #2209 from RonnyPfannschmidt/bugfix-2208/get_real_…
Browse files Browse the repository at this point in the history
…func_loop_limit

fixes #2208 by introducing a iteration limit
  • Loading branch information
nicoddemus authored Jan 19, 2017
2 parents 6c011f4 + 250597d commit 88f7bef
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 6 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
=======================
Expand Down
14 changes: 12 additions & 2 deletions _pytest/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,18 @@ 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=py.io.saferepr(start_obj),
current=py.io.saferepr(obj)))
if isinstance(obj, functools.partial):
obj = obj.func
return obj
Expand Down
33 changes: 29 additions & 4 deletions testing/test_compat.py
Original file line number Diff line number Diff line change
@@ -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():
Expand All @@ -15,7 +15,30 @@ def foo():
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
Expand All @@ -27,12 +50,14 @@ def baz():
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
Expand Down

0 comments on commit 88f7bef

Please sign in to comment.