mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 07:31:38 +00:00 
			
		
		
		
	
		
			
	
	
		
			199 lines
		
	
	
	
		
			6.4 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			199 lines
		
	
	
	
		
			6.4 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| 
								 | 
							
								"""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
							 |