mirror of https://github.com/python/cpython
bpo-37755: Use configured output in pydoc instead of pager (GH-15105)
If the Helper() class was initialized with an output, the topics, keywords and symbols help still use the pager instead of the output. Change the behavior so the output is used if available while keeping the previous behavior if no output was configured.
This commit is contained in:
parent
95f4db88d5
commit
2080425154
|
@ -2034,7 +2034,7 @@ has the same effect as typing a particular string at the help> prompt.
|
||||||
elif request in self.symbols: self.showsymbol(request)
|
elif request in self.symbols: self.showsymbol(request)
|
||||||
elif request in ['True', 'False', 'None']:
|
elif request in ['True', 'False', 'None']:
|
||||||
# special case these keywords since they are objects too
|
# special case these keywords since they are objects too
|
||||||
doc(eval(request), 'Help on %s:', is_cli=is_cli)
|
doc(eval(request), 'Help on %s:', output=self._output, is_cli=is_cli)
|
||||||
elif request in self.keywords: self.showtopic(request)
|
elif request in self.keywords: self.showtopic(request)
|
||||||
elif request in self.topics: self.showtopic(request)
|
elif request in self.topics: self.showtopic(request)
|
||||||
elif request: doc(request, 'Help on %s:', output=self._output, is_cli=is_cli)
|
elif request: doc(request, 'Help on %s:', output=self._output, is_cli=is_cli)
|
||||||
|
@ -2127,7 +2127,11 @@ 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, f'Help on {topic!s}')
|
|
||||||
|
if self._output is None:
|
||||||
|
pager(doc, f'Help on {topic!s}')
|
||||||
|
else:
|
||||||
|
self.output.write(doc)
|
||||||
|
|
||||||
def _gettopic(self, topic, more_xrefs=''):
|
def _gettopic(self, topic, more_xrefs=''):
|
||||||
"""Return unbuffered tuple of (topic, xrefs).
|
"""Return unbuffered tuple of (topic, xrefs).
|
||||||
|
|
|
@ -17,6 +17,7 @@ import time
|
||||||
import types
|
import types
|
||||||
import typing
|
import typing
|
||||||
import unittest
|
import unittest
|
||||||
|
import unittest.mock
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
import xml.etree
|
import xml.etree
|
||||||
import xml.etree.ElementTree
|
import xml.etree.ElementTree
|
||||||
|
@ -658,16 +659,13 @@ class PydocDocTest(unittest.TestCase):
|
||||||
|
|
||||||
@unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(),
|
@unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(),
|
||||||
'trace function introduces __locals__ unexpectedly')
|
'trace function introduces __locals__ unexpectedly')
|
||||||
|
@unittest.mock.patch('pydoc.pager')
|
||||||
@requires_docstrings
|
@requires_docstrings
|
||||||
def test_help_output_redirect(self):
|
def test_help_output_redirect(self, pager_mock):
|
||||||
# issue 940286, if output is set in Helper, then all output from
|
# issue 940286, if output is set in Helper, then all output from
|
||||||
# Helper.help should be redirected
|
# Helper.help should be redirected
|
||||||
getpager_old = pydoc.getpager
|
|
||||||
getpager_new = lambda: (lambda x: x)
|
|
||||||
self.maxDiff = None
|
self.maxDiff = None
|
||||||
|
|
||||||
buf = StringIO()
|
|
||||||
helper = pydoc.Helper(output=buf)
|
|
||||||
unused, doc_loc = get_pydoc_text(pydoc_mod)
|
unused, doc_loc = get_pydoc_text(pydoc_mod)
|
||||||
module = "test.test_pydoc.pydoc_mod"
|
module = "test.test_pydoc.pydoc_mod"
|
||||||
help_header = """
|
help_header = """
|
||||||
|
@ -677,21 +675,112 @@ class PydocDocTest(unittest.TestCase):
|
||||||
help_header = textwrap.dedent(help_header)
|
help_header = textwrap.dedent(help_header)
|
||||||
expected_help_pattern = help_header + expected_text_pattern
|
expected_help_pattern = help_header + expected_text_pattern
|
||||||
|
|
||||||
pydoc.getpager = getpager_new
|
with captured_output('stdout') as output, \
|
||||||
try:
|
captured_output('stderr') as err, \
|
||||||
|
StringIO() as buf:
|
||||||
|
helper = pydoc.Helper(output=buf)
|
||||||
|
helper.help(module)
|
||||||
|
result = buf.getvalue().strip()
|
||||||
|
expected_text = expected_help_pattern % (
|
||||||
|
(doc_loc,) +
|
||||||
|
expected_text_data_docstrings +
|
||||||
|
(inspect.getabsfile(pydoc_mod),))
|
||||||
|
self.assertEqual('', output.getvalue())
|
||||||
|
self.assertEqual('', err.getvalue())
|
||||||
|
self.assertEqual(expected_text, result)
|
||||||
|
|
||||||
|
pager_mock.assert_not_called()
|
||||||
|
|
||||||
|
@unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(),
|
||||||
|
'trace function introduces __locals__ unexpectedly')
|
||||||
|
@requires_docstrings
|
||||||
|
@unittest.mock.patch('pydoc.pager')
|
||||||
|
def test_help_output_redirect_various_requests(self, pager_mock):
|
||||||
|
# issue 940286, if output is set in Helper, then all output from
|
||||||
|
# Helper.help should be redirected
|
||||||
|
|
||||||
|
def run_pydoc_for_request(request, expected_text_part):
|
||||||
|
"""Helper function to run pydoc with its output redirected"""
|
||||||
with captured_output('stdout') as output, \
|
with captured_output('stdout') as output, \
|
||||||
captured_output('stderr') as err:
|
captured_output('stderr') as err, \
|
||||||
helper.help(module)
|
StringIO() as buf:
|
||||||
|
helper = pydoc.Helper(output=buf)
|
||||||
|
helper.help(request)
|
||||||
result = buf.getvalue().strip()
|
result = buf.getvalue().strip()
|
||||||
expected_text = expected_help_pattern % (
|
self.assertEqual('', output.getvalue(), msg=f'failed on request "{request}"')
|
||||||
(doc_loc,) +
|
self.assertEqual('', err.getvalue(), msg=f'failed on request "{request}"')
|
||||||
expected_text_data_docstrings +
|
self.assertIn(expected_text_part, result, msg=f'failed on request "{request}"')
|
||||||
(inspect.getabsfile(pydoc_mod),))
|
pager_mock.assert_not_called()
|
||||||
self.assertEqual('', output.getvalue())
|
|
||||||
self.assertEqual('', err.getvalue())
|
self.maxDiff = None
|
||||||
self.assertEqual(expected_text, result)
|
|
||||||
finally:
|
# test for "keywords"
|
||||||
pydoc.getpager = getpager_old
|
run_pydoc_for_request('keywords', 'Here is a list of the Python keywords.')
|
||||||
|
# test for "symbols"
|
||||||
|
run_pydoc_for_request('symbols', 'Here is a list of the punctuation symbols')
|
||||||
|
# test for "topics"
|
||||||
|
run_pydoc_for_request('topics', 'Here is a list of available topics.')
|
||||||
|
# test for "modules" skipped, see test_modules()
|
||||||
|
# test for symbol "%"
|
||||||
|
run_pydoc_for_request('%', 'The power operator')
|
||||||
|
# test for special True, False, None keywords
|
||||||
|
run_pydoc_for_request('True', 'class bool(int)')
|
||||||
|
run_pydoc_for_request('False', 'class bool(int)')
|
||||||
|
run_pydoc_for_request('None', 'class NoneType(object)')
|
||||||
|
# test for keyword "assert"
|
||||||
|
run_pydoc_for_request('assert', 'The "assert" statement')
|
||||||
|
# test for topic "TYPES"
|
||||||
|
run_pydoc_for_request('TYPES', 'The standard type hierarchy')
|
||||||
|
# test for "pydoc.Helper.help"
|
||||||
|
run_pydoc_for_request('pydoc.Helper.help', 'Help on function help in pydoc.Helper:')
|
||||||
|
# test for pydoc.Helper.help
|
||||||
|
run_pydoc_for_request(pydoc.Helper.help, 'Help on function help in module pydoc:')
|
||||||
|
# test for pydoc.Helper() instance skipped because it is always meant to be interactive
|
||||||
|
|
||||||
|
def test_showtopic(self):
|
||||||
|
with captured_stdout() as showtopic_io:
|
||||||
|
helper = pydoc.Helper()
|
||||||
|
helper.showtopic('with')
|
||||||
|
helptext = showtopic_io.getvalue()
|
||||||
|
self.assertIn('The "with" statement', helptext)
|
||||||
|
|
||||||
|
def test_fail_showtopic(self):
|
||||||
|
with captured_stdout() as showtopic_io:
|
||||||
|
helper = pydoc.Helper()
|
||||||
|
helper.showtopic('abd')
|
||||||
|
expected = "no documentation found for 'abd'"
|
||||||
|
self.assertEqual(expected, showtopic_io.getvalue().strip())
|
||||||
|
|
||||||
|
@unittest.mock.patch('pydoc.pager')
|
||||||
|
def test_fail_showtopic_output_redirect(self, pager_mock):
|
||||||
|
with StringIO() as buf:
|
||||||
|
helper = pydoc.Helper(output=buf)
|
||||||
|
helper.showtopic("abd")
|
||||||
|
expected = "no documentation found for 'abd'"
|
||||||
|
self.assertEqual(expected, buf.getvalue().strip())
|
||||||
|
|
||||||
|
pager_mock.assert_not_called()
|
||||||
|
|
||||||
|
@unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(),
|
||||||
|
'trace function introduces __locals__ unexpectedly')
|
||||||
|
@requires_docstrings
|
||||||
|
@unittest.mock.patch('pydoc.pager')
|
||||||
|
def test_showtopic_output_redirect(self, pager_mock):
|
||||||
|
# issue 940286, if output is set in Helper, then all output from
|
||||||
|
# Helper.showtopic should be redirected
|
||||||
|
self.maxDiff = None
|
||||||
|
|
||||||
|
with captured_output('stdout') as output, \
|
||||||
|
captured_output('stderr') as err, \
|
||||||
|
StringIO() as buf:
|
||||||
|
helper = pydoc.Helper(output=buf)
|
||||||
|
helper.showtopic('with')
|
||||||
|
result = buf.getvalue().strip()
|
||||||
|
self.assertEqual('', output.getvalue())
|
||||||
|
self.assertEqual('', err.getvalue())
|
||||||
|
self.assertIn('The "with" statement', result)
|
||||||
|
|
||||||
|
pager_mock.assert_not_called()
|
||||||
|
|
||||||
def test_lambda_with_return_annotation(self):
|
def test_lambda_with_return_annotation(self):
|
||||||
func = lambda a, b, c: 1
|
func = lambda a, b, c: 1
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
:meth:`!help` and :meth:`!showtopic` methods now respect a
|
||||||
|
configured *output* argument to :class:`!pydoc.Helper` and not use the
|
||||||
|
pager in such cases. Patch by Enrico Tröger.
|
Loading…
Reference in New Issue