Use a custom holder class so we can be sure __pytest_wrapper__ was set by us
This commit is contained in:
parent
ef8ec01e39
commit
67106f056b
|
@ -228,6 +228,18 @@ else:
|
||||||
return val.encode("unicode-escape")
|
return val.encode("unicode-escape")
|
||||||
|
|
||||||
|
|
||||||
|
class _PytestWrapper(object):
|
||||||
|
"""Dummy wrapper around a function object for internal use only.
|
||||||
|
|
||||||
|
Used to correctly unwrap the underlying function object
|
||||||
|
when we are creating fixtures, because we wrap the function object ourselves with a decorator
|
||||||
|
to issue warnings when the fixture function is called directly.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, obj):
|
||||||
|
self.obj = obj
|
||||||
|
|
||||||
|
|
||||||
def get_real_func(obj):
|
def get_real_func(obj):
|
||||||
""" gets the real function object of the (possibly) wrapped object by
|
""" gets the real function object of the (possibly) wrapped object by
|
||||||
functools.wraps or functools.partial.
|
functools.wraps or functools.partial.
|
||||||
|
@ -238,8 +250,8 @@ def get_real_func(obj):
|
||||||
# to trigger a warning if it gets called directly instead of by pytest: we don't
|
# to trigger a warning if it gets called directly instead of by pytest: we don't
|
||||||
# want to unwrap further than this otherwise we lose useful wrappings like @mock.patch (#3774)
|
# want to unwrap further than this otherwise we lose useful wrappings like @mock.patch (#3774)
|
||||||
new_obj = getattr(obj, "__pytest_wrapped__", None)
|
new_obj = getattr(obj, "__pytest_wrapped__", None)
|
||||||
if new_obj is not None:
|
if isinstance(new_obj, _PytestWrapper):
|
||||||
obj = new_obj
|
obj = new_obj.obj
|
||||||
break
|
break
|
||||||
new_obj = getattr(obj, "__wrapped__", None)
|
new_obj = getattr(obj, "__wrapped__", None)
|
||||||
if new_obj is None:
|
if new_obj is None:
|
||||||
|
|
|
@ -31,6 +31,7 @@ from _pytest.compat import (
|
||||||
safe_getattr,
|
safe_getattr,
|
||||||
FuncargnamesCompatAttr,
|
FuncargnamesCompatAttr,
|
||||||
get_real_method,
|
get_real_method,
|
||||||
|
_PytestWrapper,
|
||||||
)
|
)
|
||||||
from _pytest.deprecated import FIXTURE_FUNCTION_CALL, RemovedInPytest4Warning
|
from _pytest.deprecated import FIXTURE_FUNCTION_CALL, RemovedInPytest4Warning
|
||||||
from _pytest.outcomes import fail, TEST_OUTCOME
|
from _pytest.outcomes import fail, TEST_OUTCOME
|
||||||
|
@ -981,7 +982,7 @@ def wrap_function_to_warning_if_called_directly(function, fixture_marker):
|
||||||
|
|
||||||
# keep reference to the original function in our own custom attribute so we don't unwrap
|
# keep reference to the original function in our own custom attribute so we don't unwrap
|
||||||
# further than this point and lose useful wrappings like @mock.patch (#3774)
|
# further than this point and lose useful wrappings like @mock.patch (#3774)
|
||||||
result.__pytest_wrapped__ = function
|
result.__pytest_wrapped__ = _PytestWrapper(function)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ from functools import wraps
|
||||||
import six
|
import six
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from _pytest.compat import is_generator, get_real_func, safe_getattr
|
from _pytest.compat import is_generator, get_real_func, safe_getattr, _PytestWrapper
|
||||||
from _pytest.outcomes import OutcomeException
|
from _pytest.outcomes import OutcomeException
|
||||||
|
|
||||||
|
|
||||||
|
@ -29,8 +29,6 @@ def test_real_func_loop_limit():
|
||||||
return "<Evil left={left}>".format(left=self.left)
|
return "<Evil left={left}>".format(left=self.left)
|
||||||
|
|
||||||
def __getattr__(self, attr):
|
def __getattr__(self, attr):
|
||||||
if attr == "__pytest_wrapped__":
|
|
||||||
raise AttributeError
|
|
||||||
if not self.left:
|
if not self.left:
|
||||||
raise RuntimeError("its over")
|
raise RuntimeError("its over")
|
||||||
self.left -= 1
|
self.left -= 1
|
||||||
|
@ -66,7 +64,7 @@ def test_get_real_func():
|
||||||
|
|
||||||
# special case for __pytest_wrapped__ attribute: used to obtain the function up until the point
|
# special case for __pytest_wrapped__ attribute: used to obtain the function up until the point
|
||||||
# a function was wrapped by pytest itself
|
# a function was wrapped by pytest itself
|
||||||
wrapped_func2.__pytest_wrapped__ = wrapped_func
|
wrapped_func2.__pytest_wrapped__ = _PytestWrapper(wrapped_func)
|
||||||
assert get_real_func(wrapped_func2) is wrapped_func
|
assert get_real_func(wrapped_func2) is wrapped_func
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue