Skip to content

Commit fece15d

Browse files
authored
gh-136914: Fix support of cached functions and properties in DocTest's lineno computation (GH-136930)
Previously, DocTest's lineno of functions and methods decorated with functools.cache(), functools.lru_cache() and functools.cached_property() was not properly returned (None was returned) because the computation relied on inspect.isfunction() which does not consider the decorated result as a function. We now use the more generic inspect.isroutine(), as elsewhere in doctest's logic. Also, added a special case for functools.cached_property().
1 parent d5e75c0 commit fece15d

File tree

4 files changed

+39
-1
lines changed

4 files changed

+39
-1
lines changed

Lib/doctest.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ def _test():
9494

9595
import __future__
9696
import difflib
97+
import functools
9798
import inspect
9899
import linecache
99100
import os
@@ -1141,7 +1142,9 @@ def _find_lineno(self, obj, source_lines):
11411142
if inspect.ismethod(obj): obj = obj.__func__
11421143
if isinstance(obj, property):
11431144
obj = obj.fget
1144-
if inspect.isfunction(obj) and getattr(obj, '__doc__', None):
1145+
if isinstance(obj, functools.cached_property):
1146+
obj = obj.func
1147+
if inspect.isroutine(obj) and getattr(obj, '__doc__', None):
11451148
# We don't use `docstring` var here, because `obj` can be changed.
11461149
obj = inspect.unwrap(obj)
11471150
try:

Lib/test/test_doctest/doctest_lineno.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,32 @@ def property_with_doctest(self):
7676
@decorator
7777
def func_with_docstring_wrapped():
7878
"""Some unrelated info."""
79+
80+
81+
# https://github.com/python/cpython/issues/136914
82+
import functools
83+
84+
85+
@functools.cache
86+
def cached_func_with_doctest(value):
87+
"""
88+
>>> cached_func_with_doctest(1)
89+
-1
90+
"""
91+
return -value
92+
93+
94+
@functools.cache
95+
def cached_func_without_docstring(value):
96+
return value + 1
97+
98+
99+
class ClassWithACachedProperty:
100+
101+
@functools.cached_property
102+
def cached(self):
103+
"""
104+
>>> X().cached
105+
-1
106+
"""
107+
return 0

Lib/test/test_doctest/test_doctest.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,8 @@ def basics(): r"""
678678
>>> for t in tests:
679679
... print('%5s %s' % (t.lineno, t.name))
680680
None test.test_doctest.doctest_lineno
681+
None test.test_doctest.doctest_lineno.ClassWithACachedProperty
682+
102 test.test_doctest.doctest_lineno.ClassWithACachedProperty.cached
681683
22 test.test_doctest.doctest_lineno.ClassWithDocstring
682684
30 test.test_doctest.doctest_lineno.ClassWithDoctest
683685
None test.test_doctest.doctest_lineno.ClassWithoutDocstring
@@ -687,6 +689,8 @@ def basics(): r"""
687689
45 test.test_doctest.doctest_lineno.MethodWrapper.method_with_doctest
688690
None test.test_doctest.doctest_lineno.MethodWrapper.method_without_docstring
689691
61 test.test_doctest.doctest_lineno.MethodWrapper.property_with_doctest
692+
86 test.test_doctest.doctest_lineno.cached_func_with_doctest
693+
None test.test_doctest.doctest_lineno.cached_func_without_docstring
690694
4 test.test_doctest.doctest_lineno.func_with_docstring
691695
77 test.test_doctest.doctest_lineno.func_with_docstring_wrapped
692696
12 test.test_doctest.doctest_lineno.func_with_doctest
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix retrieval of :attr:`doctest.DocTest.lineno` for objects decorated with
2+
:func:`functools.cache` or :class:`functools.cached_property`.

0 commit comments

Comments
 (0)