##// END OF EJS Templates
handle more than one line of macros in threaded shells
Ville M. Vainio -
Show More
@@ -1,1249 +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 return
418 return False
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 779 # Check for set_interactive, coming up in new pygtk.
780 780 # Disable it so that this code works, but notify
781 781 # the user that he has a better option as well.
782 782 # XXX TODO better support when set_interactive is released
783 783 try:
784 784 gtk.set_interactive(False)
785 785 print "Your PyGtk has set_interactive(), so you can use the"
786 786 print "more stable single-threaded Gtk mode."
787 787 print "See https://bugs.launchpad.net/ipython/+bug/270856"
788 788 except AttributeError:
789 789 pass
790 790
791 791 self.gtk = gtk
792 792 self.gtk_mainloop = hijack_gtk()
793 793
794 794 # Allows us to use both Tk and GTK.
795 795 self.tk = get_tk()
796 796
797 797 if gtk.pygtk_version >= (2,4,0): mainquit = self.gtk.main_quit
798 798 else: mainquit = self.gtk.mainquit
799 799
800 800 self.IP = make_IPython(argv,user_ns=user_ns,
801 801 user_global_ns=user_global_ns,
802 802 debug=debug,
803 803 shell_class=shell_class,
804 804 on_kill=[mainquit])
805 805
806 806 # HACK: slot for banner in self; it will be passed to the mainloop
807 807 # method only and .run() needs it. The actual value will be set by
808 808 # .mainloop().
809 809 self._banner = None
810 810
811 811 threading.Thread.__init__(self)
812 812
813 813 def mainloop(self,sys_exit=0,banner=None):
814 814
815 815 self._banner = banner
816 816
817 817 if self.gtk.pygtk_version >= (2,4,0):
818 818 import gobject
819 819 gobject.idle_add(self.on_timer)
820 820 else:
821 821 self.gtk.idle_add(self.on_timer)
822 822
823 823 if sys.platform != 'win32':
824 824 try:
825 825 if self.gtk.gtk_version[0] >= 2:
826 826 self.gtk.gdk.threads_init()
827 827 except AttributeError:
828 828 pass
829 829 except RuntimeError:
830 830 error('Your pyGTK likely has not been compiled with '
831 831 'threading support.\n'
832 832 'The exception printout is below.\n'
833 833 'You can either rebuild pyGTK with threads, or '
834 834 'try using \n'
835 835 'matplotlib with a different backend (like Tk or WX).\n'
836 836 'Note that matplotlib will most likely not work in its '
837 837 'current state!')
838 838 self.IP.InteractiveTB()
839 839
840 840 self.start()
841 841 self.gtk.gdk.threads_enter()
842 842 self.gtk_mainloop()
843 843 self.gtk.gdk.threads_leave()
844 844 self.join()
845 845
846 846 def on_timer(self):
847 847 """Called when GTK is idle.
848 848
849 849 Must return True always, otherwise GTK stops calling it"""
850 850
851 851 update_tk(self.tk)
852 852 self.IP.runcode()
853 853 time.sleep(0.01)
854 854 return True
855 855
856 856
857 857 class IPShellWX(IPThread):
858 858 """Run a wx mainloop() in a separate thread.
859 859
860 860 Python commands can be passed to the thread where they will be executed.
861 861 This is implemented by periodically checking for passed code using a
862 862 GTK timeout callback."""
863 863
864 864 TIMEOUT = 100 # Millisecond interval between timeouts.
865 865
866 866 def __init__(self,argv=None,user_ns=None,user_global_ns=None,
867 867 debug=1,shell_class=MTInteractiveShell):
868 868
869 869 self.IP = make_IPython(argv,user_ns=user_ns,
870 870 user_global_ns=user_global_ns,
871 871 debug=debug,
872 872 shell_class=shell_class,
873 873 on_kill=[self.wxexit])
874 874
875 875 wantedwxversion=self.IP.rc.wxversion
876 876 if wantedwxversion!="0":
877 877 try:
878 878 import wxversion
879 879 except ImportError:
880 880 error('The wxversion module is needed for WX version selection')
881 881 else:
882 882 try:
883 883 wxversion.select(wantedwxversion)
884 884 except:
885 885 self.IP.InteractiveTB()
886 886 error('Requested wxPython version %s could not be loaded' %
887 887 wantedwxversion)
888 888
889 889 import wx
890 890
891 891 threading.Thread.__init__(self)
892 892 self.wx = wx
893 893 self.wx_mainloop = hijack_wx()
894 894
895 895 # Allows us to use both Tk and GTK.
896 896 self.tk = get_tk()
897 897
898 898 # HACK: slot for banner in self; it will be passed to the mainloop
899 899 # method only and .run() needs it. The actual value will be set by
900 900 # .mainloop().
901 901 self._banner = None
902 902
903 903 self.app = None
904 904
905 905 def wxexit(self, *args):
906 906 if self.app is not None:
907 907 self.app.agent.timer.Stop()
908 908 self.app.ExitMainLoop()
909 909
910 910 def mainloop(self,sys_exit=0,banner=None):
911 911
912 912 self._banner = banner
913 913
914 914 self.start()
915 915
916 916 class TimerAgent(self.wx.MiniFrame):
917 917 wx = self.wx
918 918 IP = self.IP
919 919 tk = self.tk
920 920 def __init__(self, parent, interval):
921 921 style = self.wx.DEFAULT_FRAME_STYLE | self.wx.TINY_CAPTION_HORIZ
922 922 self.wx.MiniFrame.__init__(self, parent, -1, ' ', pos=(200, 200),
923 923 size=(100, 100),style=style)
924 924 self.Show(False)
925 925 self.interval = interval
926 926 self.timerId = self.wx.NewId()
927 927
928 928 def StartWork(self):
929 929 self.timer = self.wx.Timer(self, self.timerId)
930 930 self.wx.EVT_TIMER(self, self.timerId, self.OnTimer)
931 931 self.timer.Start(self.interval)
932 932
933 933 def OnTimer(self, event):
934 934 update_tk(self.tk)
935 935 self.IP.runcode()
936 936
937 937 class App(self.wx.App):
938 938 wx = self.wx
939 939 TIMEOUT = self.TIMEOUT
940 940 def OnInit(self):
941 941 'Create the main window and insert the custom frame'
942 942 self.agent = TimerAgent(None, self.TIMEOUT)
943 943 self.agent.Show(False)
944 944 self.agent.StartWork()
945 945 return True
946 946
947 947 self.app = App(redirect=False)
948 948 self.wx_mainloop(self.app)
949 949 self.join()
950 950
951 951
952 952 class IPShellQt(IPThread):
953 953 """Run a Qt event loop in a separate thread.
954 954
955 955 Python commands can be passed to the thread where they will be executed.
956 956 This is implemented by periodically checking for passed code using a
957 957 Qt timer / slot."""
958 958
959 959 TIMEOUT = 100 # Millisecond interval between timeouts.
960 960
961 961 def __init__(self, argv=None, user_ns=None, user_global_ns=None,
962 962 debug=0, shell_class=MTInteractiveShell):
963 963
964 964 import qt
965 965
966 966 self.exec_loop = hijack_qt()
967 967
968 968 # Allows us to use both Tk and QT.
969 969 self.tk = get_tk()
970 970
971 971 self.IP = make_IPython(argv,
972 972 user_ns=user_ns,
973 973 user_global_ns=user_global_ns,
974 974 debug=debug,
975 975 shell_class=shell_class,
976 976 on_kill=[qt.qApp.exit])
977 977
978 978 # HACK: slot for banner in self; it will be passed to the mainloop
979 979 # method only and .run() needs it. The actual value will be set by
980 980 # .mainloop().
981 981 self._banner = None
982 982
983 983 threading.Thread.__init__(self)
984 984
985 985 def mainloop(self, sys_exit=0, banner=None):
986 986
987 987 import qt
988 988
989 989 self._banner = banner
990 990
991 991 if qt.QApplication.startingUp():
992 992 a = qt.QApplication(sys.argv)
993 993
994 994 self.timer = qt.QTimer()
995 995 qt.QObject.connect(self.timer,
996 996 qt.SIGNAL('timeout()'),
997 997 self.on_timer)
998 998
999 999 self.start()
1000 1000 self.timer.start(self.TIMEOUT, True)
1001 1001 while True:
1002 1002 if self.IP._kill: break
1003 1003 self.exec_loop()
1004 1004 self.join()
1005 1005
1006 1006 def on_timer(self):
1007 1007 update_tk(self.tk)
1008 1008 result = self.IP.runcode()
1009 1009 self.timer.start(self.TIMEOUT, True)
1010 1010 return result
1011 1011
1012 1012
1013 1013 class IPShellQt4(IPThread):
1014 1014 """Run a Qt event loop in a separate thread.
1015 1015
1016 1016 Python commands can be passed to the thread where they will be executed.
1017 1017 This is implemented by periodically checking for passed code using a
1018 1018 Qt timer / slot."""
1019 1019
1020 1020 TIMEOUT = 100 # Millisecond interval between timeouts.
1021 1021
1022 1022 def __init__(self, argv=None, user_ns=None, user_global_ns=None,
1023 1023 debug=0, shell_class=MTInteractiveShell):
1024 1024
1025 1025 from PyQt4 import QtCore, QtGui
1026 1026
1027 1027 try:
1028 1028 # present in PyQt4-4.2.1 or later
1029 1029 QtCore.pyqtRemoveInputHook()
1030 1030 except AttributeError:
1031 1031 pass
1032 1032
1033 1033 if QtCore.PYQT_VERSION_STR == '4.3':
1034 1034 warn('''PyQt4 version 4.3 detected.
1035 1035 If you experience repeated threading warnings, please update PyQt4.
1036 1036 ''')
1037 1037
1038 1038 self.exec_ = hijack_qt4()
1039 1039
1040 1040 # Allows us to use both Tk and QT.
1041 1041 self.tk = get_tk()
1042 1042
1043 1043 self.IP = make_IPython(argv,
1044 1044 user_ns=user_ns,
1045 1045 user_global_ns=user_global_ns,
1046 1046 debug=debug,
1047 1047 shell_class=shell_class,
1048 1048 on_kill=[QtGui.qApp.exit])
1049 1049
1050 1050 # HACK: slot for banner in self; it will be passed to the mainloop
1051 1051 # method only and .run() needs it. The actual value will be set by
1052 1052 # .mainloop().
1053 1053 self._banner = None
1054 1054
1055 1055 threading.Thread.__init__(self)
1056 1056
1057 1057 def mainloop(self, sys_exit=0, banner=None):
1058 1058
1059 1059 from PyQt4 import QtCore, QtGui
1060 1060
1061 1061 self._banner = banner
1062 1062
1063 1063 if QtGui.QApplication.startingUp():
1064 1064 a = QtGui.QApplication(sys.argv)
1065 1065
1066 1066 self.timer = QtCore.QTimer()
1067 1067 QtCore.QObject.connect(self.timer,
1068 1068 QtCore.SIGNAL('timeout()'),
1069 1069 self.on_timer)
1070 1070
1071 1071 self.start()
1072 1072 self.timer.start(self.TIMEOUT)
1073 1073 while True:
1074 1074 if self.IP._kill: break
1075 1075 self.exec_()
1076 1076 self.join()
1077 1077
1078 1078 def on_timer(self):
1079 1079 update_tk(self.tk)
1080 1080 result = self.IP.runcode()
1081 1081 self.timer.start(self.TIMEOUT)
1082 1082 return result
1083 1083
1084 1084
1085 1085 # A set of matplotlib public IPython shell classes, for single-threaded (Tk*
1086 1086 # and FLTK*) and multithreaded (GTK*, WX* and Qt*) backends to use.
1087 1087 def _load_pylab(user_ns):
1088 1088 """Allow users to disable pulling all of pylab into the top-level
1089 1089 namespace.
1090 1090
1091 1091 This little utility must be called AFTER the actual ipython instance is
1092 1092 running, since only then will the options file have been fully parsed."""
1093 1093
1094 1094 ip = IPython.ipapi.get()
1095 1095 if ip.options.pylab_import_all:
1096 1096 ip.ex("from matplotlib.pylab import *")
1097 1097 ip.IP.user_config_ns.update(ip.user_ns)
1098 1098
1099 1099
1100 1100 class IPShellMatplotlib(IPShell):
1101 1101 """Subclass IPShell with MatplotlibShell as the internal shell.
1102 1102
1103 1103 Single-threaded class, meant for the Tk* and FLTK* backends.
1104 1104
1105 1105 Having this on a separate class simplifies the external driver code."""
1106 1106
1107 1107 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
1108 1108 IPShell.__init__(self,argv,user_ns,user_global_ns,debug,
1109 1109 shell_class=MatplotlibShell)
1110 1110 _load_pylab(self.IP.user_ns)
1111 1111
1112 1112 class IPShellMatplotlibGTK(IPShellGTK):
1113 1113 """Subclass IPShellGTK with MatplotlibMTShell as the internal shell.
1114 1114
1115 1115 Multi-threaded class, meant for the GTK* backends."""
1116 1116
1117 1117 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
1118 1118 IPShellGTK.__init__(self,argv,user_ns,user_global_ns,debug,
1119 1119 shell_class=MatplotlibMTShell)
1120 1120 _load_pylab(self.IP.user_ns)
1121 1121
1122 1122 class IPShellMatplotlibWX(IPShellWX):
1123 1123 """Subclass IPShellWX with MatplotlibMTShell as the internal shell.
1124 1124
1125 1125 Multi-threaded class, meant for the WX* backends."""
1126 1126
1127 1127 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
1128 1128 IPShellWX.__init__(self,argv,user_ns,user_global_ns,debug,
1129 1129 shell_class=MatplotlibMTShell)
1130 1130 _load_pylab(self.IP.user_ns)
1131 1131
1132 1132 class IPShellMatplotlibQt(IPShellQt):
1133 1133 """Subclass IPShellQt with MatplotlibMTShell as the internal shell.
1134 1134
1135 1135 Multi-threaded class, meant for the Qt* backends."""
1136 1136
1137 1137 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
1138 1138 IPShellQt.__init__(self,argv,user_ns,user_global_ns,debug,
1139 1139 shell_class=MatplotlibMTShell)
1140 1140 _load_pylab(self.IP.user_ns)
1141 1141
1142 1142 class IPShellMatplotlibQt4(IPShellQt4):
1143 1143 """Subclass IPShellQt4 with MatplotlibMTShell as the internal shell.
1144 1144
1145 1145 Multi-threaded class, meant for the Qt4* backends."""
1146 1146
1147 1147 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
1148 1148 IPShellQt4.__init__(self,argv,user_ns,user_global_ns,debug,
1149 1149 shell_class=MatplotlibMTShell)
1150 1150 _load_pylab(self.IP.user_ns)
1151 1151
1152 1152 #-----------------------------------------------------------------------------
1153 1153 # Factory functions to actually start the proper thread-aware shell
1154 1154
1155 1155 def _select_shell(argv):
1156 1156 """Select a shell from the given argv vector.
1157 1157
1158 1158 This function implements the threading selection policy, allowing runtime
1159 1159 control of the threading mode, both for general users and for matplotlib.
1160 1160
1161 1161 Return:
1162 1162 Shell class to be instantiated for runtime operation.
1163 1163 """
1164 1164
1165 1165 global USE_TK
1166 1166
1167 1167 mpl_shell = {'gthread' : IPShellMatplotlibGTK,
1168 1168 'wthread' : IPShellMatplotlibWX,
1169 1169 'qthread' : IPShellMatplotlibQt,
1170 1170 'q4thread' : IPShellMatplotlibQt4,
1171 1171 'tkthread' : IPShellMatplotlib, # Tk is built-in
1172 1172 }
1173 1173
1174 1174 th_shell = {'gthread' : IPShellGTK,
1175 1175 'wthread' : IPShellWX,
1176 1176 'qthread' : IPShellQt,
1177 1177 'q4thread' : IPShellQt4,
1178 1178 'tkthread' : IPShell, # Tk is built-in
1179 1179 }
1180 1180
1181 1181 backends = {'gthread' : 'GTKAgg',
1182 1182 'wthread' : 'WXAgg',
1183 1183 'qthread' : 'QtAgg',
1184 1184 'q4thread' :'Qt4Agg',
1185 1185 'tkthread' :'TkAgg',
1186 1186 }
1187 1187
1188 1188 all_opts = set(['tk','pylab','gthread','qthread','q4thread','wthread',
1189 1189 'tkthread'])
1190 1190 user_opts = set([s.replace('-','') for s in argv[:3]])
1191 1191 special_opts = user_opts & all_opts
1192 1192
1193 1193 if 'tk' in special_opts:
1194 1194 USE_TK = True
1195 1195 special_opts.remove('tk')
1196 1196
1197 1197 if 'pylab' in special_opts:
1198 1198
1199 1199 try:
1200 1200 import matplotlib
1201 1201 except ImportError:
1202 1202 error('matplotlib could NOT be imported! Starting normal IPython.')
1203 1203 return IPShell
1204 1204
1205 1205 special_opts.remove('pylab')
1206 1206 # If there's any option left, it means the user wants to force the
1207 1207 # threading backend, else it's auto-selected from the rc file
1208 1208 if special_opts:
1209 1209 th_mode = special_opts.pop()
1210 1210 matplotlib.rcParams['backend'] = backends[th_mode]
1211 1211 else:
1212 1212 backend = matplotlib.rcParams['backend']
1213 1213 if backend.startswith('GTK'):
1214 1214 th_mode = 'gthread'
1215 1215 elif backend.startswith('WX'):
1216 1216 th_mode = 'wthread'
1217 1217 elif backend.startswith('Qt4'):
1218 1218 th_mode = 'q4thread'
1219 1219 elif backend.startswith('Qt'):
1220 1220 th_mode = 'qthread'
1221 1221 else:
1222 1222 # Any other backend, use plain Tk
1223 1223 th_mode = 'tkthread'
1224 1224
1225 1225 return mpl_shell[th_mode]
1226 1226 else:
1227 1227 # No pylab requested, just plain threads
1228 1228 try:
1229 1229 th_mode = special_opts.pop()
1230 1230 except KeyError:
1231 1231 th_mode = 'tkthread'
1232 1232 return th_shell[th_mode]
1233 1233
1234 1234
1235 1235 # This is the one which should be called by external code.
1236 1236 def start(user_ns = None):
1237 1237 """Return a running shell instance, dealing with threading options.
1238 1238
1239 1239 This is a factory function which will instantiate the proper IPython shell
1240 1240 based on the user's threading choice. Such a selector is needed because
1241 1241 different GUI toolkits require different thread handling details."""
1242 1242
1243 1243 shell = _select_shell(sys.argv)
1244 1244 return shell(user_ns = user_ns)
1245 1245
1246 1246 # Some aliases for backwards compatibility
1247 1247 IPythonShell = IPShell
1248 1248 IPythonShellEmbed = IPShellEmbed
1249 1249 #************************ End of file <Shell.py> ***************************
General Comments 0
You need to be logged in to leave comments. Login now