mirror of
https://github.com/python/cpython.git
synced 2025-10-19 21:28:02 +00:00
Issue #15582: inspect.getdoc() now follows inheritance chains.
This commit is contained in:
parent
41525e31a5
commit
5cf2b7253d
5 changed files with 112 additions and 3 deletions
|
@ -356,6 +356,9 @@ Retrieving source code
|
||||||
.. function:: getdoc(object)
|
.. function:: getdoc(object)
|
||||||
|
|
||||||
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
|
||||||
|
a class, a method, a property or a descriptor, retrieve the documentation
|
||||||
|
string from the inheritance hierarchy.
|
||||||
|
|
||||||
|
|
||||||
.. function:: getcomments(object)
|
.. function:: getcomments(object)
|
||||||
|
|
|
@ -468,6 +468,74 @@ def indentsize(line):
|
||||||
expline = line.expandtabs()
|
expline = line.expandtabs()
|
||||||
return len(expline) - len(expline.lstrip())
|
return len(expline) - len(expline.lstrip())
|
||||||
|
|
||||||
|
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 isclass(cls):
|
||||||
|
return None
|
||||||
|
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__
|
||||||
|
if (isclass(self) and
|
||||||
|
getattr(getattr(self, name, None), '__func__') is obj.__func__):
|
||||||
|
# classmethod
|
||||||
|
cls = self
|
||||||
|
else:
|
||||||
|
cls = self.__class__
|
||||||
|
elif isfunction(obj):
|
||||||
|
name = obj.__name__
|
||||||
|
cls = _findclass(obj)
|
||||||
|
if cls is None or getattr(cls, name) is not obj:
|
||||||
|
return None
|
||||||
|
elif isbuiltin(obj):
|
||||||
|
name = obj.__name__
|
||||||
|
self = obj.__self__
|
||||||
|
if (isclass(self) and
|
||||||
|
self.__qualname__ + '.' + name == obj.__qualname__):
|
||||||
|
# classmethod
|
||||||
|
cls = self
|
||||||
|
else:
|
||||||
|
cls = self.__class__
|
||||||
|
elif ismethoddescriptor(obj) or isdatadescriptor(obj):
|
||||||
|
name = obj.__name__
|
||||||
|
cls = obj.__objclass__
|
||||||
|
if getattr(cls, name) is not obj:
|
||||||
|
return None
|
||||||
|
elif isinstance(obj, property):
|
||||||
|
func = f.fget
|
||||||
|
name = func.__name__
|
||||||
|
cls = _findclass(func)
|
||||||
|
if cls is None or getattr(cls, name) is not obj:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
for base in cls.__mro__:
|
||||||
|
try:
|
||||||
|
doc = getattr(base, name).__doc__
|
||||||
|
except AttributeError:
|
||||||
|
continue
|
||||||
|
if doc is not None:
|
||||||
|
return doc
|
||||||
|
return None
|
||||||
|
|
||||||
def getdoc(object):
|
def getdoc(object):
|
||||||
"""Get the documentation string for an object.
|
"""Get the documentation string for an object.
|
||||||
|
|
||||||
|
@ -478,6 +546,11 @@ def getdoc(object):
|
||||||
doc = object.__doc__
|
doc = object.__doc__
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return None
|
return None
|
||||||
|
if doc is None:
|
||||||
|
try:
|
||||||
|
doc = _finddoc(object)
|
||||||
|
except (AttributeError, TypeError):
|
||||||
|
return None
|
||||||
if not isinstance(doc, str):
|
if not isinstance(doc, str):
|
||||||
return None
|
return None
|
||||||
return cleandoc(doc)
|
return cleandoc(doc)
|
||||||
|
|
|
@ -45,9 +45,16 @@ class StupidGit:
|
||||||
self.ex = sys.exc_info()
|
self.ex = sys.exc_info()
|
||||||
self.tr = inspect.trace()
|
self.tr = inspect.trace()
|
||||||
|
|
||||||
|
def contradiction(self):
|
||||||
|
'The automatic gainsaying.'
|
||||||
|
pass
|
||||||
|
|
||||||
# line 48
|
# line 48
|
||||||
class MalodorousPervert(StupidGit):
|
class MalodorousPervert(StupidGit):
|
||||||
pass
|
def abuse(self, a, b, c):
|
||||||
|
pass
|
||||||
|
def contradiction(self):
|
||||||
|
pass
|
||||||
|
|
||||||
Tit = MalodorousPervert
|
Tit = MalodorousPervert
|
||||||
|
|
||||||
|
@ -55,4 +62,7 @@ class ParrotDroppings:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class FesteringGob(MalodorousPervert, ParrotDroppings):
|
class FesteringGob(MalodorousPervert, ParrotDroppings):
|
||||||
pass
|
def abuse(self, a, b, c):
|
||||||
|
pass
|
||||||
|
def contradiction(self):
|
||||||
|
pass
|
||||||
|
|
|
@ -292,6 +292,27 @@ class TestRetrievingSourceCode(GetSourceBase):
|
||||||
self.assertEqual(inspect.getdoc(git.abuse),
|
self.assertEqual(inspect.getdoc(git.abuse),
|
||||||
'Another\n\ndocstring\n\ncontaining\n\ntabs')
|
'Another\n\ndocstring\n\ncontaining\n\ntabs')
|
||||||
|
|
||||||
|
@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.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_finddoc(self):
|
||||||
|
finddoc = inspect._finddoc
|
||||||
|
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__)
|
||||||
|
self.assertEqual(finddoc(int.real), int.real.__doc__)
|
||||||
|
|
||||||
def test_cleandoc(self):
|
def test_cleandoc(self):
|
||||||
self.assertEqual(inspect.cleandoc('An\n indented\n docstring.'),
|
self.assertEqual(inspect.cleandoc('An\n indented\n docstring.'),
|
||||||
'An\nindented\ndocstring.')
|
'An\nindented\ndocstring.')
|
||||||
|
@ -316,7 +337,7 @@ class TestRetrievingSourceCode(GetSourceBase):
|
||||||
|
|
||||||
def test_getsource(self):
|
def test_getsource(self):
|
||||||
self.assertSourceEqual(git.abuse, 29, 39)
|
self.assertSourceEqual(git.abuse, 29, 39)
|
||||||
self.assertSourceEqual(mod.StupidGit, 21, 46)
|
self.assertSourceEqual(mod.StupidGit, 21, 50)
|
||||||
|
|
||||||
def test_getsourcefile(self):
|
def test_getsourcefile(self):
|
||||||
self.assertEqual(normcase(inspect.getsourcefile(mod.spam)), modfile)
|
self.assertEqual(normcase(inspect.getsourcefile(mod.spam)), modfile)
|
||||||
|
|
|
@ -16,6 +16,8 @@ Core and Builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue #15582: inspect.getdoc() now follows inheritance chains.
|
||||||
|
|
||||||
- Issue #2175: SAX parsers now support a character stream of InputSource object.
|
- Issue #2175: SAX parsers now support a character stream of InputSource object.
|
||||||
|
|
||||||
- Issue #16840: Tkinter now supports 64-bit integers added in Tcl 8.4 and
|
- Issue #16840: Tkinter now supports 64-bit integers added in Tcl 8.4 and
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue