Commits

Anonymous committed a47e0c9

Changed indentation to 8, use a better technique to handle window I/O.

  • Participants
  • Parent commits a7cf20c
  • Branches legacy-trunk

Comments (0)

Files changed (1)

Demo/stdwin/python.py

 #! /usr/local/bin/python
 
-# :set tabsize=4:
-
 # A STDWIN-based front end for the Python interpreter.
 #
 # This is useful if you want to avoid console I/O and instead
 #
 # BUGS AND CAVEATS:
 #
-# I wrote this about two years ago.  There are now some features in
-# Python that make it possible to overcome some of the bugs below,
-# but I haven't the time to adapt it; it's just meant as a little
-# thing to get you started...
+# 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.
 #
-# Everything written to stdout or stderr is saved on a file which
-# is inserted in the window at the next input request.
+# 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 request.  (On the Mac, interrupts work.)
-#
-# Direct input from stdin should not be attempted.
+# 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 os
 
 
-# Filename used to capture output from commands; change to suit your taste
-#
-OUTFILE = '@python.stdout.tmp'
-
-
 # Stack of windows waiting for [raw_]input().
 # Element [0] is the top.
 # If there are multiple windows waiting for input, only the
 inputwindows = []
 
 
-# Exception raised when input is available.
+# Exception raised when input is available
 #
 InputAvailable = 'input available for raw_input (not an error)'
 
 
-# Main program.  Create the window and call the mainloop.
+# Main program -- create the window and call the mainloop
 #
 def main():
 	# Hack so 'import python' won't load another copy
 	mainloop.mainloop()
 
 
-# Create a new window.
+# 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
-	# stdwin.setdefwinsize(stdwin.textwidth('in')*40, stdwin.lineheight() * 24)
+	# 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.outfile = OUTFILE + `rand.rand()`
-	win.globals = {}	# Dictionary for user's global variables
-	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.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 associated with this window
+	win.filename = ''		# Empty if no file for this window
 	makefilemenu(win)
 	makeeditmenu(win)
 	win.dispatch = pdispatch	# Event dispatch function
 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)
+	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)
+	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)
+	additem(mp, 'Autoexecute', '', do_auto)
 	mp.check(win.iauto, win.auto)
 	win.insertOutputNum = len(mp.callback)
-	additem(mp, 'Insert Output',	'', do_insertOutputOption)
+	additem(mp, 'Insert Output', '', do_insertOutputOption)
 	win.insertErrorNum = len(mp.callback)
-	additem(mp, 'Insert Error',	'', do_insertErrorOption)
-	additem(mp, 'Exec',	'\r', do_exec)
+	additem(mp, 'Insert Error', '', do_insertErrorOption)
+	additem(mp, 'Exec', '\r', do_exec)
 
 
 # Helper to add a menu item and callback function
 
 # Dispatch a single event to the interpreter.
 # Resize events cause a resize of the editor.
-# Other events are directly sent to the editor.
-#
-# Exception: WE_COMMAND/WC_RETURN causes the current selection
-# (if not empty) or current line (if empty) to be sent to the
-# interpreter.  (In the future, there should be a way to insert
-# newlines in the text; or perhaps Enter or Meta-RETURN should be
-# used to trigger execution, like in MPW, though personally I prefer
-# using a plain Return to trigger execution, as this is what I want
-# in the majority of cases.)
-#
-# Also, WE_COMMAND/WC_CANCEL cancels any command in progress.
+# 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
 			void = win.editor.event(event)
 	elif type == WE_COMMAND and detail == WC_CANCEL:
 		if win.busy:
-			raise InputAvailable, (EOFError, None)
+			raise KeyboardInterrupt
 		else:
 			win.command = ''
 			settitle(win)
 			win.editor.setfocus(win.editor.getfocus())
 
 
-# Helper to set the title of the window. 
+# Helper to set the title of the window
 #
 def settitle(win):
 	if win.filename == '':
 		win.settitle(win.filename)
 
 
-# Helper to replace the text of the focus.
+# 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
+	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
 		win.settitle(win.filename)
 		#
 	except KeyboardInterrupt:
-		pass # Don't give an error on cancel.
+		pass			# Don't give an error on cancel
 #
 def do_save(win):
 	try:
 		f.write(win.editor.gettext())
 		#
 	except KeyboardInterrupt:
-		pass # Don't give an error on cancel.
+		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.
+	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):
 		return		# need to fail if quitting??
 	win.editor = None # Break circular reference
 	#del win.editmenu	# What about the filemenu??
-	try:
-		os.unlink(win.outfile)
-	except os.error:
-		pass
 	mainloop.unregister(win)
 	win.close()
 #
 	# 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
+		mainloop.dispatch((WE_CLOSE, win, None))
+		# need to catch failed close
 
 
 # Edit menu handlers
 def do_clear(win):
 	replace(win, '')
 
