]> git.lizzy.rs Git - plan9front.git/blob - sys/lib/python/code.py
dist/mkfile: run binds in subshell
[plan9front.git] / sys / lib / python / code.py
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                 except EOFError:
236                     self.write("\n")
237                     break
238                 else:
239                     more = self.push(line)
240             except KeyboardInterrupt:
241                 self.write("\nKeyboardInterrupt\n")
242                 self.resetbuffer()
243                 more = 0
244
245     def push(self, line):
246         """Push a line to the interpreter.
247
248         The line should not have a trailing newline; it may have
249         internal newlines.  The line is appended to a buffer and the
250         interpreter's runsource() method is called with the
251         concatenated contents of the buffer as source.  If this
252         indicates that the command was executed or invalid, the buffer
253         is reset; otherwise, the command is incomplete, and the buffer
254         is left as it was after the line was appended.  The return
255         value is 1 if more input is required, 0 if the line was dealt
256         with in some way (this is the same as runsource()).
257
258         """
259         self.buffer.append(line)
260         source = "\n".join(self.buffer)
261         more = self.runsource(source, self.filename)
262         if not more:
263             self.resetbuffer()
264         return more
265
266     def raw_input(self, prompt=""):
267         """Write a prompt and read a line.
268
269         The returned line does not include the trailing newline.
270         When the user enters the EOF key sequence, EOFError is raised.
271
272         The base implementation uses the built-in function
273         raw_input(); a subclass may replace this with a different
274         implementation.
275
276         """
277         return raw_input(prompt)
278
279
280 def interact(banner=None, readfunc=None, local=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
293     """
294     console = InteractiveConsole(local)
295     if readfunc is not None:
296         console.raw_input = readfunc
297     else:
298         try:
299             import readline
300         except ImportError:
301             pass
302     console.interact(banner)
303
304
305 if __name__ == '__main__':
306     import pdb
307     pdb.run("interact()\n")