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:
 | 
				
			||||||
 | 
					                if stop is None or stop is _is_mocked:
 | 
				
			||||||
                    return real_unwrap(obj, stop=_is_mocked)
 | 
					                    return real_unwrap(obj, stop=_is_mocked)
 | 
				
			||||||
            else:
 | 
					 | 
				
			||||||
                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