Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Workaround so only one CTRL-C is required for a new prompt in --gui=qt #3219

Merged
merged 6 commits into from Apr 29, 2013
Merged
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
20 changes: 19 additions & 1 deletion IPython/lib/inputhookqt4.py
Expand Up @@ -16,6 +16,11 @@
# Imports
#-----------------------------------------------------------------------------

import os
import signal
import time
import threading

from IPython.core.interactiveshell import InteractiveShell
from IPython.external.qt_for_kernel import QtCore, QtGui
from IPython.lib.inputhook import allow_CTRL_C, ignore_CTRL_C, stdin_ready
Expand Down Expand Up @@ -116,8 +121,21 @@ def inputhook_qt4():
except KeyboardInterrupt:
ignore_CTRL_C()
got_kbdint[0] = True
print("\nKeyboardInterrupt - Ctrl-C again for new prompt")
mgr.clear_inputhook()
if(os.name == 'posix'):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a short descriptive comment before this if that indicates what the intent is and why it's done with os.kill and a timer. It will help anyone who in the future has to revisit this code.

pid = os.getpid()
print("^C")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for this print statement here, as it doesn't match how the shell behaves in other cases:

In [1]: asldfh a
KeyboardInterrupt

In [1]: pylab qt
[elided]

In [2]: asdlkhjfa^C

KeyboardInterrupt

That means that if we ever add an explicit ^C print in the general handlers, we'd get it doubled-up. I think it's just best to leave it without, so that such things come from a single place and the behavior is uniform.


timer = threading.Timer( .01,
os.kill,
args=[pid, signal.SIGINT]
)

timer.start()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what would happen if you did join here - I've never understood how this particular bit works, where does the code need to be for this to be properly handled?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way ipython seem to integrate with qt eventloop is by registering a callback that gets executed periodically when Python is waiting for input. The callback runs the qt eventloop for 50 ms so all the mouse/windowing stuff gets handled. The problem is you can't raise exceptions in this callback, so CTRL-C won't work at all.

The original author got around this by catching the SIGINT, and the disabling the callback (which means the next CTRL-C will be caught by python proper and not the callback).

I tried to get around that by making it send a ctrl-c to itself after the first ctrl-c, but this SIGINT has to arrive after it exits the inputhook callback, hence the need for threads/timers.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assuming that the join I mentioned above is not appropriate, let's keep track of the timer instance, and cancel (Timer.cancel()) the previous one before starting the next, just so we can be sure we don't have more than one waiting, however unlikely that may be with 10ms timeout.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great, thanks for the explanation - I figured it was something like that, I just wanted to understand.

else:
print("\nKeyboardInterrupt - Ctrl-C again for new prompt")


except: # NO exceptions are allowed to escape from a ctypes callback
ignore_CTRL_C()
from traceback import print_exc
Expand Down