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