Skip to content

Commit 6877ed3

Browse files
author
Steven D'Aprano
committed
Issue #27573 make the exit message configurable.
1 parent f4d28d4 commit 6877ed3

File tree

3 files changed

+46
-9
lines changed

3 files changed

+46
-9
lines changed

Doc/library/code.rst

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,19 @@ build applications which provide an interactive interpreter prompt.
3030
``sys.ps1`` and ``sys.ps2``, and input buffering.
3131

3232

33-
.. function:: interact(banner=None, readfunc=None, local=None)
33+
.. function:: interact(banner=None, readfunc=None, local=None, exitmsg=None)
3434

3535
Convenience function to run a read-eval-print loop. This creates a new
3636
instance of :class:`InteractiveConsole` and sets *readfunc* to be used as
3737
the :meth:`InteractiveConsole.raw_input` method, if provided. If *local* is
3838
provided, it is passed to the :class:`InteractiveConsole` constructor for
3939
use as the default namespace for the interpreter loop. The :meth:`interact`
40-
method of the instance is then run with *banner* passed as the banner to
41-
use, if provided. The console object is discarded after use.
40+
method of the instance is then run with *banner* and *exitmsg* passed as the
41+
banner and exit message to use, if provided. The console object is discarded
42+
after use.
43+
44+
.. versionchanged:: 3.6
45+
Added *exitmsg* parameter.
4246

4347

4448
.. function:: compile_command(source, filename="<input>", symbol="single")
@@ -136,19 +140,23 @@ The :class:`InteractiveConsole` class is a subclass of
136140
interpreter objects as well as the following additions.
137141

138142

139-
.. method:: InteractiveConsole.interact(banner=None)
143+
.. method:: InteractiveConsole.interact(banner=None, exitmsg=None)
140144

141145
Closely emulate the interactive Python console. The optional *banner* argument
142146
specify the banner to print before the first interaction; by default it prints a
143147
banner similar to the one printed by the standard Python interpreter, followed
144148
by the class name of the console object in parentheses (so as not to confuse
145149
this with the real interpreter -- since it's so close!).
146150

151+
The optional *exitmsg* argument specifies an exit message printed when exiting.
152+
Pass the empty string to suppress the exit message. If *exitmsg* is not given or
153+
None, a default message is printed.
154+
147155
.. versionchanged:: 3.4
148156
To suppress printing any banner, pass an empty string.
149157

150158
.. versionchanged:: 3.6
151-
Now prints a brief message when exiting.
159+
Print an exit message when exiting.
152160

153161

154162
.. method:: InteractiveConsole.push(line)

Lib/code.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ def resetbuffer(self):
186186
"""Reset the input buffer."""
187187
self.buffer = []
188188

189-
def interact(self, banner=None):
189+
def interact(self, banner=None, exitmsg=None):
190190
"""Closely emulate the interactive Python console.
191191
192192
The optional banner argument specifies the banner to print
@@ -196,6 +196,11 @@ def interact(self, banner=None):
196196
to confuse this with the real interpreter -- since it's so
197197
close!).
198198
199+
The optional exitmsg argument specifies the exit message
200+
printed when exiting. Pass the empty string to suppress
201+
printing an exit message. If exitmsg is not given or None,
202+
a default message is printed.
203+
199204
"""
200205
try:
201206
sys.ps1
@@ -230,7 +235,10 @@ def interact(self, banner=None):
230235
self.write("\nKeyboardInterrupt\n")
231236
self.resetbuffer()
232237
more = 0
233-
self.write('now exiting %s...\n' % self.__class__.__name__)
238+
if exitmsg is None:
239+
self.write('now exiting %s...\n' % self.__class__.__name__)
240+
elif exitmsg != '':
241+
self.write('%s\n' % exitmsg)
234242

235243
def push(self, line):
236244
"""Push a line to the interpreter.
@@ -268,7 +276,7 @@ def raw_input(self, prompt=""):
268276

269277

270278

271-
def interact(banner=None, readfunc=None, local=None):
279+
def interact(banner=None, readfunc=None, local=None, exitmsg=None):
272280
"""Closely emulate the interactive Python interpreter.
273281
274282
This is a backwards compatible interface to the InteractiveConsole
@@ -280,6 +288,7 @@ def interact(banner=None, readfunc=None, local=None):
280288
banner -- passed to InteractiveConsole.interact()
281289
readfunc -- if not None, replaces InteractiveConsole.raw_input()
282290
local -- passed to InteractiveInterpreter.__init__()
291+
exitmsg -- passed to InteractiveConsole.interact()
283292
284293
"""
285294
console = InteractiveConsole(local)
@@ -290,7 +299,7 @@ def interact(banner=None, readfunc=None, local=None):
290299
import readline
291300
except ImportError:
292301
pass
293-
console.interact(banner)
302+
console.interact(banner, exitmsg)
294303

295304

296305
if __name__ == "__main__":

Lib/test/test_code_module.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,33 @@ def test_banner(self):
8080
self.assertEqual(len(self.stderr.method_calls), 2)
8181

8282
def test_exit_msg(self):
83+
# default exit message
8384
self.infunc.side_effect = EOFError('Finished')
8485
self.console.interact(banner='')
8586
self.assertEqual(len(self.stderr.method_calls), 2)
8687
err_msg = self.stderr.method_calls[1]
8788
expected = 'now exiting InteractiveConsole...\n'
8889
self.assertEqual(err_msg, ['write', (expected,), {}])
8990

91+
# no exit message
92+
self.stderr.reset_mock()
93+
self.infunc.side_effect = EOFError('Finished')
94+
self.console.interact(banner='', exitmsg='')
95+
self.assertEqual(len(self.stderr.method_calls), 1)
96+
97+
# custom exit message
98+
self.stderr.reset_mock()
99+
message = (
100+
'bye! \N{GREEK SMALL LETTER ZETA}\N{CYRILLIC SMALL LETTER ZHE}'
101+
)
102+
self.infunc.side_effect = EOFError('Finished')
103+
self.console.interact(banner='', exitmsg=message)
104+
self.assertEqual(len(self.stderr.method_calls), 2)
105+
err_msg = self.stderr.method_calls[1]
106+
expected = message + '\n'
107+
self.assertEqual(err_msg, ['write', (expected,), {}])
108+
109+
90110
def test_cause_tb(self):
91111
self.infunc.side_effect = ["raise ValueError('') from AttributeError",
92112
EOFError('Finished')]

0 commit comments

Comments
 (0)