Title: `inspect.getdoc()` failing to return docstring for cached_property for subclass that overrides code but not docstring · Issue #131116 · python/cpython · GitHub
Open Graph Title: `inspect.getdoc()` failing to return docstring for cached_property for subclass that overrides code but not docstring · Issue #131116 · python/cpython
X Title: `inspect.getdoc()` failing to return docstring for cached_property for subclass that overrides code but not docstring · Issue #131116 · python/cpython
Description: 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 th...
Open Graph Description: 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 it...
X Description: 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...
Opengraph URL: https://github.com/python/cpython/issues/131116
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"`inspect.getdoc()` failing to return docstring for cached_property for subclass that overrides code but not docstring","articleBody":"# Bug report\n\n### Bug description:\n\nWhen 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.\n\nHowever, 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.\n\n```python\nfrom functools import cached_property\nimport inspect\n\nclass Parent:\n @cached_property\n def foo(self):\n \"this is the docstring for foo\"\n return 1\n\nclass Child(Parent):\n @cached_property\n def foo(self): ...\n\nassert inspect.getdoc(Child.foo) == inspect.getdoc(Parent.foo) # fails but we want this to be True\n```\n\nI 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.\n\nThe below test code shows passes for all those other scenarios but only fails on `test_cached_property_defined_in_parent_and_child`.\n\n```python\nfrom functools import cached_property\nimport inspect\n\nimport unittest\n\n\nclass TestMatchingDocstrings(unittest.TestCase):\n def setUp(self):\n self.parent_class = ParentClass\n self.child_class = ChildClass\n\n def compare_docstrings(self, attrname):\n parent_attr = getattr(self.parent_class, attrname)\n parent_docstring = inspect.getdoc(parent_attr)\n child_attr = getattr(self.child_class, attrname)\n child_docstring = inspect.getdoc(child_attr)\n self.assertEqual(parent_docstring, child_docstring)\n\n def test_method_defined_in_parent_only(self):\n self.compare_docstrings(\"method_defined_in_parent_only\")\n\n def test_method_defined_in_parent_and_child(self):\n self.compare_docstrings(\"method_defined_in_parent_and_child\")\n\n def test_property_defined_in_parent_only(self):\n self.compare_docstrings(\"property_defined_in_parent_only\")\n\n def test_property_defined_in_parent_and_child(self):\n self.compare_docstrings(\"property_defined_in_parent_and_child\")\n\n def test_cached_property_defined_in_parent_only(self):\n self.compare_docstrings(\"cached_property_defined_in_parent_only\")\n\n def test_cached_property_defined_in_parent_and_child(self):\n self.compare_docstrings(\"cached_property_defined_in_parent_and_child\")\n\n def test_static_method_defined_in_parent_only(self):\n self.compare_docstrings(\"static_method_defined_in_parent_only\")\n\n def test_static_method_defined_in_parent_and_child(self):\n self.compare_docstrings(\"static_method_defined_in_parent_and_child\")\n\n def test_class_method_defined_in_parent_only(self):\n self.compare_docstrings(\"class_method_defined_in_parent_only\")\n\n def test_class_method_defined_in_parent_and_child(self):\n self.compare_docstrings(\"class_method_defined_in_parent_and_child\")\n\n\nclass ParentClass:\n def method_defined_in_parent_only(self):\n \"This is a method in the parent only\"\n\n def method_defined_in_parent_and_child(self):\n \"This is a method in the parent and child\"\n\n @property\n def property_defined_in_parent_only(self):\n \"This is a property with the decorator in the parent only\"\n\n @property\n def property_defined_in_parent_and_child(self):\n \"This is a property with the decorator in the parent and child\"\n\n @cached_property\n def cached_property_defined_in_parent_only(self):\n \"This is a cached property with the decorator in the parent only\"\n\n @cached_property\n def cached_property_defined_in_parent_and_child(self):\n \"This is a cached property with the decorator in the parent and child\"\n\n @staticmethod\n def static_method_defined_in_parent_only():\n \"This is a static method with the decorator in the parent only\"\n\n @staticmethod\n def static_method_defined_in_parent_and_child():\n \"This is a static method with the decorator in the parent and child\"\n\n @classmethod\n def class_method_defined_in_parent_only(cls):\n \"This is a class method with the decorator in the parent only\"\n\n @classmethod\n def class_method_defined_in_parent_and_child(cls):\n \"This is a class method with the decorator in the parent and child\"\n\n\nclass ChildClass(ParentClass):\n def method_defined_in_parent_and_child(self): ...\n\n @property\n def property_defined_in_parent_and_child(self): ...\n\n @cached_property\n def cached_property_defined_in_parent_and_child(self): ...\n\n @staticmethod\n def static_method_defined_in_parent_and_child(): ...\n\n @classmethod\n def class_method_defined_in_parent_and_child(cls): ...\n\n\nif __name__ == \"__main__\":\n unittest.main()\n```\n\n\n\n### CPython versions tested on:\n\n3.9, 3.10, 3.11, 3.12, 3.13, 3.14\n\n### Operating systems tested on:\n\nWindows\n\n\u003c!-- gh-linked-prs -->\n### Linked PRs\n* gh-131165\n\u003c!-- /gh-linked-prs -->\n","author":{"url":"https://github.com/scotscotmcc","@type":"Person","name":"scotscotmcc"},"datePublished":"2025-03-11T22:18:46Z","url":"https://github.com/python/cpython/issues/131116"}
| react-profiling | 0 |
| react-import-map | react,react-dom,react-dom/client,react-dom/profiling,react-is,react-reconciler,react/compiler-runtime,react/jsx-dev-runtime,react/jsx-runtime,scheduler@d5750093facf |
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:10cf1b58-8466-5f6b-6b5c-14a7ebf0a047 |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | DBA6:A3F58:391BC2:4C5831:6AECCA57 |
| html-safe-nonce | d7809a7b526a1e7041e821eeaa0d8942360c95eb14e7b6d55ff62d6b6f9c03b4 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJEQkE2OkEzRjU4OjM5MUJDMjo0QzU4MzE6NkFFQ0NBNTciLCJ2aXNpdG9yX2lkIjoiNzU3Mjg4ODY2MzE4MTE2NzEiLCJyZWdpb25fZWRnZSI6ImlhZCIsInJlZ2lvbl9yZW5kZXIiOiJpYWQifQ== |
| visitor-hmac | 555becf76166d9e97b4143876c9ad970983fb7760a9fa6f53f88e2f83ab1093f |
| hovercard-subject-tag | issue:2912005657 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/python/cpython/131116/issue_layout |
| twitter:image | https://opengraph.githubassets.com/b2643a7bb2273937c6da8fa8db87cad509f06661caafd674675ef332c9b0f66a/python/cpython/issues/131116 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/b2643a7bb2273937c6da8fa8db87cad509f06661caafd674675ef332c9b0f66a/python/cpython/issues/131116 |
| og:image:alt | 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 it... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | scotscotmcc |
| hostname | github.com |
| expected-hostname | github.com |
| None | dc0b519c5798dc1150b88dc4c06a8b9634d0069218dc04e469970e76aad74016 |
| turbo-cache-control | no-preview |
| go-import | github.com/python/cpython git https://github.com/python/cpython.git |
| octolytics-dimension-user_id | 1525981 |
| octolytics-dimension-user_login | python |
| octolytics-dimension-repository_id | 81598961 |
| octolytics-dimension-repository_nwo | python/cpython |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 81598961 |
| octolytics-dimension-repository_network_root_nwo | python/cpython |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | db139f0bf57b59bf9a8c4abb0849924d6c6ca559 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
| react-app-name | issues |
Links:
Viewport: width=device-width