Show More
@@ -4,7 +4,7 b'' | |||
|
4 | 4 | All the matplotlib support code was co-developed with John Hunter, |
|
5 | 5 | matplotlib's author. |
|
6 | 6 | |
|
7 |
$Id: Shell.py 216 |
|
|
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> |
@@ -18,15 +18,26 b" __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 |
@@ -35,12 +46,19 b' 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 |
@@ -241,20 +259,70 b' class IPShellEmbed:' | |||
|
241 | 259 | self.exit_msg = exit_msg |
|
242 | 260 | |
|
243 | 261 | #----------------------------------------------------------------------------- |
|
244 | def sigint_handler (signum,stack_frame): | |
|
245 | """Sigint handler for threaded apps. | |
|
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).""" | |
|
246 | 287 | |
|
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).""" | |
|
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: | |
|
298 | def sigint_handler (signum,stack_frame): | |
|
299 | """Sigint handler for threaded apps. | |
|
300 | ||
|
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).""" | |
|
306 | ||
|
307 | global KBINT | |
|
308 | ||
|
309 | print '\nKeyboardInterrupt - Press <Enter> to continue.', | |
|
310 | Term.cout.flush() | |
|
311 | # Set global flag so that runsource can know that Ctrl-C was hit | |
|
312 | KBINT = True | |
|
251 | 313 | |
|
252 | global KBINT | |
|
253 | ||
|
254 | print '\nKeyboardInterrupt - Press <Enter> to continue.', | |
|
255 | Term.cout.flush() | |
|
256 | # Set global flag so that runsource can know that Ctrl-C was hit | |
|
257 | KBINT = True | |
|
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 | |
|
258 | 326 | |
|
259 | 327 | class MTInteractiveShell(InteractiveShell): |
|
260 | 328 | """Simple multi-threaded shell.""" |
@@ -338,17 +406,16 b' class MTInteractiveShell(InteractiveShell):' | |||
|
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() |
@@ -356,6 +423,15 b' class MTInteractiveShell(InteractiveShell):' | |||
|
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: |
@@ -372,6 +448,9 b' class MTInteractiveShell(InteractiveShell):' | |||
|
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 | |
@@ -597,7 +676,16 b' def hijack_gtk():' | |||
|
597 | 676 | # desired, the factory function start() below should be used instead (it |
|
598 | 677 | # selects the proper threaded class). |
|
599 | 678 | |
|
600 |
class IP |
|
|
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. |
@@ -633,10 +721,6 b' class IPShellGTK(threading.Thread):' | |||
|
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 |
@@ -680,7 +764,8 b' class IPShellGTK(threading.Thread):' | |||
|
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. |
@@ -721,7 +806,6 b' class IPShellWX(threading.Thread):' | |||
|
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(). |
@@ -734,10 +818,6 b' class IPShellWX(threading.Thread):' | |||
|
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 |
@@ -774,13 +854,14 b' class IPShellWX(threading.Thread):' | |||
|
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( |
|
|
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. |
@@ -825,10 +906,6 b' class IPShellQt(threading.Thread):' | |||
|
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 |
@@ -854,7 +931,7 b' class IPShellQt(threading.Thread):' | |||
|
854 | 931 | return result |
|
855 | 932 | |
|
856 | 933 | |
|
857 |
class IPShellQt4( |
|
|
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. |
@@ -899,10 +976,6 b' class IPShellQt4(threading.Thread):' | |||
|
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 |
@@ -1,3 +1,24 b'' | |||
|
1 | 2007-04-04 Fernando Perez <Fernando.Perez@colorado.edu> | |
|
2 | ||
|
3 | * IPython/Shell.py (sigint_handler): I *THINK* I finally got | |
|
4 | asynchronous exceptions working, i.e., Ctrl-C can actually | |
|
5 | interrupt long-running code in the multithreaded shells. | |
|
6 | ||
|
7 | This is using Tomer Filiba's great ctypes-based trick: | |
|
8 | http://sebulba.wikispaces.com/recipe+thread2. I'd already tried | |
|
9 | this in the past, but hadn't been able to make it work before. So | |
|
10 | far it looks like it's actually running, but this needs more | |
|
11 | testing. If it really works, I'll be *very* happy, and we'll owe | |
|
12 | a huge thank you to Tomer. My current implementation is ugly, | |
|
13 | hackish and uses nasty globals, but I don't want to try and clean | |
|
14 | anything up until we know if it actually works. | |
|
15 | ||
|
16 | NOTE: this feature needs ctypes to work. ctypes is included in | |
|
17 | Python2.5, but 2.4 users will need to manually install it. This | |
|
18 | feature makes multi-threaded shells so much more usable that it's | |
|
19 | a minor price to pay (ctypes is very easy to install, already a | |
|
20 | requirement for win32 and available in major linux distros). | |
|
21 | ||
|
1 | 22 | 2007-04-04 Ville Vainio <vivainio@gmail.com> |
|
2 | 23 | |
|
3 | 24 | * Extensions/ipy_completers.py, ipy_stock_completers.py: |
General Comments 0
You need to be logged in to leave comments.
Login now