From c917b3e8e113a3e1ffe118e581fac29eaf365191 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 17 Apr 2024 13:05:22 +0300 Subject: [PATCH] gh-65824: Add "Help on ..." to the "less" prompt in pydoc (GH-116183) --- Lib/pydoc.py | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/Lib/pydoc.py b/Lib/pydoc.py index d9cf03fb4ff..02af672a628 100755 --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -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).