diff --git a/changelog/14700.bugfix.rst b/changelog/14700.bugfix.rst new file mode 100644 index 00000000000..46847b59486 --- /dev/null +++ b/changelog/14700.bugfix.rst @@ -0,0 +1 @@ +Fixed an internal error on Python 3.12 when a doctest on a fixture-decorated function was skipped. diff --git a/src/_pytest/doctest.py b/src/_pytest/doctest.py index c5b8d09b09d..ea894b3f441 100644 --- a/src/_pytest/doctest.py +++ b/src/_pytest/doctest.py @@ -32,6 +32,7 @@ from _pytest.config import Config from _pytest.config.argparsing import Parser from _pytest.fixtures import fixture +from _pytest.fixtures import FixtureFunctionDefinition from _pytest.fixtures import TopRequest from _pytest.nodes import Collector from _pytest.nodes import Item @@ -529,6 +530,17 @@ def _find_lineno(self, obj, source_lines): obj, source_lines, ) + elif py_ver_info_minor == (3, 12): + + def _find_lineno(self, obj, source_lines): + if isinstance(obj, FixtureFunctionDefinition): + obj = inspect.unwrap(obj) + + # Type ignored because this is a private function. + return super()._find_lineno( # type:ignore[misc] + obj, + source_lines, + ) if sys.version_info < (3, 13): diff --git a/testing/test_doctest.py b/testing/test_doctest.py index 9c788d0fc41..835288a1a30 100644 --- a/testing/test_doctest.py +++ b/testing/test_doctest.py @@ -875,6 +875,29 @@ def foo(x): reportinfo = items[0].reportinfo() assert reportinfo[1] == 1 + @pytest.mark.skipif( + sys.version_info < (3, 12), reason="requires Python 3.12 or later" + ) + def test_fixture_doctest_skip_has_line_number(self, pytester: Pytester): + p = pytester.makepyfile( + test_fixture_doctest_skip=""" + import pytest + + @pytest.fixture + def unavailable(): + ''' + >>> getfixture("unavailable") + ''' + pytest.skip("unavailable") + """ + ) + items, _reprec = pytester.inline_genitems(p, "--doctest-modules") + assert items[0].reportinfo()[1] is not None + + result = pytester.runpytest(p, "--doctest-modules") + assert "INTERNALERROR" not in result.stdout.str() + result.assert_outcomes(skipped=1) + def test_valid_setup_py(self, pytester: Pytester): """ Test to make sure that pytest ignores valid setup.py files when ran