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