mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 02:15:10 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			449 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			449 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
| #! /usr/bin/env python
 | |
| 
 | |
| # A STDWIN-based front end for the Python interpreter.
 | |
| #
 | |
| # This is useful if you want to avoid console I/O and instead
 | |
| # use text windows to issue commands to the interpreter.
 | |
| #
 | |
| # It supports multiple interpreter windows, each with its own context.
 | |
| #
 | |
| # BUGS AND CAVEATS:
 | |
| #
 | |
| # This was written long ago as a demonstration, and slightly hacked to
 | |
| # keep it up-to-date, but never as an industry-strength alternative
 | |
| # interface to Python.  It should be rewritten using more classes, and
 | |
| # merged with something like wdb.
 | |
| #
 | |
| # Although this supports multiple windows, the whole application
 | |
| # is deaf and dumb when a command is running in one window.
 | |
| #
 | |
| # Interrupt is (ab)used to signal EOF on input requests.
 | |
| #
 | |
| # On UNIX (using X11), interrupts typed in the window will not be
 | |
| # seen until the next input or output operation.  When you are stuck
 | |
| # in an infinite loop, try typing ^C in the shell window where you
 | |
| # started this interpreter.  (On the Mac, interrupts work normally.)
 | |
| 
 | |
| 
 | |
| import sys
 | |
| import stdwin
 | |
| from stdwinevents import *
 | |
| import rand
 | |
| import mainloop
 | |
| import os
 | |
| 
 | |
| 
 | |
| # Stack of windows waiting for [raw_]input().
 | |
| # Element [0] is the top.
 | |
| # If there are multiple windows waiting for input, only the
 | |
| # one on top of the stack can accept input, because the way
 | |
| # raw_input() is implemented (using recursive mainloop() calls).
 | |
| #
 | |
| inputwindows = []
 | |
| 
 | |
| 
 | |
| # Exception raised when input is available
 | |
| #
 | |
| InputAvailable = 'input available for raw_input (not an error)'
 | |
| 
 | |
| 
 | |
| # Main program -- create the window and call the mainloop
 | |
| #
 | |
| def main():
 | |
| 	# Hack so 'import python' won't load another copy
 | |
| 	# of this if we were loaded though 'python python.py'.
 | |
| 	# (Should really look at sys.argv[0]...)
 | |
| 	if 'inputwindows' in dir(sys.modules['__main__']) and \
 | |
| 			sys.modules['__main__'].inputwindows is inputwindows:
 | |
| 		sys.modules['python'] = sys.modules['__main__']
 | |
| 	#
 | |
| 	win = makewindow()
 | |
| 	mainloop.mainloop()
 | |
| 
 | |
| 
 | |
| # Create a new window
 | |
| #
 | |
| def makewindow():
 | |
| 	# stdwin.setdefscrollbars(0, 1) # Not in Python 0.9.1
 | |
| 	# stdwin.setfont('monaco') # Not on UNIX! and not Python 0.9.1
 | |
| 	# width, height = stdwin.textwidth('in')*40, stdwin.lineheight()*24
 | |
| 	# stdwin.setdefwinsize(width, height)
 | |
| 	win = stdwin.open('Python interpreter ready')
 | |
| 	win.editor = win.textcreate((0,0), win.getwinsize())
 | |
| 	win.globals = {}		# Dictionary for user's globals
 | |
| 	win.command = ''		# Partially read command
 | |
| 	win.busy = 0			# Ready to accept a command
 | |
| 	win.auto = 1			# [CR] executes command
 | |
| 	win.insertOutput = 1		# Insert output at focus
 | |
| 	win.insertError = 1		# Insert error output at focus
 | |
| 	win.setwincursor('ibeam')
 | |
| 	win.filename = ''		# Empty if no file for this window
 | |
| 	makefilemenu(win)
 | |
| 	makeeditmenu(win)
 | |
| 	win.dispatch = pdispatch	# Event dispatch function
 | |
| 	mainloop.register(win)
 | |
| 	return win
 | |
| 
 | |
| 
 | |
