Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
allendowney
GitHub Repository: allendowney/cpython
Path: blob/main/Lib/code.py
12 views
1
"""Utilities needed to emulate Python's interactive interpreter.
2
3
"""
4
5
# Inspired by similar code by Jeff Epler and Fredrik Lundh.
6
7
8
import sys
9
import traceback
10
from codeop import CommandCompiler, compile_command
11
12
__all__ = ["InteractiveInterpreter", "InteractiveConsole", "interact",
13
"compile_command"]
14
15
class InteractiveInterpreter:
16
"""Base class for InteractiveConsole.
17
18
This class deals with parsing and interpreter state (the user's
19
namespace); it doesn't deal with input buffering or prompting or
20
input file naming (the filename is always passed in explicitly).
21
22
"""
23
24
def __init__(self, locals=None):
25
"""Constructor.
26
27
The optional 'locals' argument specifies the dictionary in
28
which code will be executed; it defaults to a newly created
29
dictionary with key "__name__" set to "__console__" and key
30
"__doc__" set to None.
31
32
"""
33
if locals is None:
34
locals = {"__name__": "__console__", "__doc__": None}
35
self.locals = locals
36
self.compile = CommandCompiler()
37
38
def runsource(self, source, filename="<input>", symbol="single"):
39
"""Compile and run some source in the interpreter.
40
41
Arguments are as for compile_command().
42
43
One of several things can happen:
44
45
1) The input is incorrect; compile_command() raised an
46
exception (SyntaxError or OverflowError). A syntax traceback
47
will be printed by calling the showsyntaxerror() method.
48
49
2) The input is incomplete, and more input is required;
50
compile_command() returned None. Nothing happens.
51
52
3) The input is complete; compile_command() returned a code
53
object. The code is executed by calling self.runcode() (which
54
also handles run-time exceptions, except for SystemExit).
55
56
The return value is True in case 2, False in the other cases (unless
57
an exception is raised). The return value can be used to
58
decide whether to use sys.ps1 or sys.ps2 to prompt the next
59
line.
60
61
"""
62
try:
63
code = self.compile(source, filename, symbol)
64
except (OverflowError, SyntaxError, ValueError):
65
# Case 1
66
self.showsyntaxerror(filename)
67
return False
68
69
if code is None:
70
# Case 2
71
return True
72
73
# Case 3
74
self.runcode(code)
75
return False
76
77
def runcode(self, code):
78
"""Execute a code object.
79
80
When an exception occurs, self.showtraceback() is called to
81
display a traceback. All exceptions are caught except
82
SystemExit, which is reraised.
83
84
A note about KeyboardInterrupt: this exception may occur
85
elsewhere in this code, and may not always be caught. The
86
caller should be prepared to deal with it.
87
88
"""
89
try:
90
exec(code, self.locals)
91
except SystemExit:
92
raise
93
except:
94
self.showtraceback()
95
96
def showsyntaxerror(self, filename=None):
97
"""Display the syntax error that just occurred.
98
99
This doesn't display a stack trace because there isn't one.
100
101
If a filename is given, it is stuffed in the exception instead
102
of what was there before (because Python's parser always uses
103
"<string>" when reading from a string).
104
105
The output is written by self.write(), below.
106
107
"""
108
type, value, tb = sys.exc_info()
109
sys.last_exc = value
110
sys.last_type = type
111
sys.last_value = value
112
sys.last_traceback = tb
113
if filename and type is SyntaxError:
114
# Work hard to stuff the correct filename in the exception
115
try:
116
msg, (dummy_filename, lineno, offset, line) = value.args
117
except ValueError:
118
# Not the format we expect; leave it alone
119
pass
120
else:
121
# Stuff in the right filename
122
value = SyntaxError(msg, (filename, lineno, offset, line))
123
sys.last_exc = sys.last_value = value
124
if sys.excepthook is sys.__excepthook__:
125
lines = traceback.format_exception_only(type, value)
126
self.write(''.join(lines))
127
else:
128
# If someone has set sys.excepthook, we let that take precedence
129
# over self.write
130
sys.excepthook(type, value, tb)
131
132
def showtraceback(self):
133
"""Display the exception that just occurred.
134
135
We remove the first stack item because it is our own code.
136
137
The output is written by self.write(), below.
138
139
"""
140
sys.last_type, sys.last_value, last_tb = ei = sys.exc_info()
141
sys.last_traceback = last_tb
142
sys.last_exc = ei[1]
143
try:
144
lines = traceback.format_exception(ei[0], ei[1], last_tb.tb_next)
145
if sys.excepthook is sys.__excepthook__:
146
self.write(''.join(lines))
147
else:
148
# If someone has set sys.excepthook, we let that take precedence
149
# over self.write
150
sys.excepthook(ei[0], ei[1], last_tb)
151
finally:
152
last_tb = ei = None
153
154
def write(self, data):
155
"""Write a string.
156
157
The base implementation writes to sys.stderr; a subclass may
158
replace this with a different implementation.
159
160
"""
161
sys.stderr.write(data)
162
163
164
class InteractiveConsole(InteractiveInterpreter):
165
"""Closely emulate the behavior of the interactive Python interpreter.
166
167
This class builds on InteractiveInterpreter and adds prompting
168
using the familiar sys.ps1 and sys.ps2, and input buffering.
169
170
"""
171
172
def __init__(self, locals=None, filename="<console>"):
173
"""Constructor.
174
175
The optional locals argument will be passed to the
176
InteractiveInterpreter base class.
177
178
The optional filename argument should specify the (file)name
179
of the input stream; it will show up in tracebacks.
180
181
"""
182
InteractiveInterpreter.__init__(self, locals)
183
self.filename = filename
184
self.resetbuffer()
185
186
def resetbuffer(self):
187
"""Reset the input buffer."""
188
self.buffer = []
189
190
def interact(self, banner=None, exitmsg=None):
191
"""Closely emulate the interactive Python console.
192
193
The optional banner argument specifies the banner to print
194
before the first interaction; by default it prints a banner
195
similar to the one printed by the real Python interpreter,
196
followed by the current class name in parentheses (so as not
197
to confuse this with the real interpreter -- since it's so
198
close!).
199
200
The optional exitmsg argument specifies the exit message
201
printed when exiting. Pass the empty string to suppress
202
printing an exit message. If exitmsg is not given or None,
203
a default message is printed.
204
205
"""
206
try:
207
sys.ps1
208
except AttributeError:
209
sys.ps1 = ">>> "
210
try:
211
sys.ps2
212
except AttributeError:
213
sys.ps2 = "... "
214
cprt = 'Type "help", "copyright", "credits" or "license" for more information.'
215
if banner is None:
216
self.write("Python %s on %s\n%s\n(%s)\n" %
217
(sys.version, sys.platform, cprt,
218
self.__class__.__name__))
219
elif banner:
220
self.write("%s\n" % str(banner))
221
more = 0
222
while 1:
223
try:
224
if more:
225
prompt = sys.ps2
226
else:
227
prompt = sys.ps1
228
try:
229
line = self.raw_input(prompt)
230
except EOFError:
231
self.write("\n")
232
break
233
else:
234
more = self.push(line)
235
except KeyboardInterrupt:
236
self.write("\nKeyboardInterrupt\n")
237
self.resetbuffer()
238
more = 0
239
if exitmsg is None:
240
self.write('now exiting %s...\n' % self.__class__.__name__)
241
elif exitmsg != '':
242
self.write('%s\n' % exitmsg)
243
244
def push(self, line):
245
"""Push a line to the interpreter.
246
247
The line should not have a trailing newline; it may have
248
internal newlines. The line is appended to a buffer and the
249
interpreter's runsource() method is called with the
250
concatenated contents of the buffer as source. If this
251
indicates that the command was executed or invalid, the buffer
252
is reset; otherwise, the command is incomplete, and the buffer
253
is left as it was after the line was appended. The return
254
value is 1 if more input is required, 0 if the line was dealt
255
with in some way (this is the same as runsource()).
256
257
"""
258
self.buffer.append(line)
259
source = "\n".join(self.buffer)
260
more = self.runsource(source, self.filename)
261
if not more:
262
self.resetbuffer()
263
return more
264
265
def raw_input(self, prompt=""):
266
"""Write a prompt and read a line.
267
268
The returned line does not include the trailing newline.
269
When the user enters the EOF key sequence, EOFError is raised.
270
271
The base implementation uses the built-in function
272
input(); a subclass may replace this with a different
273
implementation.
274
275
"""
276
return input(prompt)
277
278
279
280
def interact(banner=None, readfunc=None, local=None, exitmsg=None):
281
"""Closely emulate the interactive Python interpreter.
282
283
This is a backwards compatible interface to the InteractiveConsole
284
class. When readfunc is not specified, it attempts to import the
285
readline module to enable GNU readline if it is available.
286
287
Arguments (all optional, all default to None):
288
289
banner -- passed to InteractiveConsole.interact()
290
readfunc -- if not None, replaces InteractiveConsole.raw_input()
291
local -- passed to InteractiveInterpreter.__init__()
292
exitmsg -- passed to InteractiveConsole.interact()
293
294
"""
295
console = InteractiveConsole(local)
296
if readfunc is not None:
297
console.raw_input = readfunc
298
else:
299
try:
300
import readline
301
except ImportError:
302
pass
303
console.interact(banner, exitmsg)
304
305
306
if __name__ == "__main__":
307
import argparse
308
309
parser = argparse.ArgumentParser()
310
parser.add_argument('-q', action='store_true',
311
help="don't print version and copyright messages")
312
args = parser.parse_args()
313
if args.q or sys.flags.quiet:
314
banner = ''
315
else:
316
banner = None
317
interact(banner)
318
319