Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

inputhook: disable CTRL+C when a hook is active.

On systems with 'readline', it's very likely to intercept a signal
during a select() call. The default SIGINT handler will schedule a
KeyboardInterrupt exception to be raised as soon as possible. If
ctypes is used to install a Python callback for PyOS_InputHook, this
will happen as soon as the bytecode execution starts, so even if the
first instruction of the callback is a
`try: ... except KeyboardInterrupt` clause, it's actually too late.

As ctypes doesn't allow a Python callback to raise an exception, this
ends up with IPython detecting an internal error... not pretty.  We
must therefore ignore the SIGINT signals until we are sure the
exception handler is active, in the Python callback.
  • Loading branch information...
commit 0fc80df31e0b11aafbcca408310345e3f0305f97 1 parent 4826692
@cboos authored
Showing with 34 additions and 2 deletions.
  1. +30 −0 IPython/lib/inputhook.py
  2. +4 −2 IPython/lib/inputhookqt4.py
View
30 IPython/lib/inputhook.py
@@ -51,14 +51,38 @@ def _stdin_ready_other():
"""Return True, assuming there's something to read on stdin."""
return True #
+
+def _ignore_CTRL_C_posix():
+ """Ignore CTRL+C (SIGINT)."""
+ signal.signal(signal.SIGINT, signal.SIG_IGN)
+
+def _allow_CTRL_C_posix():
+ """Take CTRL+C into account (SIGINT)."""
+ signal.signal(signal.SIGINT, signal.default_int_handler)
+
+def _ignore_CTRL_C_other():
+ """Ignore CTRL+C (not implemented)."""
+ pass
+
+def _allow_CTRL_C_other():
+ """Take CTRL+C into account (not implemented)."""
+ pass
+
if os.name == 'posix':
import select
+ import signal
stdin_ready = _stdin_ready_posix
+ ignore_CTRL_C = _ignore_CTRL_C_posix
+ allow_CTRL_C = _allow_CTRL_C_posix
elif os.name == 'nt':
import msvcrt
stdin_ready = _stdin_ready_nt
+ ignore_CTRL_C = _ignore_CTRL_C_other
+ allow_CTRL_C = _allow_CTRL_C_other
else:
stdin_ready = _stdin_ready_other
+ ignore_CTRL_C = _ignore_CTRL_C_other
+ allow_CTRL_C = _allow_CTRL_C_other
#-----------------------------------------------------------------------------
@@ -94,6 +118,11 @@ def get_pyos_inputhook_as_func(self):
def set_inputhook(self, callback):
"""Set PyOS_InputHook to callback and return the previous one."""
+ # On platforms with 'readline' support, it's all too likely to
+ # have a KeyboardInterrupt signal delivered *even before* an
+ # initial ``try:`` clause in the callback can be executed, so
+ # we need to disable CTRL+C in this situation.
+ ignore_CTRL_C()
self._callback = callback
self._callback_pyfunctype = self.PYFUNC(callback)
pyos_inputhook_ptr = self.get_pyos_inputhook()
@@ -117,6 +146,7 @@ def clear_inputhook(self, app=None):
pyos_inputhook_ptr = self.get_pyos_inputhook()
original = self.get_pyos_inputhook_as_func()
pyos_inputhook_ptr.value = ctypes.c_void_p(None).value
+ allow_CTRL_C()
self._reset()
return original
View
6 IPython/lib/inputhookqt4.py
@@ -17,7 +17,7 @@
#-----------------------------------------------------------------------------
from IPython.external.qt_for_kernel import QtCore, QtGui
-from IPython.lib.inputhook import stdin_ready
+from IPython.lib.inputhook import allow_CTRL_C, ignore_CTRL_C, stdin_ready
#-----------------------------------------------------------------------------
# Code
@@ -78,6 +78,7 @@ def inputhook_qt4():
back to a clean prompt line.
"""
try:
+ allow_CTRL_C()
app = QtCore.QCoreApplication.instance()
app.processEvents(QtCore.QEventLoop.AllEvents, 300)
if not stdin_ready():
@@ -88,13 +89,14 @@ def inputhook_qt4():
app.exec_()
timer.stop()
except KeyboardInterrupt:
+ ignore_CTRL_C()
got_kbdint[0] = True
- mgr.clear_inputhook()
print("\nKeyboardInterrupt - qt4 event loop interrupted!"
"\n * hit CTRL+C again to clear the prompt"
"\n * use '%gui none' to disable the event loop"
" permanently"
"\n and '%gui qt4' to re-enable it later")
+ mgr.clear_inputhook()
return 0
def preprompthook_qt4(ishell):
Please sign in to comment.
Something went wrong with that request. Please try again.