File: Console.py

package info (click to toggle)
jython 2.5.3-3
  • links: PTS, VCS
  • area: main
  • in suites: jessie-kfreebsd
  • size: 43,304 kB
  • sloc: python: 351,314; java: 216,338; xml: 1,547; sh: 330; perl: 124; ansic: 102; makefile: 101
file content (211 lines) | stat: -rw-r--r-- 5,902 bytes parent folder | download | duplicates (10)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
"""This is a substantially improved version of the older Interpreter.py demo
It creates a simple GUI JPython console window with simple history
as well as the ability to interupt running code (with the ESC key).

Like Interpreter.py, this is still just a demo, and needs substantial
work before serious use.

Thanks to Geza Groma (groma@everx.szbk.u-szeged.hu) for several valuable
ideas for this tool -- his JPConsole is a more refined implementation
of similar ideas.
"""

from Styles import Styles
from Keymap import Keymap

from pawt import swing, colors
from java.awt.event.KeyEvent import VK_UP, VK_DOWN
from java.awt.event import ActionEvent
from java.lang import Thread, System
from code import compile_command
import string, sys, re

class OutputBuffer:
	def __init__(self, console, stylename):
		self.console = console
		self.stylename = stylename
		
	def flush(self):
		pass
		
	def write(self, text):
		self.console.write(text, self.stylename)

class Console:
	def __init__(self, styles=None, keymap=None):
		if styles is None:
			styles = Styles()
			basic = styles.add('normal', tabsize=3, fontSize=12, fontFamily="Courier")
			styles.add('error', parent=basic, foreground=colors.red)
			styles.add('output', parent=basic, foreground=colors.blue)
			styles.add('input', parent=basic, foreground=colors.black)
			styles.add('prompt', parent=basic, foreground=colors.purple)
		self.styles = styles
		
		# This is a hack to get at an inner class
		# This will not be required in JPython-1.1
		ForegroundAction = getattr(swing.text, 'StyledEditorKit$ForegroundAction')
		self.inputAction = ForegroundAction("start input", colors.black)

		if keymap is None:
			keymap = Keymap()
		keymap.bind('enter', self.enter)
		keymap.bind('tab', self.tab)
		keymap.bind('escape', self.escape)
		keymap.bind('up', self.uphistory)
		keymap.bind('down', self.downhistory)
		
		self.keymap = keymap
		
		self.document = swing.text.DefaultStyledDocument(self.styles)
		self.document.setLogicalStyle(0, self.styles.get('normal'))

		self.textpane = swing.JTextPane(self.document)
		self.textpane.keymap = self.keymap
		
		self.history = []
		self.oldHistoryLength = 0
		self.historyPosition = 0
		
		self.command = []
		self.locals = {}

	def write(self, text, stylename='normal'):
		style = self.styles.get(stylename)
		self.document.insertString(self.document.length, text, style)
		
	def beep(self):
		self.textpane.toolkit.beep()

	def startUserInput(self, prompt=None):
		if prompt is not None:
			self.write(prompt, 'prompt')
		self.startInput = self.document.createPosition(self.document.length-1)
		#self.document.setCharacterAttributes(self.document.length-1, 1, self.styles.get('input'), 1)
		self.textpane.caretPosition = self.document.length
		ae = ActionEvent(self.textpane, ActionEvent.ACTION_PERFORMED, 'start input')
		self.inputAction.actionPerformed(ae)

	def getinput(self):
		offset = self.startInput.offset
		line = self.document.getText(offset+1, self.document.length-offset)
		return string.rstrip(line)

	def replaceinput(self, text):
		offset = self.startInput.offset + 1
		self.document.remove(offset, self.document.length-offset)
		self.write(text, 'input')
		
	def enter(self):
		line = self.getinput()
		self.write('\n', 'input')
		
		self.history.append(line)
		self.handleLine(line)
		
	def gethistory(self, direction):
		historyLength = len(self.history)
		if self.oldHistoryLength < historyLength:
			# new line was entered after last call
			self.oldHistoryLength = historyLength
			if self.history[self.historyPosition] != self.history[-1]:
				self.historyPosition = historyLength

		pos = self.historyPosition + direction

		if 0 <= pos < historyLength:
			self.historyPosition = pos
			self.replaceinput(self.history[pos])
		else:
			self.beep()

	def uphistory(self):
		self.gethistory(-1)

	def downhistory(self):
		self.gethistory(1)

	def tab(self):
		self.write('\t', 'input')
		
	def escape(self):
		if (not hasattr(self, 'pythonThread') or self.pythonThread is None or
				not self.pythonThread.alive):
			self.beep()
			return
			
		self.pythonThread.stopPython()

	def capturePythonOutput(self, stdoutStyle='output', stderrStyle='error'):
		import sys
		sys.stdout = OutputBuffer(self, stdoutStyle)
		sys.stderr = OutputBuffer(self, stderrStyle)

	def handleLine(self, text):
		self.command.append(text)
		
		try:
			code = compile_command(string.join(self.command, '\n'))
		except SyntaxError:
			traceback.print_exc(0)
			self.command = []
			self.startUserInput(str(sys.ps1)+'\t')
			return

		if code is None:
			self.startUserInput(str(sys.ps2)+'\t')
			return
		
		self.command = []
		
		pt = PythonThread(code, self)
		self.pythonThread = pt
		pt.start()
		
	def newInput(self):
		self.startUserInput(str(sys.ps1)+'\t')
		
import traceback

class PythonThread(Thread):
	def __init__(self, code, console):
		self.code = code
		self.console = console
		self.locals = console.locals
		
	def run(self):
		try:
			exec self.code in self.locals
			
		#Include these lines to actually exit on a sys.exit() call
		#except SystemExit, value:
		#	raise SystemExit, value
		
		except:
			exc_type, exc_value, exc_traceback = sys.exc_info()
			l = len(traceback.extract_tb(sys.exc_traceback))
			try:
				1/0
			except:
				m = len(traceback.extract_tb(sys.exc_traceback))
			traceback.print_exception(exc_type, exc_value, exc_traceback, l-m)
			
		self.console.newInput()

	def stopPython(self):
		#Should spend 2 seconds trying to kill thread in nice Python style first...
		self.stop()

header = """\
JPython %(version)s on %(platform)s
%(copyright)s
""" % {'version':sys.version, 'platform':sys.platform, 'copyright':sys.copyright}

if __name__ == '__main__':
	c = Console()
	pane = swing.JScrollPane(c.textpane)
	swing.test(pane, size=(500,400), name='JPython Console')
	c.write(header, 'output')
	c.capturePythonOutput()
	c.textpane.requestFocus()
	c.newInput()