##// END OF EJS Templates
Added a workaround for %reset magic as it is using raw_input via ask_yes_no.
laurent dufrechou -
Show More
@@ -1,512 +1,532 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 from thread_ex import ThreadEx
25 25
26 26 try:
27 27 import IPython
28 28 except Exception,e:
29 29 print "Error importing IPython (%s)" % str(e)
30 30 raise Exception, e
31 31
32 32 ##############################################################################
33 33 class _Helper(object):
34 34 """Redefine the built-in 'help'.
35 35 This is a wrapper around pydoc.help (with a twist).
36 36 """
37 37
38 38 def __init__(self, pager):
39 39 self._pager = pager
40 40
41 41 def __repr__(self):
42 42 return "Type help() for interactive help, " \
43 43 "or help(object) for help about object."
44 44
45 45 def __call__(self, *args, **kwds):
46 46 class DummyWriter(object):
47 47 '''Dumy class to handle help output'''
48 48 def __init__(self, pager):
49 49 self._pager = pager
50 50
51 51 def write(self, data):
52 52 '''hook to fill self._pager'''
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 59 return pydoc.help(*args, **kwds)
60 60
61 61
62 62 ##############################################################################
63 63 class _CodeExecutor(ThreadEx):
64 64 ''' Thread that execute ipython code '''
65 65 def __init__(self, instance):
66 66 ThreadEx.__init__(self)
67 67 self.instance = instance
68 68
69 69 def run(self):
70 70 '''Thread main loop'''
71 71 try:
72 72 self.instance._doc_text = None
73 73 self.instance._help_text = None
74 74 self.instance._execute()
75 75 # used for uper class to generate event after execution
76 76 self.instance._afterExecute()
77 77
78 78 except KeyboardInterrupt:
79 79 pass
80 80
81 81
82 82 ##############################################################################
83 83 class NonBlockingIPShell(object):
84 84 '''
85 85 Create an IPython instance, running the commands in a separate,
86 86 non-blocking thread.
87 87 This allows embedding in any GUI without blockage.
88 88
89 89 Note: The ThreadEx class supports asynchroneous function call
90 90 via raise_exc()
91 91 '''
92 92
93 93 def __init__(self, argv=[], user_ns={}, user_global_ns=None,
94 94 cin=None, cout=None, cerr=None,
95 95 ask_exit_handler=None):
96 96 '''
97 97 @param argv: Command line options for IPython
98 98 @type argv: list
99 99 @param user_ns: User namespace.
100 100 @type user_ns: dictionary
101 101 @param user_global_ns: User global namespace.
102 102 @type user_global_ns: dictionary.
103 103 @param cin: Console standard input.
104 104 @type cin: IO stream
105 105 @param cout: Console standard output.
106 106 @type cout: IO stream
107 107 @param cerr: Console standard error.
108 108 @type cerr: IO stream
109 109 @param exit_handler: Replacement for builtin exit() function
110 110 @type exit_handler: function
111 111 @param time_loop: Define the sleep time between two thread's loop
112 112 @type int
113 113 '''
114 114 #ipython0 initialisation
115 115 self._IP = None
116 116 self.initIpython0(argv, user_ns, user_global_ns,
117 117 cin, cout, cerr,
118 118 ask_exit_handler)
119 119
120 120 #vars used by _execute
121 121 self._iter_more = 0
122 122 self._history_level = 0
123 123 self._complete_sep = re.compile('[\s\{\}\[\]\(\)\=]')
124 124 self._prompt = str(self._IP.outputcache.prompt1).strip()
125 125
126 126 #thread working vars
127 127 self._line_to_execute = ''
128 128 self._threading = True
129 129
130 130 #vars that will be checked by GUI loop to handle thread states...
131 131 #will be replaced later by PostEvent GUI funtions...
132 132 self._doc_text = None
133 133 self._help_text = None
134 134 self._add_button = None
135 135
136 136 def initIpython0(self, argv=[], user_ns={}, user_global_ns=None,
137 137 cin=None, cout=None, cerr=None,
138 138 ask_exit_handler=None):
139 139 ''' Initialize an ipython0 instance '''
140 140
141 141 #first we redefine in/out/error functions of IPython
142 142 #BUG: we've got a limitation form ipython0 there
143 143 #only one instance can be instanciated else tehre will be
144 144 #cin/cout/cerr clash...
145 145 if cin:
146 146 IPython.genutils.Term.cin = cin
147 147 if cout:
148 148 IPython.genutils.Term.cout = cout
149 149 if cerr:
150 150 IPython.genutils.Term.cerr = cerr
151 151
152 152 excepthook = sys.excepthook
153 153
154 154 #Hack to save sys.displayhook, because ipython seems to overwrite it...
155 155 self.sys_displayhook_ori = sys.displayhook
156 156
157 157 self._IP = IPython.Shell.make_IPython(
158 158 argv,user_ns=user_ns,
159 159 user_global_ns=user_global_ns,
160 160 embedded=True,
161 161 shell_class=IPython.Shell.InteractiveShell)
162 162
163 163 #we save ipython0 displayhook and we restore sys.displayhook
164 164 self.displayhook = sys.displayhook
165 165 sys.displayhook = self.sys_displayhook_ori
166 166
167 167 #we replace IPython default encoding by wx locale encoding
168 168 loc = locale.getpreferredencoding()
169 169 if loc:
170 170 self._IP.stdin_encoding = loc
171 171 #we replace the ipython default pager by our pager
172 172 self._IP.set_hook('show_in_pager', self._pager)
173 173
174 174 #we replace the ipython default shell command caller by our shell handler
175 175 self._IP.set_hook('shell_hook', self._shell)
176 176
177 177 #we replace the ipython default input command caller by our method
178 178 IPython.iplib.raw_input_original = self._raw_input
179 179 #we replace the ipython default exit command by our method
180 180 self._IP.exit = ask_exit_handler
181 181 #we replace the help command
182 182 self._IP.user_ns['help'] = _Helper(self._pager_help)
183 183
184 184 #we disable cpase magic... until we found a way to use it properly.
185 185 #import IPython.ipapi
186 186 ip = IPython.ipapi.get()
187 187 def bypassMagic(self, arg):
188 188 print '%this magic is currently disabled.'
189 189 ip.expose_magic('cpaste', bypassMagic)
190
190
191 def resetMagic(self, arg):
192 """Resets the namespace by removing all names defined by the user.
193
194 Input/Output history are left around in case you need them."""
195
196 ans = True ##todo find away to ask the user...
197 ##seems hard to do it cleanly...
198 if not ans:
199 print 'Nothing done.'
200 return
201 user_ns = self.shell.user_ns
202 for i in self.magic_who_ls():
203 del(user_ns[i])
204
205 # Also flush the private list of module references kept for script
206 # execution protection
207 self.shell._user_main_modules[:] = []
208
209 ip.expose_magic('reset', resetMagic)
210
191 211 sys.excepthook = excepthook
192 212
193 213 #----------------------- Thread management section ----------------------
194 214 def doExecute(self, line):
195 215 """
196 216 Tell the thread to process the 'line' command
197 217 """
198 218
199 219 self._line_to_execute = line
200 220
201 221 if self._threading:
202 222 #we launch the ipython line execution in a thread to make it interruptible
203 223 #with include it in self namespace to be able to call ce.raise_exc(KeyboardInterrupt)
204 224 self.ce = _CodeExecutor(self)
205 225 self.ce.start()
206 226 else:
207 227 try:
208 228 self._doc_text = None
209 229 self._help_text = None
210 230 self._execute()
211 231 # used for uper class to generate event after execution
212 232 self._afterExecute()
213 233
214 234 except KeyboardInterrupt:
215 235 pass
216 236
217 237 #----------------------- IPython management section ----------------------
218 238 def getThreading(self):
219 239 """
220 240 Returns threading status, is set to True, then each command sent to
221 241 the interpreter will be executed in a separated thread allowing,
222 242 for example, breaking a long running commands.
223 243 Disallowing it, permits better compatibilty with instance that is embedding
224 244 IPython instance.
225 245
226 246 @return: Execution method
227 247 @rtype: bool
228 248 """
229 249 return self._threading
230 250
231 251 def setThreading(self, state):
232 252 """
233 253 Sets threading state, if set to True, then each command sent to
234 254 the interpreter will be executed in a separated thread allowing,
235 255 for example, breaking a long running commands.
236 256 Disallowing it, permits better compatibilty with instance that is embedding
237 257 IPython instance.
238 258
239 259 @param state: Sets threading state
240 260 @type bool
241 261 """
242 262 self._threading = state
243 263
244 264 def getDocText(self):
245 265 """
246 266 Returns the output of the processing that need to be paged (if any)
247 267
248 268 @return: The std output string.
249 269 @rtype: string
250 270 """
251 271 return self._doc_text
252 272
253 273 def getHelpText(self):
254 274 """
255 275 Returns the output of the processing that need to be paged via help pager(if any)
256 276
257 277 @return: The std output string.
258 278 @rtype: string
259 279 """
260 280 return self._help_text
261 281
262 282 def getBanner(self):
263 283 """
264 284 Returns the IPython banner for useful info on IPython instance
265 285
266 286 @return: The banner string.
267 287 @rtype: string
268 288 """
269 289 return self._IP.BANNER
270 290
271 291 def getPromptCount(self):
272 292 """
273 293 Returns the prompt number.
274 294 Each time a user execute a line in the IPython shell the prompt count is increased
275 295
276 296 @return: The prompt number
277 297 @rtype: int
278 298 """
279 299 return self._IP.outputcache.prompt_count
280 300
281 301 def getPrompt(self):
282 302 """
283 303 Returns current prompt inside IPython instance
284 304 (Can be In [...]: ot ...:)
285 305
286 306 @return: The current prompt.
287 307 @rtype: string
288 308 """
289 309 return self._prompt
290 310
291 311 def getIndentation(self):
292 312 """
293 313 Returns the current indentation level
294 314 Usefull to put the caret at the good start position if we want to do autoindentation.
295 315
296 316 @return: The indentation level.
297 317 @rtype: int
298 318 """
299 319 return self._IP.indent_current_nsp
300 320
301 321 def updateNamespace(self, ns_dict):
302 322 '''
303 323 Add the current dictionary to the shell namespace.
304 324
305 325 @param ns_dict: A dictionary of symbol-values.
306 326 @type ns_dict: dictionary
307 327 '''
308 328 self._IP.user_ns.update(ns_dict)
309 329
310 330 def complete(self, line):
311 331 '''
312 332 Returns an auto completed line and/or posibilities for completion.
313 333
314 334 @param line: Given line so far.
315 335 @type line: string
316 336
317 337 @return: Line completed as for as possible,
318 338 and possible further completions.
319 339 @rtype: tuple
320 340 '''
321 341 split_line = self._complete_sep.split(line)
322 342 possibilities = self._IP.complete(split_line[-1])
323 343 if possibilities:
324 344
325 345 def _commonPrefix(str1, str2):
326 346 '''
327 347 Reduction function. returns common prefix of two given strings.
328 348
329 349 @param str1: First string.
330 350 @type str1: string
331 351 @param str2: Second string
332 352 @type str2: string
333 353
334 354 @return: Common prefix to both strings.
335 355 @rtype: string
336 356 '''
337 357 for i in range(len(str1)):
338 358 if not str2.startswith(str1[:i+1]):
339 359 return str1[:i]
340 360 return str1
341 361 common_prefix = reduce(_commonPrefix, possibilities)
342 362 completed = line[:-len(split_line[-1])]+common_prefix
343 363 else:
344 364 completed = line
345 365 return completed, possibilities
346 366
347 367 def historyBack(self):
348 368 '''
349 369 Provides one history command back.
350 370
351 371 @return: The command string.
352 372 @rtype: string
353 373 '''
354 374 history = ''
355 375 #the below while loop is used to suppress empty history lines
356 376 while((history == '' or history == '\n') and self._history_level >0):
357 377 if self._history_level >= 1:
358 378 self._history_level -= 1
359 379 history = self._getHistory()
360 380 return history
361 381
362 382 def historyForward(self):
363 383 '''
364 384 Provides one history command forward.
365 385
366 386 @return: The command string.
367 387 @rtype: string
368 388 '''
369 389 history = ''
370 390 #the below while loop is used to suppress empty history lines
371 391 while((history == '' or history == '\n') \
372 392 and self._history_level <= self._getHistoryMaxIndex()):
373 393 if self._history_level < self._getHistoryMaxIndex():
374 394 self._history_level += 1
375 395 history = self._getHistory()
376 396 else:
377 397 if self._history_level == self._getHistoryMaxIndex():
378 398 history = self._getHistory()
379 399 self._history_level += 1
380 400 else:
381 401 history = ''
382 402 return history
383 403
384 404 def initHistoryIndex(self):
385 405 '''
386 406 set history to last command entered
387 407 '''
388 408 self._history_level = self._getHistoryMaxIndex()+1
389 409
390 410 #----------------------- IPython PRIVATE management section --------------
391 411 def _afterExecute(self):
392 412 '''
393 413 Can be redefined to generate post event after excution is done
394 414 '''
395 415 pass
396 416
397 417 #def _askExit(self):
398 418 # '''
399 419 # Can be redefined to generate post event to exit the Ipython shell
400 420 # '''
401 421 # pass
402 422
403 423 def _getHistoryMaxIndex(self):
404 424 '''
405 425 returns the max length of the history buffer
406 426
407 427 @return: history length
408 428 @rtype: int
409 429 '''
410 430 return len(self._IP.input_hist_raw)-1
411 431
412 432 def _getHistory(self):
413 433 '''
414 434 Get's the command string of the current history level.
415 435
416 436 @return: Historic command stri
417 437 @rtype: string
418 438 '''
419 439 rv = self._IP.input_hist_raw[self._history_level].strip('\n')
420 440 return rv
421 441
422 442 def _pager_help(self, text):
423 443 '''
424 444 This function is used as a callback replacment to IPython help pager function
425 445
426 446 It puts the 'text' value inside the self._help_text string that can be retrived via
427 447 getHelpText function.
428 448 '''
429 449 if self._help_text == None:
430 450 self._help_text = text
431 451 else:
432 452 self._help_text += text
433 453
434 454 def _pager(self, IP, text):
435 455 '''
436 456 This function is used as a callback replacment to IPython pager function
437 457
438 458 It puts the 'text' value inside the self._doc_text string that can be retrived via
439 459 getDocText function.
440 460 '''
441 461 self._doc_text = text
442 462
443 463 def _raw_input(self, prompt=''):
444 464 '''
445 465 Custom raw_input() replacement. Get's current line from console buffer.
446 466
447 467 @param prompt: Prompt to print. Here for compatability as replacement.
448 468 @type prompt: string
449 469
450 470 @return: The current command line text.
451 471 @rtype: string
452 472 '''
453 473 return self._line_to_execute
454 474
455 475 def _execute(self):
456 476 '''
457 477 Executes the current line provided by the shell object.
458 478 '''
459 479
460 480 orig_stdout = sys.stdout
461 481 sys.stdout = IPython.Shell.Term.cout
462 482 #self.sys_displayhook_ori = sys.displayhook
463 483 #sys.displayhook = self.displayhook
464 484
465 485 try:
466 486 line = self._IP.raw_input(None, self._iter_more)
467 487 if self._IP.autoindent:
468 488 self._IP.readline_startup_hook(None)
469 489
470 490 except KeyboardInterrupt:
471 491 self._IP.write('\nKeyboardInterrupt\n')
472 492 self._IP.resetbuffer()
473 493 # keep cache in sync with the prompt counter:
474 494 self._IP.outputcache.prompt_count -= 1
475 495
476 496 if self._IP.autoindent:
477 497 self._IP.indent_current_nsp = 0
478 498 self._iter_more = 0
479 499 except:
480 500 self._IP.showtraceback()
481 501 else:
482 502 self._iter_more = self._IP.push(line)
483 503 if (self._IP.SyntaxTB.last_syntax_error and self._IP.rc.autoedit_syntax):
484 504 self._IP.edit_syntax_error()
485 505 if self._iter_more:
486 506 self._prompt = str(self._IP.outputcache.prompt2).strip()
487 507 if self._IP.autoindent:
488 508 self._IP.readline_startup_hook(self._IP.pre_readline)
489 509 else:
490 510 self._prompt = str(self._IP.outputcache.prompt1).strip()
491 511 self._IP.indent_current_nsp = 0 #we set indentation to 0
492 512
493 513 sys.stdout = orig_stdout
494 514 #sys.displayhook = self.sys_displayhook_ori
495 515
496 516 def _shell(self, ip, cmd):
497 517 '''
498 518 Replacement method to allow shell commands without them blocking.
499 519
500 520 @param ip: Ipython instance, same as self._IP
501 521 @type cmd: Ipython instance
502 522 @param cmd: Shell command to execute.
503 523 @type cmd: string
504 524 '''
505 525 stdin, stdout = os.popen4(cmd)
506 526 result = stdout.read().decode('cp437').encode(locale.getpreferredencoding())
507 527 #we use print command because the shell command is called
508 528 #inside IPython instance and thus is redirected to thread cout
509 529 #"\x01\x1b[1;36m\x02" <-- add colour to the text...
510 530 print "\x01\x1b[1;36m\x02"+result
511 531 stdout.close()
512 532 stdin.close()
General Comments 0
You need to be logged in to leave comments. Login now