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