Description: TODO: Put a short summary on the line above and replace this paragraph with a longer explanation of this change. Complete the meta-information with other relevant fields (see below for details). To make it easier, the information below has been extracted from the changelog. Adjust it or drop it. . pygobject (3.28.2-1.1) begnac; urgency=medium . * Non-maintainer upload. * Correct _ossignalheper. Author: Itaï BEN YAACOV --- The information above should follow the Patch Tagging Guidelines, please checkout http://dep.debian.net/deps/dep3/ to learn about the format. Here are templates for supplementary fields that you might want to add: Origin: , Bug: Bug-Debian: https://bugs.debian.org/ Bug-Ubuntu: https://launchpad.net/bugs/ Forwarded: Reviewed-By: Last-Update: 2018-05-10 --- pygobject-3.28.2.orig/gi/_ossighelper.py +++ pygobject-3.28.2/gi/_ossighelper.py @@ -155,41 +155,27 @@ PyOS_getsig = pydll.PyOS_getsig PyOS_getsig.restype = ctypes.c_void_p PyOS_getsig.argtypes = [ctypes.c_int] -# We save the signal pointer so we can detect if glib has changed the -# signal handler behind Python's back (GLib.unix_signal_add) -if signal.getsignal(signal.SIGINT) is signal.default_int_handler: - startup_sigint_ptr = PyOS_getsig(signal.SIGINT) -else: - # Something has set the handler before import, we can't get a ptr - # for the default handler so make sure the pointer will never match. - startup_sigint_ptr = -1 - -def sigint_handler_is_default(): - """Returns if on SIGINT the default Python handler would be called""" - - return (signal.getsignal(signal.SIGINT) is signal.default_int_handler and - PyOS_getsig(signal.SIGINT) == startup_sigint_ptr) +_callback_stack = [signal.default_int_handler] +_sigint_called = False +_sigint_ptr = -1 -@contextmanager -def sigint_handler_set_and_restore_default(handler): - """Context manager for saving/restoring the SIGINT handler default state. +def sigint_handler(sig_num, frame): + global _callback_stack, _sigint_called, _sigint_ptr - Will only restore the default handler again if the handler is not changed - while the context is active. - """ + # Only execute if no changes to the SIGINT handler + if PyOS_getsig(signal.SIGINT) == _sigint_ptr: + _sigint_called = True + _callback_stack.pop()() + else: + signal.default_int_handler() - assert sigint_handler_is_default() - signal.signal(signal.SIGINT, handler) - sig_ptr = PyOS_getsig(signal.SIGINT) - try: - yield - finally: - if signal.getsignal(signal.SIGINT) is handler and \ - PyOS_getsig(signal.SIGINT) == sig_ptr: - signal.signal(signal.SIGINT, signal.default_int_handler) +# Only install over the default handler +if signal.getsignal(signal.SIGINT) is signal.default_int_handler: + signal.signal(signal.SIGINT, sigint_handler) + _sigint_ptr = PyOS_getsig(signal.SIGINT) def is_main_thread(): @@ -198,66 +184,40 @@ def is_main_thread(): return threading.current_thread().name == "MainThread" -_callback_stack = [] -_sigint_called = False - - @contextmanager def register_sigint_fallback(callback): - """Installs a SIGINT signal handler in case the default Python one is - active which calls 'callback' in case the signal occurs. + """Adds a callback to the default SIGINT handler stack. Only does something if called from the main thread. - In case of nested context managers the signal handler will be only - installed once and the callbacks will be called in the reverse order - of their registration. - - The old signal handler will be restored in case no signal handler is - registered while the context is active. + In case of nested context managers the callbacks will be + called in the reverse order of their registration. """ # To handle multiple levels of event loops we need to call the last - # callback first, wait until the inner most event loop returns control + # callback first, wait until the innermost event loop returns control # and only then call the next callback, and so on... until we # reach the outer most which manages the signal handler and raises # in the end global _callback_stack, _sigint_called + assert not _sigint_called + if not is_main_thread(): yield return - if not sigint_handler_is_default(): - if _callback_stack: - # This is an inner event loop, append our callback - # to the stack so the parent context can call it. - _callback_stack.append(callback) - try: - yield - finally: - if _sigint_called: - _callback_stack.pop()() - else: - # There is a signal handler set by the user, just do nothing - yield - return - - _sigint_called = False - - def sigint_handler(sig_num, frame): - global _callback_stack, _sigint_called - - if _sigint_called: - return - _sigint_called = True - _callback_stack.pop()() - _callback_stack.append(callback) - with sigint_handler_set_and_restore_default(sigint_handler): - try: - yield - finally: - if _sigint_called: - signal.default_int_handler() + try: + yield + finally: + # If _sigint_called is True, then this is the callback of the outer context + # If _sigint_called is False, then this is the callback of the current context + next_callback = _callback_stack.pop() + assert _sigint_called or callback == next_callback + if _sigint_called: + if not _callback_stack: # Handling is over, revert to initial setting + _callback_stack = [signal.default_int_handler] + _sigint_called = False + next_callback()