diff --git a/IPython/shellglobals.py b/IPython/shellglobals.py new file mode 100644 index 0000000..7b69bec --- /dev/null +++ b/IPython/shellglobals.py @@ -0,0 +1,82 @@ +from IPython.genutils import Term,warn,error,flag_calls, ask_yes_no + +import thread,inspect + +try: + import ctypes + HAS_CTYPES = True +except ImportError: + HAS_CTYPES = False + + +# Globals +# global flag to pass around information about Ctrl-C without exceptions +KBINT = False + +# global flag to turn on/off Tk support. +USE_TK = False + +# ID for the main thread, used for cross-thread exceptions +MAIN_THREAD_ID = thread.get_ident() + +# Tag when runcode() is active, for exception handling +CODE_RUN = None + + +#----------------------------------------------------------------------------- +# This class is trivial now, but I want to have it in to publish a clean +# interface. Later when the internals are reorganized, code that uses this +# shouldn't have to change. + + +if HAS_CTYPES: + # Add async exception support. Trick taken from: + # http://sebulba.wikispaces.com/recipe+thread2 + def _async_raise(tid, exctype): + """raises the exception, performs cleanup if needed""" + if not inspect.isclass(exctype): + raise TypeError("Only types can be raised (not instances)") + res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, + ctypes.py_object(exctype)) + if res == 0: + raise ValueError("invalid thread id") + elif res != 1: + # """if it returns a number greater than one, you're in trouble, + # and you should call it again with exc=NULL to revert the effect""" + ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, 0) + raise SystemError("PyThreadState_SetAsyncExc failed") + + def sigint_handler (signum,stack_frame): + """Sigint handler for threaded apps. + + This is a horrible hack to pass information about SIGINT _without_ + using exceptions, since I haven't been able to properly manage + cross-thread exceptions in GTK/WX. In fact, I don't think it can be + done (or at least that's my understanding from a c.l.py thread where + this was discussed).""" + + global KBINT + + if CODE_RUN: + _async_raise(MAIN_THREAD_ID,KeyboardInterrupt) + else: + KBINT = True + print '\nKeyboardInterrupt - Press to continue.', + Term.cout.flush() + +else: + def sigint_handler (signum,stack_frame): + """Sigint handler for threaded apps. + + This is a horrible hack to pass information about SIGINT _without_ + using exceptions, since I haven't been able to properly manage + cross-thread exceptions in GTK/WX. In fact, I don't think it can be + done (or at least that's my understanding from a c.l.py thread where + this was discussed).""" + + global KBINT + + print '\nKeyboardInterrupt - Press to continue.', + Term.cout.flush() + # Set global flag so that runsource can know that Ctrl-C was hit + KBINT = True diff --git a/IPython/twshell.py b/IPython/twshell.py index f6dc6f1..ad30ba2 100644 --- a/IPython/twshell.py +++ b/IPython/twshell.py @@ -12,10 +12,10 @@ from IPython.ipmaker import make_IPython from IPython.iplib import InteractiveShell from IPython.ipstruct import Struct import Queue,thread,threading,signal - -from IPython.Shell import * - from signal import signal, SIGINT +from IPython.genutils import Term,warn,error,flag_calls, ask_yes_no +import shellglobals + def hijack_reactor(): """Modifies Twisted's reactor with a dummy so user code does @@ -81,12 +81,9 @@ class TwistedInteractiveShell(InteractiveShell): Modified version of code.py's runsource(), to handle threading issues. See the original for full docstring details.""" - print "rs" - global KBINT - # If Ctrl-C was typed, we reset the flag and return right away - if KBINT: - KBINT = False + if shellglobals.KBINT: + shellglobals.KBINT = False return False if self._kill: @@ -113,7 +110,6 @@ class TwistedInteractiveShell(InteractiveShell): InteractiveShell.runcode(self,code) return - self.first_run = False # Case 3 # Store code in queue, so the execution thread can handle it. @@ -130,6 +126,7 @@ class TwistedInteractiveShell(InteractiveShell): print "switching to nonthreaded mode (until mainloop wakes up again)" self.worker_ident = None else: + shellglobals.CURRENT_COMPLETE_EV = completed_ev completed_ev.wait() return False @@ -139,7 +136,6 @@ class TwistedInteractiveShell(InteractiveShell): Multithreaded wrapper around IPython's runcode().""" - global CODE_RUN # we are in worker thread, stash out the id for runsource() self.worker_ident = thread.get_ident() @@ -154,10 +150,11 @@ class TwistedInteractiveShell(InteractiveShell): self._kill.set() return True - # Install sigint handler. We do it every time to ensure that if user + # Install SIGINT handler. We do it every time to ensure that if user # code modifies it, we restore our own handling. try: - signal(SIGINT,sigint_handler) + pass + signal(SIGINT,shellglobals.sigint_handler) except SystemError: # This happens under Windows, which seems to have all sorts # of problems with signal handling. Oh well... @@ -176,13 +173,13 @@ class TwistedInteractiveShell(InteractiveShell): # Exceptions need to be raised differently depending on which # thread is active. This convoluted try/except is only there to - # protect against asynchronous exceptions, to ensure that a KBINT + # protect against asynchronous exceptions, to ensure that a shellglobals.KBINT # at the wrong time doesn't deadlock everything. The global # CODE_TO_RUN is set to true/false as close as possible to the # runcode() call, so that the KBINT handler is correctly informed. try: try: - CODE_RUN = True + shellglobals.CODE_RUN = True InteractiveShell.runcode(self,code_to_run) except KeyboardInterrupt: print "Keyboard interrupted in mainloop" @@ -190,7 +187,7 @@ class TwistedInteractiveShell(InteractiveShell): code = self.code_queue.get_nowait() break finally: - CODE_RUN = False + shellglobals.CODE_RUN = False # allow runsource() return from wait completed_ev.set()