##// END OF EJS Templates
Simplify MTInteractiveShell: ditch Condition(RLock), use much simpler threading.Event mechanism (relying more on built-in-functionality in Queue)
Ville M. Vainio -
Show More
@@ -1,1216 +1,1201 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 # Locking control variable.
362 self.thread_ready = threading.Condition(threading.RLock())
363 361
364 # A queue to hold the code to be executed. A scalar variable is NOT
365 # enough, because uses like macros cause reentrancy.
362 # A queue to hold the code to be executed.
366 363 self.code_queue = Queue.Queue()
367 364
368 365 # Stuff to do at closing time
369 self._kill = False
370 on_kill = kw.get('on_kill')
371 if on_kill is None:
372 on_kill = []
366 self._kill = None
367 on_kill = kw.get('on_kill', [])
373 368 # Check that all things to kill are callable:
374 369 for t in on_kill:
375 370 if not callable(t):
376 371 raise TypeError,'on_kill must be a list of callables'
377 372 self.on_kill = on_kill
378 373 # thread identity of the "worker thread" (that may execute code directly)
379 374 self.worker_ident = None
380 375 def runsource(self, source, filename="<input>", symbol="single"):
381 376 """Compile and run some source in the interpreter.
382 377
383 378 Modified version of code.py's runsource(), to handle threading issues.
384 379 See the original for full docstring details."""
385 380
386 381 global KBINT
387 382
388 383 # If Ctrl-C was typed, we reset the flag and return right away
389 384 if KBINT:
390 385 KBINT = False
391 386 return False
392 387
393 388 try:
394 389 code = self.compile(source, filename, symbol)
395 390 except (OverflowError, SyntaxError, ValueError):
396 391 # Case 1
397 392 self.showsyntaxerror(filename)
398 393 return False
399 394
400 395 if code is None:
401 396 # Case 2
402 397 return True
403 398
404 # Case 3
405 # Store code in queue, so the execution thread can handle it.
406
407 # Note that with macros and other applications, we MAY re-enter this
408 # section, so we have to acquire the lock with non-blocking semantics,
409 # else we deadlock.
410
411 399 # shortcut - if we are in worker thread, or the worker thread is not running,
412 400 # execute directly (to allow recursion and prevent deadlock if code is run early
413 401 # in IPython construction)
414 402
415 403 if self.worker_ident is None or self.worker_ident == thread.get_ident():
416 404 InteractiveShell.runcode(self,code)
417 405 return
418
419 got_lock = self.thread_ready.acquire(blocking=False)
420 self.code_queue.put(code)
421 if got_lock:
422 self.thread_ready.wait() # Wait until processed in timeout interval
423 self.thread_ready.release()
424 406
407 # Case 3
408 # Store code in queue, so the execution thread can handle it.
409
410 ev = threading.Event()
411 self.code_queue.put((code,ev))
412 ev.wait()
425 413 return False
426 414
427 415 def runcode(self):
428 416 """Execute a code object.
429 417
430 418 Multithreaded wrapper around IPython's runcode()."""
431 419
432 420 global CODE_RUN
433 # lock thread-protected stuff
421
422 # we are in worker thread, stash out the id for runsource()
434 423 self.worker_ident = thread.get_ident()
435 got_lock = self.thread_ready.acquire()
436 424
437 425 if self._kill:
438 426 print >>Term.cout, 'Closing threads...',
439 427 Term.cout.flush()
440 428 for tokill in self.on_kill:
441 429 tokill()
442 430 print >>Term.cout, 'Done.'
431 # allow kill() to return
432 self._kill.set()
433 return
443 434
444 435 # Install sigint handler. We do it every time to ensure that if user
445 436 # code modifies it, we restore our own handling.
446 437 try:
447 438 signal(SIGINT,sigint_handler)
448 439 except SystemError:
449 440 # This happens under Windows, which seems to have all sorts
450 441 # of problems with signal handling. Oh well...
451 442 pass
452 443
453 444 # Flush queue of pending code by calling the run methood of the parent
454 445 # class with all items which may be in the queue.
455 446 code_to_run = None
456 447 while 1:
457 448 try:
458 code_to_run = self.code_queue.get_nowait()
449 code_to_run, event = self.code_queue.get_nowait()
459 450 except Queue.Empty:
460 451 break
461 452 # Exceptions need to be raised differently depending on which
462 453 # thread is active. This convoluted try/except is only there to
463 454 # protect against asynchronous exceptions, to ensure that a KBINT
464 455 # at the wrong time doesn't deadlock everything. The global
465 456 # CODE_TO_RUN is set to true/false as close as possible to the
466 457 # runcode() call, so that the KBINT handler is correctly informed.
467 458 try:
468 459 try:
469 460 CODE_RUN = True
470 461 InteractiveShell.runcode(self,code_to_run)
471 462 except KeyboardInterrupt:
472 463 print "Keyboard interrupted in mainloop"
473 464 while not self.code_queue.empty():
474 self.code_queue.get_nowait()
465 code, ev = self.code_queue.get_nowait()
466 ev.set()
475 467 break
476 468 finally:
477 if got_lock:
478 CODE_RUN = False
479
480 # We're done with thread-protected variables
481 if code_to_run is not None:
482 self.thread_ready.notify()
483 self.thread_ready.release()
484
485 # We're done...
486 CODE_RUN = False
469 CODE_RUN = False
470 # allow runsource() return from wait
471 event.set()
472
473
487 474 # This MUST return true for gtk threading to work
488 475 return True
489 476
490 477 def kill(self):
491 478 """Kill the thread, returning when it has been shut down."""
492 got_lock = self.thread_ready.acquire(False)
493 self._kill = True
494 if got_lock:
495 self.thread_ready.release()
479 self._kill = threading.Event()
480 self._kill.wait()
496 481
497 482 class MatplotlibShellBase:
498 483 """Mixin class to provide the necessary modifications to regular IPython
499 484 shell classes for matplotlib support.
500 485
501 486 Given Python's MRO, this should be used as the FIRST class in the
502 487 inheritance hierarchy, so that it overrides the relevant methods."""
503 488
504 489 def _matplotlib_config(self,name,user_ns):
505 490 """Return items needed to setup the user's shell with matplotlib"""
506 491
507 492 # Initialize matplotlib to interactive mode always
508 493 import matplotlib
509 494 from matplotlib import backends
510 495 matplotlib.interactive(True)
511 496
512 497 def use(arg):
513 498 """IPython wrapper for matplotlib's backend switcher.
514 499
515 500 In interactive use, we can not allow switching to a different
516 501 interactive backend, since thread conflicts will most likely crash
517 502 the python interpreter. This routine does a safety check first,
518 503 and refuses to perform a dangerous switch. It still allows
519 504 switching to non-interactive backends."""
520 505
521 506 if arg in backends.interactive_bk and arg != self.mpl_backend:
522 507 m=('invalid matplotlib backend switch.\n'
523 508 'This script attempted to switch to the interactive '
524 509 'backend: `%s`\n'
525 510 'Your current choice of interactive backend is: `%s`\n\n'
526 511 'Switching interactive matplotlib backends at runtime\n'
527 512 'would crash the python interpreter, '
528 513 'and IPython has blocked it.\n\n'
529 514 'You need to either change your choice of matplotlib backend\n'
530 515 'by editing your .matplotlibrc file, or run this script as a \n'
531 516 'standalone file from the command line, not using IPython.\n' %
532 517 (arg,self.mpl_backend) )
533 518 raise RuntimeError, m
534 519 else:
535 520 self.mpl_use(arg)
536 521 self.mpl_use._called = True
537 522
538 523 self.matplotlib = matplotlib
539 524 self.mpl_backend = matplotlib.rcParams['backend']
540 525
541 526 # we also need to block switching of interactive backends by use()
542 527 self.mpl_use = matplotlib.use
543 528 self.mpl_use._called = False
544 529 # overwrite the original matplotlib.use with our wrapper
545 530 matplotlib.use = use
546 531
547 532 # This must be imported last in the matplotlib series, after
548 533 # backend/interactivity choices have been made
549 534 import matplotlib.pylab as pylab
550 535 self.pylab = pylab
551 536
552 537 self.pylab.show._needmain = False
553 538 # We need to detect at runtime whether show() is called by the user.
554 539 # For this, we wrap it into a decorator which adds a 'called' flag.
555 540 self.pylab.draw_if_interactive = flag_calls(self.pylab.draw_if_interactive)
556 541
557 542 # Build a user namespace initialized with matplotlib/matlab features.
558 543 user_ns = IPython.ipapi.make_user_ns(user_ns)
559 544
560 545 exec ("import matplotlib\n"
561 546 "import matplotlib.pylab as pylab\n") in user_ns
562 547
563 548 # Build matplotlib info banner
564 549 b="""
565 550 Welcome to pylab, a matplotlib-based Python environment.
566 551 For more information, type 'help(pylab)'.
567 552 """
568 553 return user_ns,b
569 554
570 555 def mplot_exec(self,fname,*where,**kw):
571 556 """Execute a matplotlib script.
572 557
573 558 This is a call to execfile(), but wrapped in safeties to properly
574 559 handle interactive rendering and backend switching."""
575 560
576 561 #print '*** Matplotlib runner ***' # dbg
577 562 # turn off rendering until end of script
578 563 isInteractive = self.matplotlib.rcParams['interactive']
579 564 self.matplotlib.interactive(False)
580 565 self.safe_execfile(fname,*where,**kw)
581 566 self.matplotlib.interactive(isInteractive)
582 567 # make rendering call now, if the user tried to do it
583 568 if self.pylab.draw_if_interactive.called:
584 569 self.pylab.draw()
585 570 self.pylab.draw_if_interactive.called = False
586 571
587 572 # if a backend switch was performed, reverse it now
588 573 if self.mpl_use._called:
589 574 self.matplotlib.rcParams['backend'] = self.mpl_backend
590 575
591 576 def magic_run(self,parameter_s=''):
592 577 Magic.magic_run(self,parameter_s,runner=self.mplot_exec)
593 578
594 579 # Fix the docstring so users see the original as well
595 580 magic_run.__doc__ = "%s\n%s" % (Magic.magic_run.__doc__,
596 581 "\n *** Modified %run for Matplotlib,"
597 582 " with proper interactive handling ***")
598 583
599 584 # Now we provide 2 versions of a matplotlib-aware IPython base shells, single
600 585 # and multithreaded. Note that these are meant for internal use, the IPShell*
601 586 # classes below are the ones meant for public consumption.
602 587
603 588 class MatplotlibShell(MatplotlibShellBase,InteractiveShell):
604 589 """Single-threaded shell with matplotlib support."""
605 590
606 591 def __init__(self,name,usage=None,rc=Struct(opts=None,args=None),
607 592 user_ns=None,user_global_ns=None,**kw):
608 593 user_ns,b2 = self._matplotlib_config(name,user_ns)
609 594 InteractiveShell.__init__(self,name,usage,rc,user_ns,user_global_ns,
610 595 banner2=b2,**kw)
611 596
612 597 class MatplotlibMTShell(MatplotlibShellBase,MTInteractiveShell):
613 598 """Multi-threaded shell with matplotlib support."""
614 599
615 600 def __init__(self,name,usage=None,rc=Struct(opts=None,args=None),
616 601 user_ns=None,user_global_ns=None, **kw):
617 602 user_ns,b2 = self._matplotlib_config(name,user_ns)
618 603 MTInteractiveShell.__init__(self,name,usage,rc,user_ns,user_global_ns,
619 604 banner2=b2,**kw)
620 605
621 606 #-----------------------------------------------------------------------------
622 607 # Utility functions for the different GUI enabled IPShell* classes.
623 608
624 609 def get_tk():
625 610 """Tries to import Tkinter and returns a withdrawn Tkinter root
626 611 window. If Tkinter is already imported or not available, this
627 612 returns None. This function calls `hijack_tk` underneath.
628 613 """
629 614 if not USE_TK or sys.modules.has_key('Tkinter'):
630 615 return None
631 616 else:
632 617 try:
633 618 import Tkinter
634 619 except ImportError:
635 620 return None
636 621 else:
637 622 hijack_tk()
638 623 r = Tkinter.Tk()
639 624 r.withdraw()
640 625 return r
641 626
642 627 def hijack_tk():
643 628 """Modifies Tkinter's mainloop with a dummy so when a module calls
644 629 mainloop, it does not block.
645 630
646 631 """
647 632 def misc_mainloop(self, n=0):
648 633 pass
649 634 def tkinter_mainloop(n=0):
650 635 pass
651 636
652 637 import Tkinter
653 638 Tkinter.Misc.mainloop = misc_mainloop
654 639 Tkinter.mainloop = tkinter_mainloop
655 640
656 641 def update_tk(tk):
657 642 """Updates the Tkinter event loop. This is typically called from
658 643 the respective WX or GTK mainloops.
659 644 """
660 645 if tk:
661 646 tk.update()
662 647
663 648 def hijack_wx():
664 649 """Modifies wxPython's MainLoop with a dummy so user code does not
665 650 block IPython. The hijacked mainloop function is returned.
666 651 """
667 652 def dummy_mainloop(*args, **kw):
668 653 pass
669 654
670 655 try:
671 656 import wx
672 657 except ImportError:
673 658 # For very old versions of WX
674 659 import wxPython as wx
675 660
676 661 ver = wx.__version__
677 662 orig_mainloop = None
678 663 if ver[:3] >= '2.5':
679 664 import wx
680 665 if hasattr(wx, '_core_'): core = getattr(wx, '_core_')
681 666 elif hasattr(wx, '_core'): core = getattr(wx, '_core')
682 667 else: raise AttributeError('Could not find wx core module')
683 668 orig_mainloop = core.PyApp_MainLoop
684 669 core.PyApp_MainLoop = dummy_mainloop
685 670 elif ver[:3] == '2.4':
686 671 orig_mainloop = wx.wxc.wxPyApp_MainLoop
687 672 wx.wxc.wxPyApp_MainLoop = dummy_mainloop
688 673 else:
689 674 warn("Unable to find either wxPython version 2.4 or >= 2.5.")
690 675 return orig_mainloop
691 676
692 677 def hijack_gtk():
693 678 """Modifies pyGTK's mainloop with a dummy so user code does not
694 679 block IPython. This function returns the original `gtk.mainloop`
695 680 function that has been hijacked.
696 681 """
697 682 def dummy_mainloop(*args, **kw):
698 683 pass
699 684 import gtk
700 685 if gtk.pygtk_version >= (2,4,0): orig_mainloop = gtk.main
701 686 else: orig_mainloop = gtk.mainloop
702 687 gtk.mainloop = dummy_mainloop
703 688 gtk.main = dummy_mainloop
704 689 return orig_mainloop
705 690
706 691 def hijack_qt():
707 692 """Modifies PyQt's mainloop with a dummy so user code does not
708 693 block IPython. This function returns the original
709 694 `qt.qApp.exec_loop` function that has been hijacked.
710 695 """
711 696 def dummy_mainloop(*args, **kw):
712 697 pass
713 698 import qt
714 699 orig_mainloop = qt.qApp.exec_loop
715 700 qt.qApp.exec_loop = dummy_mainloop
716 701 qt.QApplication.exec_loop = dummy_mainloop
717 702 return orig_mainloop
718 703
719 704 def hijack_qt4():
720 705 """Modifies PyQt4's mainloop with a dummy so user code does not
721 706 block IPython. This function returns the original
722 707 `QtGui.qApp.exec_` function that has been hijacked.
723 708 """
724 709 def dummy_mainloop(*args, **kw):
725 710 pass
726 711 from PyQt4 import QtGui, QtCore
727 712 orig_mainloop = QtGui.qApp.exec_
728 713 QtGui.qApp.exec_ = dummy_mainloop
729 714 QtGui.QApplication.exec_ = dummy_mainloop
730 715 QtCore.QCoreApplication.exec_ = dummy_mainloop
731 716 return orig_mainloop
732 717
733 718 #-----------------------------------------------------------------------------
734 719 # The IPShell* classes below are the ones meant to be run by external code as
735 720 # IPython instances. Note that unless a specific threading strategy is
736 721 # desired, the factory function start() below should be used instead (it
737 722 # selects the proper threaded class).
738 723
739 724 class IPThread(threading.Thread):
740 725 def run(self):
741 726 self.IP.mainloop(self._banner)
742 727 self.IP.kill()
743 728
744 729 class IPShellGTK(IPThread):
745 730 """Run a gtk mainloop() in a separate thread.
746 731
747 732 Python commands can be passed to the thread where they will be executed.
748 733 This is implemented by periodically checking for passed code using a
749 734 GTK timeout callback."""
750 735
751 736 TIMEOUT = 100 # Millisecond interval between timeouts.
752 737
753 738 def __init__(self,argv=None,user_ns=None,user_global_ns=None,
754 739 debug=1,shell_class=MTInteractiveShell):
755 740
756 741 import gtk
757 742
758 743 self.gtk = gtk
759 744 self.gtk_mainloop = hijack_gtk()
760 745
761 746 # Allows us to use both Tk and GTK.
762 747 self.tk = get_tk()
763 748
764 749 if gtk.pygtk_version >= (2,4,0): mainquit = self.gtk.main_quit
765 750 else: mainquit = self.gtk.mainquit
766 751
767 752 self.IP = make_IPython(argv,user_ns=user_ns,
768 753 user_global_ns=user_global_ns,
769 754 debug=debug,
770 755 shell_class=shell_class,
771 756 on_kill=[mainquit])
772 757
773 758 # HACK: slot for banner in self; it will be passed to the mainloop
774 759 # method only and .run() needs it. The actual value will be set by
775 760 # .mainloop().
776 761 self._banner = None
777 762
778 763 threading.Thread.__init__(self)
779 764
780 765 def mainloop(self,sys_exit=0,banner=None):
781 766
782 767 self._banner = banner
783 768
784 769 if self.gtk.pygtk_version >= (2,4,0):
785 770 import gobject
786 771 gobject.idle_add(self.on_timer)
787 772 else:
788 773 self.gtk.idle_add(self.on_timer)
789 774
790 775 if sys.platform != 'win32':
791 776 try:
792 777 if self.gtk.gtk_version[0] >= 2:
793 778 self.gtk.gdk.threads_init()
794 779 except AttributeError:
795 780 pass
796 781 except RuntimeError:
797 782 error('Your pyGTK likely has not been compiled with '
798 783 'threading support.\n'
799 784 'The exception printout is below.\n'
800 785 'You can either rebuild pyGTK with threads, or '
801 786 'try using \n'
802 787 'matplotlib with a different backend (like Tk or WX).\n'
803 788 'Note that matplotlib will most likely not work in its '
804 789 'current state!')
805 790 self.IP.InteractiveTB()
806 791
807 792 self.start()
808 793 self.gtk.gdk.threads_enter()
809 794 self.gtk_mainloop()
810 795 self.gtk.gdk.threads_leave()
811 796 self.join()
812 797
813 798 def on_timer(self):
814 799 """Called when GTK is idle.
815 800
816 801 Must return True always, otherwise GTK stops calling it"""
817 802
818 803 update_tk(self.tk)
819 804 self.IP.runcode()
820 805 time.sleep(0.01)
821 806 return True
822 807
823 808
824 809 class IPShellWX(IPThread):
825 810 """Run a wx mainloop() in a separate thread.
826 811
827 812 Python commands can be passed to the thread where they will be executed.
828 813 This is implemented by periodically checking for passed code using a
829 814 GTK timeout callback."""
830 815
831 816 TIMEOUT = 100 # Millisecond interval between timeouts.
832 817
833 818 def __init__(self,argv=None,user_ns=None,user_global_ns=None,
834 819 debug=1,shell_class=MTInteractiveShell):
835 820
836 821 self.IP = make_IPython(argv,user_ns=user_ns,
837 822 user_global_ns=user_global_ns,
838 823 debug=debug,
839 824 shell_class=shell_class,
840 825 on_kill=[self.wxexit])
841 826
842 827 wantedwxversion=self.IP.rc.wxversion
843 828 if wantedwxversion!="0":
844 829 try:
845 830 import wxversion
846 831 except ImportError:
847 832 error('The wxversion module is needed for WX version selection')
848 833 else:
849 834 try:
850 835 wxversion.select(wantedwxversion)
851 836 except:
852 837 self.IP.InteractiveTB()
853 838 error('Requested wxPython version %s could not be loaded' %
854 839 wantedwxversion)
855 840
856 841 import wx
857 842
858 843 threading.Thread.__init__(self)
859 844 self.wx = wx
860 845 self.wx_mainloop = hijack_wx()
861 846
862 847 # Allows us to use both Tk and GTK.
863 848 self.tk = get_tk()
864 849
865 850 # HACK: slot for banner in self; it will be passed to the mainloop
866 851 # method only and .run() needs it. The actual value will be set by
867 852 # .mainloop().
868 853 self._banner = None
869 854
870 855 self.app = None
871 856
872 857 def wxexit(self, *args):
873 858 if self.app is not None:
874 859 self.app.agent.timer.Stop()
875 860 self.app.ExitMainLoop()
876 861
877 862 def mainloop(self,sys_exit=0,banner=None):
878 863
879 864 self._banner = banner
880 865
881 866 self.start()
882 867
883 868 class TimerAgent(self.wx.MiniFrame):
884 869 wx = self.wx
885 870 IP = self.IP
886 871 tk = self.tk
887 872 def __init__(self, parent, interval):
888 873 style = self.wx.DEFAULT_FRAME_STYLE | self.wx.TINY_CAPTION_HORIZ
889 874 self.wx.MiniFrame.__init__(self, parent, -1, ' ', pos=(200, 200),
890 875 size=(100, 100),style=style)
891 876 self.Show(False)
892 877 self.interval = interval
893 878 self.timerId = self.wx.NewId()
894 879
895 880 def StartWork(self):
896 881 self.timer = self.wx.Timer(self, self.timerId)
897 882 self.wx.EVT_TIMER(self, self.timerId, self.OnTimer)
898 883 self.timer.Start(self.interval)
899 884
900 885 def OnTimer(self, event):
901 886 update_tk(self.tk)
902 887 self.IP.runcode()
903 888
904 889 class App(self.wx.App):
905 890 wx = self.wx
906 891 TIMEOUT = self.TIMEOUT
907 892 def OnInit(self):
908 893 'Create the main window and insert the custom frame'
909 894 self.agent = TimerAgent(None, self.TIMEOUT)
910 895 self.agent.Show(False)
911 896 self.agent.StartWork()
912 897 return True
913 898
914 899 self.app = App(redirect=False)
915 900 self.wx_mainloop(self.app)
916 901 self.join()
917 902
918 903
919 904 class IPShellQt(IPThread):
920 905 """Run a Qt event loop in a separate thread.
921 906
922 907 Python commands can be passed to the thread where they will be executed.
923 908 This is implemented by periodically checking for passed code using a
924 909 Qt timer / slot."""
925 910
926 911 TIMEOUT = 100 # Millisecond interval between timeouts.
927 912
928 913 def __init__(self, argv=None, user_ns=None, user_global_ns=None,
929 914 debug=0, shell_class=MTInteractiveShell):
930 915
931 916 import qt
932 917
933 918 self.exec_loop = hijack_qt()
934 919
935 920 # Allows us to use both Tk and QT.
936 921 self.tk = get_tk()
937 922
938 923 self.IP = make_IPython(argv,
939 924 user_ns=user_ns,
940 925 user_global_ns=user_global_ns,
941 926 debug=debug,
942 927 shell_class=shell_class,
943 928 on_kill=[qt.qApp.exit])
944 929
945 930 # HACK: slot for banner in self; it will be passed to the mainloop
946 931 # method only and .run() needs it. The actual value will be set by
947 932 # .mainloop().
948 933 self._banner = None
949 934
950 935 threading.Thread.__init__(self)
951 936
952 937 def mainloop(self, sys_exit=0, banner=None):
953 938
954 939 import qt
955 940
956 941 self._banner = banner
957 942
958 943 if qt.QApplication.startingUp():
959 944 a = qt.QApplication(sys.argv)
960 945
961 946 self.timer = qt.QTimer()
962 947 qt.QObject.connect(self.timer,
963 948 qt.SIGNAL('timeout()'),
964 949 self.on_timer)
965 950
966 951 self.start()
967 952 self.timer.start(self.TIMEOUT, True)
968 953 while True:
969 954 if self.IP._kill: break
970 955 self.exec_loop()
971 956 self.join()
972 957
973 958 def on_timer(self):
974 959 update_tk(self.tk)
975 960 result = self.IP.runcode()
976 961 self.timer.start(self.TIMEOUT, True)
977 962 return result
978 963
979 964
980 965 class IPShellQt4(IPThread):
981 966 """Run a Qt event loop in a separate thread.
982 967
983 968 Python commands can be passed to the thread where they will be executed.
984 969 This is implemented by periodically checking for passed code using a
985 970 Qt timer / slot."""
986 971
987 972 TIMEOUT = 100 # Millisecond interval between timeouts.
988 973
989 974 def __init__(self, argv=None, user_ns=None, user_global_ns=None,
990 975 debug=0, shell_class=MTInteractiveShell):
991 976
992 977 from PyQt4 import QtCore, QtGui
993 978
994 979 try:
995 980 # present in PyQt4-4.2.1 or later
996 981 QtCore.pyqtRemoveInputHook()
997 982 except AttributeError:
998 983 pass
999 984
1000 985 if QtCore.PYQT_VERSION_STR == '4.3':
1001 986 warn('''PyQt4 version 4.3 detected.
1002 987 If you experience repeated threading warnings, please update PyQt4.
1003 988 ''')
1004 989
1005 990 self.exec_ = hijack_qt4()
1006 991
1007 992 # Allows us to use both Tk and QT.
1008 993 self.tk = get_tk()
1009 994
1010 995 self.IP = make_IPython(argv,
1011 996 user_ns=user_ns,
1012 997 user_global_ns=user_global_ns,
1013 998 debug=debug,
1014 999 shell_class=shell_class,
1015 1000 on_kill=[QtGui.qApp.exit])
1016 1001
1017 1002 # HACK: slot for banner in self; it will be passed to the mainloop
1018 1003 # method only and .run() needs it. The actual value will be set by
1019 1004 # .mainloop().
1020 1005 self._banner = None
1021 1006
1022 1007 threading.Thread.__init__(self)
1023 1008
1024 1009 def mainloop(self, sys_exit=0, banner=None):
1025 1010
1026 1011 from PyQt4 import QtCore, QtGui
1027 1012
1028 1013 self._banner = banner
1029 1014
1030 1015 if QtGui.QApplication.startingUp():
1031 1016 a = QtGui.QApplication(sys.argv)
1032 1017
1033 1018 self.timer = QtCore.QTimer()
1034 1019 QtCore.QObject.connect(self.timer,
1035 1020 QtCore.SIGNAL('timeout()'),
1036 1021 self.on_timer)
1037 1022
1038 1023 self.start()
1039 1024 self.timer.start(self.TIMEOUT)
1040 1025 while True:
1041 1026 if self.IP._kill: break
1042 1027 self.exec_()
1043 1028 self.join()
1044 1029
1045 1030 def on_timer(self):
1046 1031 update_tk(self.tk)
1047 1032 result = self.IP.runcode()
1048 1033 self.timer.start(self.TIMEOUT)
1049 1034 return result
1050 1035
1051 1036
1052 1037 # A set of matplotlib public IPython shell classes, for single-threaded (Tk*
1053 1038 # and FLTK*) and multithreaded (GTK*, WX* and Qt*) backends to use.
1054 1039 def _load_pylab(user_ns):
1055 1040 """Allow users to disable pulling all of pylab into the top-level
1056 1041 namespace.
1057 1042
1058 1043 This little utility must be called AFTER the actual ipython instance is
1059 1044 running, since only then will the options file have been fully parsed."""
1060 1045
1061 1046 ip = IPython.ipapi.get()
1062 1047 if ip.options.pylab_import_all:
1063 1048 ip.ex("from matplotlib.pylab import *")
1064 1049 ip.IP.user_config_ns.update(ip.user_ns)
1065 1050
1066 1051
1067 1052 class IPShellMatplotlib(IPShell):
1068 1053 """Subclass IPShell with MatplotlibShell as the internal shell.
1069 1054
1070 1055 Single-threaded class, meant for the Tk* and FLTK* backends.
1071 1056
1072 1057 Having this on a separate class simplifies the external driver code."""
1073 1058
1074 1059 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
1075 1060 IPShell.__init__(self,argv,user_ns,user_global_ns,debug,
1076 1061 shell_class=MatplotlibShell)
1077 1062 _load_pylab(self.IP.user_ns)
1078 1063
1079 1064 class IPShellMatplotlibGTK(IPShellGTK):
1080 1065 """Subclass IPShellGTK with MatplotlibMTShell as the internal shell.
1081 1066
1082 1067 Multi-threaded class, meant for the GTK* backends."""
1083 1068
1084 1069 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
1085 1070 IPShellGTK.__init__(self,argv,user_ns,user_global_ns,debug,
1086 1071 shell_class=MatplotlibMTShell)
1087 1072 _load_pylab(self.IP.user_ns)
1088 1073
1089 1074 class IPShellMatplotlibWX(IPShellWX):
1090 1075 """Subclass IPShellWX with MatplotlibMTShell as the internal shell.
1091 1076
1092 1077 Multi-threaded class, meant for the WX* backends."""
1093 1078
1094 1079 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
1095 1080 IPShellWX.__init__(self,argv,user_ns,user_global_ns,debug,
1096 1081 shell_class=MatplotlibMTShell)
1097 1082 _load_pylab(self.IP.user_ns)
1098 1083
1099 1084 class IPShellMatplotlibQt(IPShellQt):
1100 1085 """Subclass IPShellQt with MatplotlibMTShell as the internal shell.
1101 1086
1102 1087 Multi-threaded class, meant for the Qt* backends."""
1103 1088
1104 1089 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
1105 1090 IPShellQt.__init__(self,argv,user_ns,user_global_ns,debug,
1106 1091 shell_class=MatplotlibMTShell)
1107 1092 _load_pylab(self.IP.user_ns)
1108 1093
1109 1094 class IPShellMatplotlibQt4(IPShellQt4):
1110 1095 """Subclass IPShellQt4 with MatplotlibMTShell as the internal shell.
1111 1096
1112 1097 Multi-threaded class, meant for the Qt4* backends."""
1113 1098
1114 1099 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
1115 1100 IPShellQt4.__init__(self,argv,user_ns,user_global_ns,debug,
1116 1101 shell_class=MatplotlibMTShell)
1117 1102 _load_pylab(self.IP.user_ns)
1118 1103
1119 1104 #-----------------------------------------------------------------------------
1120 1105 # Factory functions to actually start the proper thread-aware shell
1121 1106
1122 1107 def _select_shell(argv):
1123 1108 """Select a shell from the given argv vector.
1124 1109
1125 1110 This function implements the threading selection policy, allowing runtime
1126 1111 control of the threading mode, both for general users and for matplotlib.
1127 1112
1128 1113 Return:
1129 1114 Shell class to be instantiated for runtime operation.
1130 1115 """
1131 1116
1132 1117 global USE_TK
1133 1118
1134 1119 mpl_shell = {'gthread' : IPShellMatplotlibGTK,
1135 1120 'wthread' : IPShellMatplotlibWX,
1136 1121 'qthread' : IPShellMatplotlibQt,
1137 1122 'q4thread' : IPShellMatplotlibQt4,
1138 1123 'tkthread' : IPShellMatplotlib, # Tk is built-in
1139 1124 }
1140 1125
1141 1126 th_shell = {'gthread' : IPShellGTK,
1142 1127 'wthread' : IPShellWX,
1143 1128 'qthread' : IPShellQt,
1144 1129 'q4thread' : IPShellQt4,
1145 1130 'tkthread' : IPShell, # Tk is built-in
1146 1131 }
1147 1132
1148 1133 backends = {'gthread' : 'GTKAgg',
1149 1134 'wthread' : 'WXAgg',
1150 1135 'qthread' : 'QtAgg',
1151 1136 'q4thread' :'Qt4Agg',
1152 1137 'tkthread' :'TkAgg',
1153 1138 }
1154 1139
1155 1140 all_opts = set(['tk','pylab','gthread','qthread','q4thread','wthread',
1156 1141 'tkthread'])
1157 1142 user_opts = set([s.replace('-','') for s in argv[:3]])
1158 1143 special_opts = user_opts & all_opts
1159 1144
1160 1145 if 'tk' in special_opts:
1161 1146 USE_TK = True
1162 1147 special_opts.remove('tk')
1163 1148
1164 1149 if 'pylab' in special_opts:
1165 1150
1166 1151 try:
1167 1152 import matplotlib
1168 1153 except ImportError:
1169 1154 error('matplotlib could NOT be imported! Starting normal IPython.')
1170 1155 return IPShell
1171 1156
1172 1157 special_opts.remove('pylab')
1173 1158 # If there's any option left, it means the user wants to force the
1174 1159 # threading backend, else it's auto-selected from the rc file
1175 1160 if special_opts:
1176 1161 th_mode = special_opts.pop()
1177 1162 matplotlib.rcParams['backend'] = backends[th_mode]
1178 1163 else:
1179 1164 backend = matplotlib.rcParams['backend']
1180 1165 if backend.startswith('GTK'):
1181 1166 th_mode = 'gthread'
1182 1167 elif backend.startswith('WX'):
1183 1168 th_mode = 'wthread'
1184 1169 elif backend.startswith('Qt4'):
1185 1170 th_mode = 'q4thread'
1186 1171 elif backend.startswith('Qt'):
1187 1172 th_mode = 'qthread'
1188 1173 else:
1189 1174 # Any other backend, use plain Tk
1190 1175 th_mode = 'tkthread'
1191 1176
1192 1177 return mpl_shell[th_mode]
1193 1178 else:
1194 1179 # No pylab requested, just plain threads
1195 1180 try:
1196 1181 th_mode = special_opts.pop()
1197 1182 except KeyError:
1198 1183 th_mode = 'tkthread'
1199 1184 return th_shell[th_mode]
1200 1185
1201 1186
1202 1187 # This is the one which should be called by external code.
1203 1188 def start(user_ns = None):
1204 1189 """Return a running shell instance, dealing with threading options.
1205 1190
1206 1191 This is a factory function which will instantiate the proper IPython shell
1207 1192 based on the user's threading choice. Such a selector is needed because
1208 1193 different GUI toolkits require different thread handling details."""
1209 1194
1210 1195 shell = _select_shell(sys.argv)
1211 1196 return shell(user_ns = user_ns)
1212 1197
1213 1198 # Some aliases for backwards compatibility
1214 1199 IPythonShell = IPShell
1215 1200 IPythonShellEmbed = IPShellEmbed
1216 1201 #************************ End of file <Shell.py> ***************************
General Comments 0
You need to be logged in to leave comments. Login now