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().
This commit is contained in:
Denis Laxalde 2025-07-25 11:46:12 +02:00 committed by GitHub
parent d5e75c0768
commit fece15d29f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 39 additions and 1 deletions

View file

@ -94,6 +94,7 @@ def _test():
import __future__
import difflib
import functools
import inspect
import linecache
import os
@ -1141,7 +1142,9 @@ def _find_lineno(self, obj, source_lines):
if inspect.ismethod(obj): obj = obj.__func__
if isinstance(obj, property):
obj = obj.fget
if inspect.isfunction(obj) and getattr(obj, '__doc__', None):
if isinstance(obj, functools.cached_property):
obj = obj.func
if inspect.isroutine(obj) and getattr(obj, '__doc__', None):
# We don't use `docstring` var here, because `obj` can be changed.
obj = inspect.unwrap(obj)
try:

View file

@ -76,3 +76,32 @@ def property_with_doctest(self):
@decorator
def func_with_docstring_wrapped():
"""Some unrelated info."""
# https://github.com/python/cpython/issues/136914
import functools
@functools.cache
def cached_func_with_doctest(value):
"""
>>> cached_func_with_doctest(1)
-1
"""
return -value
@functools.cache
def cached_func_without_docstring(value):
return value + 1
class ClassWithACachedProperty:
@functools.cached_property
def cached(self):
"""
>>> X().cached
-1
"""
return 0

View file

@ -678,6 +678,8 @@ def basics(): r"""
>>> for t in tests:
... print('%5s %s' % (t.lineno, t.name))
None test.test_doctest.doctest_lineno
None test.test_doctest.doctest_lineno.ClassWithACachedProperty
102 test.test_doctest.doctest_lineno.ClassWithACachedProperty.cached
22 test.test_doctest.doctest_lineno.ClassWithDocstring
30 test.test_doctest.doctest_lineno.ClassWithDoctest
None test.test_doctest.doctest_lineno.ClassWithoutDocstring
@ -687,6 +689,8 @@ def basics(): r"""
45 test.test_doctest.doctest_lineno.MethodWrapper.method_with_doctest
None test.test_doctest.doctest_lineno.MethodWrapper.method_without_docstring
61 test.test_doctest.doctest_lineno.MethodWrapper.property_with_doctest
86 test.test_doctest.doctest_lineno.cached_func_with_doctest
None test.test_doctest.doctest_lineno.cached_func_without_docstring
4 test.test_doctest.doctest_lineno.func_with_docstring
77 test.test_doctest.doctest_lineno.func_with_docstring_wrapped
12 test.test_doctest.doctest_lineno.func_with_doctest

View file

@ -0,0 +1,2 @@
Fix retrieval of :attr:`doctest.DocTest.lineno` for objects decorated with
:func:`functools.cache` or :class:`functools.cached_property`.