mirror of
				https://github.com/python/cpython.git
				synced 2025-10-28 12:15:13 +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 |