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