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