##// END OF EJS Templates
BUG: Make Shell use the new make_user_namespaces API.
Robert Kern -
Show More
@@ -1,1235 +1,1236 b''
1 1 # -*- coding: utf-8 -*-
2 2 """IPython Shell classes.
3 3
4 4 All the matplotlib support code was co-developed with John Hunter,
5 5 matplotlib's author.
6 6
7 7 $Id: Shell.py 3024 2008-02-07 15:34:42Z darren.dale $"""
8 8
9 9 #*****************************************************************************
10 10 # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu>
11 11 #
12 12 # Distributed under the terms of the BSD License. The full license is in
13 13 # the file COPYING, distributed as part of this software.
14 14 #*****************************************************************************
15 15
16 16 from IPython import Release
17 17 __author__ = '%s <%s>' % Release.authors['Fernando']
18 18 __license__ = Release.license
19 19
20 20 # Code begins
21 21 # Stdlib imports
22 22 import __builtin__
23 23 import __main__
24 24 import Queue
25 25 import inspect
26 26 import os
27 27 import sys
28 28 import thread
29 29 import threading
30 30 import time
31 31
32 32 from signal import signal, SIGINT
33 33
34 34 try:
35 35 import ctypes
36 36 HAS_CTYPES = True
37 37 except ImportError:
38 38 HAS_CTYPES = False
39 39
40 40 # IPython imports
41 41 import IPython
42 42 from IPython import ultraTB, ipapi
43 43 from IPython.genutils import Term,warn,error,flag_calls, ask_yes_no
44 44 from IPython.iplib import InteractiveShell
45 45 from IPython.ipmaker import make_IPython
46 46 from IPython.Magic import Magic
47 47 from IPython.ipstruct import Struct
48 48
49 49 try: # Python 2.3 compatibility
50 50 set
51 51 except NameError:
52 52 import sets
53 53 set = sets.Set
54 54
55 55
56 56 # Globals
57 57 # global flag to pass around information about Ctrl-C without exceptions
58 58 KBINT = False
59 59
60 60 # global flag to turn on/off Tk support.
61 61 USE_TK = False
62 62
63 63 # ID for the main thread, used for cross-thread exceptions
64 64 MAIN_THREAD_ID = thread.get_ident()
65 65
66 66 # Tag when runcode() is active, for exception handling
67 67 CODE_RUN = None
68 68
69 69 #-----------------------------------------------------------------------------
70 70 # This class is trivial now, but I want to have it in to publish a clean
71 71 # interface. Later when the internals are reorganized, code that uses this
72 72 # shouldn't have to change.
73 73
74 74 class IPShell:
75 75 """Create an IPython instance."""
76 76
77 77 def __init__(self,argv=None,user_ns=None,user_global_ns=None,
78 78 debug=1,shell_class=InteractiveShell):
79 79 self.IP = make_IPython(argv,user_ns=user_ns,
80 80 user_global_ns=user_global_ns,
81 81 debug=debug,shell_class=shell_class)
82 82
83 83 def mainloop(self,sys_exit=0,banner=None):
84 84 self.IP.mainloop(banner)
85 85 if sys_exit:
86 86 sys.exit()
87 87
88 88 #-----------------------------------------------------------------------------
89 89 def kill_embedded(self,parameter_s=''):
90 90 """%kill_embedded : deactivate for good the current embedded IPython.
91 91
92 92 This function (after asking for confirmation) sets an internal flag so that
93 93 an embedded IPython will never activate again. This is useful to
94 94 permanently disable a shell that is being called inside a loop: once you've
95 95 figured out what you needed from it, you may then kill it and the program
96 96 will then continue to run without the interactive shell interfering again.
97 97 """
98 98
99 99 kill = ask_yes_no("Are you sure you want to kill this embedded instance "
100 100 "(y/n)? [y/N] ",'n')
101 101 if kill:
102 102 self.shell.embedded_active = False
103 103 print "This embedded IPython will not reactivate anymore once you exit."
104 104
105 105 class IPShellEmbed:
106 106 """Allow embedding an IPython shell into a running program.
107 107
108 108 Instances of this class are callable, with the __call__ method being an
109 109 alias to the embed() method of an InteractiveShell instance.
110 110
111 111 Usage (see also the example-embed.py file for a running example):
112 112
113 113 ipshell = IPShellEmbed([argv,banner,exit_msg,rc_override])
114 114
115 115 - argv: list containing valid command-line options for IPython, as they
116 116 would appear in sys.argv[1:].
117 117
118 118 For example, the following command-line options:
119 119
120 120 $ ipython -prompt_in1 'Input <\\#>' -colors LightBG
121 121
122 122 would be passed in the argv list as:
123 123
124 124 ['-prompt_in1','Input <\\#>','-colors','LightBG']
125 125
126 126 - banner: string which gets printed every time the interpreter starts.
127 127
128 128 - exit_msg: string which gets printed every time the interpreter exits.
129 129
130 130 - rc_override: a dict or Struct of configuration options such as those
131 131 used by IPython. These options are read from your ~/.ipython/ipythonrc
132 132 file when the Shell object is created. Passing an explicit rc_override
133 133 dict with any options you want allows you to override those values at
134 134 creation time without having to modify the file. This way you can create
135 135 embeddable instances configured in any way you want without editing any
136 136 global files (thus keeping your interactive IPython configuration
137 137 unchanged).
138 138
139 139 Then the ipshell instance can be called anywhere inside your code:
140 140
141 141 ipshell(header='') -> Opens up an IPython shell.
142 142
143 143 - header: string printed by the IPython shell upon startup. This can let
144 144 you know where in your code you are when dropping into the shell. Note
145 145 that 'banner' gets prepended to all calls, so header is used for
146 146 location-specific information.
147 147
148 148 For more details, see the __call__ method below.
149 149
150 150 When the IPython shell is exited with Ctrl-D, normal program execution
151 151 resumes.
152 152
153 153 This functionality was inspired by a posting on comp.lang.python by cmkl
154 154 <cmkleffner@gmx.de> on Dec. 06/01 concerning similar uses of pyrepl, and
155 155 by the IDL stop/continue commands."""
156 156
157 157 def __init__(self,argv=None,banner='',exit_msg=None,rc_override=None,
158 158 user_ns=None):
159 159 """Note that argv here is a string, NOT a list."""
160 160 self.set_banner(banner)
161 161 self.set_exit_msg(exit_msg)
162 162 self.set_dummy_mode(0)
163 163
164 164 # sys.displayhook is a global, we need to save the user's original
165 165 # Don't rely on __displayhook__, as the user may have changed that.
166 166 self.sys_displayhook_ori = sys.displayhook
167 167
168 168 # save readline completer status
169 169 try:
170 170 #print 'Save completer',sys.ipcompleter # dbg
171 171 self.sys_ipcompleter_ori = sys.ipcompleter
172 172 except:
173 173 pass # not nested with IPython
174 174
175 175 self.IP = make_IPython(argv,rc_override=rc_override,
176 176 embedded=True,
177 177 user_ns=user_ns)
178 178
179 179 ip = ipapi.IPApi(self.IP)
180 180 ip.expose_magic("kill_embedded",kill_embedded)
181 181
182 182 # copy our own displayhook also
183 183 self.sys_displayhook_embed = sys.displayhook
184 184 # and leave the system's display hook clean
185 185 sys.displayhook = self.sys_displayhook_ori
186 186 # don't use the ipython crash handler so that user exceptions aren't
187 187 # trapped
188 188 sys.excepthook = ultraTB.FormattedTB(color_scheme = self.IP.rc.colors,
189 189 mode = self.IP.rc.xmode,
190 190 call_pdb = self.IP.rc.pdb)
191 191 self.restore_system_completer()
192 192
193 193 def restore_system_completer(self):
194 194 """Restores the readline completer which was in place.
195 195
196 196 This allows embedded IPython within IPython not to disrupt the
197 197 parent's completion.
198 198 """
199 199
200 200 try:
201 201 self.IP.readline.set_completer(self.sys_ipcompleter_ori)
202 202 sys.ipcompleter = self.sys_ipcompleter_ori
203 203 except:
204 204 pass
205 205
206 206 def __call__(self,header='',local_ns=None,global_ns=None,dummy=None):
207 207 """Activate the interactive interpreter.
208 208
209 209 __call__(self,header='',local_ns=None,global_ns,dummy=None) -> Start
210 210 the interpreter shell with the given local and global namespaces, and
211 211 optionally print a header string at startup.
212 212
213 213 The shell can be globally activated/deactivated using the
214 214 set/get_dummy_mode methods. This allows you to turn off a shell used
215 215 for debugging globally.
216 216
217 217 However, *each* time you call the shell you can override the current
218 218 state of dummy_mode with the optional keyword parameter 'dummy'. For
219 219 example, if you set dummy mode on with IPShell.set_dummy_mode(1), you
220 220 can still have a specific call work by making it as IPShell(dummy=0).
221 221
222 222 The optional keyword parameter dummy controls whether the call
223 223 actually does anything. """
224 224
225 225 # If the user has turned it off, go away
226 226 if not self.IP.embedded_active:
227 227 return
228 228
229 229 # Normal exits from interactive mode set this flag, so the shell can't
230 230 # re-enter (it checks this variable at the start of interactive mode).
231 231 self.IP.exit_now = False
232 232
233 233 # Allow the dummy parameter to override the global __dummy_mode
234 234 if dummy or (dummy != 0 and self.__dummy_mode):
235 235 return
236 236
237 237 # Set global subsystems (display,completions) to our values
238 238 sys.displayhook = self.sys_displayhook_embed
239 239 if self.IP.has_readline:
240 240 self.IP.set_completer()
241 241
242 242 if self.banner and header:
243 243 format = '%s\n%s\n'
244 244 else:
245 245 format = '%s%s\n'
246 246 banner = format % (self.banner,header)
247 247
248 248 # Call the embedding code with a stack depth of 1 so it can skip over
249 249 # our call and get the original caller's namespaces.
250 250 self.IP.embed_mainloop(banner,local_ns,global_ns,stack_depth=1)
251 251
252 252 if self.exit_msg:
253 253 print self.exit_msg
254 254
255 255 # Restore global systems (display, completion)
256 256 sys.displayhook = self.sys_displayhook_ori
257 257 self.restore_system_completer()
258 258
259 259 def set_dummy_mode(self,dummy):
260 260 """Sets the embeddable shell's dummy mode parameter.
261 261
262 262 set_dummy_mode(dummy): dummy = 0 or 1.
263 263
264 264 This parameter is persistent and makes calls to the embeddable shell
265 265 silently return without performing any action. This allows you to
266 266 globally activate or deactivate a shell you're using with a single call.
267 267
268 268 If you need to manually"""
269 269
270 270 if dummy not in [0,1,False,True]:
271 271 raise ValueError,'dummy parameter must be boolean'
272 272 self.__dummy_mode = dummy
273 273
274 274 def get_dummy_mode(self):
275 275 """Return the current value of the dummy mode parameter.
276 276 """
277 277 return self.__dummy_mode
278 278
279 279 def set_banner(self,banner):
280 280 """Sets the global banner.
281 281
282 282 This banner gets prepended to every header printed when the shell
283 283 instance is called."""
284 284
285 285 self.banner = banner
286 286
287 287 def set_exit_msg(self,exit_msg):
288 288 """Sets the global exit_msg.
289 289
290 290 This exit message gets printed upon exiting every time the embedded
291 291 shell is called. It is None by default. """
292 292
293 293 self.exit_msg = exit_msg
294 294
295 295 #-----------------------------------------------------------------------------
296 296 if HAS_CTYPES:
297 297 # Add async exception support. Trick taken from:
298 298 # http://sebulba.wikispaces.com/recipe+thread2
299 299 def _async_raise(tid, exctype):
300 300 """raises the exception, performs cleanup if needed"""
301 301 if not inspect.isclass(exctype):
302 302 raise TypeError("Only types can be raised (not instances)")
303 303 res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid,
304 304 ctypes.py_object(exctype))
305 305 if res == 0:
306 306 raise ValueError("invalid thread id")
307 307 elif res != 1:
308 308 # """if it returns a number greater than one, you're in trouble,
309 309 # and you should call it again with exc=NULL to revert the effect"""
310 310 ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, 0)
311 311 raise SystemError("PyThreadState_SetAsyncExc failed")
312 312
313 313 def sigint_handler (signum,stack_frame):
314 314 """Sigint handler for threaded apps.
315 315
316 316 This is a horrible hack to pass information about SIGINT _without_
317 317 using exceptions, since I haven't been able to properly manage
318 318 cross-thread exceptions in GTK/WX. In fact, I don't think it can be
319 319 done (or at least that's my understanding from a c.l.py thread where
320 320 this was discussed)."""
321 321
322 322 global KBINT
323 323
324 324 if CODE_RUN:
325 325 _async_raise(MAIN_THREAD_ID,KeyboardInterrupt)
326 326 else:
327 327 KBINT = True
328 328 print '\nKeyboardInterrupt - Press <Enter> to continue.',
329 329 Term.cout.flush()
330 330
331 331 else:
332 332 def sigint_handler (signum,stack_frame):
333 333 """Sigint handler for threaded apps.
334 334
335 335 This is a horrible hack to pass information about SIGINT _without_
336 336 using exceptions, since I haven't been able to properly manage
337 337 cross-thread exceptions in GTK/WX. In fact, I don't think it can be
338 338 done (or at least that's my understanding from a c.l.py thread where
339 339 this was discussed)."""
340 340
341 341 global KBINT
342 342
343 343 print '\nKeyboardInterrupt - Press <Enter> to continue.',
344 344 Term.cout.flush()
345 345 # Set global flag so that runsource can know that Ctrl-C was hit
346 346 KBINT = True
347 347
348 348
349 349 class MTInteractiveShell(InteractiveShell):
350 350 """Simple multi-threaded shell."""
351 351
352 352 # Threading strategy taken from:
353 353 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/65109, by Brian
354 354 # McErlean and John Finlay. Modified with corrections by Antoon Pardon,
355 355 # from the pygtk mailing list, to avoid lockups with system calls.
356 356
357 357 # class attribute to indicate whether the class supports threads or not.
358 358 # Subclasses with thread support should override this as needed.
359 359 isthreaded = True
360 360
361 361 def __init__(self,name,usage=None,rc=Struct(opts=None,args=None),
362 362 user_ns=None,user_global_ns=None,banner2='',**kw):
363 363 """Similar to the normal InteractiveShell, but with threading control"""
364 364
365 365 InteractiveShell.__init__(self,name,usage,rc,user_ns,
366 366 user_global_ns,banner2)
367 367
368 368
369 369 # A queue to hold the code to be executed.
370 370 self.code_queue = Queue.Queue()
371 371
372 372 # Stuff to do at closing time
373 373 self._kill = None
374 374 on_kill = kw.get('on_kill', [])
375 375 # Check that all things to kill are callable:
376 376 for t in on_kill:
377 377 if not callable(t):
378 378 raise TypeError,'on_kill must be a list of callables'
379 379 self.on_kill = on_kill
380 380 # thread identity of the "worker thread" (that may execute code directly)
381 381 self.worker_ident = None
382 382
383 383 def runsource(self, source, filename="<input>", symbol="single"):
384 384 """Compile and run some source in the interpreter.
385 385
386 386 Modified version of code.py's runsource(), to handle threading issues.
387 387 See the original for full docstring details."""
388 388
389 389 global KBINT
390 390
391 391 # If Ctrl-C was typed, we reset the flag and return right away
392 392 if KBINT:
393 393 KBINT = False
394 394 return False
395 395
396 396 if self._kill:
397 397 # can't queue new code if we are being killed
398 398 return True
399 399
400 400 try:
401 401 code = self.compile(source, filename, symbol)
402 402 except (OverflowError, SyntaxError, ValueError):
403 403 # Case 1
404 404 self.showsyntaxerror(filename)
405 405 return False
406 406
407 407 if code is None:
408 408 # Case 2
409 409 return True
410 410
411 411 # shortcut - if we are in worker thread, or the worker thread is not running,
412 412 # execute directly (to allow recursion and prevent deadlock if code is run early
413 413 # in IPython construction)
414 414
415 415 if (self.worker_ident is None or self.worker_ident == thread.get_ident()):
416 416 InteractiveShell.runcode(self,code)
417 417 return
418 418
419 419 # Case 3
420 420 # Store code in queue, so the execution thread can handle it.
421 421
422 422 completed_ev, received_ev = threading.Event(), threading.Event()
423 423
424 424 self.code_queue.put((code,completed_ev, received_ev))
425 425 # first make sure the message was received, with timeout
426 426 received_ev.wait(5)
427 427 if not received_ev.isSet():
428 428 # the mainloop is dead, start executing code directly
429 429 print "Warning: Timeout for mainloop thread exceeded"
430 430 print "switching to nonthreaded mode (until mainloop wakes up again)"
431 431 self.worker_ident = None
432 432 else:
433 433 completed_ev.wait()
434 434 return False
435 435
436 436 def runcode(self):
437 437 """Execute a code object.
438 438
439 439 Multithreaded wrapper around IPython's runcode()."""
440 440
441 441 global CODE_RUN
442 442
443 443 # we are in worker thread, stash out the id for runsource()
444 444 self.worker_ident = thread.get_ident()
445 445
446 446 if self._kill:
447 447 print >>Term.cout, 'Closing threads...',
448 448 Term.cout.flush()
449 449 for tokill in self.on_kill:
450 450 tokill()
451 451 print >>Term.cout, 'Done.'
452 452 # allow kill() to return
453 453 self._kill.set()
454 454 return True
455 455
456 456 # Install sigint handler. We do it every time to ensure that if user
457 457 # code modifies it, we restore our own handling.
458 458 try:
459 459 signal(SIGINT,sigint_handler)
460 460 except SystemError:
461 461 # This happens under Windows, which seems to have all sorts
462 462 # of problems with signal handling. Oh well...
463 463 pass
464 464
465 465 # Flush queue of pending code by calling the run methood of the parent
466 466 # class with all items which may be in the queue.
467 467 code_to_run = None
468 468 while 1:
469 469 try:
470 470 code_to_run, completed_ev, received_ev = self.code_queue.get_nowait()
471 471 except Queue.Empty:
472 472 break
473 473 received_ev.set()
474 474
475 475 # Exceptions need to be raised differently depending on which
476 476 # thread is active. This convoluted try/except is only there to
477 477 # protect against asynchronous exceptions, to ensure that a KBINT
478 478 # at the wrong time doesn't deadlock everything. The global
479 479 # CODE_TO_RUN is set to true/false as close as possible to the
480 480 # runcode() call, so that the KBINT handler is correctly informed.
481 481 try:
482 482 try:
483 483 CODE_RUN = True
484 484 InteractiveShell.runcode(self,code_to_run)
485 485 except KeyboardInterrupt:
486 486 print "Keyboard interrupted in mainloop"
487 487 while not self.code_queue.empty():
488 488 code, ev1,ev2 = self.code_queue.get_nowait()
489 489 ev1.set()
490 490 ev2.set()
491 491 break
492 492 finally:
493 493 CODE_RUN = False
494 494 # allow runsource() return from wait
495 495 completed_ev.set()
496 496
497 497
498 498 # This MUST return true for gtk threading to work
499 499 return True
500 500
501 501 def kill(self):
502 502 """Kill the thread, returning when it has been shut down."""
503 503 self._kill = threading.Event()
504 504 self._kill.wait()
505 505
506 506 class MatplotlibShellBase:
507 507 """Mixin class to provide the necessary modifications to regular IPython
508 508 shell classes for matplotlib support.
509 509
510 510 Given Python's MRO, this should be used as the FIRST class in the
511 511 inheritance hierarchy, so that it overrides the relevant methods."""
512 512
513 def _matplotlib_config(self,name,user_ns):
513 def _matplotlib_config(self,name,user_ns,user_global_ns=None):
514 514 """Return items needed to setup the user's shell with matplotlib"""
515 515
516 516 # Initialize matplotlib to interactive mode always
517 517 import matplotlib
518 518 from matplotlib import backends
519 519 matplotlib.interactive(True)
520 520
521 521 def use(arg):
522 522 """IPython wrapper for matplotlib's backend switcher.
523 523
524 524 In interactive use, we can not allow switching to a different
525 525 interactive backend, since thread conflicts will most likely crash
526 526 the python interpreter. This routine does a safety check first,
527 527 and refuses to perform a dangerous switch. It still allows
528 528 switching to non-interactive backends."""
529 529
530 530 if arg in backends.interactive_bk and arg != self.mpl_backend:
531 531 m=('invalid matplotlib backend switch.\n'
532 532 'This script attempted to switch to the interactive '
533 533 'backend: `%s`\n'
534 534 'Your current choice of interactive backend is: `%s`\n\n'
535 535 'Switching interactive matplotlib backends at runtime\n'
536 536 'would crash the python interpreter, '
537 537 'and IPython has blocked it.\n\n'
538 538 'You need to either change your choice of matplotlib backend\n'
539 539 'by editing your .matplotlibrc file, or run this script as a \n'
540 540 'standalone file from the command line, not using IPython.\n' %
541 541 (arg,self.mpl_backend) )
542 542 raise RuntimeError, m
543 543 else:
544 544 self.mpl_use(arg)
545 545 self.mpl_use._called = True
546 546
547 547 self.matplotlib = matplotlib
548 548 self.mpl_backend = matplotlib.rcParams['backend']
549 549
550 550 # we also need to block switching of interactive backends by use()
551 551 self.mpl_use = matplotlib.use
552 552 self.mpl_use._called = False
553 553 # overwrite the original matplotlib.use with our wrapper
554 554 matplotlib.use = use
555 555
556 556 # This must be imported last in the matplotlib series, after
557 557 # backend/interactivity choices have been made
558 558 import matplotlib.pylab as pylab
559 559 self.pylab = pylab
560 560
561 561 self.pylab.show._needmain = False
562 562 # We need to detect at runtime whether show() is called by the user.
563 563 # For this, we wrap it into a decorator which adds a 'called' flag.
564 564 self.pylab.draw_if_interactive = flag_calls(self.pylab.draw_if_interactive)
565 565
566 566 # Build a user namespace initialized with matplotlib/matlab features.
567 user_ns = IPython.ipapi.make_user_ns(user_ns)
567 user_ns, user_global_ns = IPython.ipapi.make_user_namespaces(user_ns,
568 user_global_ns)
568 569
569 570 # Import numpy as np/pyplot as plt are conventions we're trying to
570 571 # somewhat standardize on. Making them available to users by default
571 572 # will greatly help this.
572 573 exec ("import numpy\n"
573 574 "import numpy as np\n"
574 575 "import matplotlib\n"
575 576 "import matplotlib.pylab as pylab\n"
576 577 "try:\n"
577 578 " import matplotlib.pyplot as plt\n"
578 579 "except ImportError:\n"
579 580 " pass\n"
580 581 ) in user_ns
581 582
582 583 # Build matplotlib info banner
583 584 b="""
584 585 Welcome to pylab, a matplotlib-based Python environment.
585 586 For more information, type 'help(pylab)'.
586 587 """
587 return user_ns,b
588 return user_ns,,user_global_ns,b
588 589
589 590 def mplot_exec(self,fname,*where,**kw):
590 591 """Execute a matplotlib script.
591 592
592 593 This is a call to execfile(), but wrapped in safeties to properly
593 594 handle interactive rendering and backend switching."""
594 595
595 596 #print '*** Matplotlib runner ***' # dbg
596 597 # turn off rendering until end of script
597 598 isInteractive = self.matplotlib.rcParams['interactive']
598 599 self.matplotlib.interactive(False)
599 600 self.safe_execfile(fname,*where,**kw)
600 601 self.matplotlib.interactive(isInteractive)
601 602 # make rendering call now, if the user tried to do it
602 603 if self.pylab.draw_if_interactive.called:
603 604 self.pylab.draw()
604 605 self.pylab.draw_if_interactive.called = False
605 606
606 607 # if a backend switch was performed, reverse it now
607 608 if self.mpl_use._called:
608 609 self.matplotlib.rcParams['backend'] = self.mpl_backend
609 610
610 611 def magic_run(self,parameter_s=''):
611 612 Magic.magic_run(self,parameter_s,runner=self.mplot_exec)
612 613
613 614 # Fix the docstring so users see the original as well
614 615 magic_run.__doc__ = "%s\n%s" % (Magic.magic_run.__doc__,
615 616 "\n *** Modified %run for Matplotlib,"
616 617 " with proper interactive handling ***")
617 618
618 619 # Now we provide 2 versions of a matplotlib-aware IPython base shells, single
619 620 # and multithreaded. Note that these are meant for internal use, the IPShell*
620 621 # classes below are the ones meant for public consumption.
621 622
622 623 class MatplotlibShell(MatplotlibShellBase,InteractiveShell):
623 624 """Single-threaded shell with matplotlib support."""
624 625
625 626 def __init__(self,name,usage=None,rc=Struct(opts=None,args=None),
626 627 user_ns=None,user_global_ns=None,**kw):
627 user_ns,b2 = self._matplotlib_config(name,user_ns)
628 user_ns,user_global_ns,b2 = self._matplotlib_config(name,user_ns,user_global_ns)
628 629 InteractiveShell.__init__(self,name,usage,rc,user_ns,user_global_ns,
629 630 banner2=b2,**kw)
630 631
631 632 class MatplotlibMTShell(MatplotlibShellBase,MTInteractiveShell):
632 633 """Multi-threaded shell with matplotlib support."""
633 634
634 635 def __init__(self,name,usage=None,rc=Struct(opts=None,args=None),
635 636 user_ns=None,user_global_ns=None, **kw):
636 637 user_ns,b2 = self._matplotlib_config(name,user_ns)
637 638 MTInteractiveShell.__init__(self,name,usage,rc,user_ns,user_global_ns,
638 639 banner2=b2,**kw)
639 640
640 641 #-----------------------------------------------------------------------------
641 642 # Utility functions for the different GUI enabled IPShell* classes.
642 643
643 644 def get_tk():
644 645 """Tries to import Tkinter and returns a withdrawn Tkinter root
645 646 window. If Tkinter is already imported or not available, this
646 647 returns None. This function calls `hijack_tk` underneath.
647 648 """
648 649 if not USE_TK or sys.modules.has_key('Tkinter'):
649 650 return None
650 651 else:
651 652 try:
652 653 import Tkinter
653 654 except ImportError:
654 655 return None
655 656 else:
656 657 hijack_tk()
657 658 r = Tkinter.Tk()
658 659 r.withdraw()
659 660 return r
660 661
661 662 def hijack_tk():
662 663 """Modifies Tkinter's mainloop with a dummy so when a module calls
663 664 mainloop, it does not block.
664 665
665 666 """
666 667 def misc_mainloop(self, n=0):
667 668 pass
668 669 def tkinter_mainloop(n=0):
669 670 pass
670 671
671 672 import Tkinter
672 673 Tkinter.Misc.mainloop = misc_mainloop
673 674 Tkinter.mainloop = tkinter_mainloop
674 675
675 676 def update_tk(tk):
676 677 """Updates the Tkinter event loop. This is typically called from
677 678 the respective WX or GTK mainloops.
678 679 """
679 680 if tk:
680 681 tk.update()
681 682
682 683 def hijack_wx():
683 684 """Modifies wxPython's MainLoop with a dummy so user code does not
684 685 block IPython. The hijacked mainloop function is returned.
685 686 """
686 687 def dummy_mainloop(*args, **kw):
687 688 pass
688 689
689 690 try:
690 691 import wx
691 692 except ImportError:
692 693 # For very old versions of WX
693 694 import wxPython as wx
694 695
695 696 ver = wx.__version__
696 697 orig_mainloop = None
697 698 if ver[:3] >= '2.5':
698 699 import wx
699 700 if hasattr(wx, '_core_'): core = getattr(wx, '_core_')
700 701 elif hasattr(wx, '_core'): core = getattr(wx, '_core')
701 702 else: raise AttributeError('Could not find wx core module')
702 703 orig_mainloop = core.PyApp_MainLoop
703 704 core.PyApp_MainLoop = dummy_mainloop
704 705 elif ver[:3] == '2.4':
705 706 orig_mainloop = wx.wxc.wxPyApp_MainLoop
706 707 wx.wxc.wxPyApp_MainLoop = dummy_mainloop
707 708 else:
708 709 warn("Unable to find either wxPython version 2.4 or >= 2.5.")
709 710 return orig_mainloop
710 711
711 712 def hijack_gtk():
712 713 """Modifies pyGTK's mainloop with a dummy so user code does not
713 714 block IPython. This function returns the original `gtk.mainloop`
714 715 function that has been hijacked.
715 716 """
716 717 def dummy_mainloop(*args, **kw):
717 718 pass
718 719 import gtk
719 720 if gtk.pygtk_version >= (2,4,0): orig_mainloop = gtk.main
720 721 else: orig_mainloop = gtk.mainloop
721 722 gtk.mainloop = dummy_mainloop
722 723 gtk.main = dummy_mainloop
723 724 return orig_mainloop
724 725
725 726 def hijack_qt():
726 727 """Modifies PyQt's mainloop with a dummy so user code does not
727 728 block IPython. This function returns the original
728 729 `qt.qApp.exec_loop` function that has been hijacked.
729 730 """
730 731 def dummy_mainloop(*args, **kw):
731 732 pass
732 733 import qt
733 734 orig_mainloop = qt.qApp.exec_loop
734 735 qt.qApp.exec_loop = dummy_mainloop
735 736 qt.QApplication.exec_loop = dummy_mainloop
736 737 return orig_mainloop
737 738
738 739 def hijack_qt4():
739 740 """Modifies PyQt4's mainloop with a dummy so user code does not
740 741 block IPython. This function returns the original
741 742 `QtGui.qApp.exec_` function that has been hijacked.
742 743 """
743 744 def dummy_mainloop(*args, **kw):
744 745 pass
745 746 from PyQt4 import QtGui, QtCore
746 747 orig_mainloop = QtGui.qApp.exec_
747 748 QtGui.qApp.exec_ = dummy_mainloop
748 749 QtGui.QApplication.exec_ = dummy_mainloop
749 750 QtCore.QCoreApplication.exec_ = dummy_mainloop
750 751 return orig_mainloop
751 752
752 753 #-----------------------------------------------------------------------------
753 754 # The IPShell* classes below are the ones meant to be run by external code as
754 755 # IPython instances. Note that unless a specific threading strategy is
755 756 # desired, the factory function start() below should be used instead (it
756 757 # selects the proper threaded class).
757 758
758 759 class IPThread(threading.Thread):
759 760 def run(self):
760 761 self.IP.mainloop(self._banner)
761 762 self.IP.kill()
762 763
763 764 class IPShellGTK(IPThread):
764 765 """Run a gtk mainloop() in a separate thread.
765 766
766 767 Python commands can be passed to the thread where they will be executed.
767 768 This is implemented by periodically checking for passed code using a
768 769 GTK timeout callback."""
769 770
770 771 TIMEOUT = 100 # Millisecond interval between timeouts.
771 772
772 773 def __init__(self,argv=None,user_ns=None,user_global_ns=None,
773 774 debug=1,shell_class=MTInteractiveShell):
774 775
775 776 import gtk
776 777
777 778 self.gtk = gtk
778 779 self.gtk_mainloop = hijack_gtk()
779 780
780 781 # Allows us to use both Tk and GTK.
781 782 self.tk = get_tk()
782 783
783 784 if gtk.pygtk_version >= (2,4,0): mainquit = self.gtk.main_quit
784 785 else: mainquit = self.gtk.mainquit
785 786
786 787 self.IP = make_IPython(argv,user_ns=user_ns,
787 788 user_global_ns=user_global_ns,
788 789 debug=debug,
789 790 shell_class=shell_class,
790 791 on_kill=[mainquit])
791 792
792 793 # HACK: slot for banner in self; it will be passed to the mainloop
793 794 # method only and .run() needs it. The actual value will be set by
794 795 # .mainloop().
795 796 self._banner = None
796 797
797 798 threading.Thread.__init__(self)
798 799
799 800 def mainloop(self,sys_exit=0,banner=None):
800 801
801 802 self._banner = banner
802 803
803 804 if self.gtk.pygtk_version >= (2,4,0):
804 805 import gobject
805 806 gobject.idle_add(self.on_timer)
806 807 else:
807 808 self.gtk.idle_add(self.on_timer)
808 809
809 810 if sys.platform != 'win32':
810 811 try:
811 812 if self.gtk.gtk_version[0] >= 2:
812 813 self.gtk.gdk.threads_init()
813 814 except AttributeError:
814 815 pass
815 816 except RuntimeError:
816 817 error('Your pyGTK likely has not been compiled with '
817 818 'threading support.\n'
818 819 'The exception printout is below.\n'
819 820 'You can either rebuild pyGTK with threads, or '
820 821 'try using \n'
821 822 'matplotlib with a different backend (like Tk or WX).\n'
822 823 'Note that matplotlib will most likely not work in its '
823 824 'current state!')
824 825 self.IP.InteractiveTB()
825 826
826 827 self.start()
827 828 self.gtk.gdk.threads_enter()
828 829 self.gtk_mainloop()
829 830 self.gtk.gdk.threads_leave()
830 831 self.join()
831 832
832 833 def on_timer(self):
833 834 """Called when GTK is idle.
834 835
835 836 Must return True always, otherwise GTK stops calling it"""
836 837
837 838 update_tk(self.tk)
838 839 self.IP.runcode()
839 840 time.sleep(0.01)
840 841 return True
841 842
842 843
843 844 class IPShellWX(IPThread):
844 845 """Run a wx mainloop() in a separate thread.
845 846
846 847 Python commands can be passed to the thread where they will be executed.
847 848 This is implemented by periodically checking for passed code using a
848 849 GTK timeout callback."""
849 850
850 851 TIMEOUT = 100 # Millisecond interval between timeouts.
851 852
852 853 def __init__(self,argv=None,user_ns=None,user_global_ns=None,
853 854 debug=1,shell_class=MTInteractiveShell):
854 855
855 856 self.IP = make_IPython(argv,user_ns=user_ns,
856 857 user_global_ns=user_global_ns,
857 858 debug=debug,
858 859 shell_class=shell_class,
859 860 on_kill=[self.wxexit])
860 861
861 862 wantedwxversion=self.IP.rc.wxversion
862 863 if wantedwxversion!="0":
863 864 try:
864 865 import wxversion
865 866 except ImportError:
866 867 error('The wxversion module is needed for WX version selection')
867 868 else:
868 869 try:
869 870 wxversion.select(wantedwxversion)
870 871 except:
871 872 self.IP.InteractiveTB()
872 873 error('Requested wxPython version %s could not be loaded' %
873 874 wantedwxversion)
874 875
875 876 import wx
876 877
877 878 threading.Thread.__init__(self)
878 879 self.wx = wx
879 880 self.wx_mainloop = hijack_wx()
880 881
881 882 # Allows us to use both Tk and GTK.
882 883 self.tk = get_tk()
883 884
884 885 # HACK: slot for banner in self; it will be passed to the mainloop
885 886 # method only and .run() needs it. The actual value will be set by
886 887 # .mainloop().
887 888 self._banner = None
888 889
889 890 self.app = None
890 891
891 892 def wxexit(self, *args):
892 893 if self.app is not None:
893 894 self.app.agent.timer.Stop()
894 895 self.app.ExitMainLoop()
895 896
896 897 def mainloop(self,sys_exit=0,banner=None):
897 898
898 899 self._banner = banner
899 900
900 901 self.start()
901 902
902 903 class TimerAgent(self.wx.MiniFrame):
903 904 wx = self.wx
904 905 IP = self.IP
905 906 tk = self.tk
906 907 def __init__(self, parent, interval):
907 908 style = self.wx.DEFAULT_FRAME_STYLE | self.wx.TINY_CAPTION_HORIZ
908 909 self.wx.MiniFrame.__init__(self, parent, -1, ' ', pos=(200, 200),
909 910 size=(100, 100),style=style)
910 911 self.Show(False)
911 912 self.interval = interval
912 913 self.timerId = self.wx.NewId()
913 914
914 915 def StartWork(self):
915 916 self.timer = self.wx.Timer(self, self.timerId)
916 917 self.wx.EVT_TIMER(self, self.timerId, self.OnTimer)
917 918 self.timer.Start(self.interval)
918 919
919 920 def OnTimer(self, event):
920 921 update_tk(self.tk)
921 922 self.IP.runcode()
922 923
923 924 class App(self.wx.App):
924 925 wx = self.wx
925 926 TIMEOUT = self.TIMEOUT
926 927 def OnInit(self):
927 928 'Create the main window and insert the custom frame'
928 929 self.agent = TimerAgent(None, self.TIMEOUT)
929 930 self.agent.Show(False)
930 931 self.agent.StartWork()
931 932 return True
932 933
933 934 self.app = App(redirect=False)
934 935 self.wx_mainloop(self.app)
935 936 self.join()
936 937
937 938
938 939 class IPShellQt(IPThread):
939 940 """Run a Qt event loop in a separate thread.
940 941
941 942 Python commands can be passed to the thread where they will be executed.
942 943 This is implemented by periodically checking for passed code using a
943 944 Qt timer / slot."""
944 945
945 946 TIMEOUT = 100 # Millisecond interval between timeouts.
946 947
947 948 def __init__(self, argv=None, user_ns=None, user_global_ns=None,
948 949 debug=0, shell_class=MTInteractiveShell):
949 950
950 951 import qt
951 952
952 953 self.exec_loop = hijack_qt()
953 954
954 955 # Allows us to use both Tk and QT.
955 956 self.tk = get_tk()
956 957
957 958 self.IP = make_IPython(argv,
958 959 user_ns=user_ns,
959 960 user_global_ns=user_global_ns,
960 961 debug=debug,
961 962 shell_class=shell_class,
962 963 on_kill=[qt.qApp.exit])
963 964
964 965 # HACK: slot for banner in self; it will be passed to the mainloop
965 966 # method only and .run() needs it. The actual value will be set by
966 967 # .mainloop().
967 968 self._banner = None
968 969
969 970 threading.Thread.__init__(self)
970 971
971 972 def mainloop(self, sys_exit=0, banner=None):
972 973
973 974 import qt
974 975
975 976 self._banner = banner
976 977
977 978 if qt.QApplication.startingUp():
978 979 a = qt.QApplication(sys.argv)
979 980
980 981 self.timer = qt.QTimer()
981 982 qt.QObject.connect(self.timer,
982 983 qt.SIGNAL('timeout()'),
983 984 self.on_timer)
984 985
985 986 self.start()
986 987 self.timer.start(self.TIMEOUT, True)
987 988 while True:
988 989 if self.IP._kill: break
989 990 self.exec_loop()
990 991 self.join()
991 992
992 993 def on_timer(self):
993 994 update_tk(self.tk)
994 995 result = self.IP.runcode()
995 996 self.timer.start(self.TIMEOUT, True)
996 997 return result
997 998
998 999
999 1000 class IPShellQt4(IPThread):
1000 1001 """Run a Qt event loop in a separate thread.
1001 1002
1002 1003 Python commands can be passed to the thread where they will be executed.
1003 1004 This is implemented by periodically checking for passed code using a
1004 1005 Qt timer / slot."""
1005 1006
1006 1007 TIMEOUT = 100 # Millisecond interval between timeouts.
1007 1008
1008 1009 def __init__(self, argv=None, user_ns=None, user_global_ns=None,
1009 1010 debug=0, shell_class=MTInteractiveShell):
1010 1011
1011 1012 from PyQt4 import QtCore, QtGui
1012 1013
1013 1014 try:
1014 1015 # present in PyQt4-4.2.1 or later
1015 1016 QtCore.pyqtRemoveInputHook()
1016 1017 except AttributeError:
1017 1018 pass
1018 1019
1019 1020 if QtCore.PYQT_VERSION_STR == '4.3':
1020 1021 warn('''PyQt4 version 4.3 detected.
1021 1022 If you experience repeated threading warnings, please update PyQt4.
1022 1023 ''')
1023 1024
1024 1025 self.exec_ = hijack_qt4()
1025 1026
1026 1027 # Allows us to use both Tk and QT.
1027 1028 self.tk = get_tk()
1028 1029
1029 1030 self.IP = make_IPython(argv,
1030 1031 user_ns=user_ns,
1031 1032 user_global_ns=user_global_ns,
1032 1033 debug=debug,
1033 1034 shell_class=shell_class,
1034 1035 on_kill=[QtGui.qApp.exit])
1035 1036
1036 1037 # HACK: slot for banner in self; it will be passed to the mainloop
1037 1038 # method only and .run() needs it. The actual value will be set by
1038 1039 # .mainloop().
1039 1040 self._banner = None
1040 1041
1041 1042 threading.Thread.__init__(self)
1042 1043
1043 1044 def mainloop(self, sys_exit=0, banner=None):
1044 1045
1045 1046 from PyQt4 import QtCore, QtGui
1046 1047
1047 1048 self._banner = banner
1048 1049
1049 1050 if QtGui.QApplication.startingUp():
1050 1051 a = QtGui.QApplication(sys.argv)
1051 1052
1052 1053 self.timer = QtCore.QTimer()
1053 1054 QtCore.QObject.connect(self.timer,
1054 1055 QtCore.SIGNAL('timeout()'),
1055 1056 self.on_timer)
1056 1057
1057 1058 self.start()
1058 1059 self.timer.start(self.TIMEOUT)
1059 1060 while True:
1060 1061 if self.IP._kill: break
1061 1062 self.exec_()
1062 1063 self.join()
1063 1064
1064 1065 def on_timer(self):
1065 1066 update_tk(self.tk)
1066 1067 result = self.IP.runcode()
1067 1068 self.timer.start(self.TIMEOUT)
1068 1069 return result
1069 1070
1070 1071
1071 1072 # A set of matplotlib public IPython shell classes, for single-threaded (Tk*
1072 1073 # and FLTK*) and multithreaded (GTK*, WX* and Qt*) backends to use.
1073 1074 def _load_pylab(user_ns):
1074 1075 """Allow users to disable pulling all of pylab into the top-level
1075 1076 namespace.
1076 1077
1077 1078 This little utility must be called AFTER the actual ipython instance is
1078 1079 running, since only then will the options file have been fully parsed."""
1079 1080
1080 1081 ip = IPython.ipapi.get()
1081 1082 if ip.options.pylab_import_all:
1082 1083 ip.ex("from matplotlib.pylab import *")
1083 1084 ip.IP.user_config_ns.update(ip.user_ns)
1084 1085
1085 1086
1086 1087 class IPShellMatplotlib(IPShell):
1087 1088 """Subclass IPShell with MatplotlibShell as the internal shell.
1088 1089
1089 1090 Single-threaded class, meant for the Tk* and FLTK* backends.
1090 1091
1091 1092 Having this on a separate class simplifies the external driver code."""
1092 1093
1093 1094 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
1094 1095 IPShell.__init__(self,argv,user_ns,user_global_ns,debug,
1095 1096 shell_class=MatplotlibShell)
1096 1097 _load_pylab(self.IP.user_ns)
1097 1098
1098 1099 class IPShellMatplotlibGTK(IPShellGTK):
1099 1100 """Subclass IPShellGTK with MatplotlibMTShell as the internal shell.
1100 1101
1101 1102 Multi-threaded class, meant for the GTK* backends."""
1102 1103
1103 1104 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
1104 1105 IPShellGTK.__init__(self,argv,user_ns,user_global_ns,debug,
1105 1106 shell_class=MatplotlibMTShell)
1106 1107 _load_pylab(self.IP.user_ns)
1107 1108
1108 1109 class IPShellMatplotlibWX(IPShellWX):
1109 1110 """Subclass IPShellWX with MatplotlibMTShell as the internal shell.
1110 1111
1111 1112 Multi-threaded class, meant for the WX* backends."""
1112 1113
1113 1114 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
1114 1115 IPShellWX.__init__(self,argv,user_ns,user_global_ns,debug,
1115 1116 shell_class=MatplotlibMTShell)
1116 1117 _load_pylab(self.IP.user_ns)
1117 1118
1118 1119 class IPShellMatplotlibQt(IPShellQt):
1119 1120 """Subclass IPShellQt with MatplotlibMTShell as the internal shell.
1120 1121
1121 1122 Multi-threaded class, meant for the Qt* backends."""
1122 1123
1123 1124 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
1124 1125 IPShellQt.__init__(self,argv,user_ns,user_global_ns,debug,
1125 1126 shell_class=MatplotlibMTShell)
1126 1127 _load_pylab(self.IP.user_ns)
1127 1128
1128 1129 class IPShellMatplotlibQt4(IPShellQt4):
1129 1130 """Subclass IPShellQt4 with MatplotlibMTShell as the internal shell.
1130 1131
1131 1132 Multi-threaded class, meant for the Qt4* backends."""
1132 1133
1133 1134 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
1134 1135 IPShellQt4.__init__(self,argv,user_ns,user_global_ns,debug,
1135 1136 shell_class=MatplotlibMTShell)
1136 1137 _load_pylab(self.IP.user_ns)
1137 1138
1138 1139 #-----------------------------------------------------------------------------
1139 1140 # Factory functions to actually start the proper thread-aware shell
1140 1141
1141 1142 def _select_shell(argv):
1142 1143 """Select a shell from the given argv vector.
1143 1144
1144 1145 This function implements the threading selection policy, allowing runtime
1145 1146 control of the threading mode, both for general users and for matplotlib.
1146 1147
1147 1148 Return:
1148 1149 Shell class to be instantiated for runtime operation.
1149 1150 """
1150 1151
1151 1152 global USE_TK
1152 1153
1153 1154 mpl_shell = {'gthread' : IPShellMatplotlibGTK,
1154 1155 'wthread' : IPShellMatplotlibWX,
1155 1156 'qthread' : IPShellMatplotlibQt,
1156 1157 'q4thread' : IPShellMatplotlibQt4,
1157 1158 'tkthread' : IPShellMatplotlib, # Tk is built-in
1158 1159 }
1159 1160
1160 1161 th_shell = {'gthread' : IPShellGTK,
1161 1162 'wthread' : IPShellWX,
1162 1163 'qthread' : IPShellQt,
1163 1164 'q4thread' : IPShellQt4,
1164 1165 'tkthread' : IPShell, # Tk is built-in
1165 1166 }
1166 1167
1167 1168 backends = {'gthread' : 'GTKAgg',
1168 1169 'wthread' : 'WXAgg',
1169 1170 'qthread' : 'QtAgg',
1170 1171 'q4thread' :'Qt4Agg',
1171 1172 'tkthread' :'TkAgg',
1172 1173 }
1173 1174
1174 1175 all_opts = set(['tk','pylab','gthread','qthread','q4thread','wthread',
1175 1176 'tkthread'])
1176 1177 user_opts = set([s.replace('-','') for s in argv[:3]])
1177 1178 special_opts = user_opts & all_opts
1178 1179
1179 1180 if 'tk' in special_opts:
1180 1181 USE_TK = True
1181 1182 special_opts.remove('tk')
1182 1183
1183 1184 if 'pylab' in special_opts:
1184 1185
1185 1186 try:
1186 1187 import matplotlib
1187 1188 except ImportError:
1188 1189 error('matplotlib could NOT be imported! Starting normal IPython.')
1189 1190 return IPShell
1190 1191
1191 1192 special_opts.remove('pylab')
1192 1193 # If there's any option left, it means the user wants to force the
1193 1194 # threading backend, else it's auto-selected from the rc file
1194 1195 if special_opts:
1195 1196 th_mode = special_opts.pop()
1196 1197 matplotlib.rcParams['backend'] = backends[th_mode]
1197 1198 else:
1198 1199 backend = matplotlib.rcParams['backend']
1199 1200 if backend.startswith('GTK'):
1200 1201 th_mode = 'gthread'
1201 1202 elif backend.startswith('WX'):
1202 1203 th_mode = 'wthread'
1203 1204 elif backend.startswith('Qt4'):
1204 1205 th_mode = 'q4thread'
1205 1206 elif backend.startswith('Qt'):
1206 1207 th_mode = 'qthread'
1207 1208 else:
1208 1209 # Any other backend, use plain Tk
1209 1210 th_mode = 'tkthread'
1210 1211
1211 1212 return mpl_shell[th_mode]
1212 1213 else:
1213 1214 # No pylab requested, just plain threads
1214 1215 try:
1215 1216 th_mode = special_opts.pop()
1216 1217 except KeyError:
1217 1218 th_mode = 'tkthread'
1218 1219 return th_shell[th_mode]
1219 1220
1220 1221
1221 1222 # This is the one which should be called by external code.
1222 1223 def start(user_ns = None):
1223 1224 """Return a running shell instance, dealing with threading options.
1224 1225
1225 1226 This is a factory function which will instantiate the proper IPython shell
1226 1227 based on the user's threading choice. Such a selector is needed because
1227 1228 different GUI toolkits require different thread handling details."""
1228 1229
1229 1230 shell = _select_shell(sys.argv)
1230 1231 return shell(user_ns = user_ns)
1231 1232
1232 1233 # Some aliases for backwards compatibility
1233 1234 IPythonShell = IPShell
1234 1235 IPythonShellEmbed = IPShellEmbed
1235 1236 #************************ End of file <Shell.py> ***************************
General Comments 0
You need to be logged in to leave comments. Login now