• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"""This is a substantially improved version of the older Interpreter.py demo
2It creates a simple GUI JPython console window with simple history
3as well as the ability to interupt running code (with the ESC key).
4
5Like Interpreter.py, this is still just a demo, and needs substantial
6work before serious use.
7
8Thanks to Geza Groma (groma@everx.szbk.u-szeged.hu) for several valuable
9ideas for this tool -- his JPConsole is a more refined implementation
10of similar ideas.
11"""
12
13from Styles import Styles
14from Keymap import Keymap
15
16from pawt import swing, colors
17from java.awt.event.KeyEvent import VK_UP, VK_DOWN
18from java.awt.event import ActionEvent
19from java.lang import Thread, System
20from code import compile_command
21import string, sys, re
22
23class OutputBuffer:
24	def __init__(self, console, stylename):
25		self.console = console
26		self.stylename = stylename
27
28	def flush(self):
29		pass
30
31	def write(self, text):
32		self.console.write(text, self.stylename)
33
34class Console:
35	def __init__(self, styles=None, keymap=None):
36		if styles is None:
37			styles = Styles()
38			basic = styles.add('normal', tabsize=3, fontSize=12, fontFamily="Courier")
39			styles.add('error', parent=basic, foreground=colors.red)
40			styles.add('output', parent=basic, foreground=colors.blue)
41			styles.add('input', parent=basic, foreground=colors.black)
42			styles.add('prompt', parent=basic, foreground=colors.purple)
43		self.styles = styles
44
45		# This is a hack to get at an inner class
46		# This will not be required in JPython-1.1
47		ForegroundAction = getattr(swing.text, 'StyledEditorKit$ForegroundAction')
48		self.inputAction = ForegroundAction("start input", colors.black)
49
50		if keymap is None:
51			keymap = Keymap()
52		keymap.bind('enter', self.enter)
53		keymap.bind('tab', self.tab)
54		keymap.bind('escape', self.escape)
55		keymap.bind('up', self.uphistory)
56		keymap.bind('down', self.downhistory)
57
58		self.keymap = keymap
59
60		self.document = swing.text.DefaultStyledDocument(self.styles)
61		self.document.setLogicalStyle(0, self.styles.get('normal'))
62
63		self.textpane = swing.JTextPane(self.document)
64		self.textpane.keymap = self.keymap
65
66		self.history = []
67		self.oldHistoryLength = 0
68		self.historyPosition = 0
69
70		self.command = []
71		self.locals = {}
72
73	def write(self, text, stylename='normal'):
74		style = self.styles.get(stylename)
75		self.document.insertString(self.document.length, text, style)
76
77	def beep(self):
78		self.textpane.toolkit.beep()
79
80	def startUserInput(self, prompt=None):
81		if prompt is not None:
82			self.write(prompt, 'prompt')
83		self.startInput = self.document.createPosition(self.document.length-1)
84		#self.document.setCharacterAttributes(self.document.length-1, 1, self.styles.get('input'), 1)
85		self.textpane.caretPosition = self.document.length
86		ae = ActionEvent(self.textpane, ActionEvent.ACTION_PERFORMED, 'start input')
87		self.inputAction.actionPerformed(ae)
88
89	def getinput(self):
90		offset = self.startInput.offset
91		line = self.document.getText(offset+1, self.document.length-offset)
92		return string.rstrip(line)
93
94	def replaceinput(self, text):
95		offset = self.startInput.offset + 1
96		self.document.remove(offset, self.document.length-offset)
97		self.write(text, 'input')
98
99	def enter(self):
100		line = self.getinput()
101		self.write('\n', 'input')
102
103		self.history.append(line)
104		self.handleLine(line)
105
106	def gethistory(self, direction):
107		historyLength = len(self.history)
108		if self.oldHistoryLength < historyLength:
109			# new line was entered after last call
110			self.oldHistoryLength = historyLength
111			if self.history[self.historyPosition] != self.history[-1]:
112				self.historyPosition = historyLength
113
114		pos = self.historyPosition + direction
115
116		if 0 <= pos < historyLength:
117			self.historyPosition = pos
118			self.replaceinput(self.history[pos])
119		else:
120			self.beep()
121
122	def uphistory(self):
123		self.gethistory(-1)
124
125	def downhistory(self):
126		self.gethistory(1)
127
128	def tab(self):
129		self.write('\t', 'input')
130
131	def escape(self):
132		if (not hasattr(self, 'pythonThread') or self.pythonThread is None or not self.pythonThread.alive):
133			self.beep()
134			return
135
136		self.pythonThread.stopPython()
137
138	def capturePythonOutput(self, stdoutStyle='output', stderrStyle='error'):
139		import sys
140		sys.stdout = OutputBuffer(self, stdoutStyle)
141		sys.stderr = OutputBuffer(self, stderrStyle)
142
143	def handleLine(self, text):
144		self.command.append(text)
145
146		try:
147			code = compile_command(string.join(self.command, '\n'))
148		except SyntaxError:
149			traceback.print_exc(0)
150			self.command = []
151			self.startUserInput(str(sys.ps1)+'\t')
152			return
153
154		if code is None:
155			self.startUserInput(str(sys.ps2)+'\t')
156			return
157
158		self.command = []
159
160		pt = PythonThread(code, self)
161		self.pythonThread = pt
162		pt.start()
163
164	def newInput(self):
165		self.startUserInput(str(sys.ps1)+'\t')
166
167import traceback
168
169class PythonThread(Thread):
170	def __init__(self, code, console):
171		self.code = code
172		self.console = console
173		self.locals = console.locals
174
175	def run(self):
176		try:
177			exec self.code in self.locals
178
179		#Include these lines to actually exit on a sys.exit() call
180		#except SystemExit, value:
181		#	raise SystemExit, value
182
183		except:
184			exc_type, exc_value, exc_traceback = sys.exc_info()
185			l = len(traceback.extract_tb(sys.exc_traceback))
186			try:
187				1/0
188			except:
189				m = len(traceback.extract_tb(sys.exc_traceback))
190			traceback.print_exception(exc_type, exc_value, exc_traceback, l-m)
191
192		self.console.newInput()
193
194	def stopPython(self):
195		#Should spend 2 seconds trying to kill thread in nice Python style first...
196		self.stop()
197
198header = """\
199JPython %(version)s on %(platform)s
200%(copyright)s
201""" % {'version':sys.version, 'platform':sys.platform, 'copyright':sys.copyright}
202
203if __name__ == '__main__':
204	c = Console()
205	pane = swing.JScrollPane(c.textpane)
206	swing.test(pane, size=(500,400), name='JPython Console')
207	c.write(header, 'output')
208	c.capturePythonOutput()
209	c.textpane.requestFocus()
210	c.newInput()
211