mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 07:31:38 +00:00 
			
		
		
		
	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:
		
							parent
							
								
									d5e75c0768
								
							
						
					
					
						commit
						fece15d29f
					
				
					 4 changed files with 39 additions and 1 deletions
				
			
		| 
						 | 
					@ -94,6 +94,7 @@ def _test():
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import __future__
 | 
					import __future__
 | 
				
			||||||
import difflib
 | 
					import difflib
 | 
				
			||||||
 | 
					import functools
 | 
				
			||||||
import inspect
 | 
					import inspect
 | 
				
			||||||
import linecache
 | 
					import linecache
 | 
				
			||||||
import os
 | 
					import os
 | 
				
			||||||
| 
						 | 
					@ -1141,7 +1142,9 @@ def _find_lineno(self, obj, source_lines):
 | 
				
			||||||
        if inspect.ismethod(obj): obj = obj.__func__
 | 
					        if inspect.ismethod(obj): obj = obj.__func__
 | 
				
			||||||
        if isinstance(obj, property):
 | 
					        if isinstance(obj, property):
 | 
				
			||||||
            obj = obj.fget
 | 
					            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.
 | 
					            # We don't use `docstring` var here, because `obj` can be changed.
 | 
				
			||||||
            obj = inspect.unwrap(obj)
 | 
					            obj = inspect.unwrap(obj)
 | 
				
			||||||
            try:
 | 
					            try:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -76,3 +76,32 @@ def property_with_doctest(self):
 | 
				
			||||||
@decorator
 | 
					@decorator
 | 
				
			||||||
def func_with_docstring_wrapped():
 | 
					def func_with_docstring_wrapped():
 | 
				
			||||||
    """Some unrelated info."""
 | 
					    """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
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -678,6 +678,8 @@ def basics(): r"""
 | 
				
			||||||
    >>> for t in tests:
 | 
					    >>> for t in tests:
 | 
				
			||||||
    ...     print('%5s  %s' % (t.lineno, t.name))
 | 
					    ...     print('%5s  %s' % (t.lineno, t.name))
 | 
				
			||||||
     None  test.test_doctest.doctest_lineno
 | 
					     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
 | 
					       22  test.test_doctest.doctest_lineno.ClassWithDocstring
 | 
				
			||||||
       30  test.test_doctest.doctest_lineno.ClassWithDoctest
 | 
					       30  test.test_doctest.doctest_lineno.ClassWithDoctest
 | 
				
			||||||
     None  test.test_doctest.doctest_lineno.ClassWithoutDocstring
 | 
					     None  test.test_doctest.doctest_lineno.ClassWithoutDocstring
 | 
				
			||||||
| 
						 | 
					@ -687,6 +689,8 @@ def basics(): r"""
 | 
				
			||||||
       45  test.test_doctest.doctest_lineno.MethodWrapper.method_with_doctest
 | 
					       45  test.test_doctest.doctest_lineno.MethodWrapper.method_with_doctest
 | 
				
			||||||
     None  test.test_doctest.doctest_lineno.MethodWrapper.method_without_docstring
 | 
					     None  test.test_doctest.doctest_lineno.MethodWrapper.method_without_docstring
 | 
				
			||||||
       61  test.test_doctest.doctest_lineno.MethodWrapper.property_with_doctest
 | 
					       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
 | 
					        4  test.test_doctest.doctest_lineno.func_with_docstring
 | 
				
			||||||
       77  test.test_doctest.doctest_lineno.func_with_docstring_wrapped
 | 
					       77  test.test_doctest.doctest_lineno.func_with_docstring_wrapped
 | 
				
			||||||
       12  test.test_doctest.doctest_lineno.func_with_doctest
 | 
					       12  test.test_doctest.doctest_lineno.func_with_doctest
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,2 @@
 | 
				
			||||||
 | 
					Fix retrieval of :attr:`doctest.DocTest.lineno` for objects decorated with
 | 
				
			||||||
 | 
					:func:`functools.cache` or :class:`functools.cached_property`.
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue