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