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.
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)
41 return a
.connect(client
, addr
)
43 return loader_connect(client
,addr
)
45 protocol
.publish('ExecBinding', loader_connect
)
49 '<<run-complete-script>>': ['<F5>'],
50 '<<stop-execution>>': ['<Cancel>'], #'<Control-c>'
55 ('Run program', '<<run-complete-script>>'),
56 ('Stop program', '<<stop-execution>>'),
63 def __init__(self
, editwin
):
64 self
.editwin
= editwin
68 if not hasattr(editwin
, 'source_window'):
70 self
.output
= OutputWindow
.OnDemandOutputWindow(editwin
.flist
)
71 self
.output
.close_hook
= self
.stopProgram
72 self
.output
.source_window
= editwin
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
84 def stop_execution_event(self
, event
):
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
)
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
106 if self
.client
: return None
109 client
.set_close_hook( self
.connect_lost
)
111 title
= self
.editwin
.short_title()
113 self
.output
.set_title(title
+ " Output")
115 self
.output
.set_title("Output")
116 self
.output
.write('\n',"stderr")
117 self
.output
.scroll_clear()
121 def connect_lost(self
):
122 # Called by the client's close hook when the loader closes its
125 # We print a disconnect message only if the output window is already
127 if self
.output
.owin
and self
.output
.owin
.text
:
128 self
.output
.owin
.interrupt()
129 self
.output
.write("\nProgram disconnected.\n","stderr")
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.
143 return self
.commands
.pop(0)
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')
160 self
.write(i
, 'stderr')
165 def write(self
, text
, tag
):
166 self
.output
.write(text
,tag
)
169 return self
.output
.readline()
171 def stopProgram(self
):
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():
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
):
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():
198 return self
.editwin
.io
.filename