Issue #27573 make the exit message configurable.

This commit is contained in:
Steven D'Aprano 2016-08-24 01:42:15 +10:00
parent f4d28d4385
commit 6877ed3560
3 changed files with 46 additions and 9 deletions

View File

@ -30,15 +30,19 @@ build applications which provide an interactive interpreter prompt.
``sys.ps1`` and ``sys.ps2``, and input buffering.
.. function:: interact(banner=None, readfunc=None, local=None)
.. function:: interact(banner=None, readfunc=None, local=None, exitmsg=None)
Convenience function to run a read-eval-print loop. This creates a new
instance of :class:`InteractiveConsole` and sets *readfunc* to be used as
the :meth:`InteractiveConsole.raw_input` method, if provided. If *local* is
provided, it is passed to the :class:`InteractiveConsole` constructor for
use as the default namespace for the interpreter loop. The :meth:`interact`
method of the instance is then run with *banner* passed as the banner to
use, if provided. The console object is discarded after use.
method of the instance is then run with *banner* and *exitmsg* passed as the
banner and exit message to use, if provided. The console object is discarded
after use.
.. versionchanged:: 3.6
Added *exitmsg* parameter.
.. function:: compile_command(source, filename="<input>", symbol="single")
@ -136,7 +140,7 @@ The :class:`InteractiveConsole` class is a subclass of
interpreter objects as well as the following additions.
.. method:: InteractiveConsole.interact(banner=None)
.. method:: InteractiveConsole.interact(banner=None, exitmsg=None)
Closely emulate the interactive Python console. The optional *banner* argument
specify the banner to print before the first interaction; by default it prints a
@ -144,11 +148,15 @@ interpreter objects as well as the following additions.
by the class name of the console object in parentheses (so as not to confuse
this with the real interpreter -- since it's so close!).
The optional *exitmsg* argument specifies an exit message printed when exiting.
Pass the empty string to suppress the exit message. If *exitmsg* is not given or
None, a default message is printed.
.. versionchanged:: 3.4
To suppress printing any banner, pass an empty string.
.. versionchanged:: 3.6
Now prints a brief message when exiting.
Print an exit message when exiting.
.. method:: InteractiveConsole.push(line)

View File

@ -186,7 +186,7 @@ class InteractiveConsole(InteractiveInterpreter):
"""Reset the input buffer."""
self.buffer = []
def interact(self, banner=None):
def interact(self, banner=None, exitmsg=None):
"""Closely emulate the interactive Python console.
The optional banner argument specifies the banner to print
@ -196,6 +196,11 @@ class InteractiveConsole(InteractiveInterpreter):
to confuse this with the real interpreter -- since it's so
close!).
The optional exitmsg argument specifies the exit message
printed when exiting. Pass the empty string to suppress
printing an exit message. If exitmsg is not given or None,
a default message is printed.
"""
try:
sys.ps1
@ -230,7 +235,10 @@ class InteractiveConsole(InteractiveInterpreter):
self.write("\nKeyboardInterrupt\n")
self.resetbuffer()
more = 0
self.write('now exiting %s...\n' % self.__class__.__name__)
if exitmsg is None:
self.write('now exiting %s...\n' % self.__class__.__name__)
elif exitmsg != '':
self.write('%s\n' % exitmsg)
def push(self, line):
"""Push a line to the interpreter.
@ -268,7 +276,7 @@ class InteractiveConsole(InteractiveInterpreter):
def interact(banner=None, readfunc=None, local=None):
def interact(banner=None, readfunc=None, local=None, exitmsg=None):
"""Closely emulate the interactive Python interpreter.
This is a backwards compatible interface to the InteractiveConsole
@ -280,6 +288,7 @@ def interact(banner=None, readfunc=None, local=None):
banner -- passed to InteractiveConsole.interact()
readfunc -- if not None, replaces InteractiveConsole.raw_input()
local -- passed to InteractiveInterpreter.__init__()
exitmsg -- passed to InteractiveConsole.interact()
"""
console = InteractiveConsole(local)
@ -290,7 +299,7 @@ def interact(banner=None, readfunc=None, local=None):
import readline
except ImportError:
pass
console.interact(banner)
console.interact(banner, exitmsg)
if __name__ == "__main__":

View File

@ -80,6 +80,7 @@ class TestInteractiveConsole(unittest.TestCase):
self.assertEqual(len(self.stderr.method_calls), 2)
def test_exit_msg(self):
# default exit message
self.infunc.side_effect = EOFError('Finished')
self.console.interact(banner='')
self.assertEqual(len(self.stderr.method_calls), 2)
@ -87,6 +88,25 @@ class TestInteractiveConsole(unittest.TestCase):
expected = 'now exiting InteractiveConsole...\n'
self.assertEqual(err_msg, ['write', (expected,), {}])
# no exit message
self.stderr.reset_mock()
self.infunc.side_effect = EOFError('Finished')
self.console.interact(banner='', exitmsg='')
self.assertEqual(len(self.stderr.method_calls), 1)
# custom exit message
self.stderr.reset_mock()
message = (
'bye! \N{GREEK SMALL LETTER ZETA}\N{CYRILLIC SMALL LETTER ZHE}'
)
self.infunc.side_effect = EOFError('Finished')
self.console.interact(banner='', exitmsg=message)
self.assertEqual(len(self.stderr.method_calls), 2)
err_msg = self.stderr.method_calls[1]
expected = message + '\n'
self.assertEqual(err_msg, ['write', (expected,), {}])
def test_cause_tb(self):
self.infunc.side_effect = ["raise ValueError('') from AttributeError",
EOFError('Finished')]