diff -r 6a347c0ffbfc Doc/library/inspect.rst --- a/Doc/library/inspect.rst Sat Apr 05 11:56:37 2014 +0200 +++ b/Doc/library/inspect.rst Sat Apr 05 13:46:45 2014 +0300 @@ -337,10 +337,18 @@ Retrieving source code ---------------------- -.. function:: getdoc(object) +.. function:: getdoc(object[, parent]) Get the documentation string for an object, cleaned up with :func:`cleandoc`. + If *parent* is given and *object.__doc__* is ``None``, and either the first + parameter is a bound method, or a class object is passed in + as *parent*, :func:`.getdoc` will search the MRO based + on ``object.__name__`` until it finds an attribute with a + non ``None`` ``__doc__`` value. + + .. versionchanged:: 3.5 + Added the *parent* parameter. .. function:: getcomments(object) diff -r 6a347c0ffbfc Lib/inspect.py --- a/Lib/inspect.py Sat Apr 05 11:56:37 2014 +0200 +++ b/Lib/inspect.py Sat Apr 05 13:46:45 2014 +0300 @@ -468,9 +468,14 @@ expline = line.expandtabs() return len(expline) - len(expline.lstrip()) -def getdoc(object): +def getdoc(object, parent=None): """Get the documentation string for an object. + In the case where obj.__doc__ is None, and either the first + parameter is a bound method, or a class object is passed in + as the second parameter, `getdoc` will search the MRO based + on obj.__name__ until it finds an attribute with a + non-None __doc__ value. 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.""" @@ -478,6 +483,26 @@ doc = object.__doc__ except AttributeError: return None + if doc is None: + if ismethod(object): + parent = object.__self__.__class__ + if isclass(parent): + name = object.__name__ + for base in getmro(parent): + try: + baseobj = getattr(base, name) + except AttributeError: + if name in base.__dict__: + baseobj = base.__dict__[name] + else: + continue + try: + doc = baseobj.__doc__ + except AttributeError: + continue + if doc is not None: + break + if not isinstance(doc, str): return None return cleandoc(doc) diff -r 6a347c0ffbfc Lib/test/test_inspect.py --- a/Lib/test/test_inspect.py Sat Apr 05 11:56:37 2014 +0200 +++ b/Lib/test/test_inspect.py Sat Apr 05 13:46:45 2014 +0300 @@ -280,6 +280,27 @@ self.assertEqual(inspect.getdoc(git.abuse), 'Another\n\ndocstring\n\ncontaining\n\ntabs') + # test for inheritance chain + class Parent: + def ham(self): + """eggs""" + class Child(Parent): + def ham(self): + pass + class Nephew(Child): + def ham(self): + pass + + self.assertEqual(inspect.getdoc(Child().ham), + 'eggs') + self.assertEqual(inspect.getdoc(Child.ham, parent=Child), + 'eggs') + self.assertEqual(inspect.getdoc(Nephew.ham, parent=Nephew), + 'eggs') + self.assertIsNone(inspect.getdoc(Nephew.ham)) + # test wrong parent + self.assertIsNone(inspect.getdoc(Child.ham, parent=mod.StupidGit)) + def test_cleandoc(self): self.assertEqual(inspect.cleandoc('An\n indented\n docstring.'), 'An\nindented\ndocstring.')