mirror of
https://github.com/python/cpython.git
synced 2025-12-08 06:10:17 +00:00
gh-132686: Add parameters inherit_class_doc and fallback_to_class_doc for inspect.getdoc() (GH-132691)
This commit is contained in:
parent
c744ccb2c9
commit
7906f4d96a
6 changed files with 79 additions and 101 deletions
|
|
@ -619,17 +619,26 @@ attributes (see :ref:`import-mod-attrs` for module attributes):
|
|||
Retrieving source code
|
||||
----------------------
|
||||
|
||||
.. function:: getdoc(object)
|
||||
.. function:: getdoc(object, *, inherit_class_doc=True, fallback_to_class_doc=True)
|
||||
|
||||
Get the documentation string for an object, cleaned up with :func:`cleandoc`.
|
||||
If the documentation string for an object is not provided and the object is
|
||||
a class, a method, a property or a descriptor, retrieve the documentation
|
||||
string from the inheritance hierarchy.
|
||||
If the documentation string for an object is not provided:
|
||||
|
||||
* if the object is a class and *inherit_class_doc* is true (by default),
|
||||
retrieve the documentation string from the inheritance hierarchy;
|
||||
* if the object is a method, a property or a descriptor, retrieve
|
||||
the documentation string from the inheritance hierarchy;
|
||||
* otherwise, if *fallback_to_class_doc* is true (by default), retrieve
|
||||
the documentation string from the class of the object.
|
||||
|
||||
Return ``None`` if the documentation string is invalid or missing.
|
||||
|
||||
.. versionchanged:: 3.5
|
||||
Documentation strings are now inherited if not overridden.
|
||||
|
||||
.. versionchanged:: next
|
||||
Added parameters *inherit_class_doc* and *fallback_to_class_doc*.
|
||||
|
||||
|
||||
.. function:: getcomments(object)
|
||||
|
||||
|
|
|
|||
|
|
@ -429,6 +429,14 @@ http.cookies
|
|||
(Contributed by Nick Burns and Senthil Kumaran in :gh:`92936`.)
|
||||
|
||||
|
||||
inspect
|
||||
-------
|
||||
|
||||
* Add parameters *inherit_class_doc* and *fallback_to_class_doc*
|
||||
for :func:`~inspect.getdoc`.
|
||||
(Contributed by Serhiy Storchaka in :gh:`132686`.)
|
||||
|
||||
|
||||
locale
|
||||
------
|
||||
|
||||
|
|
|
|||
|
|
@ -706,8 +706,8 @@ def _findclass(func):
|
|||
return None
|
||||
return cls
|
||||
|
||||
def _finddoc(obj):
|
||||
if isclass(obj):
|
||||
def _finddoc(obj, *, search_in_class=True):
|
||||
if search_in_class and isclass(obj):
|
||||
for base in obj.__mro__:
|
||||
if base is not object:
|
||||
try:
|
||||
|
|
@ -767,19 +767,37 @@ def _finddoc(obj):
|
|||
return doc
|
||||
return None
|
||||
|
||||
def getdoc(object):
|
||||
def _getowndoc(obj):
|
||||
"""Get the documentation string for an object if it is not
|
||||
inherited from its class."""
|
||||
try:
|
||||
doc = object.__getattribute__(obj, '__doc__')
|
||||
if doc is None:
|
||||
return None
|
||||
if obj is not type:
|
||||
typedoc = type(obj).__doc__
|
||||
if isinstance(typedoc, str) and typedoc == doc:
|
||||
return None
|
||||
return doc
|
||||
except AttributeError:
|
||||
return None
|
||||
|
||||
def getdoc(object, *, fallback_to_class_doc=True, inherit_class_doc=True):
|
||||
"""Get the documentation string for an object.
|
||||
|
||||
All tabs are expanded to spaces. To clean up docstrings that are
|
||||
indented to line up with blocks of code, any whitespace than can be
|
||||
uniformly removed from the second line onwards is removed."""
|
||||
try:
|
||||
doc = object.__doc__
|
||||
except AttributeError:
|
||||
return None
|
||||
if fallback_to_class_doc:
|
||||
try:
|
||||
doc = object.__doc__
|
||||
except AttributeError:
|
||||
return None
|
||||
else:
|
||||
doc = _getowndoc(object)
|
||||
if doc is None:
|
||||
try:
|
||||
doc = _finddoc(object)
|
||||
doc = _finddoc(object, search_in_class=inherit_class_doc)
|
||||
except (AttributeError, TypeError):
|
||||
return None
|
||||
if not isinstance(doc, str):
|
||||
|
|
|
|||
92
Lib/pydoc.py
92
Lib/pydoc.py
|
|
@ -108,96 +108,10 @@ def pathdirs():
|
|||
normdirs.append(normdir)
|
||||
return dirs
|
||||
|
||||
def _findclass(func):
|
||||
cls = sys.modules.get(func.__module__)
|
||||
if cls is None:
|
||||
return None
|
||||
for name in func.__qualname__.split('.')[:-1]:
|
||||
cls = getattr(cls, name)
|
||||
if not inspect.isclass(cls):
|
||||
return None
|
||||
return cls
|
||||
|
||||
def _finddoc(obj):
|
||||
if inspect.ismethod(obj):
|
||||
name = obj.__func__.__name__
|
||||
self = obj.__self__
|
||||
if (inspect.isclass(self) and
|
||||
getattr(getattr(self, name, None), '__func__') is obj.__func__):
|
||||
# classmethod
|
||||
cls = self
|
||||
else:
|
||||
cls = self.__class__
|
||||
elif inspect.isfunction(obj):
|
||||
name = obj.__name__
|
||||
cls = _findclass(obj)
|
||||
if cls is None or getattr(cls, name) is not obj:
|
||||
return None
|
||||
elif inspect.isbuiltin(obj):
|
||||
name = obj.__name__
|
||||
self = obj.__self__
|
||||
if (inspect.isclass(self) and
|
||||
self.__qualname__ + '.' + name == obj.__qualname__):
|
||||
# classmethod
|
||||
cls = self
|
||||
else:
|
||||
cls = self.__class__
|
||||
# Should be tested before isdatadescriptor().
|
||||
elif isinstance(obj, property):
|
||||
name = obj.__name__
|
||||
cls = _findclass(obj.fget)
|
||||
if cls is None or getattr(cls, name) is not obj:
|
||||
return None
|
||||
elif inspect.ismethoddescriptor(obj) or inspect.isdatadescriptor(obj):
|
||||
name = obj.__name__
|
||||
cls = obj.__objclass__
|
||||
if getattr(cls, name) is not obj:
|
||||
return None
|
||||
if inspect.ismemberdescriptor(obj):
|
||||
slots = getattr(cls, '__slots__', None)
|
||||
if isinstance(slots, dict) and name in slots:
|
||||
return slots[name]
|
||||
else:
|
||||
return None
|
||||
for base in cls.__mro__:
|
||||
try:
|
||||
doc = _getowndoc(getattr(base, name))
|
||||
except AttributeError:
|
||||
continue
|
||||
if doc is not None:
|
||||
return doc
|
||||
return None
|
||||
|
||||
def _getowndoc(obj):
|
||||
"""Get the documentation string for an object if it is not
|
||||
inherited from its class."""
|
||||
try:
|
||||
doc = object.__getattribute__(obj, '__doc__')
|
||||
if doc is None:
|
||||
return None
|
||||
if obj is not type:
|
||||
typedoc = type(obj).__doc__
|
||||
if isinstance(typedoc, str) and typedoc == doc:
|
||||
return None
|
||||
return doc
|
||||
except AttributeError:
|
||||
return None
|
||||
|
||||
def _getdoc(object):
|
||||
"""Get the documentation string for an object.
|
||||
|
||||
All tabs are expanded to spaces. To clean up docstrings that are
|
||||
indented to line up with blocks of code, any whitespace than can be
|
||||
uniformly removed from the second line onwards is removed."""
|
||||
doc = _getowndoc(object)
|
||||
if doc is None:
|
||||
try:
|
||||
doc = _finddoc(object)
|
||||
except (AttributeError, TypeError):
|
||||
return None
|
||||
if not isinstance(doc, str):
|
||||
return None
|
||||
return inspect.cleandoc(doc)
|
||||
return inspect.getdoc(object,
|
||||
fallback_to_class_doc=False,
|
||||
inherit_class_doc=False)
|
||||
|
||||
def getdoc(object):
|
||||
"""Get the doc string or comments for an object."""
|
||||
|
|
|
|||
|
|
@ -688,10 +688,37 @@ def test_getdoc_inherited(self):
|
|||
self.assertEqual(inspect.getdoc(mod.FesteringGob.contradiction),
|
||||
'The automatic gainsaying.')
|
||||
|
||||
@unittest.skipIf(sys.flags.optimize >= 2,
|
||||
"Docstrings are omitted with -O2 and above")
|
||||
def test_getdoc_inherited_class_doc(self):
|
||||
class A:
|
||||
"""Common base class"""
|
||||
class B(A):
|
||||
pass
|
||||
|
||||
a = A()
|
||||
self.assertEqual(inspect.getdoc(A), 'Common base class')
|
||||
self.assertEqual(inspect.getdoc(A, inherit_class_doc=False),
|
||||
'Common base class')
|
||||
self.assertEqual(inspect.getdoc(a), 'Common base class')
|
||||
self.assertIsNone(inspect.getdoc(a, fallback_to_class_doc=False))
|
||||
a.__doc__ = 'Instance'
|
||||
self.assertEqual(inspect.getdoc(a, fallback_to_class_doc=False),
|
||||
'Instance')
|
||||
|
||||
b = B()
|
||||
self.assertEqual(inspect.getdoc(B), 'Common base class')
|
||||
self.assertIsNone(inspect.getdoc(B, inherit_class_doc=False))
|
||||
self.assertIsNone(inspect.getdoc(b))
|
||||
self.assertIsNone(inspect.getdoc(b, fallback_to_class_doc=False))
|
||||
b.__doc__ = 'Instance'
|
||||
self.assertEqual(inspect.getdoc(b, fallback_to_class_doc=False), 'Instance')
|
||||
|
||||
@unittest.skipIf(MISSING_C_DOCSTRINGS, "test requires docstrings")
|
||||
def test_finddoc(self):
|
||||
finddoc = inspect._finddoc
|
||||
self.assertEqual(finddoc(int), int.__doc__)
|
||||
self.assertIsNone(finddoc(int, search_in_class=False))
|
||||
self.assertEqual(finddoc(int.to_bytes), int.to_bytes.__doc__)
|
||||
self.assertEqual(finddoc(int().to_bytes), int.to_bytes.__doc__)
|
||||
self.assertEqual(finddoc(int.from_bytes), int.from_bytes.__doc__)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
Add parameters *inherit_class_doc* and *fallback_to_class_doc* for
|
||||
:func:`inspect.getdoc`.
|
||||
Loading…
Add table
Add a link
Reference in a new issue