| # Make a 'File' menu
 | |
| #
 | |
| def makefilemenu(win):
 | |
| 	win.filemenu = mp = win.menucreate('File')
 | |
| 	mp.callback = []
 | |
| 	additem(mp, 'New', 'N', do_new)
 | |
| 	additem(mp, 'Open...', 'O', do_open)
 | |
| 	additem(mp, '', '',  None)
 | |
| 	additem(mp, 'Close', 'W', do_close)
 | |
| 	additem(mp, 'Save', 'S', do_save)
 | |
| 	additem(mp, 'Save as...', '', do_saveas)
 | |
| 	additem(mp, '', '', None)
 | |
| 	additem(mp, 'Quit', 'Q', do_quit)
 | |
| 
 | |
| 
 | |
| # Make an 'Edit' menu
 | |
| #
 | |
| def makeeditmenu(win):
 | |
| 	win.editmenu = mp = win.menucreate('Edit')
 | |
| 	mp.callback = []
 | |
| 	additem(mp, 'Cut', 'X', do_cut)
 | |
| 	additem(mp, 'Copy', 'C', do_copy)
 | |
| 	additem(mp, 'Paste', 'V', do_paste)
 | |
| 	additem(mp, 'Clear', '',  do_clear)
 | |
| 	additem(mp, '', '', None)
 | |
| 	win.iauto = len(mp.callback)
 | |
| 	additem(mp, 'Autoexecute', '', do_auto)
 | |
| 	mp.check(win.iauto, win.auto)
 | |
| 	win.insertOutputNum = len(mp.callback)
 | |
| 	additem(mp, 'Insert Output', '', do_insertOutputOption)
 | |
| 	win.insertErrorNum = len(mp.callback)
 | |
| 	additem(mp, 'Insert Error', '', do_insertErrorOption)
 | |
| 	additem(mp, 'Exec', '\r', do_exec)
 | |
| 
 | |
| 
 | |
| # Helper to add a menu item and callback function
 | |
| #
 | |
| def additem(mp, text, shortcut, handler):
 | |
| 	if shortcut:
 | |
| 		mp.additem(text, shortcut)
 | |
| 	else:
 | |
| 		mp.additem(text)
 | |
| 	mp.callback.append(handler)
 | |
| 
 | |
| 
 | |
| # Dispatch a single event to the interpreter.
 | |
| # Resize events cause a resize of the editor.
 | |
| # Some events are treated specially.
 | |
| # Most other events are passed directly to the editor.
 | |
| #
 | |
| def pdispatch(event):
 | |
| 	type, win, detail = event
 | |
| 	if not win:
 | |
| 		win = stdwin.getactive()
 | |
| 		if not win: return
 | |
| 	if type == WE_CLOSE:
 | |
| 		do_close(win)
 | |
| 		return
 | |
| 	elif type == WE_SIZE:
 | |
| 		win.editor.move((0, 0), win.getwinsize())
 | |
| 	elif type == WE_COMMAND and detail == WC_RETURN:
 | |
| 		if win.auto:
 | |
| 			do_exec(win)
 | |
| 		else:
 | |
| 			void = win.editor.event(event)
 | |
| 	elif type == WE_COMMAND and detail == WC_CANCEL:
 | |
| 		if win.busy:
 | |
| 			raise KeyboardInterrupt
 | |
| 		else:
 | |
| 			win.command = ''
 | |
| 			settitle(win)
 | |
| 	elif type == WE_MENU:
 | |
| 		mp, item = detail
 | |
| 		mp.callback[item](win)
 | |
| 	else:
 | |
| 		void = win.editor.event(event)
 | |
| 	if win in mainloop.windows:
 | |
| 		# May have been deleted by close...
 | |
| 		win.setdocsize(0, win.editor.getrect()[1][1])
 | |
| 		if type in (WE_CHAR, WE_COMMAND):
 | |
| 			win.editor.setfocus(win.editor.getfocus())
 | |
| 
 | |
| 
 | |
| # Helper to set the title of the window
 | |
| #
 | |
| def settitle(win):
 | |
