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