##// END OF EJS Templates
First cut of subprocess execution with redirection of stdin/stdout.
Gael Varoquaux -
Show More
@@ -0,0 +1,48 b''
1 # encoding: utf-8
2 """
3 Object for encapsulating process execution by using callbacks for stdout,
4 stderr and stdin.
5 """
6 __docformat__ = "restructuredtext en"
7
8 #-------------------------------------------------------------------------------
9 # Copyright (C) 2008 The IPython Development Team
10 #
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
13 #-------------------------------------------------------------------------------
14
15 #-------------------------------------------------------------------------------
16 # Imports
17 #-------------------------------------------------------------------------------
18 from subprocess import Popen, PIPE
19 from threading import Thread
20
21
22 class PipedProcess(Thread):
23
24 def __init__(self, command_string, out_callback,
25 end_callback=None,):
26 self.command_string = command_string
27 self.out_callback = out_callback
28 self.end_callback = end_callback
29 Thread.__init__(self)
30
31
32 def run(self):
33 """ Start the process and hook up the callbacks.
34 """
35 process = Popen((self.command_string + ' 2>&1', ), shell=True,
36 universal_newlines=True,
37 stdout=PIPE, stdin=PIPE)
38 self.process = process
39 while True:
40 out_char = process.stdout.read(1)
41 if out_char == '' and process.poll() is not None:
42 break
43 self.out_callback(out_char)
44
45 if self.end_callback is not None:
46 self.end_callback()
47
48
@@ -1,159 +1,163 b''
1 """
1 """
2 Frontend class that uses IPython0 to prefilter the inputs.
2 Frontend class that uses IPython0 to prefilter the inputs.
3
3
4 Using the IPython0 mechanism gives us access to the magics.
4 Using the IPython0 mechanism gives us access to the magics.
5 """
5 """
6 __docformat__ = "restructuredtext en"
6 __docformat__ = "restructuredtext en"
7
7
8 #-------------------------------------------------------------------------------
8 #-------------------------------------------------------------------------------
9 # Copyright (C) 2008 The IPython Development Team
9 # Copyright (C) 2008 The IPython Development Team
10 #
10 #
11 # Distributed under the terms of the BSD License. The full license is in
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
12 # the file COPYING, distributed as part of this software.
13 #-------------------------------------------------------------------------------
13 #-------------------------------------------------------------------------------
14
14
15 #-------------------------------------------------------------------------------
15 #-------------------------------------------------------------------------------
16 # Imports
16 # Imports
17 #-------------------------------------------------------------------------------
17 #-------------------------------------------------------------------------------
18 import sys
18 import sys
19
19
20 from linefrontendbase import LineFrontEndBase, common_prefix
20 from linefrontendbase import LineFrontEndBase, common_prefix
21
21
22 from IPython.ipmaker import make_IPython
22 from IPython.ipmaker import make_IPython
23 from IPython.ipapi import IPApi
23 from IPython.ipapi import IPApi
24 from IPython.kernel.core.redirector_output_trap import RedirectorOutputTrap
24 from IPython.kernel.core.redirector_output_trap import RedirectorOutputTrap
25
25
26 from IPython.kernel.core.sync_traceback_trap import SyncTracebackTrap
27
28 from IPython.ultraTB import ColorTB
26 from IPython.genutils import Term
29 from IPython.genutils import Term
27 import pydoc
30 import pydoc
28
31
29 #-------------------------------------------------------------------------------
32 def mk_system_call(system_call_function, command):
30 # Utility functions (temporary, should be moved out of here)
33 """ given a os.system replacement, and a leading string command,
31 #-------------------------------------------------------------------------------
34 returns a function that will execute the command with the given
32 import os
35 argument string.
33 def xterm_system(command):
34 """ Run a command in a separate console window.
35 """
36 os.system(("""xterm -title "%s" -e \'/bin/sh -c "%s ; """
37 """echo; echo press enter to close ; """
38 # """echo \\"\x1b]0;%s (finished -- press enter to close)\x07\\" ;
39 """read foo;"\' """) % (command, command) )
40
41 def system_call(command):
42 """ Temporary hack for aliases
43 """
36 """
44 def my_system_call(args):
37 def my_system_call(args):
45 os.system("%s %s" % (command, args))
38 system_call_function("%s %s" % (command, args))
46 return my_system_call
39 return my_system_call
47
40
48 #-------------------------------------------------------------------------------
41 #-------------------------------------------------------------------------------
49 # Frontend class using ipython0 to do the prefiltering.
42 # Frontend class using ipython0 to do the prefiltering.
50 #-------------------------------------------------------------------------------
43 #-------------------------------------------------------------------------------
51 class PrefilterFrontEnd(LineFrontEndBase):
44 class PrefilterFrontEnd(LineFrontEndBase):
52
45
53 def __init__(self, *args, **kwargs):
46 def __init__(self, *args, **kwargs):
54 LineFrontEndBase.__init__(self, *args, **kwargs)
47 LineFrontEndBase.__init__(self, *args, **kwargs)
55 # Instanciate an IPython0 interpreter to be able to use the
48 # Instanciate an IPython0 interpreter to be able to use the
56 # prefiltering.
49 # prefiltering.
57 self.ipython0 = make_IPython()
50 self.ipython0 = make_IPython()
58 # Set the pager:
51 # Set the pager:
59 self.ipython0.set_hook('show_in_pager',
52 self.ipython0.set_hook('show_in_pager',
60 lambda s, string: self.write("\n"+string))
53 lambda s, string: self.write("\n"+string))
61 self.ipython0.write = self.write
54 self.ipython0.write = self.write
62 self._ip = _ip = IPApi(self.ipython0)
55 self._ip = _ip = IPApi(self.ipython0)
63 # XXX: Hack: mix the two namespaces
56 # XXX: Hack: mix the two namespaces
64 self.shell.user_ns = self.ipython0.user_ns
57 self.shell.user_ns = self.ipython0.user_ns
65 self.shell.user_global_ns = self.ipython0.user_global_ns
58 self.shell.user_global_ns = self.ipython0.user_global_ns
66 # Make sure the raw system call doesn't get called, as we don't
59 # Make sure the raw system call doesn't get called, as we don't
67 # have a stdin accessible.
60 # have a stdin accessible.
68 self._ip.system = xterm_system
61 self._ip.system = self.system_call
69 # XXX: Muck around with magics so that they work better
62 # XXX: Muck around with magics so that they work better
70 # in our environment
63 # in our environment
71 self.ipython0.magic_ls = system_call('ls -CF')
64 self.ipython0.magic_ls = mk_system_call(self.system_call,
65 'ls -CF')
72 self.shell.output_trap = RedirectorOutputTrap(
66 self.shell.output_trap = RedirectorOutputTrap(
73 out_callback=self.write,
67 out_callback=self.write,
74 err_callback=self.write,
68 err_callback=self.write,
75 )
69 )
70 self.shell.traceback_trap = SyncTracebackTrap(
71 formatters=[ColorTB(color_scheme='LightBG'), ]
72 )
76 # Capture and release the outputs, to make sure all the
73 # Capture and release the outputs, to make sure all the
77 # shadow variables are set
74 # shadow variables are set
78 self.capture_output()
75 self.capture_output()
79 self.release_output()
76 self.release_output()
80
77
81
78
82 def prefilter_input(self, input_string):
79 def prefilter_input(self, input_string):
83 """ Using IPython0 to prefilter the commands.
80 """ Using IPython0 to prefilter the commands.
84 """
81 """
85 input_string = LineFrontEndBase.prefilter_input(self, input_string)
82 input_string = LineFrontEndBase.prefilter_input(self, input_string)
86 filtered_lines = []
83 filtered_lines = []
87 # The IPython0 prefilters sometime produce output. We need to
84 # The IPython0 prefilters sometime produce output. We need to
88 # capture it.
85 # capture it.
89 self.capture_output()
86 self.capture_output()
90 self.last_result = dict(number=self.prompt_number)
87 self.last_result = dict(number=self.prompt_number)
91 try:
88 try:
92 for line in input_string.split('\n'):
89 for line in input_string.split('\n'):
93 filtered_lines.append(self.ipython0.prefilter(line, False))
90 filtered_lines.append(self.ipython0.prefilter(line, False))
94 except:
91 except:
95 # XXX: probably not the right thing to do.
92 # XXX: probably not the right thing to do.
96 self.ipython0.showsyntaxerror()
93 self.ipython0.showsyntaxerror()
97 self.after_execute()
94 self.after_execute()
98 finally:
95 finally:
99 self.release_output()
96 self.release_output()
100
97
101 filtered_string = '\n'.join(filtered_lines)
98 filtered_string = '\n'.join(filtered_lines)
102 return filtered_string
99 return filtered_string
103
100
104
101
105 def show_traceback(self):
102 def show_traceback(self):
106 self.capture_output()
103 self.capture_output()
107 self.ipython0.showtraceback()
104 self.ipython0.showtraceback()
108 self.release_output()
105 self.release_output()
109
106
110
107
111 def execute(self, python_string, raw_string=None):
108 def execute(self, python_string, raw_string=None):
112 self.capture_output()
109 self.capture_output()
113 LineFrontEndBase.execute(self, python_string,
110 LineFrontEndBase.execute(self, python_string,
114 raw_string=raw_string)
111 raw_string=raw_string)
115 self.release_output()
112 self.release_output()
116
113
117
114
115 def system_call(self, command):
116 """ Allows for frontend to define their own system call, to be
117 able capture output and redirect input.
118 """
119 return os.system(command, args)
120
121
118 def capture_output(self):
122 def capture_output(self):
119 """ Capture all the output mechanisms we can think of.
123 """ Capture all the output mechanisms we can think of.
120 """
124 """
121 self.__old_cout_write = Term.cout.write
125 self.__old_cout_write = Term.cout.write
122 self.__old_err_write = Term.cerr.write
126 self.__old_err_write = Term.cerr.write
123 Term.cout.write = self.write
127 Term.cout.write = self.write
124 Term.cerr.write = self.write
128 Term.cerr.write = self.write
125 self.__old_stdout = sys.stdout
129 self.__old_stdout = sys.stdout
126 self.__old_stderr= sys.stderr
130 self.__old_stderr= sys.stderr
127 sys.stdout = Term.cout
131 sys.stdout = Term.cout
128 sys.stderr = Term.cerr
132 sys.stderr = Term.cerr
129 self.__old_help_output = pydoc.help.output
133 self.__old_help_output = pydoc.help.output
130 pydoc.help.output = self.shell.output_trap.out
134 pydoc.help.output = self.shell.output_trap.out
131
135
132
136
133 def release_output(self):
137 def release_output(self):
134 """ Release all the different captures we have made.
138 """ Release all the different captures we have made.
135 """
139 """
136 Term.cout.write = self.__old_cout_write
140 Term.cout.write = self.__old_cout_write
137 Term.cerr.write = self.__old_err_write
141 Term.cerr.write = self.__old_err_write
138 sys.stdout = self.__old_stdout
142 sys.stdout = self.__old_stdout
139 sys.stderr = self.__old_stderr
143 sys.stderr = self.__old_stderr
140 pydoc.help.output = self.__old_help_output
144 pydoc.help.output = self.__old_help_output
141
145
142
146
143 def complete(self, line):
147 def complete(self, line):
144 word = line.split('\n')[-1].split(' ')[-1]
148 word = line.split('\n')[-1].split(' ')[-1]
145 completions = self.ipython0.complete(word)
149 completions = self.ipython0.complete(word)
146 # FIXME: The proper sort should be done in the complete method.
150 # FIXME: The proper sort should be done in the complete method.
147 key = lambda x: x.replace('_', '')
151 key = lambda x: x.replace('_', '')
148 completions.sort(key=key)
152 completions.sort(key=key)
149 if completions:
153 if completions:
150 prefix = common_prefix(completions)
154 prefix = common_prefix(completions)
151 line = line[:-len(word)] + prefix
155 line = line[:-len(word)] + prefix
152 return line, completions
156 return line, completions
153
157
154
158
155 def do_exit(self):
159 def do_exit(self):
156 """ Exit the shell, cleanup and save the history.
160 """ Exit the shell, cleanup and save the history.
157 """
161 """
158 self.ipython0.atexit_operations()
162 self.ipython0.atexit_operations()
159
163
@@ -1,430 +1,432 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 A Wx widget to act as a console and input commands.
3 A Wx widget to act as a console and input commands.
4
4
5 This widget deals with prompts and provides an edit buffer
5 This widget deals with prompts and provides an edit buffer
6 restricted to after the last prompt.
6 restricted to after the last prompt.
7 """
7 """
8
8
9 __docformat__ = "restructuredtext en"
9 __docformat__ = "restructuredtext en"
10
10
11 #-------------------------------------------------------------------------------
11 #-------------------------------------------------------------------------------
12 # Copyright (C) 2008 The IPython Development Team
12 # Copyright (C) 2008 The IPython Development Team
13 #
13 #
14 # Distributed under the terms of the BSD License. The full license is
14 # Distributed under the terms of the BSD License. The full license is
15 # in the file COPYING, distributed as part of this software.
15 # in the file COPYING, distributed as part of this software.
16 #-------------------------------------------------------------------------------
16 #-------------------------------------------------------------------------------
17
17
18 #-------------------------------------------------------------------------------
18 #-------------------------------------------------------------------------------
19 # Imports
19 # Imports
20 #-------------------------------------------------------------------------------
20 #-------------------------------------------------------------------------------
21
21
22 import wx
22 import wx
23 import wx.stc as stc
23 import wx.stc as stc
24
24
25 from wx.py import editwindow
25 from wx.py import editwindow
26 import sys
26 import sys
27 LINESEP = '\n'
27 LINESEP = '\n'
28 if sys.platform == 'win32':
28 if sys.platform == 'win32':
29 LINESEP = '\n\r'
29 LINESEP = '\n\r'
30
30
31 import re
31 import re
32
32
33 # FIXME: Need to provide an API for non user-generated display on the
33 # FIXME: Need to provide an API for non user-generated display on the
34 # screen: this should not be editable by the user.
34 # screen: this should not be editable by the user.
35
35
36 _DEFAULT_SIZE = 10
36 _DEFAULT_SIZE = 10
37
37
38 _DEFAULT_STYLE = {
38 _DEFAULT_STYLE = {
39 'stdout' : 'fore:#0000FF',
39 'stdout' : 'fore:#0000FF',
40 'stderr' : 'fore:#007f00',
40 'stderr' : 'fore:#007f00',
41 'trace' : 'fore:#FF0000',
41 'trace' : 'fore:#FF0000',
42
42
43 'default' : 'size:%d' % _DEFAULT_SIZE,
43 'default' : 'size:%d' % _DEFAULT_SIZE,
44 'bracegood' : 'fore:#FFFFFF,back:#0000FF,bold',
44 'bracegood' : 'fore:#FFFFFF,back:#0000FF,bold',
45 'bracebad' : 'fore:#000000,back:#FF0000,bold',
45 'bracebad' : 'fore:#000000,back:#FF0000,bold',
46
46
47 # properties for the various Python lexer styles
47 # properties for the various Python lexer styles
48 'comment' : 'fore:#007F00',
48 'comment' : 'fore:#007F00',
49 'number' : 'fore:#007F7F',
49 'number' : 'fore:#007F7F',
50 'string' : 'fore:#7F007F,italic',
50 'string' : 'fore:#7F007F,italic',
51 'char' : 'fore:#7F007F,italic',
51 'char' : 'fore:#7F007F,italic',
52 'keyword' : 'fore:#00007F,bold',
52 'keyword' : 'fore:#00007F,bold',
53 'triple' : 'fore:#7F0000',
53 'triple' : 'fore:#7F0000',
54 'tripledouble' : 'fore:#7F0000',
54 'tripledouble' : 'fore:#7F0000',
55 'class' : 'fore:#0000FF,bold,underline',
55 'class' : 'fore:#0000FF,bold,underline',
56 'def' : 'fore:#007F7F,bold',
56 'def' : 'fore:#007F7F,bold',
57 'operator' : 'bold'
57 'operator' : 'bold'
58 }
58 }
59
59
60 # new style numbers
60 # new style numbers
61 _STDOUT_STYLE = 15
61 _STDOUT_STYLE = 15
62 _STDERR_STYLE = 16
62 _STDERR_STYLE = 16
63 _TRACE_STYLE = 17
63 _TRACE_STYLE = 17
64
64
65
65
66 # system colors
66 # system colors
67 #SYS_COLOUR_BACKGROUND = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BACKGROUND)
67 #SYS_COLOUR_BACKGROUND = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BACKGROUND)
68
68
69 #-------------------------------------------------------------------------------
69 #-------------------------------------------------------------------------------
70 # The console widget class
70 # The console widget class
71 #-------------------------------------------------------------------------------
71 #-------------------------------------------------------------------------------
72 class ConsoleWidget(editwindow.EditWindow):
72 class ConsoleWidget(editwindow.EditWindow):
73 """ Specialized styled text control view for console-like workflow.
73 """ Specialized styled text control view for console-like workflow.
74
74
75 This widget is mainly interested in dealing with the prompt and
75 This widget is mainly interested in dealing with the prompt and
76 keeping the cursor inside the editing line.
76 keeping the cursor inside the editing line.
77 """
77 """
78
78
79 title = 'Console'
79 title = 'Console'
80
80
81 style = _DEFAULT_STYLE.copy()
81 style = _DEFAULT_STYLE.copy()
82
82
83 # Translation table from ANSI escape sequences to color. Override
83 # Translation table from ANSI escape sequences to color. Override
84 # this to specify your colors.
84 # this to specify your colors.
85 ANSI_STYLES = {'0;30': [0, 'BLACK'], '0;31': [1, 'RED'],
85 ANSI_STYLES = {'0;30': [0, 'BLACK'], '0;31': [1, 'RED'],
86 '0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
86 '0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
87 '0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
87 '0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
88 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
88 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
89 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
89 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
90 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
90 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
91 '1;34': [12, 'LIGHT BLUE'], '1;35':
91 '1;34': [12, 'LIGHT BLUE'], '1;35':
92 [13, 'MEDIUM VIOLET RED'],
92 [13, 'MEDIUM VIOLET RED'],
93 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
93 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
94
94
95 # The color of the carret (call _apply_style() after setting)
95 # The color of the carret (call _apply_style() after setting)
96 carret_color = 'BLACK'
96 carret_color = 'BLACK'
97
97
98 #--------------------------------------------------------------------------
98 #--------------------------------------------------------------------------
99 # Public API
99 # Public API
100 #--------------------------------------------------------------------------
100 #--------------------------------------------------------------------------
101
101
102 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
102 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
103 size=wx.DefaultSize, style=0, ):
103 size=wx.DefaultSize, style=0, ):
104 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
104 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
105 self.configure_scintilla()
105 self.configure_scintilla()
106
106
107 # FIXME: we need to retrieve this from the interpreter.
107 # FIXME: we need to retrieve this from the interpreter.
108 self.prompt = \
108 self.prompt = \
109 '\n\x01\x1b[0;34m\x02In [\x01\x1b[1;34m\x02%i\x01\x1b[0;34m\x02]: \x01\x1b[0m\x02'
109 '\n\x01\x1b[0;34m\x02In [\x01\x1b[1;34m\x02%i\x01\x1b[0;34m\x02]: \x01\x1b[0m\x02'
110 self.new_prompt(self.prompt % 1)
110 self.new_prompt(self.prompt % 1)
111
111
112 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
112 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
113 self.Bind(wx.EVT_KEY_UP, self._on_key_up)
113 self.Bind(wx.EVT_KEY_UP, self._on_key_up)
114
114
115
115
116 def configure_scintilla(self):
116 def configure_scintilla(self):
117 self.SetEOLMode(stc.STC_EOL_LF)
117 self.SetEOLMode(stc.STC_EOL_LF)
118
118
119 # Ctrl"+" or Ctrl "-" can be used to zoomin/zoomout the text inside
119 # Ctrl"+" or Ctrl "-" can be used to zoomin/zoomout the text inside
120 # the widget
120 # the widget
121 self.CmdKeyAssign(ord('+'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
121 self.CmdKeyAssign(ord('+'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
122 self.CmdKeyAssign(ord('-'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
122 self.CmdKeyAssign(ord('-'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
123 # Also allow Ctrl Shift "=" for poor non US keyboard users.
123 # Also allow Ctrl Shift "=" for poor non US keyboard users.
124 self.CmdKeyAssign(ord('='), stc.STC_SCMOD_CTRL|stc.STC_SCMOD_SHIFT,
124 self.CmdKeyAssign(ord('='), stc.STC_SCMOD_CTRL|stc.STC_SCMOD_SHIFT,
125 stc.STC_CMD_ZOOMIN)
125 stc.STC_CMD_ZOOMIN)
126
126
127 #self.CmdKeyAssign(stc.STC_KEY_PRIOR, stc.STC_SCMOD_SHIFT,
127 #self.CmdKeyAssign(stc.STC_KEY_PRIOR, stc.STC_SCMOD_SHIFT,
128 # stc.STC_CMD_PAGEUP)
128 # stc.STC_CMD_PAGEUP)
129
129
130 #self.CmdKeyAssign(stc.STC_KEY_NEXT, stc.STC_SCMOD_SHIFT,
130 #self.CmdKeyAssign(stc.STC_KEY_NEXT, stc.STC_SCMOD_SHIFT,
131 # stc.STC_CMD_PAGEDOWN)
131 # stc.STC_CMD_PAGEDOWN)
132
132
133 # Keys: we need to clear some of the keys the that don't play
133 # Keys: we need to clear some of the keys the that don't play
134 # well with a console.
134 # well with a console.
135 self.CmdKeyClear(ord('D'), stc.STC_SCMOD_CTRL)
135 self.CmdKeyClear(ord('D'), stc.STC_SCMOD_CTRL)
136 self.CmdKeyClear(ord('L'), stc.STC_SCMOD_CTRL)
136 self.CmdKeyClear(ord('L'), stc.STC_SCMOD_CTRL)
137 self.CmdKeyClear(ord('T'), stc.STC_SCMOD_CTRL)
137 self.CmdKeyClear(ord('T'), stc.STC_SCMOD_CTRL)
138
138
139
139
140 self.SetEOLMode(stc.STC_EOL_CRLF)
140 self.SetEOLMode(stc.STC_EOL_CRLF)
141 self.SetWrapMode(stc.STC_WRAP_CHAR)
141 self.SetWrapMode(stc.STC_WRAP_CHAR)
142 self.SetWrapMode(stc.STC_WRAP_WORD)
142 self.SetWrapMode(stc.STC_WRAP_WORD)
143 self.SetBufferedDraw(True)
143 self.SetBufferedDraw(True)
144 self.SetUseAntiAliasing(True)
144 self.SetUseAntiAliasing(True)
145 self.SetLayoutCache(stc.STC_CACHE_PAGE)
145 self.SetLayoutCache(stc.STC_CACHE_PAGE)
146 self.SetUndoCollection(False)
146 self.SetUndoCollection(False)
147 self.SetUseTabs(True)
147 self.SetUseTabs(True)
148 self.SetIndent(4)
148 self.SetIndent(4)
149 self.SetTabWidth(4)
149 self.SetTabWidth(4)
150
150
151 self.EnsureCaretVisible()
151 self.EnsureCaretVisible()
152 # we don't want scintilla's autocompletion to choose
152 # we don't want scintilla's autocompletion to choose
153 # automaticaly out of a single choice list, as we pop it up
153 # automaticaly out of a single choice list, as we pop it up
154 # automaticaly
154 # automaticaly
155 self.AutoCompSetChooseSingle(False)
155 self.AutoCompSetChooseSingle(False)
156 self.AutoCompSetMaxHeight(10)
156 self.AutoCompSetMaxHeight(10)
157
157
158 self.SetMargins(3, 3) #text is moved away from border with 3px
158 self.SetMargins(3, 3) #text is moved away from border with 3px
159 # Suppressing Scintilla margins
159 # Suppressing Scintilla margins
160 self.SetMarginWidth(0, 0)
160 self.SetMarginWidth(0, 0)
161 self.SetMarginWidth(1, 0)
161 self.SetMarginWidth(1, 0)
162 self.SetMarginWidth(2, 0)
162 self.SetMarginWidth(2, 0)
163
163
164 self._apply_style()
164 self._apply_style()
165
165
166 # Xterm escape sequences
166 # Xterm escape sequences
167 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
167 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
168 self.title_pat = re.compile('\x1b]0;(.*?)\x07')
168 self.title_pat = re.compile('\x1b]0;(.*?)\x07')
169
169
170 #self.SetEdgeMode(stc.STC_EDGE_LINE)
170 #self.SetEdgeMode(stc.STC_EDGE_LINE)
171 #self.SetEdgeColumn(80)
171 #self.SetEdgeColumn(80)
172
172
173 # styles
173 # styles
174 p = self.style
174 p = self.style
175 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default'])
175 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default'])
176 self.StyleClearAll()
176 self.StyleClearAll()
177 self.StyleSetSpec(_STDOUT_STYLE, p['stdout'])
177 self.StyleSetSpec(_STDOUT_STYLE, p['stdout'])
178 self.StyleSetSpec(_STDERR_STYLE, p['stderr'])
178 self.StyleSetSpec(_STDERR_STYLE, p['stderr'])
179 self.StyleSetSpec(_TRACE_STYLE, p['trace'])
179 self.StyleSetSpec(_TRACE_STYLE, p['trace'])
180
180
181 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, p['bracegood'])
181 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, p['bracegood'])
182 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, p['bracebad'])
182 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, p['bracebad'])
183 self.StyleSetSpec(stc.STC_P_COMMENTLINE, p['comment'])
183 self.StyleSetSpec(stc.STC_P_COMMENTLINE, p['comment'])
184 self.StyleSetSpec(stc.STC_P_NUMBER, p['number'])
184 self.StyleSetSpec(stc.STC_P_NUMBER, p['number'])
185 self.StyleSetSpec(stc.STC_P_STRING, p['string'])
185 self.StyleSetSpec(stc.STC_P_STRING, p['string'])
186 self.StyleSetSpec(stc.STC_P_CHARACTER, p['char'])
186 self.StyleSetSpec(stc.STC_P_CHARACTER, p['char'])
187 self.StyleSetSpec(stc.STC_P_WORD, p['keyword'])
187 self.StyleSetSpec(stc.STC_P_WORD, p['keyword'])
188 self.StyleSetSpec(stc.STC_P_WORD2, p['keyword'])
188 self.StyleSetSpec(stc.STC_P_WORD2, p['keyword'])
189 self.StyleSetSpec(stc.STC_P_TRIPLE, p['triple'])
189 self.StyleSetSpec(stc.STC_P_TRIPLE, p['triple'])
190 self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, p['tripledouble'])
190 self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, p['tripledouble'])
191 self.StyleSetSpec(stc.STC_P_CLASSNAME, p['class'])
191 self.StyleSetSpec(stc.STC_P_CLASSNAME, p['class'])
192 self.StyleSetSpec(stc.STC_P_DEFNAME, p['def'])
192 self.StyleSetSpec(stc.STC_P_DEFNAME, p['def'])
193 self.StyleSetSpec(stc.STC_P_OPERATOR, p['operator'])
193 self.StyleSetSpec(stc.STC_P_OPERATOR, p['operator'])
194 self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, p['comment'])
194 self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, p['comment'])
195
195
196
196
197 def write(self, text):
197 def write(self, text):
198 """ Write given text to buffer, while translating the ansi escape
198 """ Write given text to buffer, while translating the ansi escape
199 sequences.
199 sequences.
200 """
200 """
201 # XXX: do not put print statements in this method, the print
201 # XXX: do not put print statements to sys.stdout/sys.stderr in
202 # statements will call this method, and you will end up with
202 # this method, the print statements will call this method, as
203 # an infinit loop
203 # you will end up with an infinit loop
204 if self.debug:
205 print >>sys.__stderr__, text
204 title = self.title_pat.split(text)
206 title = self.title_pat.split(text)
205 if len(title)>1:
207 if len(title)>1:
206 self.title = title[-2]
208 self.title = title[-2]
207
209
208 text = self.title_pat.sub('', text)
210 text = self.title_pat.sub('', text)
209 segments = self.color_pat.split(text)
211 segments = self.color_pat.split(text)
210 segment = segments.pop(0)
212 segment = segments.pop(0)
211 self.GotoPos(self.GetLength())
213 self.GotoPos(self.GetLength())
212 self.StartStyling(self.GetLength(), 0xFF)
214 self.StartStyling(self.GetLength(), 0xFF)
213 self.AppendText(segment)
215 self.AppendText(segment)
214
216
215 if segments:
217 if segments:
216 for ansi_tag, text in zip(segments[::2], segments[1::2]):
218 for ansi_tag, text in zip(segments[::2], segments[1::2]):
217 self.StartStyling(self.GetLength(), 0xFF)
219 self.StartStyling(self.GetLength(), 0xFF)
218 self.AppendText(text)
220 self.AppendText(text)
219
221
220 if ansi_tag not in self.ANSI_STYLES:
222 if ansi_tag not in self.ANSI_STYLES:
221 style = 0
223 style = 0
222 else:
224 else:
223 style = self.ANSI_STYLES[ansi_tag][0]
225 style = self.ANSI_STYLES[ansi_tag][0]
224
226
225 self.SetStyling(len(text), style)
227 self.SetStyling(len(text), style)
226
228
227 self.GotoPos(self.GetLength())
229 self.GotoPos(self.GetLength())
228 wx.Yield()
230 wx.Yield()
229
231
230
232
231 def new_prompt(self, prompt):
233 def new_prompt(self, prompt):
232 """ Prints a prompt at start of line, and move the start of the
234 """ Prints a prompt at start of line, and move the start of the
233 current block there.
235 current block there.
234
236
235 The prompt can be give with ascii escape sequences.
237 The prompt can be give with ascii escape sequences.
236 """
238 """
237 self.write(prompt)
239 self.write(prompt)
238 # now we update our cursor giving end of prompt
240 # now we update our cursor giving end of prompt
239 self.current_prompt_pos = self.GetLength()
241 self.current_prompt_pos = self.GetLength()
240 self.current_prompt_line = self.GetCurrentLine()
242 self.current_prompt_line = self.GetCurrentLine()
241 wx.Yield()
243 wx.Yield()
242 self.EnsureCaretVisible()
244 self.EnsureCaretVisible()
243
245
244
246
245 def replace_current_edit_buffer(self, text):
247 def replace_current_edit_buffer(self, text):
246 """ Replace currently entered command line with given text.
248 """ Replace currently entered command line with given text.
247 """
249 """
248 self.SetSelection(self.current_prompt_pos, self.GetLength())
250 self.SetSelection(self.current_prompt_pos, self.GetLength())
249 self.ReplaceSelection(text)
251 self.ReplaceSelection(text)
250 self.GotoPos(self.GetLength())
252 self.GotoPos(self.GetLength())
251
253
252
254
253 def get_current_edit_buffer(self):
255 def get_current_edit_buffer(self):
254 """ Returns the text in current edit buffer.
256 """ Returns the text in current edit buffer.
255 """
257 """
256 current_edit_buffer = self.GetTextRange(self.current_prompt_pos,
258 current_edit_buffer = self.GetTextRange(self.current_prompt_pos,
257 self.GetLength())
259 self.GetLength())
258 current_edit_buffer = current_edit_buffer.replace(LINESEP, '\n')
260 current_edit_buffer = current_edit_buffer.replace(LINESEP, '\n')
259 return current_edit_buffer
261 return current_edit_buffer
260
262
261
263
262 #--------------------------------------------------------------------------
264 #--------------------------------------------------------------------------
263 # Private API
265 # Private API
264 #--------------------------------------------------------------------------
266 #--------------------------------------------------------------------------
265
267
266 def _apply_style(self):
268 def _apply_style(self):
267 """ Applies the colors for the different text elements and the
269 """ Applies the colors for the different text elements and the
268 carret.
270 carret.
269 """
271 """
270 self.SetCaretForeground(self.carret_color)
272 self.SetCaretForeground(self.carret_color)
271
273
272 #self.StyleClearAll()
274 #self.StyleClearAll()
273 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,
275 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,
274 "fore:#FF0000,back:#0000FF,bold")
276 "fore:#FF0000,back:#0000FF,bold")
275 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,
277 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,
276 "fore:#000000,back:#FF0000,bold")
278 "fore:#000000,back:#FF0000,bold")
277
279
278 for style in self.ANSI_STYLES.values():
280 for style in self.ANSI_STYLES.values():
279 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
281 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
280
282
281
283
282 def write_completion(self, possibilities):
284 def write_completion(self, possibilities):
283 # FIXME: This is non Wx specific and needs to be moved into
285 # FIXME: This is non Wx specific and needs to be moved into
284 # the base class.
286 # the base class.
285 current_buffer = self.get_current_edit_buffer()
287 current_buffer = self.get_current_edit_buffer()
286
288
287 self.write('\n')
289 self.write('\n')
288 max_len = len(max(possibilities, key=len)) + 1
290 max_len = len(max(possibilities, key=len)) + 1
289
291
290 #now we check how much symbol we can put on a line...
292 #now we check how much symbol we can put on a line...
291 chars_per_line = self.GetSize()[0]/self.GetCharWidth()
293 chars_per_line = self.GetSize()[0]/self.GetCharWidth()
292 symbols_per_line = max(1, chars_per_line/max_len)
294 symbols_per_line = max(1, chars_per_line/max_len)
293
295
294 pos = 1
296 pos = 1
295 buf = []
297 buf = []
296 for symbol in possibilities:
298 for symbol in possibilities:
297 if pos < symbols_per_line:
299 if pos < symbols_per_line:
298 buf.append(symbol.ljust(max_len))
300 buf.append(symbol.ljust(max_len))
299 pos += 1
301 pos += 1
300 else:
302 else:
301 buf.append(symbol.rstrip() + '\n')
303 buf.append(symbol.rstrip() + '\n')
302 pos = 1
304 pos = 1
303 self.write(''.join(buf))
305 self.write(''.join(buf))
304 self.new_prompt(self.prompt % (self.last_result['number'] + 1))
306 self.new_prompt(self.prompt % (self.last_result['number'] + 1))
305 self.replace_current_edit_buffer(current_buffer)
307 self.replace_current_edit_buffer(current_buffer)
306
308
307
309
308 def pop_completion(self, possibilities, offset=0):
310 def pop_completion(self, possibilities, offset=0):
309 """ Pops up an autocompletion menu. Offset is the offset
311 """ Pops up an autocompletion menu. Offset is the offset
310 in characters of the position at which the menu should
312 in characters of the position at which the menu should
311 appear, relativ to the cursor.
313 appear, relativ to the cursor.
312 """
314 """
313 self.AutoCompSetIgnoreCase(False)
315 self.AutoCompSetIgnoreCase(False)
314 self.AutoCompSetAutoHide(False)
316 self.AutoCompSetAutoHide(False)
315 self.AutoCompSetMaxHeight(len(possibilities))
317 self.AutoCompSetMaxHeight(len(possibilities))
316 self.AutoCompShow(offset, " ".join(possibilities))
318 self.AutoCompShow(offset, " ".join(possibilities))
317
319
318
320
319 def scroll_to_bottom(self):
321 def scroll_to_bottom(self):
320 maxrange = self.GetScrollRange(wx.VERTICAL)
322 maxrange = self.GetScrollRange(wx.VERTICAL)
321 self.ScrollLines(maxrange)
323 self.ScrollLines(maxrange)
322
324
323
325
324 def _on_key_down(self, event, skip=True):
326 def _on_key_down(self, event, skip=True):
325 """ Key press callback used for correcting behavior for
327 """ Key press callback used for correcting behavior for
326 console-like interfaces: the cursor is constraint to be after
328 console-like interfaces: the cursor is constraint to be after
327 the last prompt.
329 the last prompt.
328
330
329 Return True if event as been catched.
331 Return True if event as been catched.
330 """
332 """
331 catched = True
333 catched = True
332 # Intercept some specific keys.
334 # Intercept some specific keys.
333 if event.KeyCode == ord('L') and event.ControlDown() :
335 if event.KeyCode == ord('L') and event.ControlDown() :
334 self.scroll_to_bottom()
336 self.scroll_to_bottom()
335 elif event.KeyCode == ord('K') and event.ControlDown() :
337 elif event.KeyCode == ord('K') and event.ControlDown() :
336 self.replace_current_edit_buffer('')
338 self.replace_current_edit_buffer('')
337 elif event.KeyCode == wx.WXK_PAGEUP and event.ShiftDown():
339 elif event.KeyCode == wx.WXK_PAGEUP and event.ShiftDown():
338 self.ScrollPages(-1)
340 self.ScrollPages(-1)
339 elif event.KeyCode == wx.WXK_PAGEDOWN and event.ShiftDown():
341 elif event.KeyCode == wx.WXK_PAGEDOWN and event.ShiftDown():
340 self.ScrollPages(1)
342 self.ScrollPages(1)
341 elif event.KeyCode == wx.WXK_UP and event.ShiftDown():
343 elif event.KeyCode == wx.WXK_UP and event.ShiftDown():
342 self.ScrollLines(-1)
344 self.ScrollLines(-1)
343 elif event.KeyCode == wx.WXK_DOWN and event.ShiftDown():
345 elif event.KeyCode == wx.WXK_DOWN and event.ShiftDown():
344 self.ScrollLines(1)
346 self.ScrollLines(1)
345 else:
347 else:
346 catched = False
348 catched = False
347
349
348 if self.AutoCompActive():
350 if self.AutoCompActive():
349 event.Skip()
351 event.Skip()
350 else:
352 else:
351 if event.KeyCode in (13, wx.WXK_NUMPAD_ENTER) and \
353 if event.KeyCode in (13, wx.WXK_NUMPAD_ENTER) and \
352 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
354 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
353 catched = True
355 catched = True
354 self.CallTipCancel()
356 self.CallTipCancel()
355 self.write('\n')
357 self.write('\n')
356 # Under windows scintilla seems to be doing funny stuff to the
358 # Under windows scintilla seems to be doing funny stuff to the
357 # line returns here, but get_current_edit_buffer filters this
359 # line returns here, but get_current_edit_buffer filters this
358 # out.
360 # out.
359 if sys.platform == 'win32':
361 if sys.platform == 'win32':
360 self.replace_current_edit_buffer(
362 self.replace_current_edit_buffer(
361 self.get_current_edit_buffer())
363 self.get_current_edit_buffer())
362 self._on_enter()
364 self._on_enter()
363
365
364 elif event.KeyCode == wx.WXK_HOME:
366 elif event.KeyCode == wx.WXK_HOME:
365 if event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
367 if event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
366 self.GotoPos(self.current_prompt_pos)
368 self.GotoPos(self.current_prompt_pos)
367 catched = True
369 catched = True
368
370
369 elif event.Modifiers in (wx.MOD_SHIFT, wx.MOD_WIN) :
371 elif event.Modifiers in (wx.MOD_SHIFT, wx.MOD_WIN) :
370 # FIXME: This behavior is not ideal: if the selection
372 # FIXME: This behavior is not ideal: if the selection
371 # is already started, it will jump.
373 # is already started, it will jump.
372 self.SetSelectionStart(self.current_prompt_pos)
374 self.SetSelectionStart(self.current_prompt_pos)
373 self.SetSelectionEnd(self.GetCurrentPos())
375 self.SetSelectionEnd(self.GetCurrentPos())
374 catched = True
376 catched = True
375
377
376 elif event.KeyCode == wx.WXK_UP:
378 elif event.KeyCode == wx.WXK_UP:
377 if self.GetCurrentLine() > self.current_prompt_line:
379 if self.GetCurrentLine() > self.current_prompt_line:
378 if self.GetCurrentLine() == self.current_prompt_line + 1 \
380 if self.GetCurrentLine() == self.current_prompt_line + 1 \
379 and self.GetColumn(self.GetCurrentPos()) < \
381 and self.GetColumn(self.GetCurrentPos()) < \
380 self.GetColumn(self.current_prompt_pos):
382 self.GetColumn(self.current_prompt_pos):
381 self.GotoPos(self.current_prompt_pos)
383 self.GotoPos(self.current_prompt_pos)
382 else:
384 else:
383 event.Skip()
385 event.Skip()
384 catched = True
386 catched = True
385
387
386 elif event.KeyCode in (wx.WXK_LEFT, wx.WXK_BACK):
388 elif event.KeyCode in (wx.WXK_LEFT, wx.WXK_BACK):
387 if self.GetCurrentPos() > self.current_prompt_pos:
389 if self.GetCurrentPos() > self.current_prompt_pos:
388 event.Skip()
390 event.Skip()
389 catched = True
391 catched = True
390
392
391 if skip and not catched:
393 if skip and not catched:
392 # Put the cursor back in the edit region
394 # Put the cursor back in the edit region
393 if self.GetCurrentPos() < self.current_prompt_pos:
395 if self.GetCurrentPos() < self.current_prompt_pos:
394 self.GotoPos(self.current_prompt_pos)
396 self.GotoPos(self.current_prompt_pos)
395 else:
397 else:
396 event.Skip()
398 event.Skip()
397
399
398 return catched
400 return catched
399
401
400
402
401 def _on_key_up(self, event, skip=True):
403 def _on_key_up(self, event, skip=True):
402 """ If cursor is outside the editing region, put it back.
404 """ If cursor is outside the editing region, put it back.
403 """
405 """
404 event.Skip()
406 event.Skip()
405 if self.GetCurrentPos() < self.current_prompt_pos:
407 if self.GetCurrentPos() < self.current_prompt_pos:
406 self.GotoPos(self.current_prompt_pos)
408 self.GotoPos(self.current_prompt_pos)
407
409
408
410
409
411
410
412
411 if __name__ == '__main__':
413 if __name__ == '__main__':
412 # Some simple code to test the console widget.
414 # Some simple code to test the console widget.
413 class MainWindow(wx.Frame):
415 class MainWindow(wx.Frame):
414 def __init__(self, parent, id, title):
416 def __init__(self, parent, id, title):
415 wx.Frame.__init__(self, parent, id, title, size=(300,250))
417 wx.Frame.__init__(self, parent, id, title, size=(300,250))
416 self._sizer = wx.BoxSizer(wx.VERTICAL)
418 self._sizer = wx.BoxSizer(wx.VERTICAL)
417 self.console_widget = ConsoleWidget(self)
419 self.console_widget = ConsoleWidget(self)
418 self._sizer.Add(self.console_widget, 1, wx.EXPAND)
420 self._sizer.Add(self.console_widget, 1, wx.EXPAND)
419 self.SetSizer(self._sizer)
421 self.SetSizer(self._sizer)
420 self.SetAutoLayout(1)
422 self.SetAutoLayout(1)
421 self.Show(True)
423 self.Show(True)
422
424
423 app = wx.PySimpleApp()
425 app = wx.PySimpleApp()
424 w = MainWindow(None, wx.ID_ANY, 'ConsoleWidget')
426 w = MainWindow(None, wx.ID_ANY, 'ConsoleWidget')
425 w.SetSize((780, 460))
427 w.SetSize((780, 460))
426 w.Show()
428 w.Show()
427
429
428 app.MainLoop()
430 app.MainLoop()
429
431
430
432
@@ -1,73 +1,75 b''
1 """
1 """
2 Entry point for a simple application giving a graphical frontend to
2 Entry point for a simple application giving a graphical frontend to
3 ipython.
3 ipython.
4 """
4 """
5
5
6 import wx
6 import wx
7 from wx_frontend import WxController
7 from wx_frontend import WxController
8 import __builtin__
8
9
9 class WIPythonController(WxController):
10 class WIPythonController(WxController):
10 """ Sub class of WxController that adds some application-specific
11 """ Sub class of WxController that adds some application-specific
11 bindings.
12 bindings.
12 """
13 """
13
14
14 def __init__(self, *args, **kwargs):
15 def __init__(self, *args, **kwargs):
15 WxController.__init__(self, *args, **kwargs)
16 WxController.__init__(self, *args, **kwargs)
16 self.ipython0.ask_exit = self.do_exit
17 self.ipython0.ask_exit = self.do_exit
17
18
18
19
19 def _on_key_down(self, event, skip=True):
20 def _on_key_down(self, event, skip=True):
20 # Intercept Ctrl-D to quit
21 # Intercept Ctrl-D to quit
21 if event.KeyCode == ord('D') and event.ControlDown() and \
22 if event.KeyCode == ord('D') and event.ControlDown() and \
22 self.get_current_edit_buffer()=='':
23 self.get_current_edit_buffer()=='' and \
24 not self.raw_input == __builtin__.raw_input:
23 wx.CallAfter(self.ask_exit)
25 wx.CallAfter(self.ask_exit)
24 else:
26 else:
25 WxController._on_key_down(self, event, skip=skip)
27 WxController._on_key_down(self, event, skip=skip)
26
28
27
29
28 def ask_exit(self):
30 def ask_exit(self):
29 """ Ask the user whether to exit.
31 """ Ask the user whether to exit.
30 """
32 """
31 self.write('\n')
33 self.write('\n')
32 self.capture_output()
34 self.capture_output()
33 self.ipython0.shell.exit()
35 self.ipython0.shell.exit()
34 self.release_output()
36 self.release_output()
35 wx.Yield()
37 wx.Yield()
36 if not self.ipython0.exit_now:
38 if not self.ipython0.exit_now:
37 self.new_prompt(self.prompt % (self.last_result['number'] + 1))
39 self.new_prompt(self.prompt % (self.last_result['number'] + 1))
38
40
39
41
40 def do_exit(self):
42 def do_exit(self):
41 """ Exits the interpreter, kills the windows.
43 """ Exits the interpreter, kills the windows.
42 """
44 """
43 WxController.do_exit(self)
45 WxController.do_exit(self)
44 self.release_output()
46 self.release_output()
45 wx.CallAfter(wx.Exit)
47 wx.CallAfter(wx.Exit)
46
48
47
49
48
50
49 class WIPython(wx.Frame):
51 class WIPython(wx.Frame):
50 """ Main frame of the WIPython app.
52 """ Main frame of the WIPython app.
51 """
53 """
52
54
53 def __init__(self, parent, id, title):
55 def __init__(self, parent, id, title):
54 wx.Frame.__init__(self, parent, id, title, size=(300,250))
56 wx.Frame.__init__(self, parent, id, title, size=(300,250))
55 self._sizer = wx.BoxSizer(wx.VERTICAL)
57 self._sizer = wx.BoxSizer(wx.VERTICAL)
56 self.shell = WIPythonController(self)
58 self.shell = WIPythonController(self)
57 self._sizer.Add(self.shell, 1, wx.EXPAND)
59 self._sizer.Add(self.shell, 1, wx.EXPAND)
58 self.SetSizer(self._sizer)
60 self.SetSizer(self._sizer)
59 self.SetAutoLayout(1)
61 self.SetAutoLayout(1)
60 self.Show(True)
62 self.Show(True)
61
63
62
64
63 def main():
65 def main():
64 app = wx.PySimpleApp()
66 app = wx.PySimpleApp()
65 frame = WIPython(None, wx.ID_ANY, 'WIPython')
67 frame = WIPython(None, wx.ID_ANY, 'WIPython')
66 frame.shell.SetFocus()
68 frame.shell.SetFocus()
67 frame.shell.app = app
69 frame.shell.app = app
68 frame.SetSize((680, 460))
70 frame.SetSize((680, 460))
69
71
70 app.MainLoop()
72 app.MainLoop()
71
73
72 if __name__ == '__main__':
74 if __name__ == '__main__':
73 main()
75 main()
@@ -1,277 +1,351 b''
1 # encoding: utf-8 -*- test-case-name:
1 # encoding: utf-8 -*- test-case-name:
2 # FIXME: Need to add tests.
2 # FIXME: Need to add tests.
3 # ipython1.frontend.cocoa.tests.test_cocoa_frontend -*-
3 # ipython1.frontend.cocoa.tests.test_cocoa_frontend -*-
4
4
5 """Classes to provide a Wx frontend to the
5 """Classes to provide a Wx frontend to the
6 IPython.kernel.core.interpreter.
6 IPython.kernel.core.interpreter.
7
7
8 """
8 """
9
9
10 __docformat__ = "restructuredtext en"
10 __docformat__ = "restructuredtext en"
11
11
12 #-------------------------------------------------------------------------------
12 #-------------------------------------------------------------------------------
13 # Copyright (C) 2008 The IPython Development Team
13 # Copyright (C) 2008 The IPython Development Team
14 #
14 #
15 # Distributed under the terms of the BSD License. The full license is in
15 # Distributed under the terms of the BSD License. The full license is in
16 # the file COPYING, distributed as part of this software.
16 # the file COPYING, distributed as part of this software.
17 #-------------------------------------------------------------------------------
17 #-------------------------------------------------------------------------------
18
18
19 #-------------------------------------------------------------------------------
19 #-------------------------------------------------------------------------------
20 # Imports
20 # Imports
21 #-------------------------------------------------------------------------------
21 #-------------------------------------------------------------------------------
22
22
23
23
24 import wx
24 import wx
25 import re
25 import re
26 from wx import stc
26 from wx import stc
27 from console_widget import ConsoleWidget
27 from console_widget import ConsoleWidget
28 import __builtin__
28 import __builtin__
29 from time import sleep
29 from time import sleep
30 import sys
30
31
32 from threading import Lock
33
34 from IPython.frontend.piped_process import PipedProcess
31 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
35 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
32
36
33 #_COMMAND_BG = '#FAFAF1' # Nice green
37 #_COMMAND_BG = '#FAFAF1' # Nice green
34 _RUNNING_BUFFER_BG = '#FDFFD3' # Nice yellow
38 _RUNNING_BUFFER_BG = '#FDFFD3' # Nice yellow
35 _ERROR_BG = '#FFF1F1' # Nice red
39 _ERROR_BG = '#FFF1F1' # Nice red
36
40
37 _RUNNING_BUFFER_MARKER = 31
41 _RUNNING_BUFFER_MARKER = 31
38 _ERROR_MARKER = 30
42 _ERROR_MARKER = 30
39
43
40 #-------------------------------------------------------------------------------
44 #-------------------------------------------------------------------------------
41 # Classes to implement the Wx frontend
45 # Classes to implement the Wx frontend
42 #-------------------------------------------------------------------------------
46 #-------------------------------------------------------------------------------
43 class WxController(PrefilterFrontEnd, ConsoleWidget):
47 class WxController(PrefilterFrontEnd, ConsoleWidget):
44
48
45 output_prompt = \
49 output_prompt = \
46 '\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02%i\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02'
50 '\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02%i\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02'
47
51
48 debug = True
52 debug = True
49
53
54 # Attribute to store reference to the pipes of a subprocess, if we
55 # are running any.
56 running_process = False
57
58 # A queue for writing fast streams to the screen without flooding the
59 # event loop
60 write_buffer = []
61
62 # A lock to lock the write_buffer to make sure we don't empty it
63 # while it is being swapped
64 write_buffer_lock = Lock()
65
50 #--------------------------------------------------------------------------
66 #--------------------------------------------------------------------------
51 # Public API
67 # Public API
52 #--------------------------------------------------------------------------
68 #--------------------------------------------------------------------------
53
69
54 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
70 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
55 size=wx.DefaultSize, style=wx.CLIP_CHILDREN,
71 size=wx.DefaultSize, style=wx.CLIP_CHILDREN,
56 *args, **kwds):
72 *args, **kwds):
57 """ Create Shell instance.
73 """ Create Shell instance.
58 """
74 """
59 ConsoleWidget.__init__(self, parent, id, pos, size, style)
75 ConsoleWidget.__init__(self, parent, id, pos, size, style)
60 PrefilterFrontEnd.__init__(self)
76 PrefilterFrontEnd.__init__(self)
61
77
62 # Capture Character keys
63 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
64
65 # Marker for running buffer.
78 # Marker for running buffer.
66 self.MarkerDefine(_RUNNING_BUFFER_MARKER, stc.STC_MARK_BACKGROUND,
79 self.MarkerDefine(_RUNNING_BUFFER_MARKER, stc.STC_MARK_BACKGROUND,
67 background=_RUNNING_BUFFER_BG)
80 background=_RUNNING_BUFFER_BG)
68 # Marker for tracebacks.
81 # Marker for tracebacks.
69 self.MarkerDefine(_ERROR_MARKER, stc.STC_MARK_BACKGROUND,
82 self.MarkerDefine(_ERROR_MARKER, stc.STC_MARK_BACKGROUND,
70 background=_ERROR_BG)
83 background=_ERROR_BG)
71
84
85 # A time for flushing the write buffer
86 BUFFER_FLUSH_TIMER_ID = 100
87 self._buffer_flush_timer = wx.Timer(self, BUFFER_FLUSH_TIMER_ID)
88 wx.EVT_TIMER(self, BUFFER_FLUSH_TIMER_ID, self._buffer_flush)
72
89
73 def do_completion(self):
90 def do_completion(self):
74 """ Do code completion.
91 """ Do code completion.
75 """
92 """
93 if self.debug:
94 print >>sys.__stdout__, "do_completion",
76 line = self.get_current_edit_buffer()
95 line = self.get_current_edit_buffer()
77 new_line, completions = self.complete(line)
96 new_line, completions = self.complete(line)
78 if len(completions)>1:
97 if len(completions)>1:
79 self.write_completion(completions)
98 self.write_completion(completions)
80 self.replace_current_edit_buffer(new_line)
99 self.replace_current_edit_buffer(new_line)
100 if self.debug:
101 print >>sys.__stdout__, completions
81
102
82
103
83 def do_calltip(self):
104 def do_calltip(self):
105 if self.debug:
106 print >>sys.__stdout__, "do_calltip"
84 separators = re.compile('[\s\{\}\[\]\(\)\= ,:]')
107 separators = re.compile('[\s\{\}\[\]\(\)\= ,:]')
85 symbol = self.get_current_edit_buffer()
108 symbol = self.get_current_edit_buffer()
86 symbol_string = separators.split(symbol)[-1]
109 symbol_string = separators.split(symbol)[-1]
87 base_symbol_string = symbol_string.split('.')[0]
110 base_symbol_string = symbol_string.split('.')[0]
88 if base_symbol_string in self.shell.user_ns:
111 if base_symbol_string in self.shell.user_ns:
89 symbol = self.shell.user_ns[base_symbol_string]
112 symbol = self.shell.user_ns[base_symbol_string]
90 elif base_symbol_string in self.shell.user_global_ns:
113 elif base_symbol_string in self.shell.user_global_ns:
91 symbol = self.shell.user_global_ns[base_symbol_string]
114 symbol = self.shell.user_global_ns[base_symbol_string]
92 elif base_symbol_string in __builtin__.__dict__:
115 elif base_symbol_string in __builtin__.__dict__:
93 symbol = __builtin__.__dict__[base_symbol_string]
116 symbol = __builtin__.__dict__[base_symbol_string]
94 else:
117 else:
95 return False
118 return False
96 for name in symbol_string.split('.')[1:] + ['__doc__']:
119 for name in symbol_string.split('.')[1:] + ['__doc__']:
97 symbol = getattr(symbol, name)
120 symbol = getattr(symbol, name)
98 try:
121 try:
99 self.AutoCompCancel()
122 self.AutoCompCancel()
100 wx.Yield()
123 wx.Yield()
101 self.CallTipShow(self.GetCurrentPos(), symbol)
124 self.CallTipShow(self.GetCurrentPos(), symbol)
102 except:
125 except:
103 # The retrieve symbol couldn't be converted to a string
126 # The retrieve symbol couldn't be converted to a string
104 pass
127 pass
105
128
106
129
107 def popup_completion(self, create=False):
130 def popup_completion(self, create=False):
108 """ Updates the popup completion menu if it exists. If create is
131 """ Updates the popup completion menu if it exists. If create is
109 true, open the menu.
132 true, open the menu.
110 """
133 """
134 if self.debug:
135 print >>sys.__stdout__, "popup_completion",
111 line = self.get_current_edit_buffer()
136 line = self.get_current_edit_buffer()
112 if (self.AutoCompActive() and not line[-1] == '.') \
137 if (self.AutoCompActive() and not line[-1] == '.') \
113 or create==True:
138 or create==True:
114 suggestion, completions = self.complete(line)
139 suggestion, completions = self.complete(line)
115 offset=0
140 offset=0
116 if completions:
141 if completions:
117 complete_sep = re.compile('[\s\{\}\[\]\(\)\= ,:]')
142 complete_sep = re.compile('[\s\{\}\[\]\(\)\= ,:]')
118 residual = complete_sep.split(line)[-1]
143 residual = complete_sep.split(line)[-1]
119 offset = len(residual)
144 offset = len(residual)
120 self.pop_completion(completions, offset=offset)
145 self.pop_completion(completions, offset=offset)
146 if self.debug:
147 print >>sys.__stdout__, completions
121
148
122
149
123 def raw_input(self, prompt):
150 def raw_input(self, prompt):
124 """ A replacement from python's raw_input.
151 """ A replacement from python's raw_input.
125 """
152 """
126 self.new_prompt(prompt)
153 self.new_prompt(prompt)
127 self.waiting = True
154 self.waiting = True
128 self.__old_on_enter = self._on_enter
155 self.__old_on_enter = self._on_enter
129 def my_on_enter():
156 def my_on_enter():
130 self.waiting = False
157 self.waiting = False
131 self._on_enter = my_on_enter
158 self._on_enter = my_on_enter
132 # XXX: Busy waiting, ugly.
159 # XXX: Busy waiting, ugly.
133 while self.waiting:
160 while self.waiting:
134 wx.Yield()
161 wx.Yield()
135 sleep(0.1)
162 sleep(0.1)
136 self._on_enter = self.__old_on_enter
163 self._on_enter = self.__old_on_enter
137 return self.get_current_edit_buffer().rstrip('\n')
164 return self.get_current_edit_buffer().rstrip('\n')
138
165
139
166
140 def execute(self, python_string, raw_string=None):
167 def execute(self, python_string, raw_string=None):
141 self.CallTipCancel()
168 self.CallTipCancel()
142 self._cursor = wx.BusyCursor()
169 self._cursor = wx.BusyCursor()
143 if raw_string is None:
170 if raw_string is None:
144 raw_string = python_string
171 raw_string = python_string
145 end_line = self.current_prompt_line \
172 end_line = self.current_prompt_line \
146 + max(1, len(raw_string.split('\n'))-1)
173 + max(1, len(raw_string.split('\n'))-1)
147 for i in range(self.current_prompt_line, end_line):
174 for i in range(self.current_prompt_line, end_line):
148 self.MarkerAdd(i, _RUNNING_BUFFER_MARKER)
175 self.MarkerAdd(i, _RUNNING_BUFFER_MARKER)
149 # Update the display:
176 # Update the display:
150 wx.Yield()
177 wx.Yield()
151 ## Remove the trailing "\n" for cleaner display
152 #self.SetSelection(self.GetLength()-1, self.GetLength())
153 #self.ReplaceSelection('')
154 self.GotoPos(self.GetLength())
178 self.GotoPos(self.GetLength())
155 PrefilterFrontEnd.execute(self, python_string, raw_string=raw_string)
179 PrefilterFrontEnd.execute(self, python_string, raw_string=raw_string)
156
180
157
181
158 def capture_output(self):
182 def capture_output(self):
159 self.__old_raw_input = __builtin__.raw_input
183 self.__old_raw_input = __builtin__.raw_input
160 __builtin__.raw_input = self.raw_input
184 __builtin__.raw_input = self.raw_input
161 PrefilterFrontEnd.capture_output(self)
185 PrefilterFrontEnd.capture_output(self)
162
186
163
187
164 def release_output(self):
188 def release_output(self):
165 __builtin__.raw_input = self.__old_raw_input
189 __builtin__.raw_input = self.__old_raw_input
166 PrefilterFrontEnd.capture_output(self)
190 PrefilterFrontEnd.capture_output(self)
167
191
168
192
169 def after_execute(self):
193 def after_execute(self):
170 PrefilterFrontEnd.after_execute(self)
194 PrefilterFrontEnd.after_execute(self)
171 if hasattr(self, '_cursor'):
195 if hasattr(self, '_cursor'):
172 del self._cursor
196 del self._cursor
173
197
198
199 def system_call(self, command_string):
200 self.running_process = PipedProcess(command_string,
201 out_callback=self.buffered_write,
202 end_callback = self._end_system_call)
203 self.running_process.start()
204 # XXX: another one of these polling loops to have a blocking
205 # call
206 while self.running_process:
207 wx.Yield()
208 sleep(0.1)
209
210
211 def buffered_write(self, text):
212 """ A write method for streams, that caches the stream in order
213 to avoid flooding the event loop.
214
215 This can be called outside of the main loop, in separate
216 threads.
217 """
218 self.write_buffer_lock.acquire()
219 self.write_buffer.append(text)
220 self.write_buffer_lock.release()
221 if not self._buffer_flush_timer.IsRunning():
222 self._buffer_flush_timer.Start(100) # milliseconds
223
224
225 def _end_system_call(self):
226 """ Called at the end of a system call.
227 """
228 self.running_process = False
229
230
231 def _buffer_flush(self, event):
232 """ Called by the timer to flush the write buffer.
233
234 This is always called in the mainloop, by the wx timer.
235 """
236 self.write_buffer_lock.acquire()
237 write_buffer = self.write_buffer
238 self.write_buffer = []
239 self.write_buffer_lock.release()
240 self.write(''.join(write_buffer))
241 self._buffer_flush_timer.Stop()
242
174
243
175 def show_traceback(self):
244 def show_traceback(self):
176 start_line = self.GetCurrentLine()
245 start_line = self.GetCurrentLine()
177 PrefilterFrontEnd.show_traceback(self)
246 PrefilterFrontEnd.show_traceback(self)
178 wx.Yield()
247 wx.Yield()
179 for i in range(start_line, self.GetCurrentLine()):
248 for i in range(start_line, self.GetCurrentLine()):
180 self.MarkerAdd(i, _ERROR_MARKER)
249 self.MarkerAdd(i, _ERROR_MARKER)
181
250
182
251
183 #--------------------------------------------------------------------------
252 #--------------------------------------------------------------------------
184 # Private API
253 # Private API
185 #--------------------------------------------------------------------------
254 #--------------------------------------------------------------------------
186
255
187
188 def _on_key_down(self, event, skip=True):
256 def _on_key_down(self, event, skip=True):
189 """ Capture the character events, let the parent
257 """ Capture the character events, let the parent
190 widget handle them, and put our logic afterward.
258 widget handle them, and put our logic afterward.
191 """
259 """
192 current_line_number = self.GetCurrentLine()
260 current_line_number = self.GetCurrentLine()
193 # Calltips
261 if self.running_process and event.KeyCode<256 \
194 if event.KeyCode == ord('('):
262 and event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
263 # We are running a process, let us not be too clever.
264 ConsoleWidget._on_key_down(self, event, skip=skip)
265 if self.debug:
266 print >>sys.__stderr__, chr(event.KeyCode)
267 self.running_process.process.stdin.write(chr(event.KeyCode))
268 elif event.KeyCode in (ord('('), 57):
269 # Calltips
195 event.Skip()
270 event.Skip()
196 self.do_calltip()
271 self.do_calltip()
197 elif self.AutoCompActive():
272 elif self.AutoCompActive():
198 event.Skip()
273 event.Skip()
199 if event.KeyCode in (wx.WXK_BACK, wx.WXK_DELETE):
274 if event.KeyCode in (wx.WXK_BACK, wx.WXK_DELETE):
200 wx.CallAfter(self.popup_completion, create=True)
275 wx.CallAfter(self.popup_completion, create=True)
201 elif not event.KeyCode in (wx.WXK_UP, wx.WXK_DOWN, wx.WXK_LEFT,
276 elif not event.KeyCode in (wx.WXK_UP, wx.WXK_DOWN, wx.WXK_LEFT,
202 wx.WXK_RIGHT):
277 wx.WXK_RIGHT):
203 wx.CallAfter(self.popup_completion)
278 wx.CallAfter(self.popup_completion)
204 else:
279 else:
205 # Up history
280 # Up history
206 if event.KeyCode == wx.WXK_UP and (
281 if event.KeyCode == wx.WXK_UP and (
207 ( current_line_number == self.current_prompt_line and
282 ( current_line_number == self.current_prompt_line and
208 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
283 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
209 or event.ControlDown() ):
284 or event.ControlDown() ):
210 new_buffer = self.get_history_previous(
285 new_buffer = self.get_history_previous(
211 self.get_current_edit_buffer())
286 self.get_current_edit_buffer())
212 if new_buffer is not None:
287 if new_buffer is not None:
213 self.replace_current_edit_buffer(new_buffer)
288 self.replace_current_edit_buffer(new_buffer)
214 if self.GetCurrentLine() > self.current_prompt_line:
289 if self.GetCurrentLine() > self.current_prompt_line:
215 # Go to first line, for seemless history up.
290 # Go to first line, for seemless history up.
216 self.GotoPos(self.current_prompt_pos)
291 self.GotoPos(self.current_prompt_pos)
217 # Down history
292 # Down history
218 elif event.KeyCode == wx.WXK_DOWN and (
293 elif event.KeyCode == wx.WXK_DOWN and (
219 ( current_line_number == self.LineCount -1 and
294 ( current_line_number == self.LineCount -1 and
220 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
295 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
221 or event.ControlDown() ):
296 or event.ControlDown() ):
222 new_buffer = self.get_history_next()
297 new_buffer = self.get_history_next()
223 if new_buffer is not None:
298 if new_buffer is not None:
224 self.replace_current_edit_buffer(new_buffer)
299 self.replace_current_edit_buffer(new_buffer)
225 # Tab-completion
300 # Tab-completion
226 elif event.KeyCode == ord('\t'):
301 elif event.KeyCode == ord('\t'):
227 last_line = self.get_current_edit_buffer().split('\n')[-1]
302 last_line = self.get_current_edit_buffer().split('\n')[-1]
228 if not re.match(r'^\s*$', last_line):
303 if not re.match(r'^\s*$', last_line):
229 self.do_completion()
304 self.do_completion()
230 else:
305 else:
231 event.Skip()
306 event.Skip()
232 else:
307 else:
233 ConsoleWidget._on_key_down(self, event, skip=skip)
308 ConsoleWidget._on_key_down(self, event, skip=skip)
234
309
235
310
236 def _on_key_up(self, event, skip=True):
311 def _on_key_up(self, event, skip=True):
237 if event.KeyCode == 59:
312 if event.KeyCode in (59, ord('.')):
238 # Intercepting '.'
313 # Intercepting '.'
239 event.Skip()
314 event.Skip()
240 self.popup_completion(create=True)
315 self.popup_completion(create=True)
241 else:
316 else:
242 ConsoleWidget._on_key_up(self, event, skip=skip)
317 ConsoleWidget._on_key_up(self, event, skip=skip)
243
318
244 def _on_enter(self):
319 def _on_enter(self):
245 if self.debug:
320 if self.debug:
246 import sys
247 print >>sys.__stdout__, repr(self.get_current_edit_buffer())
321 print >>sys.__stdout__, repr(self.get_current_edit_buffer())
248 PrefilterFrontEnd._on_enter(self)
322 PrefilterFrontEnd._on_enter(self)
249
323
250 def _set_title(self, title):
324 def _set_title(self, title):
251 return self.Parent.SetTitle(title)
325 return self.Parent.SetTitle(title)
252
326
253 def _get_title(self):
327 def _get_title(self):
254 return self.Parent.GetTitle()
328 return self.Parent.GetTitle()
255
329
256 title = property(_get_title, _set_title)
330 title = property(_get_title, _set_title)
257
331
258
332
259 if __name__ == '__main__':
333 if __name__ == '__main__':
260 class MainWindow(wx.Frame):
334 class MainWindow(wx.Frame):
261 def __init__(self, parent, id, title):
335 def __init__(self, parent, id, title):
262 wx.Frame.__init__(self, parent, id, title, size=(300,250))
336 wx.Frame.__init__(self, parent, id, title, size=(300,250))
263 self._sizer = wx.BoxSizer(wx.VERTICAL)
337 self._sizer = wx.BoxSizer(wx.VERTICAL)
264 self.shell = WxController(self)
338 self.shell = WxController(self)
265 self._sizer.Add(self.shell, 1, wx.EXPAND)
339 self._sizer.Add(self.shell, 1, wx.EXPAND)
266 self.SetSizer(self._sizer)
340 self.SetSizer(self._sizer)
267 self.SetAutoLayout(1)
341 self.SetAutoLayout(1)
268 self.Show(True)
342 self.Show(True)
269
343
270 app = wx.PySimpleApp()
344 app = wx.PySimpleApp()
271 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
345 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
272 frame.shell.SetFocus()
346 frame.shell.SetFocus()
273 frame.SetSize((680, 460))
347 frame.SetSize((680, 460))
274 self = frame.shell
348 self = frame.shell
275
349
276 app.MainLoop()
350 app.MainLoop()
277
351
@@ -1,85 +1,80 b''
1 # encoding: utf-8
1 # encoding: utf-8
2
2
3 """Object to manage sys.excepthook()."""
3 """Object to manage sys.excepthook()."""
4
4
5 __docformat__ = "restructuredtext en"
5 __docformat__ = "restructuredtext en"
6
6
7 #-------------------------------------------------------------------------------
7 #-------------------------------------------------------------------------------
8 # Copyright (C) 2008 The IPython Development Team
8 # Copyright (C) 2008 The IPython Development Team
9 #
9 #
10 # Distributed under the terms of the BSD License. The full license is in
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
11 # the file COPYING, distributed as part of this software.
12 #-------------------------------------------------------------------------------
12 #-------------------------------------------------------------------------------
13
13
14 #-------------------------------------------------------------------------------
14 #-------------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #-------------------------------------------------------------------------------
16 #-------------------------------------------------------------------------------
17
18 import sys
17 import sys
19
18
20
21 class TracebackTrap(object):
19 class TracebackTrap(object):
22 """ Object to trap and format tracebacks.
20 """ Object to trap and format tracebacks.
23 """
21 """
24
22
25 def __init__(self, formatters=None):
23 def __init__(self, formatters=None):
26 # A list of formatters to apply.
24 # A list of formatters to apply.
27 if formatters is None:
25 if formatters is None:
28 formatters = []
26 formatters = []
29 self.formatters = formatters
27 self.formatters = formatters
30
28
31 # All of the traceback information provided to sys.excepthook().
29 # All of the traceback information provided to sys.excepthook().
32 self.args = None
30 self.args = None
33
31
34 # The previous hook before we replace it.
32 # The previous hook before we replace it.
35 self.old_hook = None
33 self.old_hook = None
36
34
37
35
38 def hook(self, *args):
36 def hook(self, *args):
39 """ This method actually implements the hook.
37 """ This method actually implements the hook.
40 """
38 """
41 import sys
42 print >>sys.stderr, "I have been raised"
43
44 self.args = args
39 self.args = args
45
40
46 def set(self):
41 def set(self):
47 """ Set the hook.
42 """ Set the hook.
48 """
43 """
49
44
50 if sys.excepthook is not self.hook:
45 if sys.excepthook is not self.hook:
51 self.old_hook = sys.excepthook
46 self.old_hook = sys.excepthook
52 sys.excepthook = self.hook
47 sys.excepthook = self.hook
53
48
54 def unset(self):
49 def unset(self):
55 """ Unset the hook.
50 """ Unset the hook.
56 """
51 """
57
52
58 sys.excepthook = self.old_hook
53 sys.excepthook = self.old_hook
59
54
60 def clear(self):
55 def clear(self):
61 """ Remove the stored traceback.
56 """ Remove the stored traceback.
62 """
57 """
63
58
64 self.args = None
59 self.args = None
65
60
66 def add_to_message(self, message):
61 def add_to_message(self, message):
67 """ Add the formatted display of the traceback to the message dictionary
62 """ Add the formatted display of the traceback to the message dictionary
68 being returned from the interpreter to its listeners.
63 being returned from the interpreter to its listeners.
69
64
70 Parameters
65 Parameters
71 ----------
66 ----------
72 message : dict
67 message : dict
73 """
68 """
74
69
75 # If there was no traceback, then don't add anything.
70 # If there was no traceback, then don't add anything.
76 if self.args is None:
71 if self.args is None:
77 return
72 return
78
73
79 # Go through the list of formatters and let them add their formatting.
74 # Go through the list of formatters and let them add their formatting.
80 traceback = {}
75 traceback = {}
81 for formatter in self.formatters:
76 for formatter in self.formatters:
82 traceback[formatter.identifier] = formatter(*self.args)
77 traceback[formatter.identifier] = formatter(*self.args)
83
78
84 message['traceback'] = traceback
79 message['traceback'] = traceback
85
80
General Comments 0
You need to be logged in to leave comments. Login now