g3cko
g3cko

Reputation: 920

How to debug an import statement changing behavior

I have a script that I was trying to add a signal handler for ctr-c, to do some final handling before exiting.

On it's own, it works fine but as soon as I try it in my program, it doesn't work. Adding or removing an import seems to change the behavior.

Without the P4API import, it works as I expect. If I do import P4API, ctr-c seems to call exit or bypass my handler, and I'm not sure why, or how to track what is changing.

signal handler source

import signal
import time
import sys

# -- with this commented out, things work 
#import P4API

def run_program():
    while True:
        time.sleep(1)
        print("a")

def exit_gracefully(signum, frame):
    # restore the original signal handler as otherwise evil things will happen
    # in raw_input when CTRL+C is pressed, and our signal handler is not re-entrant
    signal.signal(signal.SIGINT, original_sigint)

    try:
        if raw_input("\nReally quit? (y/n)> ").lower().startswith('y'):
            sys.exit(1)

    except KeyboardInterrupt:
        print("Ok ok, quitting")
        sys.exit(1)

    # restore the exit gracefully handler here
    signal.signal(signal.SIGINT, exit_gracefully)

if __name__ == '__main__':
    # store the original SIGINT handler
    original_sigint = signal.getsignal(signal.SIGINT)
    signal.signal(signal.SIGINT, exit_gracefully)
    run_program()

Upvotes: 3

Views: 94

Answers (1)

abarnert
abarnert

Reputation: 365707

According to the documentation, p4api has a class called Signaler that it uses for "interrupt handling":

The Signaler class enables the API programmer to register functions that are to be called when the client application receives an interrupt signal. The Signaler class maintains a list of registered functions and calls each one in turn.

By default, after all of the registered functions have been executed, the process exits, returning -1 to the operating system.

Presumably it installs this when you import p4api. So, your signal handler is interfering with its signal handler, and ultimately it's stealing the signal and calling _exit(-1) or similar before you get a chance to do anything useful.

You can find the details here, but it looks like Signaler::OnIntr is the C++ name of the function you want to call to register with its signal handler. There's example code in the docs, too. The first Google search result gives me this C++ source, which makes it pretty clear that's correct.

I don't see anything in the Python API docs, and I don't know if the Python API is a wrapper around the C++ API, or a separate implementation, but either way I'm guessing it has a class with a similar name that you have to use in the same way.

Upvotes: 2

Related Questions