Show More
@@ -0,0 +1,19 | |||
|
1 | """ | |
|
2 | Package for dealing for process execution in a callback environment, in a | |
|
3 | portable way. | |
|
4 | ||
|
5 | killable_process.py is a wrapper of subprocess.Popen that allows the | |
|
6 | subprocess and its children to be killed in a reliable way, including | |
|
7 | under windows. | |
|
8 | ||
|
9 | winprocess.py is required by killable_process.py to kill processes under | |
|
10 | windows. | |
|
11 | ||
|
12 | piped_process.py wraps process execution with callbacks to print output, | |
|
13 | in a non-blocking way. It can be used to interact with a subprocess in eg | |
|
14 | a GUI event loop. | |
|
15 | """ | |
|
16 | ||
|
17 | from pipedprocess import PipedProcess | |
|
18 | ||
|
19 |
@@ -0,0 +1,34 | |||
|
1 | # encoding: utf-8 | |
|
2 | # -*- test-case-name: IPython.frontend.tests.test_frontendbase -*- | |
|
3 | """ | |
|
4 | zope.interface mock. If zope is installed, this module provides a zope | |
|
5 | interface classes, if not it provides mocks for them. | |
|
6 | ||
|
7 | Classes provided: | |
|
8 | Interface, Attribute, implements, classProvides | |
|
9 | """ | |
|
10 | __docformat__ = "restructuredtext en" | |
|
11 | ||
|
12 | #------------------------------------------------------------------------------- | |
|
13 | # Copyright (C) 2008 The IPython Development Team | |
|
14 | # | |
|
15 | # Distributed under the terms of the BSD License. The full license is in | |
|
16 | # the file COPYING, distributed as part of this software. | |
|
17 | #------------------------------------------------------------------------------- | |
|
18 | ||
|
19 | #------------------------------------------------------------------------------- | |
|
20 | # Imports | |
|
21 | #------------------------------------------------------------------------------- | |
|
22 | import string | |
|
23 | import uuid | |
|
24 | import _ast | |
|
25 | ||
|
26 | try: | |
|
27 | from zope.interface import Interface, Attribute, implements, classProvides | |
|
28 | except ImportError: | |
|
29 | #zope.interface is not available | |
|
30 | Interface = object | |
|
31 | def Attribute(name, doc): pass | |
|
32 | def implements(interface): pass | |
|
33 | def classProvides(interface): pass | |
|
34 |
|
1 | NO CONTENT: file renamed from IPython/frontend/killable_process.py to IPython/frontend/_process/killableprocess.py |
@@ -15,7 +15,7 __docformat__ = "restructuredtext en" | |||
|
15 | 15 | #------------------------------------------------------------------------------- |
|
16 | 16 | # Imports |
|
17 | 17 | #------------------------------------------------------------------------------- |
|
18 |
from killable |
|
|
18 | from killableprocess import Popen, PIPE | |
|
19 | 19 | from threading import Thread |
|
20 | 20 | from time import sleep |
|
21 | 21 |
|
1 | NO CONTENT: file renamed from IPython/frontend/winprocess.py to IPython/frontend/_process/winprocess.py |
@@ -18,14 +18,11 import uuid | |||
|
18 | 18 | |
|
19 | 19 | try: |
|
20 | 20 | from zope.interface import Interface, Attribute, implements, classProvides |
|
21 | except ImportError: | |
|
22 | #zope.interface is not available | |
|
23 | Interface = object | |
|
24 | def Attribute(name, doc): pass | |
|
25 | def implements(interface): pass | |
|
26 | def classProvides(interface): pass | |
|
27 | ||
|
28 | ||
|
21 | except ImportError, e: | |
|
22 | e.message = """%s | |
|
23 | ________________________________________________________________________________ | |
|
24 | zope.interface is required to run asynchronous frontends.""" % e.message | |
|
25 | e.args = (e.message, ) + e.args[1:] | |
|
29 | 26 | |
|
30 | 27 | from frontendbase import FrontEndBase, IFrontEnd, IFrontEndFactory |
|
31 | 28 | |
@@ -34,9 +31,11 from IPython.kernel.core.history import FrontEndHistory | |||
|
34 | 31 | |
|
35 | 32 | try: |
|
36 | 33 | from twisted.python.failure import Failure |
|
37 | except ImportError: | |
|
38 | #Twisted not available | |
|
39 | Failure = Exception | |
|
34 | except ImportError, e: | |
|
35 | e.message = """%s | |
|
36 | ________________________________________________________________________________ | |
|
37 | twisted is required to run asynchronous frontends.""" % e.message | |
|
38 | e.args = (e.message, ) + e.args[1:] | |
|
40 | 39 | |
|
41 | 40 | |
|
42 | 41 |
@@ -24,14 +24,7 import string | |||
|
24 | 24 | import uuid |
|
25 | 25 | import _ast |
|
26 | 26 | |
|
27 | try: | |
|
28 | from zope.interface import Interface, Attribute, implements, classProvides | |
|
29 | except ImportError: | |
|
30 | #zope.interface is not available | |
|
31 | Interface = object | |
|
32 | def Attribute(name, doc): pass | |
|
33 | def implements(interface): pass | |
|
34 | def classProvides(interface): pass | |
|
27 | from zopeinterface import Interface, Attribute, implements, classProvides | |
|
35 | 28 | |
|
36 | 29 | from IPython.kernel.core.history import FrontEndHistory |
|
37 | 30 | from IPython.kernel.core.util import Bunch |
@@ -46,6 +39,8 rc.prompt_in2 = r'...' | |||
|
46 | 39 | rc.prompt_out = r'Out [$number]: ' |
|
47 | 40 | |
|
48 | 41 | ############################################################################## |
|
42 | # Interface definitions | |
|
43 | ############################################################################## | |
|
49 | 44 | |
|
50 | 45 | class IFrontEndFactory(Interface): |
|
51 | 46 | """Factory interface for frontends.""" |
@@ -59,7 +54,6 class IFrontEndFactory(Interface): | |||
|
59 | 54 | pass |
|
60 | 55 | |
|
61 | 56 | |
|
62 | ||
|
63 | 57 | class IFrontEnd(Interface): |
|
64 | 58 | """Interface for frontends. All methods return t.i.d.Deferred""" |
|
65 | 59 | |
@@ -72,9 +66,10 class IFrontEnd(Interface): | |||
|
72 | 66 | |
|
73 | 67 | def update_cell_prompt(result, blockID=None): |
|
74 | 68 | """Subclass may override to update the input prompt for a block. |
|
75 | Since this method will be called as a | |
|
76 | twisted.internet.defer.Deferred's callback/errback, | |
|
77 | implementations should return result when finished. | |
|
69 | ||
|
70 | In asynchronous frontends, this method will be called as a | |
|
71 | twisted.internet.defer.Deferred's callback/errback. | |
|
72 | Implementations should thus return result when finished. | |
|
78 | 73 | |
|
79 | 74 | Result is a result dict in case of success, and a |
|
80 | 75 | twisted.python.util.failure.Failure in case of an error |
@@ -82,7 +77,6 class IFrontEnd(Interface): | |||
|
82 | 77 | |
|
83 | 78 | pass |
|
84 | 79 | |
|
85 | ||
|
86 | 80 | def render_result(result): |
|
87 | 81 | """Render the result of an execute call. Implementors may choose the |
|
88 | 82 | method of rendering. |
@@ -100,16 +94,17 class IFrontEnd(Interface): | |||
|
100 | 94 | pass |
|
101 | 95 | |
|
102 | 96 | def render_error(failure): |
|
103 |
"""Subclasses must override to render the failure. |
|
|
104 | will be called as a twisted.internet.defer.Deferred's callback, | |
|
105 | implementations should return result when finished. | |
|
97 | """Subclasses must override to render the failure. | |
|
98 | ||
|
99 | In asynchronous frontend, since this method will be called as a | |
|
100 | twisted.internet.defer.Deferred's callback. Implementations | |
|
101 | should thus return result when finished. | |
|
106 | 102 | |
|
107 | 103 | blockID = failure.blockID |
|
108 | 104 | """ |
|
109 | 105 | |
|
110 | 106 | pass |
|
111 | 107 | |
|
112 | ||
|
113 | 108 | def input_prompt(number=''): |
|
114 | 109 | """Returns the input prompt by subsituting into |
|
115 | 110 | self.input_prompt_template |
@@ -140,7 +135,6 class IFrontEnd(Interface): | |||
|
140 | 135 | |
|
141 | 136 | pass |
|
142 | 137 | |
|
143 | ||
|
144 | 138 | def get_history_previous(current_block): |
|
145 | 139 | """Returns the block previous in the history. Saves currentBlock if |
|
146 | 140 | the history_cursor is currently at the end of the input history""" |
@@ -151,6 +145,20 class IFrontEnd(Interface): | |||
|
151 | 145 | |
|
152 | 146 | pass |
|
153 | 147 | |
|
148 | def complete(self, line): | |
|
149 | """Returns the list of possible completions, and the completed | |
|
150 | line. | |
|
151 | ||
|
152 | The input argument is the full line to be completed. This method | |
|
153 | returns both the line completed as much as possible, and the list | |
|
154 | of further possible completions (full words). | |
|
155 | """ | |
|
156 | pass | |
|
157 | ||
|
158 | ||
|
159 | ############################################################################## | |
|
160 | # Base class for all the frontends. | |
|
161 | ############################################################################## | |
|
154 | 162 | |
|
155 | 163 | class FrontEndBase(object): |
|
156 | 164 | """ |
@@ -321,6 +329,8 class FrontEndBase(object): | |||
|
321 | 329 | |
|
322 | 330 | def update_cell_prompt(self, result, blockID=None): |
|
323 | 331 | """Subclass may override to update the input prompt for a block. |
|
332 | ||
|
333 | This method only really makes sens in asyncrhonous frontend. | |
|
324 | 334 | Since this method will be called as a |
|
325 | 335 | twisted.internet.defer.Deferred's callback, implementations should |
|
326 | 336 | return result when finished. |
@@ -330,18 +340,22 class FrontEndBase(object): | |||
|
330 | 340 | |
|
331 | 341 | |
|
332 | 342 | def render_result(self, result): |
|
333 |
"""Subclasses must override to render result. |
|
|
334 | be called as a twisted.internet.defer.Deferred's callback, | |
|
335 | implementations should return result when finished. | |
|
343 | """Subclasses must override to render result. | |
|
344 | ||
|
345 | In asynchronous frontends, this method will be called as a | |
|
346 | twisted.internet.defer.Deferred's callback. Implementations | |
|
347 | should thus return result when finished. | |
|
336 | 348 | """ |
|
337 | 349 | |
|
338 | 350 | return result |
|
339 | 351 | |
|
340 | 352 | |
|
341 | 353 | def render_error(self, failure): |
|
342 |
"""Subclasses must override to render the failure. |
|
|
343 | will be called as a twisted.internet.defer.Deferred's callback, | |
|
344 | implementations should return result when finished. | |
|
354 | """Subclasses must override to render the failure. | |
|
355 | ||
|
356 | In asynchronous frontends, this method will be called as a | |
|
357 | twisted.internet.defer.Deferred's callback. Implementations | |
|
358 | should thus return result when finished. | |
|
345 | 359 | """ |
|
346 | 360 | |
|
347 | 361 | return failure |
@@ -1,5 +1,6 | |||
|
1 | 1 | """ |
|
2 |
Base front end class for all line-oriented frontends |
|
|
2 | Base front end class for all line-oriented frontends, rather than | |
|
3 | block-oriented. | |
|
3 | 4 | |
|
4 | 5 | Currently this focuses on synchronous frontends. |
|
5 | 6 | """ |
@@ -23,6 +24,9 from frontendbase import FrontEndBase | |||
|
23 | 24 | from IPython.kernel.core.interpreter import Interpreter |
|
24 | 25 | |
|
25 | 26 | def common_prefix(strings): |
|
27 | """ Given a list of strings, return the common prefix between all | |
|
28 | these strings. | |
|
29 | """ | |
|
26 | 30 | ref = strings[0] |
|
27 | 31 | prefix = '' |
|
28 | 32 | for size in range(len(ref)): |
@@ -38,6 +42,10 def common_prefix(strings): | |||
|
38 | 42 | # Base class for the line-oriented front ends |
|
39 | 43 | #------------------------------------------------------------------------------- |
|
40 | 44 | class LineFrontEndBase(FrontEndBase): |
|
45 | """ Concrete implementation of the FrontEndBase class. This is meant | |
|
46 | to be the base class behind all the frontend that are line-oriented, | |
|
47 | rather than block-oriented. | |
|
48 | """ | |
|
41 | 49 | |
|
42 | 50 | # We need to keep the prompt number, to be able to increment |
|
43 | 51 | # it when there is an exception. |
@@ -47,7 +55,7 class LineFrontEndBase(FrontEndBase): | |||
|
47 | 55 | last_result = dict(number=0) |
|
48 | 56 | |
|
49 | 57 | #-------------------------------------------------------------------------- |
|
50 | # Public API | |
|
58 | # FrontEndBase interface | |
|
51 | 59 | #-------------------------------------------------------------------------- |
|
52 | 60 | |
|
53 | 61 | def __init__(self, shell=None, history=None): |
@@ -81,6 +89,9 class LineFrontEndBase(FrontEndBase): | |||
|
81 | 89 | |
|
82 | 90 | |
|
83 | 91 | def render_result(self, result): |
|
92 | """ Frontend-specific rendering of the result of a calculation | |
|
93 | that has been sent to an engine. | |
|
94 | """ | |
|
84 | 95 | if 'stdout' in result and result['stdout']: |
|
85 | 96 | self.write('\n' + result['stdout']) |
|
86 | 97 | if 'display' in result and result['display']: |
@@ -91,20 +102,25 class LineFrontEndBase(FrontEndBase): | |||
|
91 | 102 | |
|
92 | 103 | |
|
93 | 104 | def render_error(self, failure): |
|
105 | """ Frontend-specific rendering of error. | |
|
106 | """ | |
|
94 | 107 | self.insert_text('\n\n'+str(failure)+'\n\n') |
|
95 | 108 | return failure |
|
96 | 109 | |
|
97 | 110 | |
|
98 | def prefilter_input(self, string): | |
|
99 | string = string.replace('\r\n', '\n') | |
|
100 | string = string.replace('\t', 4*' ') | |
|
101 | # Clean the trailing whitespace | |
|
102 | string = '\n'.join(l.rstrip() for l in string.split('\n')) | |
|
103 | return string | |
|
104 | ||
|
105 | ||
|
106 | 111 | def is_complete(self, string): |
|
112 | """ Check if a string forms a complete, executable set of | |
|
113 | commands. | |
|
114 | ||
|
115 | For the line-oriented frontend, multi-line code is not executed | |
|
116 | as soon as it is complete: the users has to enter two line | |
|
117 | returns. | |
|
118 | """ | |
|
107 | 119 | if string in ('', '\n'): |
|
120 | # Prefiltering, eg through ipython0, may return an empty | |
|
121 | # string although some operations have been accomplished. We | |
|
122 | # thus want to consider an empty string as a complete | |
|
123 | # statement. | |
|
108 | 124 | return True |
|
109 | 125 | elif ( len(self.get_current_edit_buffer().split('\n'))>2 |
|
110 | 126 | and not re.findall(r"\n[\t ]*\n[\t ]*$", string)): |
@@ -116,8 +132,8 class LineFrontEndBase(FrontEndBase): | |||
|
116 | 132 | |
|
117 | 133 | |
|
118 | 134 | def execute(self, python_string, raw_string=None): |
|
119 | """ Send the python_string to the interpreter, stores the | |
|
120 | raw_string in the history and starts a new prompt. | |
|
135 | """ Stores the raw_string in the history, and sends the | |
|
136 | python string to the interpreter. | |
|
121 | 137 | """ |
|
122 | 138 | if raw_string is None: |
|
123 | 139 | raw_string = python_string |
@@ -133,6 +149,18 class LineFrontEndBase(FrontEndBase): | |||
|
133 | 149 | finally: |
|
134 | 150 | self.after_execute() |
|
135 | 151 | |
|
152 | #-------------------------------------------------------------------------- | |
|
153 | # LineFrontEndBase interface | |
|
154 | #-------------------------------------------------------------------------- | |
|
155 | ||
|
156 | def prefilter_input(self, string): | |
|
157 | """ Priflter the input to turn it in valid python. | |
|
158 | """ | |
|
159 | string = string.replace('\r\n', '\n') | |
|
160 | string = string.replace('\t', 4*' ') | |
|
161 | # Clean the trailing whitespace | |
|
162 | string = '\n'.join(l.rstrip() for l in string.split('\n')) | |
|
163 | return string | |
|
136 | 164 | |
|
137 | 165 | def after_execute(self): |
|
138 | 166 | """ All the operations required after an execution to put the |
@@ -145,6 +173,10 class LineFrontEndBase(FrontEndBase): | |||
|
145 | 173 | self.history_cursor = len(self.history.input_cache) - 1 |
|
146 | 174 | |
|
147 | 175 | |
|
176 | #-------------------------------------------------------------------------- | |
|
177 | # Private API | |
|
178 | #-------------------------------------------------------------------------- | |
|
179 | ||
|
148 | 180 | def _on_enter(self): |
|
149 | 181 | """ Called when the return key is pressed in a line editing |
|
150 | 182 | buffer. |
@@ -160,11 +192,10 class LineFrontEndBase(FrontEndBase): | |||
|
160 | 192 | self.write('\t') |
|
161 | 193 | |
|
162 | 194 | |
|
163 | #-------------------------------------------------------------------------- | |
|
164 | # Private API | |
|
165 | #-------------------------------------------------------------------------- | |
|
166 | ||
|
167 | 195 | def _get_indent_string(self, string): |
|
196 | """ Return the string of whitespace that prefixes a line. Used to | |
|
197 | add the right amount of indendation when creating a new line. | |
|
198 | """ | |
|
168 | 199 | string = string.replace('\t', ' '*4) |
|
169 | 200 | string = string.split('\n')[-1] |
|
170 | 201 | indent_chars = len(string) - len(string.lstrip()) |
@@ -2,6 +2,12 | |||
|
2 | 2 | Frontend class that uses IPython0 to prefilter the inputs. |
|
3 | 3 | |
|
4 | 4 | Using the IPython0 mechanism gives us access to the magics. |
|
5 | ||
|
6 | This is a transitory class, used here to do the transition between | |
|
7 | ipython0 and ipython1. This class is meant to be short-lived as more | |
|
8 | functionnality is abstracted out of ipython0 in reusable functions and | |
|
9 | is added on the interpreter. This class can be a used to guide this | |
|
10 | refactoring. | |
|
5 | 11 | """ |
|
6 | 12 | __docformat__ = "restructuredtext en" |
|
7 | 13 | |
@@ -27,6 +33,8 from IPython.kernel.core.sync_traceback_trap import SyncTracebackTrap | |||
|
27 | 33 | |
|
28 | 34 | from IPython.genutils import Term |
|
29 | 35 | import pydoc |
|
36 | import os | |
|
37 | ||
|
30 | 38 | |
|
31 | 39 | def mk_system_call(system_call_function, command): |
|
32 | 40 | """ given a os.system replacement, and a leading string command, |
@@ -41,6 +49,14 def mk_system_call(system_call_function, command): | |||
|
41 | 49 | # Frontend class using ipython0 to do the prefiltering. |
|
42 | 50 | #------------------------------------------------------------------------------- |
|
43 | 51 | class PrefilterFrontEnd(LineFrontEndBase): |
|
52 | """ Class that uses ipython0 to do prefilter the input, do the | |
|
53 | completion and the magics. | |
|
54 | ||
|
55 | The core trick is to use an ipython0 instance to prefilter the | |
|
56 | input, and share the namespace between the interpreter instance used | |
|
57 | to execute the statements and the ipython0 used for code | |
|
58 | completion... | |
|
59 | """ | |
|
44 | 60 | |
|
45 | 61 | def __init__(self, *args, **kwargs): |
|
46 | 62 | LineFrontEndBase.__init__(self, *args, **kwargs) |
@@ -67,38 +83,20 class PrefilterFrontEnd(LineFrontEndBase): | |||
|
67 | 83 | err_callback=self.write, |
|
68 | 84 | ) |
|
69 | 85 | self.shell.traceback_trap = SyncTracebackTrap( |
|
70 | formatters=self.shell.traceback_trap.formatters | |
|
86 | formatters=self.shell.traceback_trap.formatters, | |
|
71 | 87 | ) |
|
72 | 88 | # Capture and release the outputs, to make sure all the |
|
73 | 89 | # shadow variables are set |
|
74 | 90 | self.capture_output() |
|
75 | 91 | self.release_output() |
|
76 | 92 | |
|
77 | ||
|
78 | def prefilter_input(self, input_string): | |
|
79 | """ Using IPython0 to prefilter the commands. | |
|
80 | """ | |
|
81 | input_string = LineFrontEndBase.prefilter_input(self, input_string) | |
|
82 | filtered_lines = [] | |
|
83 | # The IPython0 prefilters sometime produce output. We need to | |
|
84 | # capture it. | |
|
85 | self.capture_output() | |
|
86 | self.last_result = dict(number=self.prompt_number) | |
|
87 | try: | |
|
88 | for line in input_string.split('\n'): | |
|
89 | filtered_lines.append(self.ipython0.prefilter(line, False)) | |
|
90 | except: | |
|
91 | # XXX: probably not the right thing to do. | |
|
92 | self.ipython0.showsyntaxerror() | |
|
93 | self.after_execute() | |
|
94 | finally: | |
|
95 | self.release_output() | |
|
96 | ||
|
97 | filtered_string = '\n'.join(filtered_lines) | |
|
98 | return filtered_string | |
|
99 | ||
|
93 | #-------------------------------------------------------------------------- | |
|
94 | # FrontEndBase interface | |
|
95 | #-------------------------------------------------------------------------- | |
|
100 | 96 | |
|
101 | 97 | def show_traceback(self): |
|
98 | """ Use ipython0 to capture the last traceback and display it. | |
|
99 | """ | |
|
102 | 100 | self.capture_output() |
|
103 | 101 | self.ipython0.showtraceback() |
|
104 | 102 | self.release_output() |
@@ -111,13 +109,6 class PrefilterFrontEnd(LineFrontEndBase): | |||
|
111 | 109 | self.release_output() |
|
112 | 110 | |
|
113 | 111 | |
|
114 | def system_call(self, command): | |
|
115 | """ Allows for frontend to define their own system call, to be | |
|
116 | able capture output and redirect input. | |
|
117 | """ | |
|
118 | return os.system(command, args) | |
|
119 | ||
|
120 | ||
|
121 | 112 | def capture_output(self): |
|
122 | 113 | """ Capture all the output mechanisms we can think of. |
|
123 | 114 | """ |
@@ -154,6 +145,45 class PrefilterFrontEnd(LineFrontEndBase): | |||
|
154 | 145 | line = line[:-len(word)] + prefix |
|
155 | 146 | return line, completions |
|
156 | 147 | |
|
148 | ||
|
149 | #-------------------------------------------------------------------------- | |
|
150 | # LineFrontEndBase interface | |
|
151 | #-------------------------------------------------------------------------- | |
|
152 | ||
|
153 | def prefilter_input(self, input_string): | |
|
154 | """ Using IPython0 to prefilter the commands to turn them | |
|
155 | in executable statements that are valid Python strings. | |
|
156 | """ | |
|
157 | input_string = LineFrontEndBase.prefilter_input(self, input_string) | |
|
158 | filtered_lines = [] | |
|
159 | # The IPython0 prefilters sometime produce output. We need to | |
|
160 | # capture it. | |
|
161 | self.capture_output() | |
|
162 | self.last_result = dict(number=self.prompt_number) | |
|
163 | try: | |
|
164 | for line in input_string.split('\n'): | |
|
165 | filtered_lines.append(self.ipython0.prefilter(line, False)) | |
|
166 | except: | |
|
167 | # XXX: probably not the right thing to do. | |
|
168 | self.ipython0.showsyntaxerror() | |
|
169 | self.after_execute() | |
|
170 | finally: | |
|
171 | self.release_output() | |
|
172 | ||
|
173 | filtered_string = '\n'.join(filtered_lines) | |
|
174 | return filtered_string | |
|
175 | ||
|
176 | ||
|
177 | #-------------------------------------------------------------------------- | |
|
178 | # PrefilterLineFrontEnd interface | |
|
179 | #-------------------------------------------------------------------------- | |
|
180 | ||
|
181 | def system_call(self, command_string): | |
|
182 | """ Allows for frontend to define their own system call, to be | |
|
183 | able capture output and redirect input. | |
|
184 | """ | |
|
185 | return os.system(command_string) | |
|
186 | ||
|
157 | 187 | |
|
158 | 188 | def do_exit(self): |
|
159 | 189 | """ Exit the shell, cleanup and save the history. |
@@ -76,6 +76,8 class ConsoleWidget(editwindow.EditWindow): | |||
|
76 | 76 | keeping the cursor inside the editing line. |
|
77 | 77 | """ |
|
78 | 78 | |
|
79 | # This is where the title captured from the ANSI escape sequences are | |
|
80 | # stored. | |
|
79 | 81 | title = 'Console' |
|
80 | 82 | |
|
81 | 83 | style = _DEFAULT_STYLE.copy() |
@@ -102,7 +104,7 class ConsoleWidget(editwindow.EditWindow): | |||
|
102 | 104 | def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition, |
|
103 | 105 | size=wx.DefaultSize, style=0, ): |
|
104 | 106 | editwindow.EditWindow.__init__(self, parent, id, pos, size, style) |
|
105 | self.configure_scintilla() | |
|
107 | self._configure_scintilla() | |
|
106 | 108 | |
|
107 | 109 | # FIXME: we need to retrieve this from the interpreter. |
|
108 | 110 | self.prompt = \ |
@@ -113,87 +115,6 class ConsoleWidget(editwindow.EditWindow): | |||
|
113 | 115 | self.Bind(wx.EVT_KEY_UP, self._on_key_up) |
|
114 | 116 | |
|
115 | 117 | |
|
116 | def configure_scintilla(self): | |
|
117 | self.SetEOLMode(stc.STC_EOL_LF) | |
|
118 | ||
|
119 | # Ctrl"+" or Ctrl "-" can be used to zoomin/zoomout the text inside | |
|
120 | # the widget | |
|
121 | self.CmdKeyAssign(ord('+'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN) | |
|
122 | self.CmdKeyAssign(ord('-'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT) | |
|
123 | # Also allow Ctrl Shift "=" for poor non US keyboard users. | |
|
124 | self.CmdKeyAssign(ord('='), stc.STC_SCMOD_CTRL|stc.STC_SCMOD_SHIFT, | |
|
125 | stc.STC_CMD_ZOOMIN) | |
|
126 | ||
|
127 | #self.CmdKeyAssign(stc.STC_KEY_PRIOR, stc.STC_SCMOD_SHIFT, | |
|
128 | # stc.STC_CMD_PAGEUP) | |
|
129 | ||
|
130 | #self.CmdKeyAssign(stc.STC_KEY_NEXT, stc.STC_SCMOD_SHIFT, | |
|
131 | # stc.STC_CMD_PAGEDOWN) | |
|
132 | ||
|
133 | # Keys: we need to clear some of the keys the that don't play | |
|
134 | # well with a console. | |
|
135 | self.CmdKeyClear(ord('D'), stc.STC_SCMOD_CTRL) | |
|
136 | self.CmdKeyClear(ord('L'), stc.STC_SCMOD_CTRL) | |
|
137 | self.CmdKeyClear(ord('T'), stc.STC_SCMOD_CTRL) | |
|
138 | ||
|
139 | ||
|
140 | self.SetEOLMode(stc.STC_EOL_CRLF) | |
|
141 | self.SetWrapMode(stc.STC_WRAP_CHAR) | |
|
142 | self.SetWrapMode(stc.STC_WRAP_WORD) | |
|
143 | self.SetBufferedDraw(True) | |
|
144 | self.SetUseAntiAliasing(True) | |
|
145 | self.SetLayoutCache(stc.STC_CACHE_PAGE) | |
|
146 | self.SetUndoCollection(False) | |
|
147 | self.SetUseTabs(True) | |
|
148 | self.SetIndent(4) | |
|
149 | self.SetTabWidth(4) | |
|
150 | ||
|
151 | self.EnsureCaretVisible() | |
|
152 | # we don't want scintilla's autocompletion to choose | |
|
153 | # automaticaly out of a single choice list, as we pop it up | |
|
154 | # automaticaly | |
|
155 | self.AutoCompSetChooseSingle(False) | |
|
156 | self.AutoCompSetMaxHeight(10) | |
|
157 | ||
|
158 | self.SetMargins(3, 3) #text is moved away from border with 3px | |
|
159 | # Suppressing Scintilla margins | |
|
160 | self.SetMarginWidth(0, 0) | |
|
161 | self.SetMarginWidth(1, 0) | |
|
162 | self.SetMarginWidth(2, 0) | |
|
163 | ||
|
164 | self._apply_style() | |
|
165 | ||
|
166 | # Xterm escape sequences | |
|
167 | self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?') | |
|
168 | self.title_pat = re.compile('\x1b]0;(.*?)\x07') | |
|
169 | ||
|
170 | #self.SetEdgeMode(stc.STC_EDGE_LINE) | |
|
171 | #self.SetEdgeColumn(80) | |
|
172 | ||
|
173 | # styles | |
|
174 | p = self.style | |
|
175 | self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default']) | |
|
176 | self.StyleClearAll() | |
|
177 | self.StyleSetSpec(_STDOUT_STYLE, p['stdout']) | |
|
178 | self.StyleSetSpec(_STDERR_STYLE, p['stderr']) | |
|
179 | self.StyleSetSpec(_TRACE_STYLE, p['trace']) | |
|
180 | ||
|
181 | self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, p['bracegood']) | |
|
182 | self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, p['bracebad']) | |
|
183 | self.StyleSetSpec(stc.STC_P_COMMENTLINE, p['comment']) | |
|
184 | self.StyleSetSpec(stc.STC_P_NUMBER, p['number']) | |
|
185 | self.StyleSetSpec(stc.STC_P_STRING, p['string']) | |
|
186 | self.StyleSetSpec(stc.STC_P_CHARACTER, p['char']) | |
|
187 | self.StyleSetSpec(stc.STC_P_WORD, p['keyword']) | |
|
188 | self.StyleSetSpec(stc.STC_P_WORD2, p['keyword']) | |
|
189 | self.StyleSetSpec(stc.STC_P_TRIPLE, p['triple']) | |
|
190 | self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, p['tripledouble']) | |
|
191 | self.StyleSetSpec(stc.STC_P_CLASSNAME, p['class']) | |
|
192 | self.StyleSetSpec(stc.STC_P_DEFNAME, p['def']) | |
|
193 | self.StyleSetSpec(stc.STC_P_OPERATOR, p['operator']) | |
|
194 | self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, p['comment']) | |
|
195 | ||
|
196 | ||
|
197 | 118 | def write(self, text, refresh=True): |
|
198 | 119 | """ Write given text to buffer, while translating the ansi escape |
|
199 | 120 | sequences. |
@@ -270,24 +191,20 class ConsoleWidget(editwindow.EditWindow): | |||
|
270 | 191 | return current_edit_buffer |
|
271 | 192 | |
|
272 | 193 | |
|
273 | #-------------------------------------------------------------------------- | |
|
274 | # Private API | |
|
275 | #-------------------------------------------------------------------------- | |
|
276 | ||
|
277 | def _apply_style(self): | |
|
278 | """ Applies the colors for the different text elements and the | |
|
279 | carret. | |
|
280 | """ | |
|
281 | self.SetCaretForeground(self.carret_color) | |
|
194 | def scroll_to_bottom(self): | |
|
195 | maxrange = self.GetScrollRange(wx.VERTICAL) | |
|
196 | self.ScrollLines(maxrange) | |
|
282 | 197 | |
|
283 | #self.StyleClearAll() | |
|
284 | self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, | |
|
285 | "fore:#FF0000,back:#0000FF,bold") | |
|
286 | self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, | |
|
287 | "fore:#000000,back:#FF0000,bold") | |
|
288 | 198 | |
|
289 | for style in self.ANSI_STYLES.values(): | |
|
290 | self.StyleSetSpec(style[0], "bold,fore:%s" % style[1]) | |
|
199 | def pop_completion(self, possibilities, offset=0): | |
|
200 | """ Pops up an autocompletion menu. Offset is the offset | |
|
201 | in characters of the position at which the menu should | |
|
202 | appear, relativ to the cursor. | |
|
203 | """ | |
|
204 | self.AutoCompSetIgnoreCase(False) | |
|
205 | self.AutoCompSetAutoHide(False) | |
|
206 | self.AutoCompSetMaxHeight(len(possibilities)) | |
|
207 | self.AutoCompShow(offset, " ".join(possibilities)) | |
|
291 | 208 | |
|
292 | 209 | |
|
293 | 210 | def write_completion(self, possibilities): |
@@ -315,21 +232,105 class ConsoleWidget(editwindow.EditWindow): | |||
|
315 | 232 | self.new_prompt(self.prompt % (self.last_result['number'] + 1)) |
|
316 | 233 | self.replace_current_edit_buffer(current_buffer) |
|
317 | 234 | |
|
318 | ||
|
319 | def pop_completion(self, possibilities, offset=0): | |
|
320 | """ Pops up an autocompletion menu. Offset is the offset | |
|
321 | in characters of the position at which the menu should | |
|
322 | appear, relativ to the cursor. | |
|
235 | #-------------------------------------------------------------------------- | |
|
236 | # Private API | |
|
237 | #-------------------------------------------------------------------------- | |
|
238 | ||
|
239 | def _apply_style(self): | |
|
240 | """ Applies the colors for the different text elements and the | |
|
241 | carret. | |
|
323 | 242 | """ |
|
324 | self.AutoCompSetIgnoreCase(False) | |
|
325 | self.AutoCompSetAutoHide(False) | |
|
326 | self.AutoCompSetMaxHeight(len(possibilities)) | |
|
327 | self.AutoCompShow(offset, " ".join(possibilities)) | |
|
243 | self.SetCaretForeground(self.carret_color) | |
|
328 | 244 | |
|
329 | ||
|
330 | def scroll_to_bottom(self): | |
|
331 | maxrange = self.GetScrollRange(wx.VERTICAL) | |
|
332 | self.ScrollLines(maxrange) | |
|
245 | #self.StyleClearAll() | |
|
246 | self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, | |
|
247 | "fore:#FF0000,back:#0000FF,bold") | |
|
248 | self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, | |
|
249 | "fore:#000000,back:#FF0000,bold") | |
|
250 | ||
|
251 | for style in self.ANSI_STYLES.values(): | |
|
252 | self.StyleSetSpec(style[0], "bold,fore:%s" % style[1]) | |
|
253 | ||
|
254 | ||
|
255 | def _configure_scintilla(self): | |
|
256 | self.SetEOLMode(stc.STC_EOL_LF) | |
|
257 | ||
|
258 | # Ctrl"+" or Ctrl "-" can be used to zoomin/zoomout the text inside | |
|
259 | # the widget | |
|
260 | self.CmdKeyAssign(ord('+'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN) | |
|
261 | self.CmdKeyAssign(ord('-'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT) | |
|
262 | # Also allow Ctrl Shift "=" for poor non US keyboard users. | |
|
263 | self.CmdKeyAssign(ord('='), stc.STC_SCMOD_CTRL|stc.STC_SCMOD_SHIFT, | |
|
264 | stc.STC_CMD_ZOOMIN) | |
|
265 | ||
|
266 | #self.CmdKeyAssign(stc.STC_KEY_PRIOR, stc.STC_SCMOD_SHIFT, | |
|
267 | # stc.STC_CMD_PAGEUP) | |
|
268 | ||
|
269 | #self.CmdKeyAssign(stc.STC_KEY_NEXT, stc.STC_SCMOD_SHIFT, | |
|
270 | # stc.STC_CMD_PAGEDOWN) | |
|
271 | ||
|
272 | # Keys: we need to clear some of the keys the that don't play | |
|
273 | # well with a console. | |
|
274 | self.CmdKeyClear(ord('D'), stc.STC_SCMOD_CTRL) | |
|
275 | self.CmdKeyClear(ord('L'), stc.STC_SCMOD_CTRL) | |
|
276 | self.CmdKeyClear(ord('T'), stc.STC_SCMOD_CTRL) | |
|
277 | ||
|
278 | ||
|
279 | self.SetEOLMode(stc.STC_EOL_CRLF) | |
|
280 | self.SetWrapMode(stc.STC_WRAP_CHAR) | |
|
281 | self.SetWrapMode(stc.STC_WRAP_WORD) | |
|
282 | self.SetBufferedDraw(True) | |
|
283 | self.SetUseAntiAliasing(True) | |
|
284 | self.SetLayoutCache(stc.STC_CACHE_PAGE) | |
|
285 | self.SetUndoCollection(False) | |
|
286 | self.SetUseTabs(True) | |
|
287 | self.SetIndent(4) | |
|
288 | self.SetTabWidth(4) | |
|
289 | ||
|
290 | self.EnsureCaretVisible() | |
|
291 | # we don't want scintilla's autocompletion to choose | |
|
292 | # automaticaly out of a single choice list, as we pop it up | |
|
293 | # automaticaly | |
|
294 | self.AutoCompSetChooseSingle(False) | |
|
295 | self.AutoCompSetMaxHeight(10) | |
|
296 | ||
|
297 | self.SetMargins(3, 3) #text is moved away from border with 3px | |
|
298 | # Suppressing Scintilla margins | |
|
299 | self.SetMarginWidth(0, 0) | |
|
300 | self.SetMarginWidth(1, 0) | |
|
301 | self.SetMarginWidth(2, 0) | |
|
302 | ||
|
303 | self._apply_style() | |
|
304 | ||
|
305 | # Xterm escape sequences | |
|
306 | self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?') | |
|
307 | self.title_pat = re.compile('\x1b]0;(.*?)\x07') | |
|
308 | ||
|
309 | #self.SetEdgeMode(stc.STC_EDGE_LINE) | |
|
310 | #self.SetEdgeColumn(80) | |
|
311 | ||
|
312 | # styles | |
|
313 | p = self.style | |
|
314 | self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default']) | |
|
315 | self.StyleClearAll() | |
|
316 | self.StyleSetSpec(_STDOUT_STYLE, p['stdout']) | |
|
317 | self.StyleSetSpec(_STDERR_STYLE, p['stderr']) | |
|
318 | self.StyleSetSpec(_TRACE_STYLE, p['trace']) | |
|
319 | ||
|
320 | self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, p['bracegood']) | |
|
321 | self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, p['bracebad']) | |
|
322 | self.StyleSetSpec(stc.STC_P_COMMENTLINE, p['comment']) | |
|
323 | self.StyleSetSpec(stc.STC_P_NUMBER, p['number']) | |
|
324 | self.StyleSetSpec(stc.STC_P_STRING, p['string']) | |
|
325 | self.StyleSetSpec(stc.STC_P_CHARACTER, p['char']) | |
|
326 | self.StyleSetSpec(stc.STC_P_WORD, p['keyword']) | |
|
327 | self.StyleSetSpec(stc.STC_P_WORD2, p['keyword']) | |
|
328 | self.StyleSetSpec(stc.STC_P_TRIPLE, p['triple']) | |
|
329 | self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, p['tripledouble']) | |
|
330 | self.StyleSetSpec(stc.STC_P_CLASSNAME, p['class']) | |
|
331 | self.StyleSetSpec(stc.STC_P_DEFNAME, p['def']) | |
|
332 | self.StyleSetSpec(stc.STC_P_OPERATOR, p['operator']) | |
|
333 | self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, p['comment']) | |
|
333 | 334 | |
|
334 | 335 | |
|
335 | 336 | def _on_key_down(self, event, skip=True): |
@@ -418,7 +419,6 class ConsoleWidget(editwindow.EditWindow): | |||
|
418 | 419 | |
|
419 | 420 | |
|
420 | 421 | |
|
421 | ||
|
422 | 422 | if __name__ == '__main__': |
|
423 | 423 | # Some simple code to test the console widget. |
|
424 | 424 | class MainWindow(wx.Frame): |
@@ -5,6 +5,8 | |||
|
5 | 5 | """Classes to provide a Wx frontend to the |
|
6 | 6 | IPython.kernel.core.interpreter. |
|
7 | 7 | |
|
8 | This class inherits from ConsoleWidget, that provides a console-like | |
|
9 | widget to provide a text-rendering widget suitable for a terminal. | |
|
8 | 10 | """ |
|
9 | 11 | |
|
10 | 12 | __docformat__ = "restructuredtext en" |
@@ -20,21 +22,25 __docformat__ = "restructuredtext en" | |||
|
20 | 22 | # Imports |
|
21 | 23 | #------------------------------------------------------------------------------- |
|
22 | 24 | |
|
23 | ||
|
24 | import wx | |
|
25 | # Major library imports | |
|
25 | 26 | import re |
|
26 | from wx import stc | |
|
27 | from console_widget import ConsoleWidget | |
|
28 | 27 | import __builtin__ |
|
29 | 28 | from time import sleep |
|
30 | 29 | import sys |
|
31 | import signal | |
|
32 | ||
|
33 | 30 | from threading import Lock |
|
34 | 31 | |
|
35 | from IPython.frontend.piped_process import PipedProcess | |
|
32 | import wx | |
|
33 | from wx import stc | |
|
34 | ||
|
35 | # Ipython-specific imports. | |
|
36 | from IPython.frontend._process import PipedProcess | |
|
37 | from console_widget import ConsoleWidget | |
|
36 | 38 | from IPython.frontend.prefilterfrontend import PrefilterFrontEnd |
|
37 | 39 | |
|
40 | #------------------------------------------------------------------------------- | |
|
41 | # Constants | |
|
42 | #------------------------------------------------------------------------------- | |
|
43 | ||
|
38 | 44 | #_COMMAND_BG = '#FAFAF1' # Nice green |
|
39 | 45 | _RUNNING_BUFFER_BG = '#FDFFD3' # Nice yellow |
|
40 | 46 | _ERROR_BG = '#FFF1F1' # Nice red |
@@ -46,7 +52,14 _ERROR_MARKER = 30 | |||
|
46 | 52 | # Classes to implement the Wx frontend |
|
47 | 53 | #------------------------------------------------------------------------------- |
|
48 | 54 | class WxController(PrefilterFrontEnd, ConsoleWidget): |
|
55 | """Classes to provide a Wx frontend to the | |
|
56 | IPython.kernel.core.interpreter. | |
|
57 | ||
|
58 | This class inherits from ConsoleWidget, that provides a console-like | |
|
59 | widget to provide a text-rendering widget suitable for a terminal. | |
|
60 | """ | |
|
49 | 61 | |
|
62 | # FIXME: this shouldn't be there. | |
|
50 | 63 | output_prompt = \ |
|
51 | 64 | '\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02%i\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02' |
|
52 | 65 | |
@@ -55,7 +68,6 class WxController(PrefilterFrontEnd, ConsoleWidget): | |||
|
55 | 68 | |
|
56 | 69 | # The title of the terminal, as captured through the ANSI escape |
|
57 | 70 | # sequences. |
|
58 | ||
|
59 | 71 | def _set_title(self, title): |
|
60 | 72 | return self.Parent.SetTitle(title) |
|
61 | 73 | |
@@ -114,8 +126,43 class WxController(PrefilterFrontEnd, ConsoleWidget): | |||
|
114 | 126 | self._buffer_flush_timer = wx.Timer(self, BUFFER_FLUSH_TIMER_ID) |
|
115 | 127 | wx.EVT_TIMER(self, BUFFER_FLUSH_TIMER_ID, self._buffer_flush) |
|
116 | 128 | |
|
129 | ||
|
130 | def raw_input(self, prompt): | |
|
131 | """ A replacement from python's raw_input. | |
|
132 | """ | |
|
133 | self.new_prompt(prompt) | |
|
134 | self.waiting = True | |
|
135 | self.__old_on_enter = self._on_enter | |
|
136 | def my_on_enter(): | |
|
137 | self.waiting = False | |
|
138 | self._on_enter = my_on_enter | |
|
139 | # XXX: Busy waiting, ugly. | |
|
140 | while self.waiting: | |
|
141 | wx.Yield() | |
|
142 | sleep(0.1) | |
|
143 | self._on_enter = self.__old_on_enter | |
|
144 | self._input_state = 'buffering' | |
|
145 | return self.get_current_edit_buffer().rstrip('\n') | |
|
146 | ||
|
147 | ||
|
148 | def system_call(self, command_string): | |
|
149 | self._input_state = 'subprocess' | |
|
150 | self._running_process = PipedProcess(command_string, | |
|
151 | out_callback=self.buffered_write, | |
|
152 | end_callback = self._end_system_call) | |
|
153 | self._running_process.start() | |
|
154 | # XXX: another one of these polling loops to have a blocking | |
|
155 | # call | |
|
156 | wx.Yield() | |
|
157 | while self._running_process: | |
|
158 | wx.Yield() | |
|
159 | sleep(0.1) | |
|
160 | # Be sure to flush the buffer. | |
|
161 | self._buffer_flush(event=None) | |
|
162 | ||
|
163 | ||
|
117 | 164 | def do_completion(self): |
|
118 | """ Do code completion. | |
|
165 | """ Do code completion on current line. | |
|
119 | 166 | """ |
|
120 | 167 | if self.debug: |
|
121 | 168 | print >>sys.__stdout__, "do_completion", |
@@ -129,6 +176,8 class WxController(PrefilterFrontEnd, ConsoleWidget): | |||
|
129 | 176 | |
|
130 | 177 | |
|
131 | 178 | def do_calltip(self): |
|
179 | """ Analyse current and displays useful calltip for it. | |
|
180 | """ | |
|
132 | 181 | if self.debug: |
|
133 | 182 | print >>sys.__stdout__, "do_calltip" |
|
134 | 183 | separators = re.compile('[\s\{\}\[\]\(\)\= ,:]') |
@@ -154,12 +203,12 class WxController(PrefilterFrontEnd, ConsoleWidget): | |||
|
154 | 203 | pass |
|
155 | 204 | |
|
156 | 205 | |
|
157 | def popup_completion(self, create=False): | |
|
206 | def _popup_completion(self, create=False): | |
|
158 | 207 | """ Updates the popup completion menu if it exists. If create is |
|
159 | 208 | true, open the menu. |
|
160 | 209 | """ |
|
161 | 210 | if self.debug: |
|
162 | print >>sys.__stdout__, "popup_completion", | |
|
211 | print >>sys.__stdout__, "_popup_completion", | |
|
163 | 212 | line = self.get_current_edit_buffer() |
|
164 | 213 | if (self.AutoCompActive() and not line[-1] == '.') \ |
|
165 | 214 | or create==True: |
@@ -174,28 +223,23 class WxController(PrefilterFrontEnd, ConsoleWidget): | |||
|
174 | 223 | print >>sys.__stdout__, completions |
|
175 | 224 | |
|
176 | 225 | |
|
177 |
def |
|
|
178 | self._input_state = 'readline' | |
|
179 | ConsoleWidget.new_prompt(self, prompt) | |
|
180 | ||
|
226 | def buffered_write(self, text): | |
|
227 | """ A write method for streams, that caches the stream in order | |
|
228 | to avoid flooding the event loop. | |
|
181 | 229 |
|
|
182 | def raw_input(self, prompt): | |
|
183 | """ A replacement from python's raw_input. | |
|
230 | This can be called outside of the main loop, in separate | |
|
231 | threads. | |
|
184 | 232 | """ |
|
185 | self.new_prompt(prompt) | |
|
186 | self.waiting = True | |
|
187 | self.__old_on_enter = self._on_enter | |
|
188 | def my_on_enter(): | |
|
189 | self.waiting = False | |
|
190 | self._on_enter = my_on_enter | |
|
191 | # XXX: Busy waiting, ugly. | |
|
192 | while self.waiting: | |
|
193 | wx.Yield() | |
|
194 | sleep(0.1) | |
|
195 | self._on_enter = self.__old_on_enter | |
|
196 | self._input_state = 'buffering' | |
|
197 | return self.get_current_edit_buffer().rstrip('\n') | |
|
198 | ||
|
233 | self._out_buffer_lock.acquire() | |
|
234 | self._out_buffer.append(text) | |
|
235 | self._out_buffer_lock.release() | |
|
236 | if not self._buffer_flush_timer.IsRunning(): | |
|
237 | wx.CallAfter(self._buffer_flush_timer.Start, 100) # milliseconds | |
|
238 | ||
|
239 | ||
|
240 | #-------------------------------------------------------------------------- | |
|
241 | # LineFrontEnd interface | |
|
242 | #-------------------------------------------------------------------------- | |
|
199 | 243 | |
|
200 | 244 | def execute(self, python_string, raw_string=None): |
|
201 | 245 | self._input_state = 'buffering' |
@@ -226,39 +270,10 class WxController(PrefilterFrontEnd, ConsoleWidget): | |||
|
226 | 270 | |
|
227 | 271 | def after_execute(self): |
|
228 | 272 | PrefilterFrontEnd.after_execute(self) |
|
273 | # Clear the wait cursor | |
|
229 | 274 | if hasattr(self, '_cursor'): |
|
230 | 275 | del self._cursor |
|
231 | 276 | |
|
232 | ||
|
233 | def system_call(self, command_string): | |
|
234 | self._input_state = 'subprocess' | |
|
235 | self._running_process = PipedProcess(command_string, | |
|
236 | out_callback=self.buffered_write, | |
|
237 | end_callback = self._end_system_call) | |
|
238 | self._running_process.start() | |
|
239 | # XXX: another one of these polling loops to have a blocking | |
|
240 | # call | |
|
241 | wx.Yield() | |
|
242 | while self._running_process: | |
|
243 | wx.Yield() | |
|
244 | sleep(0.1) | |
|
245 | # Be sure to flush the buffer. | |
|
246 | self._buffer_flush(event=None) | |
|
247 | ||
|
248 | ||
|
249 | def buffered_write(self, text): | |
|
250 | """ A write method for streams, that caches the stream in order | |
|
251 | to avoid flooding the event loop. | |
|
252 | ||
|
253 | This can be called outside of the main loop, in separate | |
|
254 | threads. | |
|
255 | """ | |
|
256 | self._out_buffer_lock.acquire() | |
|
257 | self._out_buffer.append(text) | |
|
258 | self._out_buffer_lock.release() | |
|
259 | if not self._buffer_flush_timer.IsRunning(): | |
|
260 | wx.CallAfter(self._buffer_flush_timer.Start, 100) # milliseconds | |
|
261 | ||
|
262 | 277 | |
|
263 | 278 | def show_traceback(self): |
|
264 | 279 | start_line = self.GetCurrentLine() |
@@ -266,12 +281,19 class WxController(PrefilterFrontEnd, ConsoleWidget): | |||
|
266 | 281 | wx.Yield() |
|
267 | 282 | for i in range(start_line, self.GetCurrentLine()): |
|
268 | 283 | self.MarkerAdd(i, _ERROR_MARKER) |
|
269 | ||
|
270 | 284 | |
|
285 | ||
|
271 | 286 | #-------------------------------------------------------------------------- |
|
272 | # Private API | |
|
287 | # ConsoleWidget interface | |
|
273 | 288 | #-------------------------------------------------------------------------- |
|
274 | ||
|
289 | ||
|
290 | def new_prompt(self, prompt): | |
|
291 | """ Display a new prompt, and start a new input buffer. | |
|
292 | """ | |
|
293 | self._input_state = 'readline' | |
|
294 | ConsoleWidget.new_prompt(self, prompt) | |
|
295 | ||
|
296 | ||
|
275 | 297 | def _on_key_down(self, event, skip=True): |
|
276 | 298 | """ Capture the character events, let the parent |
|
277 | 299 | widget handle them, and put our logic afterward. |
@@ -286,8 +308,8 class WxController(PrefilterFrontEnd, ConsoleWidget): | |||
|
286 | 308 | self._running_process.process.kill() |
|
287 | 309 | elif self._input_state == 'buffering': |
|
288 | 310 | if self.debug: |
|
289 |
print >>sys.__stderr__, 'Raising Keyboard |
|
|
290 |
raise Keyboard |
|
|
311 | print >>sys.__stderr__, 'Raising KeyboardInterrupt' | |
|
312 | raise KeyboardInterrupt | |
|
291 | 313 | # XXX: We need to make really sure we |
|
292 | 314 | # get back to a prompt. |
|
293 | 315 | elif self._input_state == 'subprocess' and ( |
@@ -315,10 +337,10 class WxController(PrefilterFrontEnd, ConsoleWidget): | |||
|
315 | 337 | elif self.AutoCompActive(): |
|
316 | 338 | event.Skip() |
|
317 | 339 | if event.KeyCode in (wx.WXK_BACK, wx.WXK_DELETE): |
|
318 | wx.CallAfter(self.popup_completion, create=True) | |
|
340 | wx.CallAfter(self._popup_completion, create=True) | |
|
319 | 341 | elif not event.KeyCode in (wx.WXK_UP, wx.WXK_DOWN, wx.WXK_LEFT, |
|
320 | 342 | wx.WXK_RIGHT): |
|
321 | wx.CallAfter(self.popup_completion) | |
|
343 | wx.CallAfter(self._popup_completion) | |
|
322 | 344 | else: |
|
323 | 345 | # Up history |
|
324 | 346 | if event.KeyCode == wx.WXK_UP and ( |
@@ -352,20 +374,28 class WxController(PrefilterFrontEnd, ConsoleWidget): | |||
|
352 | 374 | |
|
353 | 375 | |
|
354 | 376 | def _on_key_up(self, event, skip=True): |
|
377 | """ Called when any key is released. | |
|
378 | """ | |
|
355 | 379 | if event.KeyCode in (59, ord('.')): |
|
356 | 380 | # Intercepting '.' |
|
357 | 381 | event.Skip() |
|
358 | self.popup_completion(create=True) | |
|
382 | self._popup_completion(create=True) | |
|
359 | 383 | else: |
|
360 | 384 | ConsoleWidget._on_key_up(self, event, skip=skip) |
|
361 | 385 | |
|
362 | 386 | |
|
363 | 387 | def _on_enter(self): |
|
388 | """ Called on return key down, in readline input_state. | |
|
389 | """ | |
|
364 | 390 | if self.debug: |
|
365 | 391 | print >>sys.__stdout__, repr(self.get_current_edit_buffer()) |
|
366 | 392 | PrefilterFrontEnd._on_enter(self) |
|
367 | 393 | |
|
368 | 394 | |
|
395 | #-------------------------------------------------------------------------- | |
|
396 | # Private API | |
|
397 | #-------------------------------------------------------------------------- | |
|
398 | ||
|
369 | 399 | def _end_system_call(self): |
|
370 | 400 | """ Called at the end of a system call. |
|
371 | 401 | """ |
@@ -22,19 +22,23 from IPython.ultraTB import ColorTB | |||
|
22 | 22 | |
|
23 | 23 | class SyncTracebackTrap(TracebackTrap): |
|
24 | 24 | |
|
25 |
def __init__(self, sync_formatter=None, formatters=None |
|
|
25 | def __init__(self, sync_formatter=None, formatters=None, | |
|
26 | raiseException=True): | |
|
26 | 27 | TracebackTrap.__init__(self, formatters=formatters) |
|
27 | 28 | if sync_formatter is None: |
|
28 | 29 | sync_formatter = ColorTB(color_scheme='LightBG') |
|
29 | 30 | self.sync_formatter = sync_formatter |
|
31 | self.raiseException = raiseException | |
|
30 | 32 | |
|
31 | 33 | |
|
32 | 34 | def hook(self, *args): |
|
33 | 35 | """ This method actually implements the hook. |
|
34 | 36 | """ |
|
35 | 37 | self.args = args |
|
36 | ||
|
37 | print self.sync_formatter(*self.args) | |
|
38 | if not self.raiseException: | |
|
39 | print self.sync_formatter(*self.args) | |
|
40 | else: | |
|
41 | raise | |
|
38 | 42 | |
|
39 | 43 | |
|
40 | 44 |
General Comments 0
You need to be logged in to leave comments.
Login now