]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-65824: Add "Help on ..." to the "less" prompt in pydoc (GH-116183)
authorSerhiy Storchaka <storchaka@gmail.com>
Wed, 17 Apr 2024 10:05:22 +0000 (13:05 +0300)
committerGitHub <noreply@github.com>
Wed, 17 Apr 2024 10:05:22 +0000 (13:05 +0300)
Lib/pydoc.py

index d9cf03fb4ffd2a324601a5ebbdf08d754cd73096..02af672a628f5ab55edc9395bd0f5d445e712df7 100755 (executable)
@@ -1637,11 +1637,11 @@ class _PlainTextDoc(TextDoc):
 
 # --------------------------------------------------------- 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."""
@@ -1656,24 +1656,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:
@@ -1683,12 +1683,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.'
         '.'
@@ -1716,7 +1722,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:
@@ -1733,7 +1739,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:
@@ -1777,7 +1783,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)))
 
@@ -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."""
     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
@@ -2253,7 +2260,7 @@ module "pydoc_data.topics" could not be found.
             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).