Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bpo-40257: Output object's own docstring in pydoc #19479

Merged
merged 3 commits into from
Apr 15, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
bpo-40257: Output object's own docstring in pydoc
  • Loading branch information
serhiy-storchaka committed Apr 11, 2020
commit b2ccea56afe80eb893d429cd153bdcdfb344d5a2
33 changes: 17 additions & 16 deletions Lib/inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -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__
Expand Down Expand Up @@ -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)
Expand Down
19 changes: 8 additions & 11 deletions Lib/pydoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -825,11 +825,8 @@ def spilldata(msg, attrs, predicate):
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,
Expand Down Expand Up @@ -1309,10 +1306,7 @@ def spilldata(msg, attrs, predicate):
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:
Expand Down Expand Up @@ -1448,7 +1442,9 @@ def docother(self, object, name=None, mod=None, parent=None, maxlen=None, doc=No
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

Expand Down Expand Up @@ -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)
Expand Down
15 changes: 12 additions & 3 deletions Lib/test/test_inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -439,19 +439,28 @@ def test_getdoc(self):
@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),
'Another\n\ndocstring\n\ncontaining\n\ntabs')
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__)
Expand Down
3 changes: 2 additions & 1 deletion Lib/test/test_pydoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -1254,7 +1254,8 @@ class X:

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), """\
Expand Down