| 	if win.filename == '':
 | |
| 		win.settitle('Python interpreter ready')
 | |
| 	else:
 | |
| 		win.settitle(win.filename)
 | |
| 
 | |
| 
 | |
| # Helper to replace the text of the focus
 | |
| #
 | |
| def replace(win, text):
 | |
| 	win.editor.replace(text)
 | |
| 	# Resize the window to display the text
 | |
| 	win.setdocsize(0, win.editor.getrect()[1][1]) # update the size before
 | |
| 	win.editor.setfocus(win.editor.getfocus()) # move focus to the change
 | |
| 
 | |
| 
 | |
| # File menu handlers
 | |
| #
 | |
| def do_new(win):
 | |
| 	win = makewindow()
 | |
| #
 | |
| def do_open(win):
 | |
| 	try:
 | |
| 		filename = stdwin.askfile('Open file', '', 0)
 | |
| 		win = makewindow()
 | |
| 		win.filename = filename
 | |
| 		win.editor.replace(open(filename, 'r').read())
 | |
| 		win.editor.setfocus(0, 0)
 | |
| 		win.settitle(win.filename)
 | |
| 		#
 | |
| 	except KeyboardInterrupt:
 | |
| 		pass			# Don't give an error on cancel
 | |
| #
 | |
| def do_save(win):
 | |
| 	try:
 | |
| 		if win.filename == '':
 | |
| 			win.filename = stdwin.askfile('Open file', '', 1)
 | |
| 		f = open(win.filename, 'w')
 | |
| 		f.write(win.editor.gettext())
 | |
| 		#
 | |
| 	except KeyboardInterrupt:
 | |
| 		pass			# Don't give an error on cancel
 | |
| 	
 | |
| def do_saveas(win):
 | |
| 	currentFilename = win.filename
 | |
| 	win.filename = ''
 | |
| 	do_save(win)		# Use do_save with empty filename
 | |
| 	if win.filename == '':	# Restore the name if do_save did not set it
 | |
| 		win.filename = currentFilename
 | |
| #
 | |
| def do_close(win):
 | |
| 	if win.busy:
 | |
| 		stdwin.message('Can\'t close busy window')
 | |
| 		return		# need to fail if quitting??
 | |
| 	win.editor = None # Break circular reference
 | |
| 	#del win.editmenu	# What about the filemenu??
 | |
| 	mainloop.unregister(win)
 | |
| 	win.close()
 | |
| #
 | |
| def do_quit(win):
 | |
| 	# Call win.dispatch instead of do_close because there
 | |
| 	# may be 'alien' windows in the list.
 | |
| 	for win in mainloop.windows[:]:
 | |
| 		mainloop.dispatch((WE_CLOSE, win, None))
 | |
| 		# need to catch failed close
 | |
| 
 | |
| 
 | |
| # Edit menu handlers
 | |
| #
 | |
| def do_cut(win):
 | |
| 	text = win.editor.getfocustext()
 | |
| 	if not text:
 | |
| 		stdwin.fleep()
 | |
| 		return
 | |
| 	stdwin.setcutbuffer(0, text)
 | |
| 	replace(win, '')
 | |
| #
 | |
| def do_copy(win):
 | |
| 	text = win.editor.getfocustext()
 | |
| 	if not text:
 | |
| 		stdwin.fleep()
 | |
| 		return
 | |
| 	stdwin.setcutbuffer(0, text)
 | |
| #
 | |
| def do_paste(win):
 | |
| 	text = stdwin.getcutbuffer(0)
 | |
| 	if not text:
 | |
| 		stdwin.fleep()
 | |
| 		return
 | |
| 	replace(win, text)
 | |
| #
 | |
| def do_clear(win):
 | |
| 	replace(win, '')
 | |
| 
 | |
| 
 | |
| # These would be better in a preferences dialog:
 | |
| #
 | |
| def do_auto(win):
 | |
| 	win.auto = (not win.auto)
 | |
| 	win.editmenu.check(win.iauto, win.auto)
 | |
| #
 | |
| def do_insertOutputOption(win):
 | |
