Skip to content

inspect.getdoc() failing to return docstring for cached_property for subclass that overrides code but not docstring #131116

Description

@scotscotmcc

Bug report

Bug description:

When a child/sub-class inherits a method from its parent/base-class, inspect.getdoc(...) returns the parent's method's docstring when the child's method does not have its own docstring. This is true even if the child overwrites the method but doesn't have a docstring as part of that.

However, this is failing when the method has the functools.cached_property decorator on it and the method is overwritten in the child. The below code errors on the assertion.

fromfunctoolsimportcached_propertyimportinspectclassParent:
@cached_propertydeffoo(self):
"this is the docstring for foo"return1classChild(Parent):
@cached_propertydeffoo(self): ...
assertinspect.getdoc(Child.foo) ==inspect.getdoc(Parent.foo) # fails but we want this to be True

I did my best to test this across a variety of similar things and this was the only case where I found the docstring to not be inherited. I tested across methods, properties (with the @property decorator), cached_properties, static_methods, and class_methods; and having the child explicitly overwrite the code (but not the docstring) of the method or not.

The below test code shows passes for all those other scenarios but only fails on test_cached_property_defined_in_parent_and_child.

fromfunctoolsimportcached_propertyimportinspectimportunittestclassTestMatchingDocstrings(unittest.TestCase):
defsetUp(self):
self.parent_class=ParentClassself.child_class=ChildClassdefcompare_docstrings(self, attrname):
parent_attr=getattr(self.parent_class, attrname)
parent_docstring=inspect.getdoc(parent_attr)
child_attr=getattr(self.child_class, attrname)
child_docstring=inspect.getdoc(child_attr)
self.assertEqual(parent_docstring, child_docstring)
deftest_method_defined_in_parent_only(self):
self.compare_docstrings("method_defined_in_parent_only")
deftest_method_defined_in_parent_and_child(self):
self.compare_docstrings("method_defined_in_parent_and_child")
deftest_property_defined_in_parent_only(self):
self.compare_docstrings("property_defined_in_parent_only")
deftest_property_defined_in_parent_and_child(self):
self.compare_docstrings("property_defined_in_parent_and_child")
deftest_cached_property_defined_in_parent_only(self):
self.compare_docstrings("cached_property_defined_in_parent_only")
deftest_cached_property_defined_in_parent_and_child(self):
self.compare_docstrings("cached_property_defined_in_parent_and_child")
deftest_static_method_defined_in_parent_only(self):
self.compare_docstrings("static_method_defined_in_parent_only")
deftest_static_method_defined_in_parent_and_child(self):
self.compare_docstrings("static_method_defined_in_parent_and_child")
deftest_class_method_defined_in_parent_only(self):
self.compare_docstrings("class_method_defined_in_parent_only")
deftest_class_method_defined_in_parent_and_child(self):
self.compare_docstrings("class_method_defined_in_parent_and_child")
classParentClass:
defmethod_defined_in_parent_only(self):
"This is a method in the parent only"defmethod_defined_in_parent_and_child(self):
"This is a method in the parent and child"@propertydefproperty_defined_in_parent_only(self):
"This is a property with the decorator in the parent only"@propertydefproperty_defined_in_parent_and_child(self):
"This is a property with the decorator in the parent and child"@cached_propertydefcached_property_defined_in_parent_only(self):
"This is a cached property with the decorator in the parent only"@cached_propertydefcached_property_defined_in_parent_and_child(self):
"This is a cached property with the decorator in the parent and child"@staticmethoddefstatic_method_defined_in_parent_only():
"This is a static method with the decorator in the parent only"@staticmethoddefstatic_method_defined_in_parent_and_child():
"This is a static method with the decorator in the parent and child"@classmethoddefclass_method_defined_in_parent_only(cls):
"This is a class method with the decorator in the parent only"@classmethoddefclass_method_defined_in_parent_and_child(cls):
"This is a class method with the decorator in the parent and child"classChildClass(ParentClass):
defmethod_defined_in_parent_and_child(self): ...
@propertydefproperty_defined_in_parent_and_child(self): ...
@cached_propertydefcached_property_defined_in_parent_and_child(self): ...
@staticmethoddefstatic_method_defined_in_parent_and_child(): ...
@classmethoddefclass_method_defined_in_parent_and_child(cls): ...
if__name__=="__main__":
unittest.main()

CPython versions tested on:

3.9, 3.10, 3.11, 3.12, 3.13, 3.14

Operating systems tested on:

Windows

Linked PRs

Metadata

Metadata

Assignees

No one assigned

    Labels

    3.15pre-release feature fixes, bugs and security fixesstdlibStandard Library Python modules in the Lib/ directorytype-featureA feature request or enhancement

    Projects

    Status
    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions