https://github.com/python/cpython/commit/fece15d29f28e89f1231afa80508c80ed28dc37d
commit: fece15d29f28e89f1231afa80508c80ed28dc37d
branch: main
author: Denis Laxalde <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2025-07-25T12:46:12+03:00
summary:
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().
files:
A Misc/NEWS.d/next/Library/2025-07-21-15-40-00.gh-issue-136914.-GNG-d.rst
M Lib/doctest.py
M Lib/test/test_doctest/doctest_lineno.py
M Lib/test/test_doctest/test_doctest.py
diff --git a/Lib/doctest.py b/Lib/doctest.py
index e77823f64b67e4..92a2ab4f7e66f8 100644
--- a/Lib/doctest.py
+++ b/Lib/doctest.py
@@ -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:
diff --git a/Lib/test/test_doctest/doctest_lineno.py
b/Lib/test/test_doctest/doctest_lineno.py
index 0dbcd9a11eaba2..0bd402e98288d0 100644
--- a/Lib/test/test_doctest/doctest_lineno.py
+++ b/Lib/test/test_doctest/doctest_lineno.py
@@ -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
+
+
[email protected]
+def cached_func_with_doctest(value):
+ """
+ >>> cached_func_with_doctest(1)
+ -1
+ """
+ return -value
+
+
[email protected]
+def cached_func_without_docstring(value):
+ return value + 1
+
+
+class ClassWithACachedProperty:
+
+ @functools.cached_property
+ def cached(self):
+ """
+ >>> X().cached
+ -1
+ """
+ return 0
diff --git a/Lib/test/test_doctest/test_doctest.py
b/Lib/test/test_doctest/test_doctest.py
index 72763d4a0132d0..0fa74407e3c436 100644
--- a/Lib/test/test_doctest/test_doctest.py
+++ b/Lib/test/test_doctest/test_doctest.py
@@ -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
diff --git
a/Misc/NEWS.d/next/Library/2025-07-21-15-40-00.gh-issue-136914.-GNG-d.rst
b/Misc/NEWS.d/next/Library/2025-07-21-15-40-00.gh-issue-136914.-GNG-d.rst
new file mode 100644
index 00000000000000..78ec8025fbc0fd
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-07-21-15-40-00.gh-issue-136914.-GNG-d.rst
@@ -0,0 +1,2 @@
+Fix retrieval of :attr:`doctest.DocTest.lineno` for objects decorated with
+:func:`functools.cache` or :class:`functools.cached_property`.
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]