##// END OF EJS Templates
Just to be the last one to commit this night in paris ;)
ldufrechou -
Show More
@@ -1,473 +1,472 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 35 class _Helper(object):
36 36 """Redefine the built-in 'help'.
37 37 This is a wrapper around pydoc.help (with a twist).
38 38 """
39 39
40 40 def __init__(self,pager):
41 41 self._pager = pager
42 42
43 43 def __repr__(self):
44 44 return "Type help() for interactive help, " \
45 45 "or help(object) for help about object."
46 46
47 47 def __call__(self, *args, **kwds):
48 48 class DummyWriter(object):
49 49 def __init__(self,pager):
50 50 self._pager = pager
51 51
52 52 def write(self,data):
53 53 self._pager(data)
54 54
55 55 import pydoc
56 56 pydoc.help.output = DummyWriter(self._pager)
57 57 pydoc.help.interact = lambda :1
58 58
59 #helper.output.write = self.doc.append
60 59 return pydoc.help(*args, **kwds)
61 60
62 61
63 62 ##############################################################################
64 63 class _CodeExecutor(ThreadEx):
65 64
66 65 def __init__(self, instance, after):
67 66 ThreadEx.__init__(self)
68 67 self.instance = instance
69 68 self._afterExecute=after
70 69
71 70 def run(self):
72 71 try:
73 72 self.instance._doc_text = None
74 73 self.instance._help_text = None
75 74 self.instance._execute()
76 75 # used for uper class to generate event after execution
77 76 self._afterExecute()
78 77
79 78 except KeyboardInterrupt:
80 79 pass
81 80
82 81
83 82 ##############################################################################
84 83 class NonBlockingIPShell(object):
85 84 '''
86 85 Create an IPython instance, running the commands in a separate,
87 86 non-blocking thread.
88 87 This allows embedding in any GUI without blockage.
89 88
90 89 Note: The ThreadEx class supports asynchroneous function call
91 90 via raise_exc()
92 91 '''
93 92
94 93 def __init__(self,argv
95 94 =[],user_ns={},user_global_ns=None,
96 95 cin=None, cout=None, cerr=None,
97 96 ask_exit_handler=None):
98 97 '''
99 98 @param argv: Command line options for IPython
100 99 @type argv: list
101 100 @param user_ns: User namespace.
102 101 @type user_ns: dictionary
103 102 @param user_global_ns: User global namespace.
104 103 @type user_global_ns: dictionary.
105 104 @param cin: Console standard input.
106 105 @type cin: IO stream
107 106 @param cout: Console standard output.
108 107 @type cout: IO stream
109 108 @param cerr: Console standard error.
110 109 @type cerr: IO stream
111 110 @param exit_handler: Replacement for builtin exit() function
112 111 @type exit_handler: function
113 112 @param time_loop: Define the sleep time between two thread's loop
114 113 @type int
115 114 '''
116 115 #first we redefine in/out/error functions of IPython
117 116 if cin:
118 117 IPython.Shell.Term.cin = cin
119 118 if cout:
120 119 IPython.Shell.Term.cout = cout
121 120 if cerr:
122 121 IPython.Shell.Term.cerr = cerr
123 122
124 123 # This is to get rid of the blockage that accurs during
125 124 # IPython.Shell.InteractiveShell.user_setup()
126 125 IPython.iplib.raw_input = lambda x: None
127 126
128 127 self._term = IPython.genutils.IOTerm(cin=cin, cout=cout, cerr=cerr)
129 128
130 129 excepthook = sys.excepthook
131 130
132 131 self._IP = IPython.Shell.make_IPython(
133 132 argv,user_ns=user_ns,
134 133 user_global_ns=user_global_ns,
135 134 embedded=True,
136 135 shell_class=IPython.Shell.InteractiveShell)
137 136
138 137 #we replace IPython default encoding by wx locale encoding
139 138 loc = locale.getpreferredencoding()
140 139 if loc:
141 140 self._IP.stdin_encoding = loc
142 141 #we replace the ipython default pager by our pager
143 142 self._IP.set_hook('show_in_pager',self._pager)
144 143
145 144 #we replace the ipython default shell command caller by our shell handler
146 145 self._IP.set_hook('shell_hook',self._shell)
147 146
148 147 #we replace the ipython default input command caller by our method
149 148 IPython.iplib.raw_input_original = self._raw_input
150 149 #we replace the ipython default exit command by our method
151 150 self._IP.exit = self._setAskExit
152 151 #we modify Exit and Quit Magic
153 152 ip = IPython.ipapi.get()
154 153 ip.expose_magic('Exit', self._setDoExit)
155 154 ip.expose_magic('Quit', self._setDoExit)
155 #we replace the help command
156 self._IP.user_ns['help'] = _Helper(self._pager_help)
156 157
157 158 sys.excepthook = excepthook
158 159
160 #vars used by _execute
159 161 self._iter_more = 0
160 162 self._history_level = 0
161 163 self._complete_sep = re.compile('[\s\{\}\[\]\(\)]')
162 164 self._prompt = str(self._IP.outputcache.prompt1).strip()
163 165
164 166 #thread working vars
165 167 self._line_to_execute = ''
166 168
167 169 #vars that will be checked by GUI loop to handle thread states...
168 170 #will be replaced later by PostEvent GUI funtions...
169 171 self._doc_text = None
170 172 self._help_text = None
171 173 self._ask_exit = False
172 174 self._add_button = None
173 175
174 #we replace the help command
175 self._IP.user_ns['help'] = _Helper(self._pager_help)
176
177 176 #----------------------- Thread management section ----------------------
178 177 def doExecute(self,line):
179 178 """
180 179 Tell the thread to process the 'line' command
181 180 """
182 181
183 182 self._line_to_execute = line
184 183
185 184 self.ce = _CodeExecutor(self,self._afterExecute)
186 185 self.ce.start()
187 186
188 187 #----------------------- IPython management section ----------------------
189 188 def getAskExit(self):
190 189 '''
191 190 returns the _ask_exit variable that can be checked by GUI to see if
192 191 IPython request an exit handling
193 192 '''
194 193 return self._ask_exit
195 194
196 195 def clearAskExit(self):
197 196 '''
198 197 clear the _ask_exit var when GUI as handled the request.
199 198 '''
200 199 self._ask_exit = False
201 200
202 201 def getDocText(self):
203 202 """
204 203 Returns the output of the processing that need to be paged (if any)
205 204
206 205 @return: The std output string.
207 206 @rtype: string
208 207 """
209 208 return self._doc_text
210 209
211 210 def getHelpText(self):
212 211 """
213 212 Returns the output of the processing that need to be paged via help pager(if any)
214 213
215 214 @return: The std output string.
216 215 @rtype: string
217 216 """
218 217 return self._help_text
219 218
220 219 def getBanner(self):
221 220 """
222 221 Returns the IPython banner for useful info on IPython instance
223 222
224 223 @return: The banner string.
225 224 @rtype: string
226 225 """
227 226 return self._IP.BANNER
228 227
229 228 def getPromptCount(self):
230 229 """
231 230 Returns the prompt number.
232 231 Each time a user execute a line in the IPython shell the prompt count is increased
233 232
234 233 @return: The prompt number
235 234 @rtype: int
236 235 """
237 236 return self._IP.outputcache.prompt_count
238 237
239 238 def getPrompt(self):
240 239 """
241 240 Returns current prompt inside IPython instance
242 241 (Can be In [...]: ot ...:)
243 242
244 243 @return: The current prompt.
245 244 @rtype: string
246 245 """
247 246 return self._prompt
248 247
249 248 def getIndentation(self):
250 249 """
251 250 Returns the current indentation level
252 251 Usefull to put the caret at the good start position if we want to do autoindentation.
253 252
254 253 @return: The indentation level.
255 254 @rtype: int
256 255 """
257 256 return self._IP.indent_current_nsp
258 257
259 258 def updateNamespace(self, ns_dict):
260 259 '''
261 260 Add the current dictionary to the shell namespace.
262 261
263 262 @param ns_dict: A dictionary of symbol-values.
264 263 @type ns_dict: dictionary
265 264 '''
266 265 self._IP.user_ns.update(ns_dict)
267 266
268 267 def complete(self, line):
269 268 '''
270 269 Returns an auto completed line and/or posibilities for completion.
271 270
272 271 @param line: Given line so far.
273 272 @type line: string
274 273
275 274 @return: Line completed as for as possible,
276 275 and possible further completions.
277 276 @rtype: tuple
278 277 '''
279 278 split_line = self._complete_sep.split(line)
280 279 possibilities = self._IP.complete(split_line[-1])
281 280 if possibilities:
282 281
283 282 def _commonPrefix(str1, str2):
284 283 '''
285 284 Reduction function. returns common prefix of two given strings.
286 285
287 286 @param str1: First string.
288 287 @type str1: string
289 288 @param str2: Second string
290 289 @type str2: string
291 290
292 291 @return: Common prefix to both strings.
293 292 @rtype: string
294 293 '''
295 294 for i in range(len(str1)):
296 295 if not str2.startswith(str1[:i+1]):
297 296 return str1[:i]
298 297 return str1
299 298 common_prefix = reduce(_commonPrefix, possibilities)
300 299 completed = line[:-len(split_line[-1])]+common_prefix
301 300 else:
302 301 completed = line
303 302 return completed, possibilities
304 303
305 304 def historyBack(self):
306 305 '''
307 306 Provides one history command back.
308 307
309 308 @return: The command string.
310 309 @rtype: string
311 310 '''
312 311 history = ''
313 312 #the below while loop is used to suppress empty history lines
314 313 while((history == '' or history == '\n') and self._history_level >0):
315 314 if self._history_level>=1:
316 315 self._history_level -= 1
317 316 history = self._getHistory()
318 317 return history
319 318
320 319 def historyForward(self):
321 320 '''
322 321 Provides one history command forward.
323 322
324 323 @return: The command string.
325 324 @rtype: string
326 325 '''
327 326 history = ''
328 327 #the below while loop is used to suppress empty history lines
329 328 while((history == '' or history == '\n') and self._history_level <= self._getHistoryMaxIndex()):
330 329 if self._history_level < self._getHistoryMaxIndex():
331 330 self._history_level += 1
332 331 history = self._getHistory()
333 332 else:
334 333 if self._history_level == self._getHistoryMaxIndex():
335 334 history = self._getHistory()
336 335 self._history_level += 1
337 336 else:
338 337 history = ''
339 338 return history
340 339
341 340 def initHistoryIndex(self):
342 341 '''
343 342 set history to last command entered
344 343 '''
345 344 self._history_level = self._getHistoryMaxIndex()+1
346 345
347 346 #----------------------- IPython PRIVATE management section --------------
348 347 def _afterExecute(self):
349 348 '''
350 349 Can be redefined to generate post event after excution is done
351 350 '''
352 351 pass
353 352
354 353 def _setAskExit(self):
355 354 '''
356 355 set the _ask_exit variable that can be checked by GUI to see if
357 356 IPython request an exit handling
358 357 '''
359 358 self._ask_exit = True
360 359
361 360 def _setDoExit(self, toto, arg):
362 361 '''
363 362 set the _do_exit variable that can be checked by GUI to see if
364 363 IPython do a direct exit of the app
365 364 '''
366 365 self._do_exit = True
367 366
368 367 def _getHistoryMaxIndex(self):
369 368 '''
370 369 returns the max length of the history buffer
371 370
372 371 @return: history length
373 372 @rtype: int
374 373 '''
375 374 return len(self._IP.input_hist_raw)-1
376 375
377 376 def _getHistory(self):
378 377 '''
379 378 Get's the command string of the current history level.
380 379
381 380 @return: Historic command stri
382 381 @rtype: string
383 382 '''
384 383 rv = self._IP.input_hist_raw[self._history_level].strip('\n')
385 384 return rv
386 385
387 386 def _pager_help(self,text):
388 387 '''
389 388 This function is used as a callback replacment to IPython help pager function
390 389
391 390 It puts the 'text' value inside the self._help_text string that can be retrived via getHelpText
392 391 function.
393 392 '''
394 393 if self._help_text == None:
395 394 self._help_text = text
396 395 else:
397 396 self._help_text += text
398 397
399 398 def _pager(self,IP,text):
400 399 '''
401 400 This function is used as a callback replacment to IPython pager function
402 401
403 402 It puts the 'text' value inside the self._doc_text string that can be retrived via getDocText
404 403 function.
405 404 '''
406 405 self._doc_text = text
407 406
408 407 def _raw_input(self, prompt=''):
409 408 '''
410 409 Custom raw_input() replacement. Get's current line from console buffer.
411 410
412 411 @param prompt: Prompt to print. Here for compatability as replacement.
413 412 @type prompt: string
414 413
415 414 @return: The current command line text.
416 415 @rtype: string
417 416 '''
418 417 return self._line_to_execute
419 418
420 419 def _execute(self):
421 420 '''
422 421 Executes the current line provided by the shell object.
423 422 '''
424 423 orig_stdout = sys.stdout
425 424 sys.stdout = IPython.Shell.Term.cout
426 425
427 426 try:
428 427 line = self._IP.raw_input(None, self._iter_more)
429 428 if self._IP.autoindent:
430 429 self._IP.readline_startup_hook(None)
431 430
432 431 except KeyboardInterrupt:
433 432 self._IP.write('\nKeyboardInterrupt\n')
434 433 self._IP.resetbuffer()
435 434 # keep cache in sync with the prompt counter:
436 435 self._IP.outputcache.prompt_count -= 1
437 436
438 437 if self._IP.autoindent:
439 438 self._IP.indent_current_nsp = 0
440 439 self._iter_more = 0
441 440 except:
442 441 self._IP.showtraceback()
443 442 else:
444 443 self._iter_more = self._IP.push(line)
445 444 if (self._IP.SyntaxTB.last_syntax_error and
446 445 self._IP.rc.autoedit_syntax):
447 446 self._IP.edit_syntax_error()
448 447 if self._iter_more:
449 448 self._prompt = str(self._IP.outputcache.prompt2).strip()
450 449 if self._IP.autoindent:
451 450 self._IP.readline_startup_hook(self._IP.pre_readline)
452 451 else:
453 452 self._prompt = str(self._IP.outputcache.prompt1).strip()
454 453 self._IP.indent_current_nsp = 0 #we set indentation to 0
455 454 sys.stdout = orig_stdout
456 455
457 456 def _shell(self, ip, cmd):
458 457 '''
459 458 Replacement method to allow shell commands without them blocking.
460 459
461 460 @param ip: Ipython instance, same as self._IP
462 461 @type cmd: Ipython instance
463 462 @param cmd: Shell command to execute.
464 463 @type cmd: string
465 464 '''
466 465 stdin, stdout = os.popen4(cmd)
467 466 result = stdout.read().decode('cp437').encode(locale.getpreferredencoding())
468 467 #we use print command because the shell command is called inside IPython instance and thus is
469 468 #redirected to thread cout
470 469 #"\x01\x1b[1;36m\x02" <-- add colour to the text...
471 470 print "\x01\x1b[1;36m\x02"+result
472 471 stdout.close()
473 472 stdin.close()
General Comments 0
You need to be logged in to leave comments. Login now