bpo-40257: Revert changes to inspect.getdoc() (GH-20073)
This commit is contained in:
parent
98e42d1f88
commit
08b47c367a
|
@ -473,15 +473,12 @@ Retrieving source code
|
|||
|
||||
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 method, a property or a descriptor, retrieve the documentation
|
||||
a class, a method, a property or a descriptor, retrieve the documentation
|
||||
string from the inheritance hierarchy.
|
||||
|
||||
.. versionchanged:: 3.5
|
||||
Documentation strings are now inherited if not overridden.
|
||||
|
||||
.. versionchanged:: 3.9
|
||||
Documentation strings for classes are no longer inherited.
|
||||
|
||||
|
||||
.. function:: getcomments(object)
|
||||
|
||||
|
|
|
@ -846,11 +846,6 @@ Changes in the Python API
|
|||
:class:`ftplib.FTP_TLS` as a keyword-only parameter, and the default encoding
|
||||
is changed from Latin-1 to UTF-8 to follow :rfc:`2640`.
|
||||
|
||||
* :func:`inspect.getdoc` no longer returns docstring inherited from the type
|
||||
of the object or from parent class if it is a class if it is not defined
|
||||
in the object itself.
|
||||
(Contributed by Serhiy Storchaka in :issue:`40257`.)
|
||||
|
||||
* :meth:`asyncio.loop.shutdown_default_executor` has been added to
|
||||
:class:`~asyncio.AbstractEventLoop`, meaning alternative event loops that
|
||||
inherit from it should have this method defined.
|
||||
|
|
|
@ -543,6 +543,17 @@ def _findclass(func):
|
|||
return cls
|
||||
|
||||
def _finddoc(obj):
|
||||
if isclass(obj):
|
||||
for base in obj.__mro__:
|
||||
if base is not object:
|
||||
try:
|
||||
doc = base.__doc__
|
||||
except AttributeError:
|
||||
continue
|
||||
if doc is not None:
|
||||
return doc
|
||||
return None
|
||||
|
||||
if ismethod(obj):
|
||||
name = obj.__func__.__name__
|
||||
self = obj.__self__
|
||||
|
@ -586,35 +597,23 @@ def _finddoc(obj):
|
|||
return None
|
||||
for base in cls.__mro__:
|
||||
try:
|
||||
doc = _getowndoc(getattr(base, name))
|
||||
doc = getattr(base, name).__doc__
|
||||
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)
|
||||
try:
|
||||
doc = object.__doc__
|
||||
except AttributeError:
|
||||
return None
|
||||
if doc is None:
|
||||
try:
|
||||
doc = _finddoc(object)
|
||||
|
|
96
Lib/pydoc.py
96
Lib/pydoc.py
|
@ -90,9 +90,101 @@ 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):
|
||||
func = obj.fget
|
||||
name = func.__name__
|
||||
cls = _findclass(func)
|
||||
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)
|
||||
|
||||
def getdoc(object):
|
||||
"""Get the doc string or comments for an object."""
|
||||
result = inspect.getdoc(object) or inspect.getcomments(object)
|
||||
result = _getdoc(object) or inspect.getcomments(object)
|
||||
return result and re.sub('^ *\n', '', result.rstrip()) or ''
|
||||
|
||||
def splitdoc(doc):
|
||||
|
@ -1669,7 +1761,7 @@ def render_doc(thing, title='Python Library Documentation: %s', forceload=0,
|
|||
inspect.isclass(object) or
|
||||
inspect.isroutine(object) or
|
||||
inspect.isdatadescriptor(object) or
|
||||
inspect.getdoc(object)):
|
||||
_getdoc(object)):
|
||||
# If the passed object is a piece of data or an instance,
|
||||
# document its available methods instead of its value.
|
||||
if hasattr(object, '__origin__'):
|
||||
|
|
|
@ -439,7 +439,8 @@ class TestRetrievingSourceCode(GetSourceBase):
|
|||
@unittest.skipIf(sys.flags.optimize >= 2,
|
||||
"Docstrings are omitted with -O2 and above")
|
||||
def test_getdoc_inherited(self):
|
||||
self.assertIsNone(inspect.getdoc(mod.FesteringGob))
|
||||
self.assertEqual(inspect.getdoc(mod.FesteringGob),
|
||||
'A longer,\n\nindented\n\ndocstring.')
|
||||
self.assertEqual(inspect.getdoc(mod.FesteringGob.abuse),
|
||||
'Another\n\ndocstring\n\ncontaining\n\ntabs')
|
||||
self.assertEqual(inspect.getdoc(mod.FesteringGob().abuse),
|
||||
|
@ -447,20 +448,10 @@ class TestRetrievingSourceCode(GetSourceBase):
|
|||
self.assertEqual(inspect.getdoc(mod.FesteringGob.contradiction),
|
||||
'The automatic gainsaying.')
|
||||
|
||||
@unittest.skipIf(MISSING_C_DOCSTRINGS, "test requires docstrings")
|
||||
def test_getowndoc(self):
|
||||
getowndoc = inspect._getowndoc
|
||||
self.assertEqual(getowndoc(type), type.__doc__)
|
||||
self.assertEqual(getowndoc(int), int.__doc__)
|
||||
self.assertEqual(getowndoc(int.to_bytes), int.to_bytes.__doc__)
|
||||
self.assertEqual(getowndoc(int().to_bytes), int.to_bytes.__doc__)
|
||||
self.assertEqual(getowndoc(int.from_bytes), int.from_bytes.__doc__)
|
||||
self.assertEqual(getowndoc(int.real), int.real.__doc__)
|
||||
|
||||
@unittest.skipIf(MISSING_C_DOCSTRINGS, "test requires docstrings")
|
||||
def test_finddoc(self):
|
||||
finddoc = inspect._finddoc
|
||||
self.assertIsNone(finddoc(int))
|
||||
self.assertEqual(finddoc(int), int.__doc__)
|
||||
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 @@
|
|||
Revert changes to :func:`inspect.getdoc`.
|
Loading…
Reference in New Issue