Skip to content

Backport PR #30209 on branch v3.10.x (Clean up Qt socket notifier to avoid spurious interrupt handler calls) #30234

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

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions lib/matplotlib/backend_bases.py
Original file line number Diff line number Diff line change
Expand Up @@ -1616,7 +1616,8 @@ def _allow_interrupt(prepare_notifier, handle_sigint):
If SIGINT was indeed caught, after exiting the on_signal() function the
interpreter reacts to the signal according to the handler function which
had been set up by a signal.signal() call; here, we arrange to call the
backend-specific *handle_sigint* function. Finally, we call the old SIGINT
backend-specific *handle_sigint* function, passing the notifier object
as returned by prepare_notifier(). Finally, we call the old SIGINT
handler with the same arguments that were given to our custom handler.

We do this only if the old handler for SIGINT was not None, which means
Expand All @@ -1626,7 +1627,7 @@ def _allow_interrupt(prepare_notifier, handle_sigint):
Parameters
----------
prepare_notifier : Callable[[socket.socket], object]
handle_sigint : Callable[[], object]
handle_sigint : Callable[[object], object]
"""

old_sigint_handler = signal.getsignal(signal.SIGINT)
Expand All @@ -1642,9 +1643,10 @@ def _allow_interrupt(prepare_notifier, handle_sigint):
notifier = prepare_notifier(rsock)

def save_args_and_handle_sigint(*args):
nonlocal handler_args
nonlocal handler_args, notifier
handler_args = args
handle_sigint()
handle_sigint(notifier)
notifier = None

signal.signal(signal.SIGINT, save_args_and_handle_sigint)
try:
Expand Down
11 changes: 8 additions & 3 deletions lib/matplotlib/backends/backend_qt.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,9 +169,14 @@ def _may_clear_sock():
# be forgiving about reading an empty socket.
pass

return sn # Actually keep the notifier alive.

def handle_sigint():
# We return the QSocketNotifier so that the caller holds a reference, and we
# also explicitly clean it up in handle_sigint(). Without doing both, deletion
# of the socket notifier can happen prematurely or not at all.
return sn

def handle_sigint(sn):
sn.deleteLater()
QtCore.QCoreApplication.sendPostedEvents(sn, QtCore.QEvent.Type.DeferredDelete)
if hasattr(qapp_or_eventloop, 'closeAllWindows'):
qapp_or_eventloop.closeAllWindows()
qapp_or_eventloop.quit()
Expand Down
4 changes: 2 additions & 2 deletions src/_macosx.m
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ static void lazy_init(void) {
}

static PyObject*
stop(PyObject* self)
stop(PyObject* self, PyObject* _ /* ignored */)
{
stopWithEvent();
Py_RETURN_NONE;
Expand Down Expand Up @@ -1863,7 +1863,7 @@ - (void)flagsChanged:(NSEvent *)event
"written on the file descriptor given as argument.")},
{"stop",
(PyCFunction)stop,
METH_NOARGS,
METH_VARARGS,
PyDoc_STR("Stop the NSApp.")},
{"show",
(PyCFunction)show,
Expand Down
Loading