Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
53 changes: 43 additions & 10 deletions Lib/pydoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -1771,7 +1771,7 @@ 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, is_cli=False):
output=None, is_cli=False, is_interactive=False):
"""Display text documentation, given an object or a path to an object."""
if output is None:
try:
Expand All @@ -1787,7 +1787,11 @@ def doc(thing, title='Python Library Documentation: %s', forceload=0,
except ImportError as exc:
if is_cli:
raise
print(exc)
if is_interactive:
# don't show the usual hints if we're running interactively
print(exc.args[0].splitlines(False)[0])
else:
print(exc)
else:
try:
s = render_doc(thing, title, forceload, plaintext)
Expand Down Expand Up @@ -2070,10 +2074,7 @@ def interact(self):
and request[0] not in request[1:-1]):
request = request[1:-1]
if request.lower() in ('q', 'quit', 'exit'): break
if request == 'help':
self.intro()
else:
self.help(request)
self.help(request, is_interactive=True)

def getline(self, prompt):
"""Read one line, using input() when appropriate."""
Expand All @@ -2084,10 +2085,12 @@ def getline(self, prompt):
self.output.flush()
return self.input.readline()

def help(self, request, is_cli=False):
def help(self, request, is_cli=False, is_interactive=False):
if isinstance(request, str):
request = request.strip()
if request == 'keywords': self.listkeywords()
if request == 'help':
self.helphelp(is_interactive=is_interactive)
elif request == 'keywords': self.listkeywords()
elif request == 'symbols': self.listsymbols()
elif request == 'topics': self.listtopics()
elif request == 'modules': self.listmodules()
Expand All @@ -2099,12 +2102,42 @@ def help(self, request, is_cli=False):
doc(eval(request), 'Help on %s:', output=self._output, is_cli=is_cli)
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, is_cli=is_cli)
elif request:
doc(request, 'Help on %s:', output=self._output, is_cli=is_cli, is_interactive=is_interactive)
else: doc(str, 'Help on %s:', output=self._output, is_cli=is_cli)
elif isinstance(request, Helper): self()
elif isinstance(request, (Helper, type(builtins.help))):
self.helphelp(is_interactive=is_interactive)
Comment on lines +2108 to +2109

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When Python is started without site initialization (-S), builtins.help isn't available, so this will raise an exception when pydoc.help is invoked manually. Let's use _sitebuiltins._Helper instead.

else: doc(request, 'Help on %s:', output=self._output, is_cli=is_cli)
self.output.write('\n')

def helphelp(self, is_interactive=False):
if is_interactive:
self.intro()
else:
pager(textwrap.dedent("""\

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calls to pager() need to be guarded by a self.output is None check, because we don't want to start the pager when output is being redirected. So, we want something like this instead:

help_text = textwrap.dedent(...)
if is_interactive:
    self.intro()
elif self._output is None:
    pager(help_text)
else:
    self.output.write(help_text)

help - Interactive Help
=======================

The built-in help function implements an interactive help utility. You
can make use of it in a few different ways:

* Calling help() with no arguments starts an interactive help session.

* The behavior of help(x) depends on the type of the argument:

* If x is a string, help(x) provides information about the given
topic. For example, help("class") will provide information about
the "class" keyword, and help("math.sqrt") will provide
information about the "math.sqrt" function.

* If x is a class or a built-in type, help(x) provides information
about that type. For example, help(str) will provide information
about the str type.

* Otherwise, help(x) provides information about x's type. For
example, help(42) will provide information about the int type.
"""))

def intro(self):
self.output.write(_introdoc())

Expand Down
4 changes: 2 additions & 2 deletions Lib/test/test_pydoc/test_pydoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -664,10 +664,10 @@ def test_builtin_on_metaclasses(self):
self.assertNotIn('Built-in subclasses', text)

def test_fail_help_cli(self):
elines = (missing_pattern % 'abd').splitlines()
elines = (missing_pattern % 'abd').splitlines(False)[:1]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Positional boolean arguments are a code smell because they're often unreadable. Could you change this (and other occurrences) to splitlines(keepends=False)?

with spawn_python("-c" "help()") as proc:
out, _ = proc.communicate(b"abd")
olines = out.decode().splitlines()[-9:-6]
olines = out.decode().splitlines(False)[-7:-6]
olines[0] = olines[0].removeprefix('help> ')
self.assertEqual(elines, olines)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Removed potentially-misleading suggestions from the interactive :func:`help`

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the dash is unnecessary here? Correct me if I'm wrong on this one.

Suggested change
Removed potentially-misleading suggestions from the interactive :func:`help`
Removed potentially misleading suggestions from the interactive :func:`help`

prompt. Patch by Adam Hartz.