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