diff --git a/changelog/6082.bugfix.rst b/changelog/6082.bugfix.rst new file mode 100644 index 000000000..24d389257 --- /dev/null +++ b/changelog/6082.bugfix.rst @@ -0,0 +1 @@ +Fix line detection for doctest samples inside ``property`` docstrings, as a workaround to `bpo-17446 `__. diff --git a/src/_pytest/doctest.py b/src/_pytest/doctest.py index f7d96257e..c81f0bfd4 100644 --- a/src/_pytest/doctest.py +++ b/src/_pytest/doctest.py @@ -435,6 +435,16 @@ class DoctestModule(pytest.Module): https://bugs.python.org/issue25532 """ + def _find_lineno(self, obj, source_lines): + """ + Doctest code does not take into account `@property`, this is a hackish way to fix it. + + https://bugs.python.org/issue17446 + """ + if isinstance(obj, property): + obj = getattr(obj, "fget", obj) + return doctest.DocTestFinder._find_lineno(self, obj, source_lines) + def _find(self, tests, obj, name, module, source_lines, globs, seen): if _is_mocked(obj): return diff --git a/testing/test_doctest.py b/testing/test_doctest.py index 79095e3e7..bd214e3de 100644 --- a/testing/test_doctest.py +++ b/testing/test_doctest.py @@ -287,13 +287,68 @@ class TestDoctests: ) ) result = testdir.runpytest("--doctest-modules") + result.stdout.fnmatch_lines( + ["*hello*", "006*>>> 1/0*", "*UNEXPECTED*ZeroDivision*", "*1 failed*"] + ) + + def test_doctest_linedata_on_property(self, testdir): + testdir.makepyfile( + """ + class Sample(object): + @property + def some_property(self): + ''' + >>> Sample().some_property + 'another thing' + ''' + return 'something' + """ + ) + result = testdir.runpytest("--doctest-modules") result.stdout.fnmatch_lines( [ - "*hello*", - "*EXAMPLE LOCATION UNKNOWN, not showing all tests of that example*", - "*1/0*", - "*UNEXPECTED*ZeroDivision*", - "*1 failed*", + "*= FAILURES =*", + "*_ [[]doctest[]] test_doctest_linedata_on_property.Sample.some_property _*", + "004 ", + "005 >>> Sample().some_property", + "Expected:", + " 'another thing'", + "Got:", + " 'something'", + "", + "*/test_doctest_linedata_on_property.py:5: DocTestFailure", + "*= 1 failed in *", + ] + ) + + def test_doctest_no_linedata_on_overriden_property(self, testdir): + testdir.makepyfile( + """ + class Sample(object): + @property + def some_property(self): + ''' + >>> Sample().some_property + 'another thing' + ''' + return 'something' + some_property = property(some_property.__get__, None, None, some_property.__doc__) + """ + ) + result = testdir.runpytest("--doctest-modules") + result.stdout.fnmatch_lines( + [ + "*= FAILURES =*", + "*_ [[]doctest[]] test_doctest_no_linedata_on_overriden_property.Sample.some_property _*", + "EXAMPLE LOCATION UNKNOWN, not showing all tests of that example", + "[?][?][?] >>> Sample().some_property", + "Expected:", + " 'another thing'", + "Got:", + " 'something'", + "", + "*/test_doctest_no_linedata_on_overriden_property.py:None: DocTestFailure", + "*= 1 failed in *", ] )