Skip to content

Commit 012551e

Browse files
committed
Emit warning for broken object
1 parent 4f597f0 commit 012551e

File tree

3 files changed

+37
-3
lines changed

3 files changed

+37
-3
lines changed

changelog/5404.bugfix.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Emit a warning when attempting to unwrap a broken object raises an exception,
2+
for easier debugging (`#5080 <https://github.com/pytest-dev/pytest/issues/5080>`__).

src/_pytest/doctest.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import platform
44
import sys
55
import traceback
6+
import warnings
67
from contextlib import contextmanager
78

89
import pytest
@@ -12,6 +13,7 @@
1213
from _pytest.compat import safe_getattr
1314
from _pytest.fixtures import FixtureRequest
1415
from _pytest.outcomes import Skipped
16+
from _pytest.warning_types import PytestWarning
1517

1618
DOCTEST_REPORT_CHOICE_NONE = "none"
1719
DOCTEST_REPORT_CHOICE_CDIFF = "cdiff"
@@ -368,10 +370,18 @@ def _patch_unwrap_mock_aware():
368370
else:
369371

370372
def _mock_aware_unwrap(obj, stop=None):
371-
if stop is None:
372-
return real_unwrap(obj, stop=_is_mocked)
373-
else:
373+
try:
374+
if stop is None or stop is _is_mocked:
375+
return real_unwrap(obj, stop=_is_mocked)
374376
return real_unwrap(obj, stop=lambda obj: _is_mocked(obj) or stop(obj))
377+
except Exception as e:
378+
warnings.warn(
379+
"Got %r when unwrapping %r. This is usually caused "
380+
"by a violation of Python's object protocol; see e.g. "
381+
"https://github.com/pytest-dev/pytest/issues/5080" % (e, obj),
382+
PytestWarning,
383+
)
384+
raise
375385

376386
inspect.unwrap = _mock_aware_unwrap
377387
try:

testing/test_doctest.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1+
import inspect
12
import textwrap
23

34
import pytest
45
from _pytest.compat import MODULE_NOT_FOUND_ERROR
6+
from _pytest.doctest import _is_mocked
7+
from _pytest.doctest import _patch_unwrap_mock_aware
58
from _pytest.doctest import DoctestItem
69
from _pytest.doctest import DoctestModule
710
from _pytest.doctest import DoctestTextfile
@@ -1224,3 +1227,22 @@ class Example(object):
12241227
)
12251228
result = testdir.runpytest("--doctest-modules")
12261229
result.stdout.fnmatch_lines(["* 1 passed *"])
1230+
1231+
1232+
class Broken:
1233+
def __getattr__(self, _):
1234+
raise KeyError("This should be an AttributeError")
1235+
1236+
1237+
@pytest.mark.parametrize(
1238+
"stop", [None, _is_mocked, lambda f: None, lambda f: False, lambda f: True]
1239+
)
1240+
def test_warning_on_unwrap_of_broken_object(stop):
1241+
bad_instance = Broken()
1242+
assert inspect.unwrap.__module__ == "inspect"
1243+
with _patch_unwrap_mock_aware():
1244+
assert inspect.unwrap.__module__ != "inspect"
1245+
with pytest.raises(KeyError):
1246+
with pytest.warns(pytest.PytestWarning):
1247+
inspect.unwrap(bad_instance, stop=stop)
1248+
assert inspect.unwrap.__module__ == "inspect"

0 commit comments

Comments
 (0)