Update output generated by test_scope
[python/dscho.git] / Lib / idlelib / ExecBinding.py
blob67b08220d9dffbff8a0cac1740f81bf4b133a32c
1 """Extension to execute a script in a separate process
3 David Scherer <dscherer@cmu.edu>
5 The ExecBinding module, a replacement for ScriptBinding, executes
6 programs in a separate process. Unlike previous versions, this version
7 communicates with the user process via an RPC protocol (see the 'protocol'
8 module). The user program is loaded by the 'loader' and 'Remote'
9 modules. Its standard output and input are directed back to the
10 ExecBinding class through the RPC mechanism and implemented here.
12 A "stop program" command is provided and bound to control-break. Closing
13 the output window also stops the running program.
14 """
16 import sys
17 import os
18 import imp
19 import OutputWindow
20 import protocol
21 import spawn
22 import traceback
23 import tempfile
25 # Find Python and the loader. This should be done as early in execution
26 # as possible, because if the current directory or sys.path is changed
27 # it may no longer be possible to get correct paths for these things.
29 pyth_exe = spawn.hardpath( sys.executable )
30 load_py = spawn.hardpath( imp.find_module("loader")[1] )
32 # The following mechanism matches loaders up with ExecBindings that are
33 # trying to load something.
35 waiting_for_loader = []
37 def loader_connect(client, addr):
38 if waiting_for_loader:
39 a = waiting_for_loader.pop(0)
40 try:
41 return a.connect(client, addr)
42 except:
43 return loader_connect(client,addr)
45 protocol.publish('ExecBinding', loader_connect)
47 class ExecBinding:
48 keydefs = {
49 '<<run-complete-script>>': ['<F5>'],
50 '<<stop-execution>>': ['<Cancel>'], #'<Control-c>'
53 menudefs = [
54 ('run', [None,
55 ('Run program', '<<run-complete-script>>'),
56 ('Stop program', '<<stop-execution>>'),
61 delegate = 1
63 def __init__(self, editwin):
64 self.editwin = editwin
65 self.client = None
66 self.temp = []
68 if not hasattr(editwin, 'source_window'):
69 self.delegate = 0
70 self.output = OutputWindow.OnDemandOutputWindow(editwin.flist)
71 self.output.close_hook = self.stopProgram
72 self.output.source_window = editwin
73 else:
74 if (self.editwin.source_window and
75 self.editwin.source_window.extensions.has_key('ExecBinding') and
76 not self.editwin.source_window.extensions['ExecBinding'].delegate):
77 delegate = self.editwin.source_window.extensions['ExecBinding']
78 self.run_complete_script_event = delegate.run_complete_script_event
79 self.stop_execution_event = delegate.stop_execution_event
81 def __del__(self):
82 self.stopProgram()
84 def stop_execution_event(self, event):
85 if self.client:
86 self.stopProgram()
87 self.write('\nProgram stopped.\n','stderr')
89 def run_complete_script_event(self, event):
90 filename = self.getfilename()
91 if not filename: return
92 filename = os.path.abspath(filename)
94 self.stopProgram()
96 self.commands = [ ('run', filename) ]
97 waiting_for_loader.append(self)
98 spawn.spawn( pyth_exe, load_py )
100 def connect(self, client, addr):
101 # Called by loader_connect() above. It is remotely possible that
102 # we get connected to two loaders if the user is running the
103 # program repeatedly in a short span of time. In this case, we
104 # simply return None, refusing to connect and letting the redundant
105 # loader die.
106 if self.client: return None
108 self.client = client
109 client.set_close_hook( self.connect_lost )
111 title = self.editwin.short_title()
112 if title:
113 self.output.set_title(title + " Output")
114 else:
115 self.output.set_title("Output")
116 self.output.write('\n',"stderr")
117 self.output.scroll_clear()
119 return self
121 def connect_lost(self):
122 # Called by the client's close hook when the loader closes its
123 # socket.
125 # We print a disconnect message only if the output window is already
126 # open.
127 if self.output.owin and self.output.owin.text:
128 self.output.owin.interrupt()
129 self.output.write("\nProgram disconnected.\n","stderr")
131 for t in self.temp:
132 try:
133 os.remove(t)
134 except:
135 pass
136 self.temp = []
137 self.client = None
139 def get_command(self):
140 # Called by Remote to find out what it should be executing.
141 # Later this will be used to implement debugging, interactivity, etc.
142 if self.commands:
143 return self.commands.pop(0)
144 return ('finish',)
146 def program_exception(self, type, value, tb, first, last):
147 if type == SystemExit: return 0
149 for i in range(len(tb)):
150 filename, lineno, name, line = tb[i]
151 if filename in self.temp:
152 filename = 'Untitled'
153 tb[i] = filename, lineno, name, line
155 list = traceback.format_list(tb[first:last])
156 exc = traceback.format_exception_only( type, value )
158 self.write('Traceback (innermost last)\n', 'stderr')
159 for i in (list+exc):
160 self.write(i, 'stderr')
162 self.commands = []
163 return 1
165 def write(self, text, tag):
166 self.output.write(text,tag)
168 def readline(self):
169 return self.output.readline()
171 def stopProgram(self):
172 if self.client:
173 self.client.close()
174 self.client = None
176 def getfilename(self):
177 # Save all files which have been named, because they might be modules
178 for edit in self.editwin.flist.inversedict.keys():
179 if edit.io and edit.io.filename and not edit.get_saved():
180 edit.io.save(None)
182 # Experimental: execute unnamed buffer
183 if not self.editwin.io.filename:
184 filename = os.path.normcase(os.path.abspath(tempfile.mktemp()))
185 self.temp.append(filename)
186 if self.editwin.io.writefile(filename):
187 return filename
189 # If the file isn't save, we save it. If it doesn't have a filename,
190 # the user will be prompted.
191 if self.editwin.io and not self.editwin.get_saved():
192 self.editwin.io.save(None)
194 # If the file *still* isn't saved, we give up.
195 if not self.editwin.get_saved():
196 return
198 return self.editwin.io.filename