mirror of
				https://github.com/python/cpython.git
				synced 2025-11-03 23:21:29 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			198 lines
		
	
	
	
		
			6.4 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			198 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
 |