##// END OF EJS Templates
Merged the cosmetic changes with the thread refactoring.
Gael Varoquaux -
r1097:f117697b merge
parent child Browse files
Show More
@@ -1,780 +1,780 b''
1 1 #!/usr/bin/python
2 2 # -*- coding: iso-8859-15 -*-
3 3 '''
4 4 Provides IPython WX console widgets.
5 5
6 6 @author: Laurent Dufrechou
7 7 laurent.dufrechou _at_ gmail.com
8 8 This WX widget is based on the original work of Eitan Isaacson
9 9 that provided the console for the GTK toolkit.
10 10
11 11 Original work from:
12 12 @author: Eitan Isaacson
13 13 @organization: IBM Corporation
14 14 @copyright: Copyright (c) 2007 IBM Corporation
15 15 @license: BSD
16 16
17 17 All rights reserved. This program and the accompanying materials are made
18 18 available under the terms of the BSD which accompanies this distribution, and
19 19 is available at U{http://www.opensource.org/licenses/bsd-license.php}
20 20 '''
21 21
22 22 __version__ = 0.8
23 23 __author__ = "Laurent Dufrechou"
24 24 __email__ = "laurent.dufrechou _at_ gmail.com"
25 25 __license__ = "BSD"
26 26
27 27 import wx
28 28 import wx.stc as stc
29 29 import wx.lib.newevent
30 30
31 31 import re
32 32 import sys
33 33 import os
34 34 import locale
35 35 import time
36 36 from StringIO import StringIO
37 37 try:
38 38 import IPython
39 39 except Exception,e:
40 40 raise "Error importing IPython (%s)" % str(e)
41 41
42 42
43 43 from non_blocking_ip_shell import *
44 44
45 45 class WxNonBlockingIPShell(NonBlockingIPShell):
46 46 '''
47 47 An NonBlockingIPShell Thread that is WX dependent.
48 48 Thus it permits direct interaction with a WX GUI without OnIdle event state machine trick...
49 49 '''
50 50 def __init__(self,wx_instance,
51 51 argv=[],user_ns={},user_global_ns=None,
52 52 cin=None, cout=None, cerr=None,
53 exit_handler=None,time_loop = 0.1):
53 ask_exit_handler=None):
54 54
55 user_ns['addGUIShortcut'] = self.addGUIShortcut
55 #user_ns['addGUIShortcut'] = self.addGUIShortcut
56 56 NonBlockingIPShell.__init__(self,argv,user_ns,user_global_ns,
57 57 cin, cout, cerr,
58 exit_handler,time_loop)
58 ask_exit_handler)
59 59
60 60 # This creates a new Event class and a EVT binder function
61 61 (self.IPythonAskExitEvent, EVT_IP_ASK_EXIT) = wx.lib.newevent.NewEvent()
62 (self.IPythonAddButtonEvent, EVT_IP_ADD_BUTTON_EXIT) = wx.lib.newevent.NewEvent()
63 (self.IPythonExecuteDoneEvent, EVT_IP_EXECUTE_DONE) = wx.lib.newevent.NewEvent()
62 (self.IPythonAddButtonEvent, EVT_IP_ADD_BUTTON_EXIT) = \
63 wx.lib.newevent.NewEvent()
64 (self.IPythonExecuteDoneEvent, EVT_IP_EXECUTE_DONE) = \
65 wx.lib.newevent.NewEvent()
64 66
65 wx_instance.Bind(EVT_IP_ASK_EXIT, wx_instance.exit_handler)
67 wx_instance.Bind(EVT_IP_ASK_EXIT, wx_instance.ask_exit_handler)
66 68 wx_instance.Bind(EVT_IP_ADD_BUTTON_EXIT, wx_instance.add_button_handler)
67 69 wx_instance.Bind(EVT_IP_EXECUTE_DONE, wx_instance.evtStateExecuteDone)
68 70
69 71 self.wx_instance = wx_instance
70 self._IP.exit = self._AskExit
72 self._IP.ask_exit = self._askExit
71 73
72 74 def addGUIShortcut(self,text,func):
73 evt = self.IPythonAddButtonEvent(button_info={'text':text,'func':self.wx_instance.doExecuteLine(func)})
75 evt = self.IPythonAddButtonEvent(
76 button_info={ 'text':text,
77 'func':self.wx_instance.doExecuteLine(func)})
74 78 wx.PostEvent(self.wx_instance, evt)
75 79
76 def _AskExit(self):
80 def _askExit(self):
77 81 evt = self.IPythonAskExitEvent()
78 82 wx.PostEvent(self.wx_instance, evt)
79 83
80 84 def _afterExecute(self):
81 85 evt = self.IPythonExecuteDoneEvent()
82 86 wx.PostEvent(self.wx_instance, evt)
83 87
84 88
85 89 class WxConsoleView(stc.StyledTextCtrl):
86 90 '''
87 91 Specialized styled text control view for console-like workflow.
88 92 We use here a scintilla frontend thus it can be reused in any GUI taht supports
89 93 scintilla with less work.
90 94
91 95 @cvar ANSI_COLORS_BLACK: Mapping of terminal colors to X11 names.(with Black background)
92 96 @type ANSI_COLORS_BLACK: dictionary
93 97
94 98 @cvar ANSI_COLORS_WHITE: Mapping of terminal colors to X11 names.(with White background)
95 99 @type ANSI_COLORS_WHITE: dictionary
96 100
97 101 @ivar color_pat: Regex of terminal color pattern
98 102 @type color_pat: _sre.SRE_Pattern
99 103 '''
100 104 ANSI_STYLES_BLACK ={'0;30': [0,'WHITE'], '0;31': [1,'RED'],
101 105 '0;32': [2,'GREEN'], '0;33': [3,'BROWN'],
102 106 '0;34': [4,'BLUE'], '0;35': [5,'PURPLE'],
103 107 '0;36': [6,'CYAN'], '0;37': [7,'LIGHT GREY'],
104 108 '1;30': [8,'DARK GREY'], '1;31': [9,'RED'],
105 109 '1;32': [10,'SEA GREEN'], '1;33': [11,'YELLOW'],
106 110 '1;34': [12,'LIGHT BLUE'], '1;35': [13,'MEDIUM VIOLET RED'],
107 111 '1;36': [14,'LIGHT STEEL BLUE'], '1;37': [15,'YELLOW']}
108 112
109 113 ANSI_STYLES_WHITE ={'0;30': [0,'BLACK'], '0;31': [1,'RED'],
110 114 '0;32': [2,'GREEN'], '0;33': [3,'BROWN'],
111 115 '0;34': [4,'BLUE'], '0;35': [5,'PURPLE'],
112 116 '0;36': [6,'CYAN'], '0;37': [7,'LIGHT GREY'],
113 117 '1;30': [8,'DARK GREY'], '1;31': [9,'RED'],
114 118 '1;32': [10,'SEA GREEN'], '1;33': [11,'YELLOW'],
115 119 '1;34': [12,'LIGHT BLUE'], '1;35': [13,'MEDIUM VIOLET RED'],
116 120 '1;36': [14,'LIGHT STEEL BLUE'], '1;37': [15,'YELLOW']}
117 121
118 122 def __init__(self,parent,prompt,intro="",background_color="BLACK",pos=wx.DefaultPosition, ID = -1, size=wx.DefaultSize,
119 123 style=0):
120 124 '''
121 125 Initialize console view.
122 126
123 127 @param parent: Parent widget
124 128 @param prompt: User specified prompt
125 129 @type intro: string
126 130 @param intro: User specified startup introduction string
127 131 @type intro: string
128 132 @param background_color: Can be BLACK or WHITE
129 133 @type background_color: string
130 134 @param other: init param of styledTextControl (can be used as-is)
131 135 '''
132 136 stc.StyledTextCtrl.__init__(self, parent, ID, pos, size, style)
133 137
134 138 ####### Scintilla configuration ##################################################
135 139
136 140 # Ctrl + B or Ctrl + N can be used to zoomin/zoomout the text inside the widget
137 141 self.CmdKeyAssign(ord('B'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
138 142 self.CmdKeyAssign(ord('N'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
139 143
140 144 #we define platform specific fonts
141 145 if wx.Platform == '__WXMSW__':
142 146 faces = { 'times': 'Times New Roman',
143 147 'mono' : 'Courier New',
144 148 'helv' : 'Arial',
145 149 'other': 'Comic Sans MS',
146 150 'size' : 10,
147 151 'size2': 8,
148 152 }
149 153 elif wx.Platform == '__WXMAC__':
150 154 faces = { 'times': 'Times New Roman',
151 155 'mono' : 'Monaco',
152 156 'helv' : 'Arial',
153 157 'other': 'Comic Sans MS',
154 158 'size' : 10,
155 159 'size2': 8,
156 160 }
157 161 else:
158 162 faces = { 'times': 'Times',
159 163 'mono' : 'Courier',
160 164 'helv' : 'Helvetica',
161 165 'other': 'new century schoolbook',
162 166 'size' : 10,
163 167 'size2': 8,
164 168 }
165 169
166 170 #We draw a line at position 80
167 171 self.SetEdgeMode(stc.STC_EDGE_LINE)
168 172 self.SetEdgeColumn(80)
169 173 self.SetEdgeColour(wx.LIGHT_GREY)
170 174
171 175 #self.SetViewWhiteSpace(True)
172 176 #self.SetViewEOL(True)
173 177 self.SetEOLMode(stc.STC_EOL_CRLF)
174 178 #self.SetWrapMode(stc.STC_WRAP_CHAR)
175 179 #self.SetWrapMode(stc.STC_WRAP_WORD)
176 180 self.SetBufferedDraw(True)
177 181 #self.SetUseAntiAliasing(True)
178 182 self.SetLayoutCache(stc.STC_CACHE_PAGE)
179 183
180 184 self.EnsureCaretVisible()
181 185
182 186 self.SetMargins(3,3) #text is moved away from border with 3px
183 187 # Suppressing Scintilla margins
184 188 self.SetMarginWidth(0,0)
185 189 self.SetMarginWidth(1,0)
186 190 self.SetMarginWidth(2,0)
187 191
188 192 # make some styles
189 193 if background_color != "BLACK":
190 194 self.background_color = "WHITE"
191 195 self.SetCaretForeground("BLACK")
192 196 self.ANSI_STYLES = self.ANSI_STYLES_WHITE
193 197 else:
194 198 self.background_color = background_color
195 199 self.SetCaretForeground("WHITE")
196 200 self.ANSI_STYLES = self.ANSI_STYLES_BLACK
197 201
198 202 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "fore:%s,back:%s,size:%d,face:%s" % (self.ANSI_STYLES['0;30'][1],
199 203 self.background_color,
200 204 faces['size'], faces['mono']))
201 205 self.StyleClearAll()
202 206 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, "fore:#FF0000,back:#0000FF,bold")
203 207 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, "fore:#000000,back:#FF0000,bold")
204 208
205 209 for style in self.ANSI_STYLES.values():
206 210 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
207 211
208 212 #######################################################################
209 213
210 214 self.indent = 0
211 215 self.prompt_count = 0
212 216 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
213 217
214 218 self.write(intro)
215 219 self.setPrompt(prompt)
216 220 self.showPrompt()
217 221
218 222 self.Bind(wx.EVT_KEY_DOWN, self._onKeypress, self)
219 223 #self.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdateUI)
220 224
221 225 def write(self, text):
222 226 '''
223 227 Write given text to buffer.
224 228
225 229 @param text: Text to append.
226 230 @type text: string
227 231 '''
228 232 segments = self.color_pat.split(text)
229 233 segment = segments.pop(0)
230 234 self.StartStyling(self.getCurrentLineEnd(),0xFF)
231 235 self.AppendText(segment)
232 236
233 237 if segments:
234 238 ansi_tags = self.color_pat.findall(text)
235 239
236 240 for tag in ansi_tags:
237 241 i = segments.index(tag)
238 242 self.StartStyling(self.getCurrentLineEnd(),0xFF)
239 243 self.AppendText(segments[i+1])
240 244
241 245 if tag != '0':
242 246 self.SetStyling(len(segments[i+1]),self.ANSI_STYLES[tag][0])
243 247
244 248 segments.pop(i)
245 249
246 250 self.moveCursor(self.getCurrentLineEnd())
247 251
248 252 def getPromptLen(self):
249 253 '''
250 254 Return the length of current prompt
251 255 '''
252 256 return len(str(self.prompt_count)) + 7
253 257
254 258 def setPrompt(self,prompt):
255 259 self.prompt = prompt
256 260
257 261 def setIndentation(self,indentation):
258 262 self.indent = indentation
259 263
260 264 def setPromptCount(self,count):
261 265 self.prompt_count = count
262 266
263 267 def showPrompt(self):
264 268 '''
265 269 Prints prompt at start of line.
266 270
267 271 @param prompt: Prompt to print.
268 272 @type prompt: string
269 273 '''
270 274 self.write(self.prompt)
271 275 #now we update the position of end of prompt
272 276 self.current_start = self.getCurrentLineEnd()
273 277
274 278 autoindent = self.indent*' '
275 279 autoindent = autoindent.replace(' ','\t')
276 280 self.write(autoindent)
277 281
278 282 def changeLine(self, text):
279 283 '''
280 284 Replace currently entered command line with given text.
281 285
282 286 @param text: Text to use as replacement.
283 287 @type text: string
284 288 '''
285 289 self.SetSelection(self.getCurrentPromptStart(),self.getCurrentLineEnd())
286 290 self.ReplaceSelection(text)
287 291 self.moveCursor(self.getCurrentLineEnd())
288 292
289 293 def getCurrentPromptStart(self):
290 294 return self.current_start
291 295
292 296 def getCurrentLineStart(self):
293 297 return self.GotoLine(self.LineFromPosition(self.GetCurrentPos()))
294 298
295 299 def getCurrentLineEnd(self):
296 300 return self.GetLength()
297 301
298 302 def getCurrentLine(self):
299 303 '''
300 304 Get text in current command line.
301 305
302 306 @return: Text of current command line.
303 307 @rtype: string
304 308 '''
305 309 return self.GetTextRange(self.getCurrentPromptStart(),
306 310 self.getCurrentLineEnd())
307 311
308 312 def showReturned(self, text):
309 313 '''
310 314 Show returned text from last command and print new prompt.
311 315
312 316 @param text: Text to show.
313 317 @type text: string
314 318 '''
315 319 self.write('\n'+text)
316 320 if text:
317 321 self.write('\n')
318 322 self.showPrompt()
319 323
320 324 def moveCursorOnNewValidKey(self):
321 325 #If cursor is at wrong position put it at last line...
322 326 if self.GetCurrentPos() < self.getCurrentPromptStart():
323 327 self.GotoPos(self.getCurrentPromptStart())
324 328
325 329 def removeFromTo(self,from_pos,to_pos):
326 330 if from_pos < to_pos:
327 331 self.SetSelection(from_pos,to_pos)
328 332 self.DeleteBack()
329 333
330 334 def removeCurrentLine(self):
331 335 self.LineDelete()
332 336
333 337 def moveCursor(self,position):
334 338 self.GotoPos(position)
335 339
336 340 def getCursorPos(self):
337 341 return self.GetCurrentPos()
338 342
339 343 def selectFromTo(self,from_pos,to_pos):
340 344 self.SetSelectionStart(from_pos)
341 345 self.SetSelectionEnd(to_pos)
342 346
343 347 def writeHistory(self,history):
344 348 self.removeFromTo(self.getCurrentPromptStart(),self.getCurrentLineEnd())
345 349 self.changeLine(history)
346 350
347 351 def writeCompletion(self, possibilities):
348 352 max_len = len(max(possibilities,key=len))
349 353 max_symbol =' '*max_len
350 354
351 355 #now we check how much symbol we can put on a line...
352 356 cursor_pos = self.getCursorPos()
353 357 test_buffer = max_symbol + ' '*4
354 358 current_lines = self.GetLineCount()
355 359
356 360 allowed_symbols = 80/len(test_buffer)
357 361 if allowed_symbols == 0:
358 362 allowed_symbols = 1
359 363
360 364 pos = 1
361 365 buf = ''
362 366 for symbol in possibilities:
363 367 #buf += symbol+'\n'#*spaces)
364 368 if pos<allowed_symbols:
365 369 spaces = max_len - len(symbol) + 4
366 370 buf += symbol+' '*spaces
367 371 pos += 1
368 372 else:
369 373 buf+=symbol+'\n'
370 374 pos = 1
371 375 self.write(buf)
372 376
373 377 def _onKeypress(self, event, skip=True):
374 378 '''
375 379 Key press callback used for correcting behavior for console-like
376 380 interfaces. For example 'home' should go to prompt, not to begining of
377 381 line.
378 382
379 383 @param widget: Widget that key press accored in.
380 384 @type widget: gtk.Widget
381 385 @param event: Event object
382 386 @type event: gtk.gdk.Event
383 387
384 388 @return: Return True if event as been catched.
385 389 @rtype: boolean
386 390 '''
387 391
388 392 if event.GetKeyCode() == wx.WXK_HOME:
389 393 if event.Modifiers == wx.MOD_NONE:
390 394 self.moveCursorOnNewValidKey()
391 395 self.moveCursor(self.getCurrentPromptStart())
392 396 return True
393 397 elif event.Modifiers == wx.MOD_SHIFT:
394 398 self.moveCursorOnNewValidKey()
395 399 self.selectFromTo(self.getCurrentPromptStart(),self.getCursorPos())
396 400 return True
397 401 else:
398 402 return False
399 403
400 404 elif event.GetKeyCode() == wx.WXK_LEFT:
401 405 if event.Modifiers == wx.MOD_NONE:
402 406 self.moveCursorOnNewValidKey()
403 407
404 408 self.moveCursor(self.getCursorPos()-1)
405 409 if self.getCursorPos() < self.getCurrentPromptStart():
406 410 self.moveCursor(self.getCurrentPromptStart())
407 411 return True
408 412
409 413 elif event.GetKeyCode() == wx.WXK_BACK:
410 414 self.moveCursorOnNewValidKey()
411 415 if self.getCursorPos() > self.getCurrentPromptStart():
412 416 self.removeFromTo(self.getCursorPos()-1,self.getCursorPos())
413 417 return True
414 418
415 419 if skip:
416 420 if event.GetKeyCode() not in [wx.WXK_PAGEUP,wx.WXK_PAGEDOWN] and event.Modifiers == wx.MOD_NONE:
417 421 self.moveCursorOnNewValidKey()
418 422
419 423 event.Skip()
420 424 return True
421 425 return False
422 426
423 427 def OnUpdateUI(self, evt):
424 428 # check for matching braces
425 429 braceAtCaret = -1
426 430 braceOpposite = -1
427 431 charBefore = None
428 432 caretPos = self.GetCurrentPos()
429 433
430 434 if caretPos > 0:
431 435 charBefore = self.GetCharAt(caretPos - 1)
432 436 styleBefore = self.GetStyleAt(caretPos - 1)
433 437
434 438 # check before
435 439 if charBefore and chr(charBefore) in "[]{}()" and styleBefore == stc.STC_P_OPERATOR:
436 440 braceAtCaret = caretPos - 1
437 441
438 442 # check after
439 443 if braceAtCaret < 0:
440 444 charAfter = self.GetCharAt(caretPos)
441 445 styleAfter = self.GetStyleAt(caretPos)
442 446
443 447 if charAfter and chr(charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR:
444 448 braceAtCaret = caretPos
445 449
446 450 if braceAtCaret >= 0:
447 451 braceOpposite = self.BraceMatch(braceAtCaret)
448 452
449 453 if braceAtCaret != -1 and braceOpposite == -1:
450 454 self.BraceBadLight(braceAtCaret)
451 455 else:
452 456 self.BraceHighlight(braceAtCaret, braceOpposite)
453 457 #pt = self.PointFromPosition(braceOpposite)
454 458 #self.Refresh(True, wxRect(pt.x, pt.y, 5,5))
455 459 #print pt
456 460 #self.Refresh(False)
457 461
458 462 class WxIPythonViewPanel(wx.Panel):
459 463 '''
460 464 This is wx.Panel that embbed the IPython Thread and the wx.StyledTextControl
461 465 If you want to port this to any other GUI toolkit, just replace the WxConsoleView
462 466 by YOURGUIConsoleView and make YOURGUIIPythonView derivate from whatever container you want.
463 467 I've choosed to derivate from a wx.Panel because it seems to be ore usefull
464 468 Any idea to make it more 'genric' welcomed.
465 469 '''
466 def __init__(self, parent, exit_handler=None, intro=None,
470
471 def __init__(self, parent, ask_exit_handler=None, intro=None,
467 472 background_color="BLACK", add_button_handler=None,
468 473 wx_ip_shell=None,
469 474 ):
470 475 '''
471 476 Initialize.
472 477 Instanciate an IPython thread.
473 478 Instanciate a WxConsoleView.
474 479 Redirect I/O to console.
475 480 '''
476 481 wx.Panel.__init__(self,parent,-1)
477 482
478 483 ### IPython thread instanciation ###
479 484 self.cout = StringIO()
480 485
481 486 self.add_button_handler = add_button_handler
482 self.exit_handler = exit_handler
487 self.ask_exit_handler = ask_exit_handler
483 488
484 489 if wx_ip_shell is not None:
485 490 self.IP = wx_ip_shell
486 491 else:
487 492 self.IP = WxNonBlockingIPShell(self,
488 493 cout=self.cout,cerr=self.cout,
489 exit_handler = exit_handler,
490 time_loop = 0.1)
491 self.IP.start()
492
494 ask_exit_handler = ask_exit_handler)
493 495 ### IPython wx console view instanciation ###
494 496 #If user didn't defined an intro text, we create one for him
495 497 #If you really wnat an empty intrp just call wxIPythonViewPanel with intro=''
496 498 if intro == None:
497 499 welcome_text = "Welcome to WxIPython Shell.\n\n"
498 500 welcome_text+= self.IP.getBanner()
499 501 welcome_text+= "!command -> Execute command in shell\n"
500 502 welcome_text+= "TAB -> Autocompletion\n"
501 503
502 504 self.text_ctrl = WxConsoleView(self,
503 505 self.IP.getPrompt(),
504 506 intro=welcome_text,
505 507 background_color=background_color)
506 508
507 509 self.text_ctrl.Bind(wx.EVT_KEY_DOWN, self.keyPress, self.text_ctrl)
508 510
509 511 ### making the layout of the panel ###
510 512 sizer = wx.BoxSizer(wx.VERTICAL)
511 513 sizer.Add(self.text_ctrl, 1, wx.EXPAND)
512 514 self.SetAutoLayout(True)
513 515 sizer.Fit(self)
514 516 sizer.SetSizeHints(self)
515 517 self.SetSizer(sizer)
516 518 #and we focus on the widget :)
517 519 self.SetFocus()
518 520
519 521 ### below are the thread communication variable ###
520 522 # the IPython thread is managed via unidirectional communication.
521 523 # It's a thread slave that can't interact by itself with the GUI.
522 524 # When the GUI event loop is done runStateMachine() is called and the thread sate is then
523 525 # managed.
524 526
525 527 #Initialize the state machine #kept for information
526 528 #self.states = ['IDLE',
527 529 # 'DO_EXECUTE_LINE',
528 530 # 'WAIT_END_OF_EXECUTION',
529 531 # 'SHOW_DOC',
530 532 # 'SHOW_PROMPT']
531 533
532 534 self.cur_state = 'IDLE'
533 535 self.pager_state = 'DONE'
534 536 #wx.CallAfter(self.runStateMachine)
535 537
536 538 # This creates a new Event class and a EVT binder function
537 539 #(self.AskExitEvent, EVT_ASK_EXIT) = wx.lib.newevent.NewEvent()
538 540 #(self.AddButtonEvent, EVT_ADDBUTTON_EXIT) = wx.lib.newevent.NewEvent()
539 541
540 542
541 543 #self.Bind(wx.EVT_IDLE, self.runStateMachine)
542 544
543 545 def __del__(self):
544 self.IP.shutdown()
545 self.IP.join()
546 546 WxConsoleView.__del__()
547 547
548 548 #---------------------------- IPython Thread Management ---------------------------------------
549 549 def stateDoExecuteLine(self):
550 550 #print >>sys.__stdout__,"command:",self.getCurrentLine()
551 551 self.doExecuteLine(self.text_ctrl.getCurrentLine())
552 552
553 553 def doExecuteLine(self,line):
554 554 #print >>sys.__stdout__,"command:",line
555 555 self.IP.doExecute(line.replace('\t',' '*4))
556 556 self.updateHistoryTracker(self.text_ctrl.getCurrentLine())
557 557 self.cur_state = 'WAIT_END_OF_EXECUTION'
558 558
559 559
560 560 def evtStateExecuteDone(self,evt):
561 561 self.doc = self.IP.getDocText()
562 562 self.help = self.IP.getHelpText()
563 563 if self.doc:
564 564 self.pager_state = 'INIT'
565 565 self.cur_state = 'SHOW_DOC'
566 566 self.pager(self.doc)
567 567 #if self.pager_state == 'DONE':
568 568 if self.help:
569 569 self.pager_state = 'INIT_HELP'
570 570 self.cur_state = 'SHOW_DOC'
571 571 self.pager(self.help)
572 572
573 573 else:
574 574 self.stateShowPrompt()
575 575
576 576 def stateShowPrompt(self):
577 577 self.cur_state = 'SHOW_PROMPT'
578 578 self.text_ctrl.setPrompt(self.IP.getPrompt())
579 579 self.text_ctrl.setIndentation(self.IP.getIndentation())
580 580 self.text_ctrl.setPromptCount(self.IP.getPromptCount())
581 581 rv = self.cout.getvalue()
582 582 if rv: rv = rv.strip('\n')
583 583 self.text_ctrl.showReturned(rv)
584 584 self.cout.truncate(0)
585 585 self.IP.initHistoryIndex()
586 586 self.cur_state = 'IDLE'
587 587
588 588 ## def runStateMachine(self,event):
589 589 ## #print >>sys.__stdout__,"state:",self.cur_state
590 590 ## self.updateStatusTracker(self.cur_state)
591 591 ##
592 592 ## #if self.cur_state == 'DO_EXECUTE_LINE':
593 593 ## # self.doExecuteLine()
594 594 ##
595 595 ## if self.cur_state == 'WAIT_END_OF_EXECUTION':
596 596 ## if self.IP.isExecuteDone():
597 597 ## #self.button = self.IP.getAddButton()
598 598 ## #if self.IP.getAskExit():
599 599 ## # evt = self.AskExitEvent()
600 600 ## # wx.PostEvent(self, evt)
601 601 ## # self.IP.clearAskExit()
602 602 ## self.doc = self.IP.getDocText()
603 603 ## if self.doc:
604 604 ## self.pager_state = 'INIT'
605 605 ## self.cur_state = 'SHOW_DOC'
606 606 ## #if self.button:
607 607 ## #self.IP.doExecute('print "cool"')#self.button['func'])
608 608 ## #self.updateHistoryTracker(self.text_ctrl.getCurrentLine())
609 609 ##
610 610 ## # self.button['func']='print "cool!"'
611 611 ## # self.add_button_handler(self.button)
612 612 ## # self.IP.shortcutProcessed()
613 613 ##
614 614 ## else:
615 615 ## self.cur_state = 'SHOW_PROMPT'
616 616 ##
617 617 ## if self.cur_state == 'SHOW_PROMPT':
618 618 ## self.text_ctrl.setPrompt(self.IP.getPrompt())
619 619 ## self.text_ctrl.setIndentation(self.IP.getIndentation())
620 620 ## self.text_ctrl.setPromptCount(self.IP.getPromptCount())
621 621 ## rv = self.cout.getvalue()
622 622 ## if rv: rv = rv.strip('\n')
623 623 ## self.text_ctrl.showReturned(rv)
624 624 ## self.cout.truncate(0)
625 625 ## self.IP.initHistoryIndex()
626 626 ## self.cur_state = 'IDLE'
627 627 ##
628 628 ## if self.cur_state == 'SHOW_DOC':
629 629 ## self.pager(self.doc)
630 630 ## if self.pager_state == 'DONE':
631 631 ## self.cur_state = 'SHOW_PROMPT'
632 632 ##
633 633 ## event.Skip()
634 634
635 635 #---------------------------- IPython pager ---------------------------------------
636 636 def pager(self,text):#,start=0,screen_lines=0,pager_cmd = None):
637 637 if self.pager_state == 'WAITING':
638 638 #print >>sys.__stdout__,"PAGER waiting"
639 639 return
640 640
641 641 if self.pager_state == 'INIT':
642 642 #print >>sys.__stdout__,"PAGER state:",self.pager_state
643 643 self.pager_lines = text[7:].split('\n')
644 644 self.pager_nb_lines = len(self.pager_lines)
645 645 self.pager_index = 0
646 646 self.pager_do_remove = False
647 647 self.text_ctrl.write('\n')
648 648 self.pager_state = 'PROCESS_LINES'
649 649
650 650 if self.pager_state == 'INIT_HELP':
651 651 #print >>sys.__stdout__,"HELP PAGER state:",self.pager_state
652 652 self.pager_lines = text[:].split('\n')
653 653 self.pager_nb_lines = len(self.pager_lines)
654 654 self.pager_index = 0
655 655 self.pager_do_remove = False
656 656 self.text_ctrl.write('\n')
657 657 self.pager_state = 'PROCESS_LINES'
658 658
659 659 if self.pager_state == 'PROCESS_LINES':
660 660 #print >>sys.__stdout__,"PAGER state:",self.pager_state
661 661 if self.pager_do_remove == True:
662 662 self.text_ctrl.removeCurrentLine()
663 663 self.pager_do_remove = False
664 664
665 665 if self.pager_nb_lines > 10:
666 666 #print >>sys.__stdout__,"PAGER processing 10 lines"
667 667 if self.pager_index > 0:
668 668 self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
669 669 else:
670 670 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
671 671
672 672 for line in self.pager_lines[self.pager_index+1:self.pager_index+9]:
673 673 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
674 674 self.pager_index += 10
675 675 self.pager_nb_lines -= 10
676 676 self.text_ctrl.write("--- Push Enter to continue or 'Q' to quit---")
677 677 self.pager_do_remove = True
678 678 self.pager_state = 'WAITING'
679 679 return
680 680 else:
681 681 #print >>sys.__stdout__,"PAGER processing last lines"
682 682 if self.pager_nb_lines > 0:
683 683 if self.pager_index > 0:
684 684 self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
685 685 else:
686 686 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
687 687
688 688 self.pager_index += 1
689 689 self.pager_nb_lines -= 1
690 690 if self.pager_nb_lines > 0:
691 691 for line in self.pager_lines[self.pager_index:]:
692 692 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
693 693 self.pager_nb_lines = 0
694 694 self.pager_state = 'DONE'
695 695 self.stateShowPrompt()
696 696
697 697 #---------------------------- Key Handler --------------------------------------------
698 698 def keyPress(self, event):
699 699 '''
700 700 Key press callback with plenty of shell goodness, like history,
701 701 autocompletions, etc.
702 702 '''
703 703
704 704 if event.GetKeyCode() == ord('C'):
705 705 if event.Modifiers == wx.MOD_CONTROL:
706 706 if self.cur_state == 'WAIT_END_OF_EXECUTION':
707 707 #we raise an exception inside the IPython thread container
708 self.IP.raise_exc(KeyboardInterrupt)
708 self.IP.ce.raise_exc(KeyboardInterrupt)
709 709 return
710 710
711 711 if event.KeyCode == wx.WXK_RETURN:
712 712 if self.cur_state == 'IDLE':
713 713 #we change the state ot the state machine
714 714 self.cur_state = 'DO_EXECUTE_LINE'
715 715 self.stateDoExecuteLine()
716 716 return
717 717 if self.pager_state == 'WAITING':
718 718 self.pager_state = 'PROCESS_LINES'
719 719 self.pager(self.doc)
720 720 return
721 721
722 722 if event.GetKeyCode() in [ord('q'),ord('Q')]:
723 723 if self.pager_state == 'WAITING':
724 724 self.pager_state = 'DONE'
725 725 self.stateShowPrompt()
726 726 return
727 727
728 728 #scroll_position = self.text_ctrl.GetScrollPos(wx.VERTICAL)
729 729 if self.cur_state == 'IDLE':
730 730 if event.KeyCode == wx.WXK_UP:
731 731 history = self.IP.historyBack()
732 732 self.text_ctrl.writeHistory(history)
733 733 return
734 734 if event.KeyCode == wx.WXK_DOWN:
735 735 history = self.IP.historyForward()
736 736 self.text_ctrl.writeHistory(history)
737 737 return
738 738 if event.KeyCode == wx.WXK_TAB:
739 739 #if line empty we disable tab completion
740 740 if not self.text_ctrl.getCurrentLine().strip():
741 741 self.text_ctrl.write('\t')
742 742 return
743 743 completed, possibilities = self.IP.complete(self.text_ctrl.getCurrentLine())
744 744 if len(possibilities) > 1:
745 745 cur_slice = self.text_ctrl.getCurrentLine()
746 746 self.text_ctrl.write('\n')
747 747 self.text_ctrl.writeCompletion(possibilities)
748 748 self.text_ctrl.write('\n')
749 749
750 750 self.text_ctrl.showPrompt()
751 751 self.text_ctrl.write(cur_slice)
752 752 self.text_ctrl.changeLine(completed or cur_slice)
753 753
754 754 return
755 755 event.Skip()
756 756
757 757 #---------------------------- Hook Section --------------------------------------------
758 758 def updateHistoryTracker(self,command_line):
759 759 '''
760 760 Default history tracker (does nothing)
761 761 '''
762 762 pass
763 763
764 764 def setHistoryTrackerHook(self,func):
765 765 '''
766 766 Define a new history tracker
767 767 '''
768 768 self.updateHistoryTracker = func
769 769 def updateStatusTracker(self,status):
770 770 '''
771 771 Default status tracker (does nothing)
772 772 '''
773 773 pass
774 774
775 775 def setStatusTrackerHook(self,func):
776 776 '''
777 777 Define a new status tracker
778 778 '''
779 779 self.updateStatusTracker = func
780 780
@@ -1,473 +1,473 b''
1 1 #!/usr/bin/python
2 2 # -*- coding: iso-8859-15 -*-
3 3 '''
4 4 Provides IPython remote instance.
5 5
6 6 @author: Laurent Dufrechou
7 7 laurent.dufrechou _at_ gmail.com
8 8 @license: BSD
9 9
10 10 All rights reserved. This program and the accompanying materials are made
11 11 available under the terms of the BSD which accompanies this distribution, and
12 12 is available at U{http://www.opensource.org/licenses/bsd-license.php}
13 13 '''
14 14
15 15 __version__ = 0.9
16 16 __author__ = "Laurent Dufrechou"
17 17 __email__ = "laurent.dufrechou _at_ gmail.com"
18 18 __license__ = "BSD"
19 19
20 20 import re
21 21 import sys
22 22 import os
23 23 import locale
24 24 import time
25 25 import pydoc,__builtin__,site
26 26 from thread_ex import ThreadEx
27 27 from StringIO import StringIO
28 28
29 29 try:
30 30 import IPython
31 31 except Exception,e:
32 32 raise "Error importing IPython (%s)" % str(e)
33 33
34 ##############################################################################
34 35 class _Helper(object):
35 36 """Redefine the built-in 'help'.
36 37 This is a wrapper around pydoc.help (with a twist).
37 38 """
39
38 40 def __init__(self,pager):
39 41 self._pager = pager
40 42
41 43 def __repr__(self):
42 44 return "Type help() for interactive help, " \
43 45 "or help(object) for help about object."
46
44 47 def __call__(self, *args, **kwds):
45 48 class DummyWriter(object):
46 49 def __init__(self,pager):
47 50 self._pager = pager
48 51
49 52 def write(self,data):
50 53 self._pager(data)
51 54
52 55 import pydoc
53 56 pydoc.help.output = DummyWriter(self._pager)
54 57 pydoc.help.interact = lambda :1
55 58
56 59 #helper.output.write = self.doc.append
57 60 return pydoc.help(*args, **kwds)
58 61
59 62
60 class NonBlockingIPShell(ThreadEx):
63 ##############################################################################
64 class _CodeExecutor(ThreadEx):
65
66 def __init__(self, instance, after):
67 ThreadEx.__init__(self)
68 self.instance = instance
69 self._afterExecute=after
70
71 def run(self):
72 try:
73 self.instance._doc_text = None
74 self.instance._help_text = None
75 self.instance._execute()
76 # used for uper class to generate event after execution
77 self._afterExecute()
78
79 except KeyboardInterrupt:
80 pass
81
82
83 ##############################################################################
84 class NonBlockingIPShell(object):
61 85 '''
62 Create an IPython instance inside a dedicated thread.
63 Does not start a blocking event loop, instead allow single iterations.
86 Create an IPython instance, running the commands in a separate,
87 non-blocking thread.
64 88 This allows embedding in any GUI without blockage.
65 The thread is a slave one, in that it doesn't interact directly with the GUI.
66 Note ThreadEx class supports asynchroneous function call
67 via raise_exc()
89
90 Note: The ThreadEx class supports asynchroneous function call
91 via raise_exc()
68 92 '''
69 93
70 94 def __init__(self,argv
71 95 =[],user_ns={},user_global_ns=None,
72 96 cin=None, cout=None, cerr=None,
73 exit_handler=None,time_loop = 0.1):
97 ask_exit_handler=None):
74 98 '''
75 99 @param argv: Command line options for IPython
76 100 @type argv: list
77 101 @param user_ns: User namespace.
78 102 @type user_ns: dictionary
79 103 @param user_global_ns: User global namespace.
80 104 @type user_global_ns: dictionary.
81 105 @param cin: Console standard input.
82 106 @type cin: IO stream
83 107 @param cout: Console standard output.
84 108 @type cout: IO stream
85 109 @param cerr: Console standard error.
86 110 @type cerr: IO stream
87 111 @param exit_handler: Replacement for builtin exit() function
88 112 @type exit_handler: function
89 113 @param time_loop: Define the sleep time between two thread's loop
90 114 @type int
91 115 '''
92 ThreadEx.__init__(self)
93
94 116 #first we redefine in/out/error functions of IPython
95 117 if cin:
96 118 IPython.Shell.Term.cin = cin
97 119 if cout:
98 120 IPython.Shell.Term.cout = cout
99 121 if cerr:
100 122 IPython.Shell.Term.cerr = cerr
101 123
102 124 # This is to get rid of the blockage that accurs during
103 125 # IPython.Shell.InteractiveShell.user_setup()
104 126 IPython.iplib.raw_input = lambda x: None
105 127
106 128 self._term = IPython.genutils.IOTerm(cin=cin, cout=cout, cerr=cerr)
107 129
108 130 excepthook = sys.excepthook
109 131
110 132 self._IP = IPython.Shell.make_IPython(
111 133 argv,user_ns=user_ns,
112 134 user_global_ns=user_global_ns,
113 135 embedded=True,
114 136 shell_class=IPython.Shell.InteractiveShell)
115 137
116 138 #we replace IPython default encoding by wx locale encoding
117 139 loc = locale.getpreferredencoding()
118 140 if loc:
119 141 self._IP.stdin_encoding = loc
120 142 #we replace the ipython default pager by our pager
121 143 self._IP.set_hook('show_in_pager',self._pager)
122 144
123 145 #we replace the ipython default shell command caller by our shell handler
124 146 self._IP.set_hook('shell_hook',self._shell)
125 147
126 148 #we replace the ipython default input command caller by our method
127 149 IPython.iplib.raw_input_original = self._raw_input
128 150 #we replace the ipython default exit command by our method
129 151 self._IP.exit = self._setAskExit
130
152 #we modify Exit and Quit Magic
153 ip = IPython.ipapi.get()
154 ip.expose_magic('Exit', self._setDoExit)
155 ip.expose_magic('Quit', self._setDoExit)
156
131 157 sys.excepthook = excepthook
132 158
133 159 self._iter_more = 0
134 160 self._history_level = 0
135 161 self._complete_sep = re.compile('[\s\{\}\[\]\(\)]')
136 162 self._prompt = str(self._IP.outputcache.prompt1).strip()
137 163
138 164 #thread working vars
139 self._terminate = False
140 self._time_loop = time_loop
141 self._do_execute = False
142 165 self._line_to_execute = ''
143 166
144 167 #vars that will be checked by GUI loop to handle thread states...
145 168 #will be replaced later by PostEvent GUI funtions...
146 169 self._doc_text = None
147 170 self._help_text = None
148 171 self._ask_exit = False
149 172 self._add_button = None
150 173
151 174 #we replace the help command
152 175 self._IP.user_ns['help'] = _Helper(self._pager_help)
153 176
154 177 #----------------------- Thread management section ----------------------
155 def run (self):
156 """
157 Thread main loop
158 The thread will run until self._terminate will be set to True via shutdown() function
159 Command processing can be interrupted with Instance.raise_exc(KeyboardInterrupt) call in the
160 GUI thread.
161 """
162 while(not self._terminate):
163 try:
164 if self._do_execute:
165 self._doc_text = None
166 self._help_text = None
167 self._execute()
168 self._do_execute = False
169 self._afterExecute() #used for uper class to generate event after execution
170
171 except KeyboardInterrupt:
172 pass
173
174 time.sleep(self._time_loop)
175
176 def shutdown(self):
177 """
178 Shutdown the tread
179 """
180 self._terminate = True
181
182 178 def doExecute(self,line):
183 179 """
184 180 Tell the thread to process the 'line' command
185 181 """
186 self._do_execute = True
182
187 183 self._line_to_execute = line
184
185 self.ce = _CodeExecutor(self,self._afterExecute)
186 self.ce.start()
188 187
189 def isExecuteDone(self):
190 """
191 Returns the processing state
192 """
193 return not self._do_execute
194
195 188 #----------------------- IPython management section ----------------------
196 189 def getAskExit(self):
197 190 '''
198 191 returns the _ask_exit variable that can be checked by GUI to see if
199 192 IPython request an exit handling
200 193 '''
201 194 return self._ask_exit
202 195
203 196 def clearAskExit(self):
204 197 '''
205 198 clear the _ask_exit var when GUI as handled the request.
206 199 '''
207 200 self._ask_exit = False
208 201
209 202 def getDocText(self):
210 203 """
211 204 Returns the output of the processing that need to be paged (if any)
212 205
213 206 @return: The std output string.
214 207 @rtype: string
215 208 """
216 209 return self._doc_text
217 210
218 211 def getHelpText(self):
219 212 """
220 213 Returns the output of the processing that need to be paged via help pager(if any)
221 214
222 215 @return: The std output string.
223 216 @rtype: string
224 217 """
225 218 return self._help_text
226 219
227 220 def getBanner(self):
228 221 """
229 222 Returns the IPython banner for useful info on IPython instance
230 223
231 224 @return: The banner string.
232 225 @rtype: string
233 226 """
234 227 return self._IP.BANNER
235 228
236 229 def getPromptCount(self):
237 230 """
238 231 Returns the prompt number.
239 232 Each time a user execute a line in the IPython shell the prompt count is increased
240 233
241 234 @return: The prompt number
242 235 @rtype: int
243 236 """
244 237 return self._IP.outputcache.prompt_count
245 238
246 239 def getPrompt(self):
247 240 """
248 241 Returns current prompt inside IPython instance
249 242 (Can be In [...]: ot ...:)
250 243
251 244 @return: The current prompt.
252 245 @rtype: string
253 246 """
254 247 return self._prompt
255 248
256 249 def getIndentation(self):
257 250 """
258 251 Returns the current indentation level
259 252 Usefull to put the caret at the good start position if we want to do autoindentation.
260 253
261 254 @return: The indentation level.
262 255 @rtype: int
263 256 """
264 257 return self._IP.indent_current_nsp
265 258
266 259 def updateNamespace(self, ns_dict):
267 260 '''
268 261 Add the current dictionary to the shell namespace.
269 262
270 263 @param ns_dict: A dictionary of symbol-values.
271 264 @type ns_dict: dictionary
272 265 '''
273 266 self._IP.user_ns.update(ns_dict)
274 267
275 268 def complete(self, line):
276 269 '''
277 270 Returns an auto completed line and/or posibilities for completion.
278 271
279 272 @param line: Given line so far.
280 273 @type line: string
281 274
282 275 @return: Line completed as for as possible,
283 276 and possible further completions.
284 277 @rtype: tuple
285 278 '''
286 279 split_line = self._complete_sep.split(line)
287 280 possibilities = self._IP.complete(split_line[-1])
288 281 if possibilities:
289 282
290 283 def _commonPrefix(str1, str2):
291 284 '''
292 285 Reduction function. returns common prefix of two given strings.
293 286
294 287 @param str1: First string.
295 288 @type str1: string
296 289 @param str2: Second string
297 290 @type str2: string
298 291
299 292 @return: Common prefix to both strings.
300 293 @rtype: string
301 294 '''
302 295 for i in range(len(str1)):
303 296 if not str2.startswith(str1[:i+1]):
304 297 return str1[:i]
305 298 return str1
306 299 common_prefix = reduce(_commonPrefix, possibilities)
307 300 completed = line[:-len(split_line[-1])]+common_prefix
308 301 else:
309 302 completed = line
310 303 return completed, possibilities
311 304
312 305 def historyBack(self):
313 306 '''
314 307 Provides one history command back.
315 308
316 309 @return: The command string.
317 310 @rtype: string
318 311 '''
319 312 history = ''
320 313 #the below while loop is used to suppress empty history lines
321 314 while((history == '' or history == '\n') and self._history_level >0):
322 315 if self._history_level>=1:
323 316 self._history_level -= 1
324 317 history = self._getHistory()
325 318 return history
326 319
327 320 def historyForward(self):
328 321 '''
329 322 Provides one history command forward.
330 323
331 324 @return: The command string.
332 325 @rtype: string
333 326 '''
334 327 history = ''
335 328 #the below while loop is used to suppress empty history lines
336 329 while((history == '' or history == '\n') and self._history_level <= self._getHistoryMaxIndex()):
337 330 if self._history_level < self._getHistoryMaxIndex():
338 331 self._history_level += 1
339 332 history = self._getHistory()
340 333 else:
341 334 if self._history_level == self._getHistoryMaxIndex():
342 335 history = self._getHistory()
343 336 self._history_level += 1
344 337 else:
345 338 history = ''
346 339 return history
347 340
348 341 def initHistoryIndex(self):
349 342 '''
350 343 set history to last command entered
351 344 '''
352 345 self._history_level = self._getHistoryMaxIndex()+1
353 346
354 #----------------------- IPython PRIVATE management section ----------------------
347 #----------------------- IPython PRIVATE management section --------------
355 348 def _afterExecute(self):
356 349 '''
357 350 Can be redefined to generate post event after excution is done
358 351 '''
359 352 pass
360 353
361 354 def _setAskExit(self):
362 355 '''
363 set the _ask_exit variable that can be cjhecked by GUI to see if
356 set the _ask_exit variable that can be checked by GUI to see if
364 357 IPython request an exit handling
365 358 '''
366 359 self._ask_exit = True
360
361 def _setDoExit(self, toto, arg):
362 '''
363 set the _do_exit variable that can be checked by GUI to see if
364 IPython do a direct exit of the app
365 '''
366 self._do_exit = True
367 367
368 368 def _getHistoryMaxIndex(self):
369 369 '''
370 370 returns the max length of the history buffer
371 371
372 372 @return: history length
373 373 @rtype: int
374 374 '''
375 375 return len(self._IP.input_hist_raw)-1
376 376
377 377 def _getHistory(self):
378 378 '''
379 379 Get's the command string of the current history level.
380 380
381 @return: Historic command string.
381 @return: Historic command stri
382 382 @rtype: string
383 383 '''
384 384 rv = self._IP.input_hist_raw[self._history_level].strip('\n')
385 385 return rv
386 386
387 387 def _pager_help(self,text):
388 388 '''
389 389 This function is used as a callback replacment to IPython help pager function
390 390
391 391 It puts the 'text' value inside the self._help_text string that can be retrived via getHelpText
392 392 function.
393 393 '''
394 394 if self._help_text == None:
395 395 self._help_text = text
396 396 else:
397 397 self._help_text += text
398 398
399 399 def _pager(self,IP,text):
400 400 '''
401 401 This function is used as a callback replacment to IPython pager function
402 402
403 403 It puts the 'text' value inside the self._doc_text string that can be retrived via getDocText
404 404 function.
405 405 '''
406 406 self._doc_text = text
407 407
408 408 def _raw_input(self, prompt=''):
409 409 '''
410 410 Custom raw_input() replacement. Get's current line from console buffer.
411 411
412 412 @param prompt: Prompt to print. Here for compatability as replacement.
413 413 @type prompt: string
414 414
415 415 @return: The current command line text.
416 416 @rtype: string
417 417 '''
418 418 return self._line_to_execute
419 419
420 420 def _execute(self):
421 421 '''
422 422 Executes the current line provided by the shell object.
423 423 '''
424 424 orig_stdout = sys.stdout
425 425 sys.stdout = IPython.Shell.Term.cout
426 426
427 427 try:
428 428 line = self._IP.raw_input(None, self._iter_more)
429 429 if self._IP.autoindent:
430 430 self._IP.readline_startup_hook(None)
431 431
432 432 except KeyboardInterrupt:
433 433 self._IP.write('\nKeyboardInterrupt\n')
434 434 self._IP.resetbuffer()
435 435 # keep cache in sync with the prompt counter:
436 436 self._IP.outputcache.prompt_count -= 1
437 437
438 438 if self._IP.autoindent:
439 439 self._IP.indent_current_nsp = 0
440 440 self._iter_more = 0
441 441 except:
442 442 self._IP.showtraceback()
443 443 else:
444 444 self._iter_more = self._IP.push(line)
445 445 if (self._IP.SyntaxTB.last_syntax_error and
446 446 self._IP.rc.autoedit_syntax):
447 447 self._IP.edit_syntax_error()
448 448 if self._iter_more:
449 449 self._prompt = str(self._IP.outputcache.prompt2).strip()
450 450 if self._IP.autoindent:
451 451 self._IP.readline_startup_hook(self._IP.pre_readline)
452 452 else:
453 453 self._prompt = str(self._IP.outputcache.prompt1).strip()
454 454 self._IP.indent_current_nsp = 0 #we set indentation to 0
455 455 sys.stdout = orig_stdout
456 456
457 457 def _shell(self, ip, cmd):
458 458 '''
459 459 Replacement method to allow shell commands without them blocking.
460 460
461 461 @param ip: Ipython instance, same as self._IP
462 462 @type cmd: Ipython instance
463 463 @param cmd: Shell command to execute.
464 464 @type cmd: string
465 465 '''
466 466 stdin, stdout = os.popen4(cmd)
467 467 result = stdout.read().decode('cp437').encode(locale.getpreferredencoding())
468 468 #we use print command because the shell command is called inside IPython instance and thus is
469 469 #redirected to thread cout
470 470 #"\x01\x1b[1;36m\x02" <-- add colour to the text...
471 471 print "\x01\x1b[1;36m\x02"+result
472 472 stdout.close()
473 473 stdin.close()
General Comments 0
You need to be logged in to leave comments. Login now