diff options
author | David Scherer <dscherer@cmu.edu> | 2000-08-15 01:13:23 (GMT) |
---|---|---|
committer | David Scherer <dscherer@cmu.edu> | 2000-08-15 01:13:23 (GMT) |
commit | 7aced17437a6b05bc4b0b5ff93aa6a5d3a374d68 (patch) | |
tree | ce0576a16111fd86ac5f56ff4ec1500f29c4f8db /Lib/idlelib/ExecBinding.py | |
parent | 33a6da9971a923ceaaee1406d0feaa64b8d1759a (diff) | |
download | cpython-7aced17437a6b05bc4b0b5ff93aa6a5d3a374d68.zip cpython-7aced17437a6b05bc4b0b5ff93aa6a5d3a374d68.tar.gz cpython-7aced17437a6b05bc4b0b5ff93aa6a5d3a374d68.tar.bz2 |
Initial revision
Diffstat (limited to 'Lib/idlelib/ExecBinding.py')
-rw-r--r-- | Lib/idlelib/ExecBinding.py | 198 |
1 files changed, 198 insertions, 0 deletions
diff --git a/Lib/idlelib/ExecBinding.py b/Lib/idlelib/ExecBinding.py new file mode 100644 index 0000000..67b0822 --- /dev/null +++ b/Lib/idlelib/ExecBinding.py @@ -0,0 +1,198 @@ +"""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: + keydefs = { + '<<run-complete-script>>': ['<F5>'], + '<<stop-execution>>': ['<Cancel>'], #'<Control-c>' + } + + 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 |