Search code examples
pythonsignals

exit(1) in signal handler just gets caught as SystemExit as there is nothing to it


I have an application that looks like this:

while True:
    try:
        self.try_to_read_usb_device()
        break
    except:
        time.sleep(1)

I also have an SIGALRM handler which is supposed to exit the program in case it got stuck somewhere:

def alarm_signal_handler(signal, frame):
    # Something went wrong
    exit(1)

However the exit(1) just get caught by the try/except and gets discarded as this is what that specific except does.

This is quite unexpected to me.

In the complete application there will be a lot of try/except and I don't see myself adding

except SystemExit:
    exit(1)

or something for all of them.

Any idea how I should handle that use-case?


Solution

  • The dirty way

    You can use os._exit instead of sys.exit.

    Note that this has obvious drawbacks exactly because it won't go through an exception:

    Exit the process with status n, without calling cleanup handlers, flushing stdio buffers, etc.

    The proper way

    I'd recommend to instead change your exception handling to catch only things inheriting from Exception, because SystemExit doesn't inherit from Exception for precisely this reason, so it won't be caught accidentally:

    except Exception:
    

    See also the SystemExit documentation:

    This exception is raised by the sys.exit() function. It inherits from BaseException instead of Exception so that it is not accidentally caught by code that catches Exception. This allows the exception to properly propagate up and cause the interpreter to exit.

    This also applies to KeyboardInterrupt by the way - Ctrl+C will be caught by except: but not by except Exception:.

    It's illustrated pretty well in the exception hierarchy diagram in the Python docs, which you can find here.