diff --git a/Lib/pydoc.py b/Lib/pydoc.py index 628f9fc7d1d1ef..9e38a200d62daa 100755 --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -1772,14 +1772,16 @@ def render_doc(thing, title='Python Library Documentation: %s', forceload=0, return title % desc + '\n\n' + renderer.document(object, name) def doc(thing, title='Python Library Documentation: %s', forceload=0, - output=None): + output=None, follow_wrapped=True): """Display text documentation, given an object or a path to an object.""" try: + if not follow_wrapped: + thing = type(thing) if output is None: pager(render_doc(thing, title, forceload)) else: output.write(render_doc(thing, title, forceload, plaintext)) - except (ImportError, ErrorDuringImport) as value: + except (ImportError, ErrorDuringImport, ValueError) as value: print(value) def writedoc(thing, forceload=0): @@ -1995,9 +1997,9 @@ def __repr__(self): self.__class__.__qualname__) _GoInteractive = object() - def __call__(self, request=_GoInteractive): + def __call__(self, request=_GoInteractive, follow_wrapped=True): if request is not self._GoInteractive: - self.help(request) + self.help(request, follow_wrapped=follow_wrapped) else: self.intro() self.interact() @@ -2038,7 +2040,7 @@ def getline(self, prompt): self.output.flush() return self.input.readline() - def help(self, request): + def help(self, request, follow_wrapped=True): if type(request) is type(''): request = request.strip() if request == 'keywords': self.listkeywords() @@ -2050,13 +2052,16 @@ def help(self, request): elif request in self.symbols: self.showsymbol(request) elif request in ['True', 'False', 'None']: # special case these keywords since they are objects too - doc(eval(request), 'Help on %s:') + doc(eval(request), 'Help on %s:', follow_wrapped=follow_wrapped) elif request in self.keywords: self.showtopic(request) elif request in self.topics: self.showtopic(request) - elif request: doc(request, 'Help on %s:', output=self._output) - else: doc(str, 'Help on %s:', output=self._output) + elif request: doc(request, 'Help on %s:', output=self._output, + follow_wrapped=follow_wrapped) + else: doc(str, 'Help on %s:', output=self._output, + follow_wrapped=follow_wrapped) elif isinstance(request, Helper): self() - else: doc(request, 'Help on %s:', output=self._output) + else: doc(request, 'Help on %s:', output=self._output, + follow_wrapped=follow_wrapped) self.output.write('\n') def intro(self): diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py index 76d2af8e461ed1..1d2e1ebc1e4399 100644 --- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -647,6 +647,44 @@ def test_builtin_on_metaclasses(self): # Testing that the subclasses section does not appear self.assertNotIn('Built-in subclasses', text) + def test_help_with_wrapper(self): + """Test help on function wrapped. + + When running help() on a function that is wrapped, + should be show the docstring of the last object in the chain + with __doc__ method. + """ + + import functools + class TestWrapper: + """This is the docstring of Wrapper""" + def __init__(self, func): + functools.update_wrapper(self, func) + def __call__(self): + pass + + @TestWrapper + def test_func1(): + """Test func1""" + pass + + buff = StringIO() + + helper = pydoc.Helper(output=buff) + + helper.help(test_func1) + self.assertIn('Test func1', buff.getvalue().strip()) + + helper.help(test_func1, follow_wrapped=False) + self.assertIn("This is the docstring of Wrapper", buff.getvalue().strip()) + + @TestWrapper + def test_func2(): + pass + + helper.help(test_func2) + self.assertIn("This is the docstring of Wrapper", buff.getvalue().strip()) + @unittest.skipIf(sys.flags.optimize >= 2, 'Docstrings are omitted with -O2 and above') @unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(), diff --git a/Misc/NEWS.d/next/Library/2019-04-22-17-25-46.bpo-29940.fslqG1.rst b/Misc/NEWS.d/next/Library/2019-04-22-17-25-46.bpo-29940.fslqG1.rst new file mode 100644 index 00000000000000..61a1a878f3bfbb --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-04-22-17-25-46.bpo-29940.fslqG1.rst @@ -0,0 +1,2 @@ +Add follow_wrapped to help function to print the docstring of a wrapped +function.