+
+# These would be better in a preferences dialog:
 #
-# These would be better in a preferences dialog:
 def do_auto(win):
 	win.auto = (not win.auto)
 	win.editmenu.check(win.iauto, win.auto)
 			stdwin.message('Can\'t run recursive commands')
 			return
 		if win <> inputwindows[0]:
-			stdwin.message( \
-				'Please complete recursive input first')
+			stdwin.message('Please complete recursive input first')
 			return
 	#
 	# Set text to the string to execute.
 	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': a = a-1 # Find beginning of line.
-		while b < n and alltext[b] <> '\n':		# Find end of line after b.
+		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.
+		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.
+		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.
 	#
 	if win.busy:
 		# Send it to raw_input() below
-		raise InputAvailable, (None, text)
+		raise InputAvailable, text
 	#
 	# Like the real Python interpreter, we want to execute
 	# single-line commands immediately, but save multi-line
 	win.command = ''
 	win.settitle('Executing command...')
 	#
-	# Some hacks: sys.stdout is temporarily redirected to a file,
-	# so we can intercept the command's output and insert it
-	# in the editor window; the built-in function raw_input
-	# and input() are replaced by out versions;
-	# and a second, undocumented 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...)
+	# 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_input = builtin.input
-	save_raw_input = builtin.raw_input
+	save_stdin = sys.stdin
 	save_stdout = sys.stdout
 	save_stderr = sys.stderr
-	iwin = Input().init(win)
 	try:
-		builtin.input = iwin.input
-		builtin.raw_input = iwin.raw_input
-		sys.stdout = sys.stderr = open(win.outfile, 'w')
+		sys.stdin = sys.stdout = sys.stderr = IOWindow().init(win)
 		win.busy = 1
 		try:
 			exec(command, win.globals)
 		except KeyboardInterrupt:
-			pass # Don't give an error.
+			print '[Interrupt]'
 		except:
 			msg = sys.exc_type
 			if sys.exc_value <> None:
 		win.busy = 0
 		sys.stderr = save_stderr
 		sys.stdout = save_stdout
-		builtin.raw_input = save_raw_input
-		builtin.input = save_input
+		sys.stdin = save_stdin
 		settitle(win)
-	getoutput(win)
 
 
-# Read any output the command may have produced back from the file
-# and show it.  Optionally insert it after the focus, like MPW does, 
-# or always append at the end.
+# Class emulating file I/O from/to a window
 #
-def getoutput(win):
-	filename = win.outfile
-	try:
-		fp = open(filename, 'r')
-	except:
-		stdwin.message('Can\'t read output from ' + filename)
-		return
-	#out = fp.read() # Not in Python 0.9.1
-	out = fp.read(10000) # For Python 0.9.1
-	del fp # Close it
-	if out or win.insertOutput:
-		replace(win, out)
-
-
-# Implementation of input() and raw_input().
-# This uses a class only because we must support calls
-# with and without arguments; this can't be done normally in Python,
-# but the extra, implicit argument for instance methods does the trick.
-#
-class Input:
+class IOWindow:
 	#
 	def init(self, win):
 		self.win = win
 		return self
 	#
-	def input(args):
-		# Hack around call with or without argument:
-		if type(args) == type(()):
-			self, prompt = args
-		else:
-			self, prompt = args, ''
-		#
-		return eval(self.raw_input(prompt), self.win.globals)
-	#
-	def raw_input(args):
-		# Hack around call with or without argument:
-		if type(args) == type(()):
-			self, prompt = args
-		else:
-			self, prompt = args, ''
-		#
-		print prompt		# Need to terminate with newline.
-		sys.stdout.close()
-		sys.stdout = sys.stderr = None
-		getoutput(self.win)
-		sys.stdout = sys.stderr = open(self.win.outfile, 'w')
+	def readline(self, *unused_args):
+		n = len(inputwindows)
 		save_title = self.win.gettitle()
-		n = len(inputwindows)
 		title = n*'(' + 'Requesting input...' + ')'*n
 		self.win.settitle(title)
 		inputwindows.insert(0, self.win)
 		try:
 			try:
 				mainloop.mainloop()
-			except InputAvailable, (exc, val): # See do_exec above.
-				if exc:
-					raise exc, val
-				if val[-1:] == '\n':
-					val = val[:-1]
-				return val
-		finally:
-			del inputwindows[0]
-			self.win.settitle(save_title)
-		# If we don't catch InputAvailable, something's wrong...
+			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
 	exec(string.joinfields(lines, '\n'))
 
 
-# Call the main program.
+# Call the main program
 #
 main()
-
-
-# This was originally coded on a Mac, so...
-# Local variables:
-# py-indent-offset: 4
-# tab-width: 4
-# end: