Skip to content

Commit 207a962

Browse files
committed
Use configured output in pydoc instead of pager
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.
1 parent f79ffc8 commit 207a962

File tree

3 files changed

+106
-2
lines changed

3 files changed

+106
-2
lines changed

Lib/pydoc.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2034,7 +2034,7 @@ def help(self, request, is_cli=False):
20342034
elif request in self.symbols: self.showsymbol(request)
20352035
elif request in ['True', 'False', 'None']:
20362036
# special case these keywords since they are objects too
2037-
doc(eval(request), 'Help on %s:', is_cli=is_cli)
2037+
doc(eval(request), 'Help on %s:', output=self._output, is_cli=is_cli)
20382038
elif request in self.keywords: self.showtopic(request)
20392039
elif request in self.topics: self.showtopic(request)
20402040
elif request: doc(request, 'Help on %s:', output=self._output, is_cli=is_cli)
@@ -2127,7 +2127,11 @@ def showtopic(self, topic, more_xrefs=''):
21272127
text = 'Related help topics: ' + ', '.join(xrefs.split()) + '\n'
21282128
wrapped_text = textwrap.wrap(text, 72)
21292129
doc += '\n%s\n' % '\n'.join(wrapped_text)
2130-
pager(doc, f'Help on {topic!s}')
2130+
2131+
if self._output is None:
2132+
pager(doc, f'Help on {topic!s}')
2133+
else:
2134+
self.output.write(doc)
21312135

21322136
def _gettopic(self, topic, more_xrefs=''):
21332137
"""Return unbuffered tuple of (topic, xrefs).

Lib/test/test_pydoc/test_pydoc.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,7 @@ def test_fail_help_output_redirect(self):
662662
def test_help_output_redirect(self):
663663
# issue 940286, if output is set in Helper, then all output from
664664
# Helper.help should be redirected
665+
665666
getpager_old = pydoc.getpager
666667
getpager_new = lambda: (lambda x: x)
667668
self.maxDiff = None
@@ -693,6 +694,102 @@ def test_help_output_redirect(self):
693694
finally:
694695
pydoc.getpager = getpager_old
695696

697+
@unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(),
698+
'trace function introduces __locals__ unexpectedly')
699+
@requires_docstrings
700+
def test_help_output_redirect_various_requests(self):
701+
# issue 940286, if output is set in Helper, then all output from
702+
# Helper.help should be redirected
703+
704+
def run_pydoc_for_request(request, expected_text_part):
705+
"""Helper function to run pydoc with its output redirected"""
706+
with captured_output('stdout') as output, captured_output('stderr') as err:
707+
buf = StringIO()
708+
helper = pydoc.Helper(output=buf)
709+
helper.help(request)
710+
result = buf.getvalue().strip()
711+
self.assertEqual('', output.getvalue(), msg=f'failed on request "{request}"')
712+
self.assertEqual('', err.getvalue(), msg=f'failed on request "{request}"')
713+
self.assertIn(expected_text_part, result, msg=f'failed on request "{request}"')
714+
715+
getpager_old = pydoc.getpager
716+
getpager_new = lambda: (lambda x: x)
717+
self.maxDiff = None
718+
719+
pydoc.getpager = getpager_new
720+
try:
721+
# test for "keywords"
722+
run_pydoc_for_request('keywords', 'Here is a list of the Python keywords.')
723+
# test for "symbols"
724+
run_pydoc_for_request('symbols', 'Here is a list of the punctuation symbols')
725+
# test for "topics"
726+
run_pydoc_for_request('topics', 'Here is a list of available topics.')
727+
# test for "modules" skipped, see test_modules()
728+
# test for symbol "%"
729+
run_pydoc_for_request('%', 'The power operator')
730+
# test for special True, False, None keywords
731+
run_pydoc_for_request('True', 'class bool(int)')
732+
run_pydoc_for_request('False', 'class bool(int)')
733+
run_pydoc_for_request('None', 'class NoneType(object)')
734+
# test for keyword "assert"
735+
run_pydoc_for_request('assert', 'The "assert" statement')
736+
# test for topic "TYPES"
737+
run_pydoc_for_request('TYPES', 'The standard type hierarchy')
738+
# test for "pydoc.Helper.help"
739+
run_pydoc_for_request('pydoc.Helper.help', 'Help on function help in pydoc.Helper:')
740+
# test for pydoc.Helper.help
741+
run_pydoc_for_request(pydoc.Helper.help, 'Help on function help in module pydoc:')
742+
# test for pydoc.Helper() instance skipped because it is always meant to be interactive
743+
finally:
744+
pydoc.getpager = getpager_old
745+
746+
def test_showtopic(self):
747+
with captured_stdout() as showtopic_io:
748+
helper = pydoc.Helper()
749+
helper.showtopic('with')
750+
helptext = showtopic_io.getvalue()
751+
self.assertIn('The "with" statement', helptext)
752+
753+
def test_fail_showtopic(self):
754+
with captured_stdout() as showtopic_io:
755+
helper = pydoc.Helper()
756+
helper.showtopic('abd')
757+
expected = "no documentation found for 'abd'"
758+
self.assertEqual(expected, showtopic_io.getvalue().strip())
759+
760+
def test_fail_showtopic_output_redirect(self):
761+
with StringIO() as buf:
762+
helper = pydoc.Helper(output=buf)
763+
helper.showtopic("abd")
764+
expected = "no documentation found for 'abd'"
765+
self.assertEqual(expected, buf.getvalue().strip())
766+
767+
@unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(),
768+
'trace function introduces __locals__ unexpectedly')
769+
@requires_docstrings
770+
def test_showtopic_output_redirect(self):
771+
# issue 940286, if output is set in Helper, then all output from
772+
# Helper.showtopic should be redirected
773+
774+
getpager_old = pydoc.getpager
775+
getpager_new = lambda: (lambda x: x)
776+
self.maxDiff = None
777+
778+
buf = StringIO()
779+
helper = pydoc.Helper(output=buf)
780+
781+
pydoc.getpager = getpager_new
782+
try:
783+
with captured_output('stdout') as output, \
784+
captured_output('stderr') as err:
785+
helper.showtopic('with')
786+
result = buf.getvalue().strip()
787+
self.assertEqual('', output.getvalue())
788+
self.assertEqual('', err.getvalue())
789+
self.assertIn('The "with" statement', result)
790+
finally:
791+
pydoc.getpager = getpager_old
792+
696793
def test_lambda_with_return_annotation(self):
697794
func = lambda a, b, c: 1
698795
func.__annotations__ = {"return": int}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
:func:`pydoc.Helper.help` and :func:`pydoc.Helper.showtopic` now respect a
2+
configured *output* argument to :class:`pydoc.Helper` and not use the
3+
pager in such cases. Patch by Enrico Tröger.

0 commit comments

Comments
 (0)