bpo-40257: Output object's own docstring in pydoc (GH-19479)
This commit is contained in:
parent
ba1bcffe5c
commit
fbf2786c4c
|
@ -473,12 +473,15 @@ 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 class, a method, a property or a descriptor, retrieve the documentation
|
||||
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)
|
||||
|
||||
|
|
|
@ -346,6 +346,13 @@ pprint
|
|||
:mod:`pprint` can now pretty-print :class:`types.SimpleNamespace`.
|
||||
(Contributed by Carl Bordum Hansen in :issue:`37376`.)
|
||||
|
||||
pydoc
|
||||
-----
|
||||
|
||||
The documentation string is now shown not only for class, function,
|
||||
method etc, but for any object that has its own ``__doc__`` attribute.
|
||||
(Contributed by Serhiy Storchaka in :issue:`40257`.)
|
||||
|
||||
signal
|
||||
------
|
||||
|
||||
|
@ -798,6 +805,12 @@ 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`.)
|
||||
|
||||
|
||||
CPython bytecode changes
|
||||
------------------------
|
||||
|
||||
|
|
|
@ -542,17 +542,6 @@ 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__
|
||||
|
@ -596,23 +585,35 @@ def _finddoc(obj):
|
|||
return None
|
||||
for base in cls.__mro__:
|
||||
try:
|
||||
doc = getattr(base, name).__doc__
|
||||
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."""
|
||||
try:
|
||||
doc = object.__doc__
|
||||
except AttributeError:
|
||||
return None
|
||||
doc = _getowndoc(object)
|
||||
if doc is None:
|
||||
try:
|
||||
doc = _finddoc(object)
|
||||
|
|
19
Lib/pydoc.py
19
Lib/pydoc.py
|
@ -825,11 +825,8 @@ class HTMLDoc(Doc):
|
|||
push(msg)
|
||||
for name, kind, homecls, value in ok:
|
||||
base = self.docother(getattr(object, name), name, mod)
|
||||
if callable(value) or inspect.isdatadescriptor(value):
|
||||
doc = getattr(value, "__doc__", None)
|
||||
else:
|
||||
doc = None
|
||||
if doc is None:
|
||||
doc = getdoc(value)
|
||||
if not doc:
|
||||
push('<dl><dt>%s</dl>\n' % base)
|
||||
else:
|
||||
doc = self.markup(getdoc(value), self.preformat,
|
||||
|
@ -1309,10 +1306,7 @@ location listed above.
|
|||
hr.maybe()
|
||||
push(msg)
|
||||
for name, kind, homecls, value in ok:
|
||||
if callable(value) or inspect.isdatadescriptor(value):
|
||||
doc = getdoc(value)
|
||||
else:
|
||||
doc = None
|
||||
doc = getdoc(value)
|
||||
try:
|
||||
obj = getattr(object, name)
|
||||
except AttributeError:
|
||||
|
@ -1448,7 +1442,9 @@ location listed above.
|
|||
chop = maxlen - len(line)
|
||||
if chop < 0: repr = repr[:chop] + '...'
|
||||
line = (name and self.bold(name) + ' = ' or '') + repr
|
||||
if doc is not None:
|
||||
if not doc:
|
||||
doc = getdoc(object)
|
||||
if doc:
|
||||
line += '\n' + self.indent(str(doc))
|
||||
return line
|
||||
|
||||
|
@ -1672,7 +1668,8 @@ def render_doc(thing, title='Python Library Documentation: %s', forceload=0,
|
|||
if not (inspect.ismodule(object) or
|
||||
inspect.isclass(object) or
|
||||
inspect.isroutine(object) or
|
||||
inspect.isdatadescriptor(object)):
|
||||
inspect.isdatadescriptor(object) or
|
||||
inspect.getdoc(object)):
|
||||
# If the passed object is a piece of data or an instance,
|
||||
# document its available methods instead of its value.
|
||||
object = type(object)
|
||||
|
|
|
@ -439,8 +439,7 @@ class TestRetrievingSourceCode(GetSourceBase):
|
|||
@unittest.skipIf(sys.flags.optimize >= 2,
|
||||
"Docstrings are omitted with -O2 and above")
|
||||
def test_getdoc_inherited(self):
|
||||
self.assertEqual(inspect.getdoc(mod.FesteringGob),
|
||||
'A longer,\n\nindented\n\ndocstring.')
|
||||
self.assertIsNone(inspect.getdoc(mod.FesteringGob))
|
||||
self.assertEqual(inspect.getdoc(mod.FesteringGob.abuse),
|
||||
'Another\n\ndocstring\n\ncontaining\n\ntabs')
|
||||
self.assertEqual(inspect.getdoc(mod.FesteringGob().abuse),
|
||||
|
@ -448,10 +447,20 @@ 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.assertEqual(finddoc(int), int.__doc__)
|
||||
self.assertIsNone(finddoc(int))
|
||||
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__)
|
||||
|
|
|
@ -1254,7 +1254,8 @@ cm(x) method of builtins.type instance
|
|||
|
||||
X.attr.__doc__ = 'Custom descriptor'
|
||||
self.assertEqual(self._get_summary_lines(X.attr), """\
|
||||
<test.test_pydoc.TestDescriptions.test_custom_non_data_descriptor.<locals>.Descr object>""")
|
||||
<test.test_pydoc.TestDescriptions.test_custom_non_data_descriptor.<locals>.Descr object>
|
||||
Custom descriptor""")
|
||||
|
||||
X.attr.__name__ = 'foo'
|
||||
self.assertEqual(self._get_summary_lines(X.attr), """\
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
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. In :mod:`pydoc` the documentation string is now shown not
|
||||
only for class, function, method etc, but for any object that has its own
|
||||
``__doc__`` attribute.
|
Loading…
Reference in New Issue