| 	win.insertOutput = (not win.insertOutput)
 | |
| 	title = ['Append Output', 'Insert Output'][win.insertOutput]
 | |
| 	win.editmenu.setitem(win.insertOutputNum, title)
 | |
| #
 | |
| def do_insertErrorOption(win):
 | |
| 	win.insertError = (not win.insertError)
 | |
| 	title = ['Error Dialog', 'Insert Error'][win.insertError]
 | |
| 	win.editmenu.setitem(win.insertErrorNum, title)
 | |
| 
 | |
| 
 | |
| # Extract a command from the editor and execute it, or pass input to
 | |
| # an interpreter waiting for it.
 | |
| # Incomplete commands are merely placed in the window's command buffer.
 | |
| # All exceptions occurring during the execution are caught and reported.
 | |
| # (Tracebacks are currently not possible, as the interpreter does not
 | |
| # save the traceback pointer until it reaches its outermost level.)
 | |
| #
 | |
| def do_exec(win):
 | |
| 	if win.busy:
 | |
| 		if win not in inputwindows:
 | |
| 			stdwin.message('Can\'t run recursive commands')
 | |
| 			return
 | |
| 		if win <> inputwindows[0]:
 | |
| 			stdwin.message('Please complete recursive input first')
 | |
| 			return
 | |
| 	#
 | |
| 	# Set text to the string to execute.
 | |
| 	a, b = win.editor.getfocus()
 | |
| 	alltext = win.editor.gettext()
 | |
| 	n = len(alltext)
 | |
| 	if a == b:
 | |
| 		# There is no selected text, just an insert point;
 | |
| 		# so execute the current line.
 | |
| 		while 0 < a and alltext[a-1] <> '\n': # Find beginning of line
 | |
| 			a = a-1
 | |
| 		while b < n and alltext[b] <> '\n': # Find end of line after b
 | |
| 			b = b+1
 | |
| 		text = alltext[a:b] + '\n'
 | |
| 	else:
 | |
| 		# Execute exactly the selected text.
 | |
| 		text = win.editor.getfocustext()
 | |
| 		if text[-1:] <> '\n': # Make sure text ends with \n
 | |
| 			text = text + '\n'
 | |
| 		while b < n and alltext[b] <> '\n': # Find end of line after b
 | |
| 			b = b+1
 | |
| 	#
 | |
| 	# Set the focus to expect the output, since there is always something.
 | |
| 	# Output will be inserted at end of line after current focus,
 | |
| 	# or appended to the end of the text.
 | |
| 	b = [n, b][win.insertOutput]
 | |
| 	win.editor.setfocus(b, b)
 | |
| 	#
 | |
| 	# Make sure there is a preceeding newline.
 | |
| 	if alltext[b-1:b] <> '\n':
 | |
| 		win.editor.replace('\n')
 | |
| 	#
 | |
| 	#
 | |
| 	if win.busy:
 | |
| 		# Send it to raw_input() below
 | |
| 		raise InputAvailable, text
 | |
| 	#
 | |
| 	# Like the real Python interpreter, we want to execute
 | |
| 	# single-line commands immediately, but save multi-line
 | |
| 	# commands until they are terminated by a blank line.
 | |
| 	# Unlike the real Python interpreter, we don't do any syntax
 | |
| 	# checking while saving up parts of a multi-line command.
 | |
| 	#
 | |
| 	# The current heuristic to determine whether a command is
 | |
| 	# the first line of a multi-line command simply checks whether
 | |
| 	# the command ends in a colon (followed by a newline).
 | |
| 	# This is not very robust (comments and continuations will
 | |
| 	# confuse it), but it is usable, and simple to implement.
 | |
| 	# (It even has the advantage that single-line loops etc.
 | |
| 	# don't need te be terminated by a blank line.)
 | |
| 	#
 | |
| 	if win.command:
 | |
| 		# Already continuing
 | |
| 		win.command = win.command + text
 | |
| 		if win.command[-2:] <> '\n\n':
 | |
| 			win.settitle('Unfinished command...')
 | |
