Emit warning for broken object
This commit is contained in:
parent
4f597f011d
commit
c5a549b5bb
|
@ -0,0 +1,2 @@
|
||||||
|
Emit a warning when attempting to unwrap a broken object raises an exception,
|
||||||
|
for easier debugging (`#5080 <https://github.com/pytest-dev/pytest/issues/5080>`__).
|
|
@ -3,6 +3,7 @@ import inspect
|
||||||
import platform
|
import platform
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
|
import warnings
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -12,6 +13,7 @@ from _pytest._code.code import TerminalRepr
|
||||||
from _pytest.compat import safe_getattr
|
from _pytest.compat import safe_getattr
|
||||||
from _pytest.fixtures import FixtureRequest
|
from _pytest.fixtures import FixtureRequest
|
||||||
from _pytest.outcomes import Skipped
|
from _pytest.outcomes import Skipped
|
||||||
|
from _pytest.warning_types import PytestWarning
|
||||||
|
|
||||||
DOCTEST_REPORT_CHOICE_NONE = "none"
|
DOCTEST_REPORT_CHOICE_NONE = "none"
|
||||||
DOCTEST_REPORT_CHOICE_CDIFF = "cdiff"
|
DOCTEST_REPORT_CHOICE_CDIFF = "cdiff"
|
||||||
|
@ -368,10 +370,18 @@ def _patch_unwrap_mock_aware():
|
||||||
else:
|
else:
|
||||||
|
|
||||||
def _mock_aware_unwrap(obj, stop=None):
|
def _mock_aware_unwrap(obj, stop=None):
|
||||||
if stop is None:
|
try:
|
||||||
return real_unwrap(obj, stop=_is_mocked)
|
if stop is None or stop is _is_mocked:
|
||||||
else:
|
return real_unwrap(obj, stop=_is_mocked)
|
||||||
return real_unwrap(obj, stop=lambda obj: _is_mocked(obj) or stop(obj))
|
return real_unwrap(obj, stop=lambda obj: _is_mocked(obj) or stop(obj))
|
||||||
|
except Exception as e:
|
||||||
|
warnings.warn(
|
||||||
|
"Got %r when unwrapping %r. This is usually caused "
|
||||||
|
"by a violation of Python's object protocol; see e.g. "
|
||||||
|
"https://github.com/pytest-dev/pytest/issues/5080" % (e, obj),
|
||||||
|
PytestWarning,
|
||||||
|
)
|
||||||
|
raise
|
||||||
|
|
||||||
inspect.unwrap = _mock_aware_unwrap
|
inspect.unwrap = _mock_aware_unwrap
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
|
import inspect
|
||||||
import textwrap
|
import textwrap
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from _pytest.compat import MODULE_NOT_FOUND_ERROR
|
from _pytest.compat import MODULE_NOT_FOUND_ERROR
|
||||||
|
from _pytest.doctest import _is_mocked
|
||||||
|
from _pytest.doctest import _patch_unwrap_mock_aware
|
||||||
from _pytest.doctest import DoctestItem
|
from _pytest.doctest import DoctestItem
|
||||||
from _pytest.doctest import DoctestModule
|
from _pytest.doctest import DoctestModule
|
||||||
from _pytest.doctest import DoctestTextfile
|
from _pytest.doctest import DoctestTextfile
|
||||||
|
@ -1224,3 +1227,24 @@ def test_doctest_mock_objects_dont_recurse_missbehaved(mock_module, testdir):
|
||||||
)
|
)
|
||||||
result = testdir.runpytest("--doctest-modules")
|
result = testdir.runpytest("--doctest-modules")
|
||||||
result.stdout.fnmatch_lines(["* 1 passed *"])
|
result.stdout.fnmatch_lines(["* 1 passed *"])
|
||||||
|
|
||||||
|
|
||||||
|
class Broken:
|
||||||
|
def __getattr__(self, _):
|
||||||
|
raise KeyError("This should be an AttributeError")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize( # pragma: no branch (lambdas are not called)
|
||||||
|
"stop", [None, _is_mocked, lambda f: None, lambda f: False, lambda f: True]
|
||||||
|
)
|
||||||
|
def test_warning_on_unwrap_of_broken_object(stop):
|
||||||
|
bad_instance = Broken()
|
||||||
|
assert inspect.unwrap.__module__ == "inspect"
|
||||||
|
with _patch_unwrap_mock_aware():
|
||||||
|
assert inspect.unwrap.__module__ != "inspect"
|
||||||
|
with pytest.warns(
|
||||||
|
pytest.PytestWarning, match="^Got KeyError.* when unwrapping"
|
||||||
|
):
|
||||||
|
with pytest.raises(KeyError):
|
||||||
|
inspect.unwrap(bad_instance, stop=stop)
|
||||||
|
assert inspect.unwrap.__module__ == "inspect"
|
||||||
|
|
Loading…
Reference in New Issue