##// END OF EJS Templates
Fix http://www.scipy.net/roundup/ipython/issue55, a threading bug. It was...
fperez -
Show More

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

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