| 			return # Need more...
 | |
| 	else:
 | |
| 		# New command
 | |
| 		win.command = text
 | |
| 		if text[-2:] == ':\n':
 | |
| 			win.settitle('Unfinished command...')
 | |
| 			return
 | |
| 	command = win.command
 | |
| 	win.command = ''
 | |
| 	win.settitle('Executing command...')
 | |
| 	#
 | |
| 	# Some hacks:
 | |
| 	# - The standard files are replaced by an IOWindow instance.
 | |
| 	# - A 2nd argument to exec() is used to specify the directory
 | |
| 	#   holding the user's global variables.  (If this wasn't done,
 | |
| 	#   the exec would be executed in the current local environment,
 | |
| 	#   and the user's assignments to globals would be lost...)
 | |
| 	#
 | |
| 	save_stdin = sys.stdin
 | |
| 	save_stdout = sys.stdout
 | |
| 	save_stderr = sys.stderr
 | |
| 	try:
 | |
| 		sys.stdin = sys.stdout = sys.stderr = IOWindow(win)
 | |
| 		win.busy = 1
 | |
| 		try:
 | |
| 			exec(command, win.globals)
 | |
| 		except KeyboardInterrupt:
 | |
| 			print '[Interrupt]'
 | |
| 		except:
 | |
| 			if type(sys.exc_type) == type(''):
 | |
| 				msg = sys.exc_type
 | |
| 			else: msg = sys.exc_type.__name__
 | |
| 			if sys.exc_value <> None:
 | |
| 				msg = msg + ': ' + `sys.exc_value`
 | |
| 			if win.insertError:
 | |
| 				stdwin.fleep()
 | |
| 				replace(win, msg + '\n')
 | |
| 			else:
 | |
| 				win.settitle('Unhandled exception')
 | |
| 				stdwin.message(msg)
 | |
| 	finally:
 | |
| 		# Restore redirected I/O in *all* cases
 | |
| 		win.busy = 0
 | |
| 		sys.stderr = save_stderr
 | |
| 		sys.stdout = save_stdout
 | |
| 		sys.stdin = save_stdin
 | |
| 		settitle(win)
 | |
| 
 | |
| 
 | |
| # Class emulating file I/O from/to a window
 | |
| #
 | |
| class IOWindow:
 | |
| 	#
 | |
| 	def __init__(self, win):
 | |
| 		self.win = win
 | |
| 	#
 | |
| 	def readline(self, *unused_args):
 | |
| 		n = len(inputwindows)
 | |
| 		save_title = self.win.gettitle()
 | |
| 		title = n*'(' + 'Requesting input...' + ')'*n
 | |
| 		self.win.settitle(title)
 | |
| 		inputwindows.insert(0, self.win)
 | |
| 		try:
 | |
| 			try:
 | |
| 				mainloop.mainloop()
 | |
| 			finally:
 | |
| 				del inputwindows[0]
 | |
| 				self.win.settitle(save_title)
 | |
| 		except InputAvailable, val: # See do_exec above
 | |
| 			return val
 | |
| 		except KeyboardInterrupt:
 | |
| 			raise EOFError # Until we have a "send EOF" key
 | |
| 		# If we didn't catch InputAvailable, something's wrong...
 | |
| 		raise EOFError
 | |
| 	#
 | |
| 	def write(self, text):
 | |
| 		mainloop.check()
 | |
| 		replace(self.win, text)
 | |
| 		mainloop.check()
 | |
| 
 | |
| 
 | |
| # Currently unused function to test a command's syntax without executing it
 | |
| #
 | |
| def testsyntax(s):
 | |
| 	import string
 | |
| 	lines = string.splitfields(s, '\n')
 | |
| 	for i in range(len(lines)): lines[i] = '\t' + lines[i]
 | |
| 	lines.insert(0, 'if 0:')
 | |
| 	lines.append('')
 | |
| 	exec(string.joinfields(lines, '\n'))
 | |
| 
 | |
| 
 | |
| # Call the main program
 | |
| #
 | |
| main()
 | 
