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