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

This commit is contained in:
Serhiy Storchaka 2024-04-17 13:05:22 +03:00 committed by GitHub
parent 438b7c3071
commit c917b3e8e1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 22 additions and 15 deletions

View File

@ -1637,11 +1637,11 @@ class _PlainTextDoc(TextDoc):
# --------------------------------------------------------- user interfaces # --------------------------------------------------------- user interfaces
def pager(text): def pager(text, title=''):
"""The first time this is called, determine what kind of pager to use.""" """The first time this is called, determine what kind of pager to use."""
global pager global pager
pager = getpager() pager = getpager()
pager(text) pager(text, title)
def getpager(): def getpager():
"""Decide what method to use for paging through text.""" """Decide what method to use for paging through text."""
@ -1656,24 +1656,24 @@ def getpager():
use_pager = os.environ.get('MANPAGER') or os.environ.get('PAGER') use_pager = os.environ.get('MANPAGER') or os.environ.get('PAGER')
if use_pager: if use_pager:
if sys.platform == 'win32': # pipes completely broken in Windows 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'): 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: 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'): if os.environ.get('TERM') in ('dumb', 'emacs'):
return plainpager return plainpager
if sys.platform == 'win32': 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: 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 import tempfile
(fd, filename) = tempfile.mkstemp() (fd, filename) = tempfile.mkstemp()
os.close(fd) os.close(fd)
try: try:
if hasattr(os, 'system') and os.system('more "%s"' % filename) == 0: 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: else:
return ttypager return ttypager
finally: finally:
@ -1683,12 +1683,18 @@ def plain(text):
"""Remove boldface formatting from text.""" """Remove boldface formatting from text."""
return re.sub('.\b', '', 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.""" """Page through text by feeding it to another program."""
import subprocess import subprocess
env = os.environ.copy() env = os.environ.copy()
if title:
title += ' '
esc_title = escape_less(title)
prompt_string = ( prompt_string = (
' ' f' {esc_title}' +
'?ltline %lt?L/%L.' '?ltline %lt?L/%L.'
':byte %bB?s/%s.' ':byte %bB?s/%s.'
'.' '.'
@ -1716,7 +1722,7 @@ def pipepager(text, cmd):
# left running and the terminal is in raw mode and unusable. # left running and the terminal is in raw mode and unusable.
pass pass
def tempfilepager(text, cmd): def tempfilepager(text, cmd, title=''):
"""Page through text by invoking a program on a temporary file.""" """Page through text by invoking a program on a temporary file."""
import tempfile import tempfile
with tempfile.TemporaryDirectory() as tempdir: with tempfile.TemporaryDirectory() as tempdir:
@ -1733,7 +1739,7 @@ def _escape_stdout(text):
encoding = getattr(sys.stdout, 'encoding', None) or 'utf-8' encoding = getattr(sys.stdout, 'encoding', None) or 'utf-8'
return text.encode(encoding, 'backslashreplace').decode(encoding) return text.encode(encoding, 'backslashreplace').decode(encoding)
def ttypager(text): def ttypager(text, title=''):
"""Page through text on a text terminal.""" """Page through text on a text terminal."""
lines = plain(_escape_stdout(text)).split('\n') lines = plain(_escape_stdout(text)).split('\n')
try: try:
@ -1777,7 +1783,7 @@ def ttypager(text):
if tty: if tty:
tty.tcsetattr(fd, tty.TCSAFLUSH, old) tty.tcsetattr(fd, tty.TCSAFLUSH, old)
def plainpager(text): def plainpager(text, title=''):
"""Simply print unformatted text. This is the ultimate fallback.""" """Simply print unformatted text. This is the ultimate fallback."""
sys.stdout.write(plain(_escape_stdout(text))) sys.stdout.write(plain(_escape_stdout(text)))
@ -1879,7 +1885,8 @@ def doc(thing, title='Python Library Documentation: %s', forceload=0,
"""Display text documentation, given an object or a path to an object.""" """Display text documentation, given an object or a path to an object."""
if output is None: if output is None:
try: 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: except ImportError as exc:
if is_cli: if is_cli:
raise raise
@ -2253,7 +2260,7 @@ module "pydoc_data.topics" could not be found.
text = 'Related help topics: ' + ', '.join(xrefs.split()) + '\n' text = 'Related help topics: ' + ', '.join(xrefs.split()) + '\n'
wrapped_text = textwrap.wrap(text, 72) wrapped_text = textwrap.wrap(text, 72)
doc += '\n%s\n' % '\n'.join(wrapped_text) doc += '\n%s\n' % '\n'.join(wrapped_text)
pager(doc) pager(doc, f'Help on {topic!s}')
def _gettopic(self, topic, more_xrefs=''): def _gettopic(self, topic, more_xrefs=''):
"""Return unbuffered tuple of (topic, xrefs). """Return unbuffered tuple of (topic, xrefs).