File: Console.py

package info (click to toggle)
jython 2.7.2%2Brepack1-3
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 62,676 kB
  • sloc: python: 640,908; java: 306,458; xml: 1,984; sh: 522; ansic: 126; makefile: 76
file content (221 lines) | stat: -rw-r--r-- 6,798 bytes parent folder | download | duplicates (2)
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
212
213
214
215
216
217
218
219
220
221
"""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
import sys
import 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()