After plt.pause() is called, ctrl-C stops working (outside plt.pause() itself). To reproduce: ``` %matplotlib import matplotlib import matplotlib.pyplot as plt plt.rcParams["figure.figsize"]=(10, 7) plt.rcParams["figure.dpi"]=144 plt.rcParams["figure.facecolor"]="white" plt.ion() ``` ↓ ``` plt.plot([1, 2, 3],[5, 4, 6]) ``` ↓ while ensuring the plot is displayed, do the following ``` plt.pause(3) ``` ↓ wait for it to finish, then ``` sleep(10) ``` try to ctrl-C it, it won't work. -------- The reason is the following. Cysignals contains ``` # Set the Python-level interrupt handler. When a SIGINT occurs, # this will not be called directly. Instead, a SIGINT is caught by # our interrupt handler, set up in implementation.c. If it happens # during pure Python code (not within sig_on()/sig_off()), the # handler will set Python's interrupt flag. Python regularly checks # this and will call its interrupt handler (which is the one we set # now). This handler issues a sig_check() which finally raises the # KeyboardInterrupt exception. import signal old = signal.signal(signal.SIGINT, python_check_interrupt) setup_alt_stack() setup_cysignals_handlers() ``` In other words, `python_check_interrupt` is set to be the Python-level signal handler, however then the *actual* signal handler is something set in C ``` /* Install signal handlers */ /* Handlers for interrupt-like signals */ sa.sa_handler = cysigs_interrupt_handler; sa.sa_flags = 0; #ifdef SIGHUP if (sigaction(SIGHUP, &sa, NULL)) {perror("cysignals sigaction"); exit(1);} #endif if (sigaction(SIGINT, &sa, NULL)) {perror("cysignals sigaction"); exit(1);} #ifdef SIGALRM if (sigaction(SIGALRM, &sa, NULL)) {perror("cysignals sigaction"); exit(1);} #endif ``` that causes a problem, because matplotlib temporarily sets the signal handler to its own then revert it later. Equivalently the following also breaks ctrl-C even though it should be a no-op: ``` import signal from signal import SIGINT signal.signal(SIGINT, signal.getsignal(SIGINT)) ``` the function returned is Python function `python_check_interrupt`. After `signal.signal(SIGINT, signal.getsignal(SIGINT))` (which is supposed to be a no-op), now on ctrl-C, only `python_check_interrupt` is called *without* actually setting the value of `interrupt_received`: ``` /* Handler for SIGHUP, SIGINT, SIGALRM, SIGTERM * * Inside sig_on() (i.e. when cysigs.sig_on_count is positive), this * raises an exception and jumps back to sig_on(). * Outside of sig_on(), we set Python's interrupt flag using * PyErr_SetInterrupt() */ static void cysigs_interrupt_handler(int sig) { […] cysigs.interrupt_received = sig; custom_set_pending_signal(sig); ``` Relevant explanation: ``` # Set the Python-level interrupt handler. When a SIGINT occurs, # this will not be called directly. Instead, a SIGINT is caught by # our interrupt handler, set up in implementation.c. If it happens # during pure Python code (not within sig_on()/sig_off()), the # handler will set Python's interrupt flag. Python regularly checks # this and will call its interrupt handler (which is the one we set # now). This handler issues a sig_check() which finally raises the # KeyboardInterrupt exception. import signal old = signal.signal(signal.SIGINT, python_check_interrupt) ``` tl;dr the Python-level interrupt handler actually need to set the signal, as the fallback. Probably. ------ Some investigation: there is this thing ``` def init_cysignals(): """ Initialize ``cysignals``. This is normally done exactly once, namely when importing ``cysignals``. However, it is legal to call this multiple times, for example when switching between the ``cysignals`` interrupt handler and a different interrupt handler. OUTPUT: the old Python-level interrupt handler ``` I think a workaround for now is just to call that function again.