3

I want to run a bunch of commands that take a while, but cannot be interrupted (firmware updates). I want to exit if a sigint was received earlier. I tried replacing signal.SIG_IGN with a class that counts each time a SIGINT was received, but after class iterates its counter the SIGINT still would go through to the main Python script.

Just ignoring it works pretty easily:

import subprocess
import signal

def dont_interrupt_me():
    """Run bash command."""
    print "Keyboard interrupt ignored during update process."
    # Stops keyboard interrupts during the Popen
    sigint_stopper = signal.signal(signal.SIGINT, signal.SIG_IGN)
    bash_cmd = subprocess.Popen(['sleep', '10'])
    bash_cmd.wait()
    install_return_code = bash_cmd.returncode
    # Return signint to normal
    signal.signal(signal.SIGINT, sigint_stopper)
    # if ctrl_c_earlier:
    #    sys.exit(1)
    return install_return_code

for each in range(1,10):
    dont_interrupt_me()

My SigintIgnore class attempt not that doesn't work:

import subprocess
import signal

class SigintIgnore(object):
    """Count the number of sigint's during ignore phase."""
    def __init__(self):
        """Init count to 0."""
        self.count = 0
        self.exit_amount = 10
    def __call__(self, first, second):
        """Init count to 0."""
        self.count += 1
        print "\nself.count: " + str(self.count)
        print "first: " + str(first)
        print "second: " + str(second)
        if self.count > 1:
            print("Press 'ctrl + c' " +
                  str(self.exit_amount - self.count) +
                  " more times to force exit.")
        if self.count > self.exit_amount:
            sys.exit(EXIT_USER_CHOICE)


def dont_interrupt_me():
    """Run bash command."""
    counter = SigintIgnore()
    print "Keyboard interrupt ignored during update process."
    sigint_stopper = signal.signal(signal.SIGINT, counter)
    # Stops keyboard interrupts during the update calls
    bash_cmd = subprocess.Popen(['sleep', '10'])
    bash_cmd.wait()
    install_return_code = bash_cmd.returncode
    signal.signal(signal.SIGINT, sigint_stopper)
    if counter.count > 1:
        sys.exit(1)
    return install_return_code


for each in range(1,10):
    dont_interrupt_me()
TaylorSanchez
  • 463
  • 1
  • 5
  • 8
  • What specific problem are you having with your SigintIgnore class? – user2357112 Mar 14 '17 at 22:53
  • @user2357112: According to the text at the top of OP's question, it sounds like `KeyboardInterrupt` is still getting raised. – Kevin Mar 14 '17 at 22:54
  • @Kevin: That might be what they're trying to say, but it's hard to tell. There are other plausible readings. In any case, I don't think it'd be KeyboardInterrupt getting raised here; maybe IOError or something, but KeyboardInterrupt is unlikely. – user2357112 Mar 14 '17 at 23:00
  • 1
    What Python version are you on? [Signal handling behavior changed in 3.5](https://www.python.org/dev/peps/pep-0475/), although I don't know if the changes reached `subprocess.Popen.wait`. – user2357112 Mar 14 '17 at 23:01
  • Python 2.7. It doesn't seem like IOError, since I have no try except, yet it still exits without throwing anything. – TaylorSanchez Mar 15 '17 at 15:14
  • If I'm understanding user2357112's link above "Signal handling behavior changed in 3.5", the process running 'sleep 30' will receive the EINTR and close out while my class catches the SIGINT. I tested this a bit more, added some code into dont_interrupt_me() and saw that code would execute after the class was called even though I wouldn't sleep for the full 30 seconds. Additionally sleep 30 would exit with "-2" return code, so python thought it finished what it was doing. What I'm not following is, how SIG_IGN stops the EINTR. – TaylorSanchez Mar 15 '17 at 17:29

1 Answers1

0

Thanks to the comments and a bit more reading, I think I understand the problem. The SIG_IGN version will stop the signal from making it through at all. Where as the class will catch the SIGINT interrupt, but the interrupt will still make it to the sleep 30 / unix command? The unix command exits, but the python code ignores the interrupt. I am not 100% on this part, but the interrupt that kills the unix command may be a Interrupted system call, or EINTR, or IOError. So not necessarily the SIGINT itself, but a byproduct of the SIGINT?

I think these links may provide some additional details: What is the proper way to handle (in python) IOError: [Errno 4] Interrupted system call, raised by multiprocessing.Queue.get

https://www.python.org/dev/peps/pep-0475/

Community
  • 1
  • 1
TaylorSanchez
  • 463
  • 1
  • 5
  • 8