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`.
|
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
|
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.
|
string from the inheritance hierarchy.
|
||||||
|
|
||||||
.. versionchanged:: 3.5
|
.. versionchanged:: 3.5
|
||||||
Documentation strings are now inherited if not overridden.
|
Documentation strings are now inherited if not overridden.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.9
|
||||||
|
Documentation strings for classes are no longer inherited.
|
||||||
|
|
||||||
|
|
||||||
.. function:: getcomments(object)
|
.. function:: getcomments(object)
|
||||||
|
|
||||||
|
|
|
@ -346,6 +346,13 @@ pprint
|
||||||
:mod:`pprint` can now pretty-print :class:`types.SimpleNamespace`.
|
:mod:`pprint` can now pretty-print :class:`types.SimpleNamespace`.
|
||||||
(Contributed by Carl Bordum Hansen in :issue:`37376`.)
|
(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
|
signal
|
||||||
------
|
------
|
||||||
|
|
||||||
|
@ -798,6 +805,12 @@ Changes in the Python API
|
||||||
:class:`ftplib.FTP_TLS` as a keyword-only parameter, and the default encoding
|
: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`.
|
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
|
CPython bytecode changes
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
|
|
|
@ -542,17 +542,6 @@ def _findclass(func):
|
||||||
return cls
|
return cls
|
||||||
|
|
||||||
def _finddoc(obj):
|
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):
|
if ismethod(obj):
|
||||||
name = obj.__func__.__name__
|
name = obj.__func__.__name__
|
||||||
self = obj.__self__
|
self = obj.__self__
|
||||||
|
@ -596,23 +585,35 @@ def _finddoc(obj):
|
||||||
return None
|
return None
|
||||||
for base in cls.__mro__:
|
for base in cls.__mro__:
|
||||||
try:
|
try:
|
||||||
doc = getattr(base, name).__doc__
|
doc = _getowndoc(getattr(base, name))
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
continue
|
continue
|
||||||
if doc is not None:
|
if doc is not None:
|
||||||
return doc
|
return doc
|
||||||
return None
|
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):
|
def getdoc(object):
|
||||||
"""Get the documentation string for an object.
|
"""Get the documentation string for an object.
|
||||||
|
|
||||||
All tabs are expanded to spaces. To clean up docstrings that are
|
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
|
indented to line up with blocks of code, any whitespace than can be
|
||||||
uniformly removed from the second line onwards is removed."""
|
uniformly removed from the second line onwards is removed."""
|
||||||
try:
|
doc = _getowndoc(object)
|
||||||
doc = object.__doc__
|
|
||||||
except AttributeError:
|
|
||||||
return None
|
|
||||||
if doc is None:
|
if doc is None:
|
||||||
try:
|
try:
|
||||||
doc = _finddoc(object)
|
doc = _finddoc(object)
|
||||||
|
|
19
Lib/pydoc.py
19
Lib/pydoc.py
|
@ -825,11 +825,8 @@ class HTMLDoc(Doc):
|
||||||
push(msg)
|
push(msg)
|
||||||
for name, kind, homecls, value in ok:
|
for name, kind, homecls, value in ok:
|
||||||
base = self.docother(getattr(object, name), name, mod)
|
base = self.docother(getattr(object, name), name, mod)
|
||||||
if callable(value) or inspect.isdatadescriptor(value):
|
doc = getdoc(value)
|
||||||
doc = getattr(value, "__doc__", None)
|
if not doc:
|
||||||
else:
|
|
||||||
doc = None
|
|
||||||
if doc is None:
|
|
||||||
push('<dl><dt>%s</dl>\n' % base)
|
push('<dl><dt>%s</dl>\n' % base)
|
||||||
else:
|
else:
|
||||||
doc = self.markup(getdoc(value), self.preformat,
|
doc = self.markup(getdoc(value), self.preformat,
|
||||||
|
@ -1309,10 +1306,7 @@ location listed above.
|
||||||
hr.maybe()
|
hr.maybe()
|
||||||
push(msg)
|
push(msg)
|
||||||
for name, kind, homecls, value in ok:
|
for name, kind, homecls, value in ok:
|
||||||
if callable(value) or inspect.isdatadescriptor(value):
|
doc = getdoc(value)
|
||||||
doc = getdoc(value)
|
|
||||||
else:
|
|
||||||
doc = None
|
|
||||||
try:
|
try:
|
||||||
obj = getattr(object, name)
|
obj = getattr(object, name)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
|
@ -1448,7 +1442,9 @@ location listed above.
|
||||||
chop = maxlen - len(line)
|
chop = maxlen - len(line)
|
||||||
if chop < 0: repr = repr[:chop] + '...'
|
if chop < 0: repr = repr[:chop] + '...'
|
||||||
line = (name and self.bold(name) + ' = ' or '') + repr
|
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))
|
line += '\n' + self.indent(str(doc))
|
||||||
return line
|
return line
|
||||||
|
|
||||||
|
@ -1672,7 +1668,8 @@ def render_doc(thing, title='Python Library Documentation: %s', forceload=0,
|
||||||
if not (inspect.ismodule(object) or
|
if not (inspect.ismodule(object) or
|
||||||
inspect.isclass(object) or
|
inspect.isclass(object) or
|
||||||
inspect.isroutine(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,
|
# If the passed object is a piece of data or an instance,
|
||||||
# document its available methods instead of its value.
|
# document its available methods instead of its value.
|
||||||
object = type(object)
|
object = type(object)
|
||||||
|
|
|
@ -439,8 +439,7 @@ class TestRetrievingSourceCode(GetSourceBase):
|
||||||
@unittest.skipIf(sys.flags.optimize >= 2,
|
@unittest.skipIf(sys.flags.optimize >= 2,
|
||||||
"Docstrings are omitted with -O2 and above")
|
"Docstrings are omitted with -O2 and above")
|
||||||
def test_getdoc_inherited(self):
|
def test_getdoc_inherited(self):
|
||||||
self.assertEqual(inspect.getdoc(mod.FesteringGob),
|
self.assertIsNone(inspect.getdoc(mod.FesteringGob))
|
||||||
'A longer,\n\nindented\n\ndocstring.')
|
|
||||||
self.assertEqual(inspect.getdoc(mod.FesteringGob.abuse),
|
self.assertEqual(inspect.getdoc(mod.FesteringGob.abuse),
|
||||||
'Another\n\ndocstring\n\ncontaining\n\ntabs')
|
'Another\n\ndocstring\n\ncontaining\n\ntabs')
|
||||||
self.assertEqual(inspect.getdoc(mod.FesteringGob().abuse),
|
self.assertEqual(inspect.getdoc(mod.FesteringGob().abuse),
|
||||||
|
@ -448,10 +447,20 @@ class TestRetrievingSourceCode(GetSourceBase):
|
||||||
self.assertEqual(inspect.getdoc(mod.FesteringGob.contradiction),
|
self.assertEqual(inspect.getdoc(mod.FesteringGob.contradiction),
|
||||||
'The automatic gainsaying.')
|
'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")
|
@unittest.skipIf(MISSING_C_DOCSTRINGS, "test requires docstrings")
|
||||||
def test_finddoc(self):
|
def test_finddoc(self):
|
||||||
finddoc = inspect._finddoc
|
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().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__)
|
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'
|
X.attr.__doc__ = 'Custom descriptor'
|
||||||
self.assertEqual(self._get_summary_lines(X.attr), """\
|
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'
|
X.attr.__name__ = 'foo'
|
||||||
self.assertEqual(self._get_summary_lines(X.attr), """\
|
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