Skip to content

Commit 51cef39

Browse files
blueyedZac-HD
authored andcommitted
Merge pull request #5404 from Zac-HD/helpful-mock-unwrapper
Emit warning for broken object
1 parent d2c1a04 commit 51cef39

File tree

3 files changed

+40
-3
lines changed

3 files changed

+40
-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
@@ -8,6 +8,7 @@
88
import platform
99
import sys
1010
import traceback
11+
import warnings
1112
from contextlib import contextmanager
1213

1314
import pytest
@@ -17,6 +18,7 @@
1718
from _pytest.compat import safe_getattr
1819
from _pytest.fixtures import FixtureRequest
1920
from _pytest.outcomes import Skipped
21+
from _pytest.warning_types import PytestWarning
2022

2123
DOCTEST_REPORT_CHOICE_NONE = "none"
2224
DOCTEST_REPORT_CHOICE_CDIFF = "cdiff"
@@ -374,10 +376,18 @@ def _patch_unwrap_mock_aware():
374376
else:
375377

376378
def _mock_aware_unwrap(obj, stop=None):
377-
if stop is None:
378-
return real_unwrap(obj, stop=_is_mocked)
379-
else:
379+
try:
380+
if stop is None or stop is _is_mocked:
381+
return real_unwrap(obj, stop=_is_mocked)
380382
return real_unwrap(obj, stop=lambda obj: _is_mocked(obj) or stop(obj))
383+
except Exception as e:
384+
warnings.warn(
385+
"Got %r when unwrapping %r. This is usually caused "
386+
"by a violation of Python's object protocol; see e.g. "
387+
"https://github.com/pytest-dev/pytest/issues/5080" % (e, obj),
388+
PytestWarning,
389+
)
390+
raise
381391

382392
inspect.unwrap = _mock_aware_unwrap
383393
try:

testing/test_doctest.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@
33
from __future__ import division
44
from __future__ import print_function
55

6+
import inspect
67
import sys
78
import textwrap
89

910
import pytest
1011
from _pytest.compat import MODULE_NOT_FOUND_ERROR
12+
from _pytest.doctest import _is_mocked
13+
from _pytest.doctest import _patch_unwrap_mock_aware
1114
from _pytest.doctest import DoctestItem
1215
from _pytest.doctest import DoctestModule
1316
from _pytest.doctest import DoctestTextfile
@@ -1237,3 +1240,25 @@ class Example(object):
12371240
)
12381241
result = testdir.runpytest("--doctest-modules")
12391242
result.stdout.fnmatch_lines(["* 1 passed *"])
1243+
1244+
1245+
class Broken:
1246+
def __getattr__(self, _):
1247+
raise KeyError("This should be an AttributeError")
1248+
1249+
1250+
@pytest.mark.skipif(not hasattr(inspect, "unwrap"))
1251+
@pytest.mark.parametrize( # pragma: no branch (lambdas are not called)
1252+
"stop", [None, _is_mocked, lambda f: None, lambda f: False, lambda f: True]
1253+
)
1254+
def test_warning_on_unwrap_of_broken_object(stop):
1255+
bad_instance = Broken()
1256+
assert inspect.unwrap.__module__ == "inspect"
1257+
with _patch_unwrap_mock_aware():
1258+
assert inspect.unwrap.__module__ != "inspect"
1259+
with pytest.warns(
1260+
pytest.PytestWarning, match="^Got KeyError.* when unwrapping"
1261+
):
1262+
with pytest.raises(KeyError):
1263+
inspect.unwrap(bad_instance, stop=stop)
1264+
assert inspect.unwrap.__module__ == "inspect"

0 commit comments

Comments
 (0)