diff --git a/AUTHORS b/AUTHORS index 153375dca..fc79b555a 100644 --- a/AUTHORS +++ b/AUTHORS @@ -96,6 +96,7 @@ David Vierra Daw-Ran Liou Debi Mishra Denis Kirisov +Denivy Braiam Rück Dhiren Serai Diego Russo Dmitry Dygalo diff --git a/changelog/8796.bugfix.rst b/changelog/8796.bugfix.rst new file mode 100644 index 000000000..1e83f4e26 --- /dev/null +++ b/changelog/8796.bugfix.rst @@ -0,0 +1 @@ +Fixed internal error when skipping doctests. diff --git a/src/_pytest/doctest.py b/src/_pytest/doctest.py index 0a4104833..baba713e1 100644 --- a/src/_pytest/doctest.py +++ b/src/_pytest/doctest.py @@ -504,12 +504,18 @@ class DoctestModule(pytest.Module): def _find_lineno(self, obj, source_lines): """Doctest code does not take into account `@property`, this - is a hackish way to fix it. + is a hackish way to fix it. https://bugs.python.org/issue17446 - https://bugs.python.org/issue17446 + Wrapped Doctests will need to be unwrapped so the correct + line number is returned. This will be reported upstream. #8796 """ if isinstance(obj, property): obj = getattr(obj, "fget", obj) + + if hasattr(obj, "__wrapped__"): + # Get the main obj in case of it being wrapped + obj = inspect.unwrap(obj) + # Type ignored because this is a private function. return super()._find_lineno( # type:ignore[misc] obj, diff --git a/testing/test_doctest.py b/testing/test_doctest.py index 47b3d6217..ca215e070 100644 --- a/testing/test_doctest.py +++ b/testing/test_doctest.py @@ -1170,6 +1170,41 @@ class TestDoctestSkips: ["*4: UnexpectedException*", "*5: DocTestFailure*", "*8: DocTestFailure*"] ) + def test_skipping_wrapped_test(self, pytester): + """ + Issue 8796: INTERNALERROR raised when skipping a decorated DocTest + through pytest_collection_modifyitems. + """ + pytester.makeconftest( + """ + import pytest + from _pytest.doctest import DoctestItem + + def pytest_collection_modifyitems(config, items): + skip_marker = pytest.mark.skip() + + for item in items: + if isinstance(item, DoctestItem): + item.add_marker(skip_marker) + """ + ) + + pytester.makepyfile( + """ + from contextlib import contextmanager + + @contextmanager + def my_config_context(): + ''' + >>> import os + ''' + """ + ) + + result = pytester.runpytest("--doctest-modules") + assert "INTERNALERROR" not in result.stdout.str() + result.assert_outcomes(skipped=1) + class TestDoctestAutoUseFixtures: