Skip to content

gh-65824: Add "Help on ..." to the "less" prompt in pydoc #116183

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

Merged
merged 1 commit into from
Apr 17, 2024
Merged
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
37 changes: 22 additions & 15 deletions Lib/pydoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -1636,11 +1636,11 @@ def bold(self, text):

# --------------------------------------------------------- user interfaces

def pager(text):
def pager(text, title=''):
"""The first time this is called, determine what kind of pager to use."""
global pager
pager = getpager()
pager(text)
pager(text, title)

def getpager():
"""Decide what method to use for paging through text."""
Expand All @@ -1655,24 +1655,24 @@ def getpager():
use_pager = os.environ.get('MANPAGER') or os.environ.get('PAGER')
if use_pager:
if sys.platform == 'win32': # pipes completely broken in Windows
return lambda text: tempfilepager(plain(text), use_pager)
return lambda text, title='': tempfilepager(plain(text), use_pager)
elif os.environ.get('TERM') in ('dumb', 'emacs'):
return lambda text: pipepager(plain(text), use_pager)
return lambda text, title='': pipepager(plain(text), use_pager, title)
else:
return lambda text: pipepager(text, use_pager)
return lambda text, title='': pipepager(text, use_pager, title)
if os.environ.get('TERM') in ('dumb', 'emacs'):
return plainpager
if sys.platform == 'win32':
return lambda text: tempfilepager(plain(text), 'more <')
return lambda text, title='': tempfilepager(plain(text), 'more <')
if hasattr(os, 'system') and os.system('(less) 2>/dev/null') == 0:
return lambda text: pipepager(text, 'less')
return lambda text, title='': pipepager(text, 'less', title)

import tempfile
(fd, filename) = tempfile.mkstemp()
os.close(fd)
try:
if hasattr(os, 'system') and os.system('more "%s"' % filename) == 0:
return lambda text: pipepager(text, 'more')
return lambda text, title='': pipepager(text, 'more', title)
else:
return ttypager
finally:
Expand All @@ -1682,12 +1682,18 @@ def plain(text):
"""Remove boldface formatting from text."""
return re.sub('.\b', '', text)

def pipepager(text, cmd):
def escape_less(s):
return re.sub(r'([?:.%\\])', r'\\\1', s)

def pipepager(text, cmd, title=''):
"""Page through text by feeding it to another program."""
import subprocess
env = os.environ.copy()
if title:
title += ' '
esc_title = escape_less(title)
prompt_string = (
' '
f' {esc_title}' +
'?ltline %lt?L/%L.'
':byte %bB?s/%s.'
'.'
Expand Down Expand Up @@ -1715,7 +1721,7 @@ def pipepager(text, cmd):
# left running and the terminal is in raw mode and unusable.
pass

def tempfilepager(text, cmd):
def tempfilepager(text, cmd, title=''):
"""Page through text by invoking a program on a temporary file."""
import tempfile
with tempfile.TemporaryDirectory() as tempdir:
Expand All @@ -1732,7 +1738,7 @@ def _escape_stdout(text):
encoding = getattr(sys.stdout, 'encoding', None) or 'utf-8'
return text.encode(encoding, 'backslashreplace').decode(encoding)

def ttypager(text):
def ttypager(text, title=''):
"""Page through text on a text terminal."""
lines = plain(_escape_stdout(text)).split('\n')
try:
Expand Down Expand Up @@ -1776,7 +1782,7 @@ def ttypager(text):
if tty:
tty.tcsetattr(fd, tty.TCSAFLUSH, old)

def plainpager(text):
def plainpager(text, title=''):
"""Simply print unformatted text. This is the ultimate fallback."""
sys.stdout.write(plain(_escape_stdout(text)))

Expand Down Expand Up @@ -1878,7 +1884,8 @@ def doc(thing, title='Python Library Documentation: %s', forceload=0,
"""Display text documentation, given an object or a path to an object."""
if output is None:
try:
pager(render_doc(thing, title, forceload))
what = thing if isinstance(thing, str) else type(thing).__name__
pager(render_doc(thing, title, forceload), f'Help on {what!s}')
except ImportError as exc:
if is_cli:
raise
Expand Down Expand Up @@ -2252,7 +2259,7 @@ def showtopic(self, topic, more_xrefs=''):
text = 'Related help topics: ' + ', '.join(xrefs.split()) + '\n'
wrapped_text = textwrap.wrap(text, 72)
doc += '\n%s\n' % '\n'.join(wrapped_text)
pager(doc)
pager(doc, f'Help on {topic!s}')

def _gettopic(self, topic, more_xrefs=''):
"""Return unbuffered tuple of (topic, xrefs).
Expand Down