Skip to content

Commit

Permalink
Merge pull request #5453 from Zac-HD/backport-unwrapper
Browse files Browse the repository at this point in the history
[4.6] Backport unwrapper PR
  • Loading branch information
Zac-HD committed Jun 17, 2019
2 parents d2c1a04 + 443af11 commit c765b83
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 3 deletions.
2 changes: 2 additions & 0 deletions changelog/5404.bugfix.rst
@@ -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>`__).
16 changes: 13 additions & 3 deletions src/_pytest/doctest.py
Expand Up @@ -8,6 +8,7 @@
import platform
import sys
import traceback
import warnings
from contextlib import contextmanager

import pytest
Expand All @@ -17,6 +18,7 @@
from _pytest.compat import safe_getattr
from _pytest.fixtures import FixtureRequest
from _pytest.outcomes import Skipped
from _pytest.warning_types import PytestWarning

DOCTEST_REPORT_CHOICE_NONE = "none"
DOCTEST_REPORT_CHOICE_CDIFF = "cdiff"
Expand Down Expand Up @@ -374,10 +376,18 @@ def _patch_unwrap_mock_aware():
else:

def _mock_aware_unwrap(obj, stop=None):
if stop is None:
return real_unwrap(obj, stop=_is_mocked)
else:
try:
if stop is None or stop is _is_mocked:
return real_unwrap(obj, stop=_is_mocked)
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
try:
Expand Down
25 changes: 25 additions & 0 deletions testing/test_doctest.py
Expand Up @@ -3,11 +3,14 @@
from __future__ import division
from __future__ import print_function

import inspect
import sys
import textwrap

import pytest
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 DoctestModule
from _pytest.doctest import DoctestTextfile
Expand Down Expand Up @@ -1237,3 +1240,25 @@ class Example(object):
)
result = testdir.runpytest("--doctest-modules")
result.stdout.fnmatch_lines(["* 1 passed *"])


class Broken:
def __getattr__(self, _):
raise KeyError("This should be an AttributeError")


@pytest.mark.skipif(not hasattr(inspect, "unwrap"), reason="nothing to patch")
@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"

0 comments on commit c765b83

Please sign in to comment.