import __future__
import difflib
+import functools
import inspect
import linecache
import os
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:
@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
>>> 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
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
--- /dev/null
+Fix retrieval of :attr:`doctest.DocTest.lineno` for objects decorated with
+:func:`functools.cache` or :class:`functools.cached_property`.