8000 [3.13] gh-120541: Improve the "less" prompt in pydoc (GH-120543) (GH-… · python/cpython@3a9f438 · GitHub
[go: up one dir, main page]

Skip to content

Commit 3a9f438

Browse files
[3.13] gh-120541: Improve the "less" prompt in pydoc (GH-120543) (GH-120562)
When help() is called with non-string argument, use __qualname__ or __name__ if available, otherwise use "{typename} object". (cherry picked from commit 31d1d72) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
1 parent 29bbd5f commit 3a9f438

File tree

3 files changed

+59
-14
lines changed

3 files changed

+59
-14
lines changed

Lib/pydoc.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1753,7 +1753,14 @@ def doc(thing, title='Python Library Documentation: %s', forceload=0,
17531753
"""Display text documentation, given an object or a path to an object."""
17541754
if output is None:
17551755
try:
1756-
what = thing if isinstance(thing, str) else type(thing).__name__
1756+
if isinstance(thing, str):
1757+
what = thing
1758+
else:
1759+
what = getattr(thing, '__qualname__', None)
1760+
if not isinstance(what, str):
1761+
what = getattr(thing, '__name__', None)
1762+
if not isinstance(what, str):
1763+
what = type(thing).__name__ + ' object'
17571764
pager(render_doc(thing, title, forceload), f'Help on {what!s}')
17581765
except ImportError as exc:
17591766
if is_cli:

Lib/test/test_pydoc/test_pydoc.py

Lines changed: 49 additions & 13 deletions
9E7A
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
from test.support.script_helper import (assert_python_ok,
3232
assert_python_failure, spawn_python)
3333
from test.support import threading_helper
34-
from test.support import (reap_children, captured_output, captured_stdout,
34+
from test.support import (reap_children, captured_stdout,
3535
captured_stderr, is_emscripten, is_wasi,
3636
requires_docstrings, MISSING_C_DOCSTRINGS)
3737
from test.support.os_helper import (TESTFN, rmtree, unlink)
@@ -675,9 +675,8 @@ def test_help_output_redirect(self, pager_mock):
675675
help_header = textwrap.dedent(help_header)
676676
expected_help_pattern = help_header + expected_text_pattern
677677

678-
with captured_output('stdout') as output, \
679-
captured_output('stderr') as err, \
680-
StringIO() as buf:
678+
with captured_stdout() as output, captured_stderr() as err:
679+
buf = StringIO()
681680
helper = pydoc.Helper(output=buf)
682681
helper.help(module)
683682
result = buf.getvalue().strip()
@@ -701,9 +700,8 @@ def test_help_output_redirect_various_requests(self, pager_mock):
701700

702701
def run_pydoc_for_request(request, expected_text_part):
703702
"""Helper function to run pydoc with its output redirected"""
704-
with captured_output('stdout') as output, \
705-
captured_output('stderr') as err, \
706-
StringIO() as buf:
703+
with captured_stdout() as output, captured_stderr() as err:
704+
buf = StringIO()
707705
helper = pydoc.Helper(output=buf)
708706
helper.help(request)
709707
result = buf.getvalue().strip()
@@ -737,6 +735,45 @@ def run_pydoc_for_request(request, expected_text_part):
737735
run_pydoc_for_request(pydoc.Helper.help, 'Help on function help in module pydoc:')
738736
# test for pydoc.Helper() instance skipped because it is always meant to be interactive
739737

738+
@unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(),
739+
'trace function introduces __locals__ unexpectedly')
740+
@requires_docstrings
741+
def test_help_output_pager(self):
742+
def run_pydoc_pager(request, what, expected_first_line):
743+
with (captured_stdout() as output,
744+
captured_stderr() as err,
745+
unittest.mock.patch('pydoc.pager') as pager_mock,
746+
self.subTest(repr(request))):
747+
helper = pydoc.Helper()
748+
helper.help(request)
749+
self.assertEqual('', err.getvalue())
750+
self.assertEqual('\n', output.getvalue())
751+
pager_mock.assert_called_once()
752+
result = clean_text(pager_mock.call_args.args[0])
753+
self.assertEqual(result.splitlines()[0], expected_first_line)
754+
self.assertEqual(pager_mock.call_args.args[1], f'Help on {what}')
755+
756+
run_pydoc_pager('%', 'EXPRESSIONS', 'Operator precedence')
757+
run_pydoc_pager('True', 'bool object', 'Help on bool object:')
758+
run_pydoc_pager(True, 'bool object', 'Help on bool object:')
759+
run_pydoc_pager('assert', 'assert', 'The "assert" statement')
760+
run_pydoc_pager('TYPES', 'TYPES', 'The standard type hierarchy')
761+
run_pydoc_pager('pydoc.Helper.help', 'pydoc.Helper.help',
762+
'Help on function help in pydoc.Helper:')
763+
run_pydoc_pager(pydoc.Helper.help, 'Helper.help',
764+
'Help on function help in module pydoc:')
765+
run_pydoc_pager('str', 'str', 'Help on class str in module builtins:')
766+
run_pydoc_pager(str, 'str', 'Help on class str in module builtins:')
767+
run_pydoc_pager('str.upper', 'str.upper', 'Help on method_descriptor in str:')
768+
run_pydoc_pager(str.upper, 'str.upper', 'Help on method_descriptor:')
769+
run_pydoc_pager(str.__add__, 'str.__add__', 'Help on wrapper_descriptor:')
770+
run_pydoc_pager(int.numerator, 'int.numerator',
771+
'Help on getset descriptor builtins.int.numerator:')
772+
run_pydoc_pager(list[int], 'list',
773+
'Help on GenericAlias in module builtins:')
774+
run_pydoc_pager('sys', 'sys', 'Help on built-in module sys:')
775+
run_pydoc_pager(sys, 'sys', 'Help on built-in module sys:')
776+
740777
def test_showtopic(self):
741778
with captured_stdout() as showtopic_io:
742779
helper = pydoc.Helper()
@@ -770,9 +807,8 @@ def test_showtopic_output_redirect(self, pager_mock):
770807
# Helper.showtopic should be redirected
771808
self.maxDiff = None
772809

773-
with captured_output('stdout') as output, \
774-
captured_output('stderr') as err, \
775-
StringIO() as buf:
810+
with captured_stdout() as output, captured_stderr() as err:
811+
buf = StringIO()
776812
helper = pydoc.Helper(output=buf)
777813
helper.showtopic('with')
778814
result = buf.getvalue().strip()
@@ -785,23 +821,23 @@ def test_showtopic_output_redirect(self, pager_mock):
785821
def test_lambda_with_return_annotation(self):
786822
func = lambda a, b, c: 1
787823
func.__annotations__ = {"return": int}
788-
with captured_output('stdout') as help_io:
824+
with captured_stdout() as help_io:
789825
pydoc.help(func)
790826
helptext = help_io.getvalue()
791827
self.assertIn("lambda (a, b, c) -> int", helptext)
792828

793829
def test_lambda_without_return_annotation(self):
794830
func = lambda a, b, c: 1
795831
func.__annotations__ = {"a": int, "b": int, "c": int}
796-
with captured_output('stdout') as help_io:
832+
with captured_stdout() as help_io:
797833
pydoc.help(func)
798834
helptext = help_io.getvalue()
799835
self.assertIn("lambda (a: int, b: int, c: int)", helptext)
800836

801837
def test_lambda_with_return_and_params_annotation(self):
802838
func = lambda a, b, c: 1
803839
func.__annotations__ = {"a": int, "b": int, "c": int, "return": int}
804-
with captured_output('stdout') as help_io:
840+
with captured_stdout() as help_io:
805841
pydoc.help(func)
806842
helptext = help_io.getvalue()
807843
self.assertIn("lambda (a: int, b: int, c: int) -> int", helptext)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Improve the prompt in the "less" pager when :func:`help` is called with
2+
non-string argument.

0 commit comments

Comments
 (0)
0