##// END OF EJS Templates
- I possibly cross-thread KeyboardInterrupt support for the multithreaded...
fperez -
Show More

The requested changes are too big and content was truncated. Show full diff

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