2

I have a main python(testmain.py) script that executes another python script(test.py) using subprocess.Popen command. When I press Ctrl-C , I want the child to exit with exit code 2 and then the parent to display that exit code and then terminate .

I have signal handlers in both parent and child scripts.

testmain.py

def signal_handler(signal, frame):
    print "outer signal handler"
    exit(2)
signal.signal(signal.SIGINT, signal_handler) 

def execute()
proc=subprocess.Popen("python test.py",shell=True)
    streamdata=proc.communicate()[0]
    rc=proc.returncode
    print "return code:",rc

execute()

test.py

def signal_handler(signal, frame):
    print "exiting: inner function"
    exit(2)
signal.signal(signal.SIGINT, signal_handler)

I checked Delegate signal handling to a child process in python that is kind of similar to my question but in that case, the parent is continuing it's execution, which I don't want.

I want to: 1.exit test.py with exit(2) 2.print that exit code in testmain.py 3.exit test.py with exit(2)

could someone please provide suggestions to do this? Thanks.

UPDATE : Handling the signal only in the child (test.py) and checking the return code in parent(testmain.py) will do what I want .

if rc==2:
   print "child was terminated"
   exit(2)

but I was wondering if there is a clean way to do this using signal handling.

Community
  • 1
  • 1
trup
  • 309
  • 1
  • 4
  • 8
  • I think you'll have a problem on a linux system since it's setting the return regardless of your logic: https://docs.python.org/2/library/subprocess.html#subprocess.Popen.returncode (read last line). As a side note: this seems like a bad way to do something. – Reut Sharabani Sep 09 '15 at 20:09
  • @PadraicCunningham , I haven't passed the whole code. Basically, the child script will be executing bunch of Linux commands . And there will be several child scripts , each doing different set of commands. each of the child scripts will be executed in sequence by the main parent script. If user presses ctrl-C in between, it should be handled appropriately as I've mentioned. i.e the current child process should terminate with exit code 2 and the parent should display this code and stop. – trup Sep 09 '15 at 20:37
  • So you are running multiple scripts from `testmain.py` waiting for each to finish before clling the next? – Padraic Cunningham Sep 09 '15 at 20:50
  • yes @PadraicCunningham – trup Sep 09 '15 at 21:18
  • possible duplicate of [Delegate signal handling to a child process in python](http://stackoverflow.com/questions/24936107/delegate-signal-handling-to-a-child-process-in-python) – pilcrow Sep 09 '15 at 22:36

1 Answers1

1

Your child process shouldn't care what the parent does i.e., if you want the child to exit with specific status on Ctrl+C then just do that:

import sys

try:
    main()
except KeyboardInterrupt: # use default SIGINT handler
    sys.exit(2)

Or you could define the signal handler explicitly:

import os
import signal

def signal_handler(signal, frame):
    os.write(1, b"outer signal handler\n")
    os._exit(2)
signal.signal(signal.SIGINT, signal_handler)
main()

There might be a difference in behavior if there are atexit handlers and/or multiple threads.

Unrelated: depending on what your main() function does, there could be a significant delay before a signal is handled in Python. Some blocking methods on Python 2 may ignore the signal completely: use Python 3 or apply a custom workaround for a specific case e.g., using a timeout parameter for some calls.


You could handle SIGINT in a similar way in the parent:

for cmd in commands:
    process = Popen(cmd)
    try:
        process.wait()
    except KeyboardInterrupt:
        # child process may still be alive here
        for _ in range(5): # wait a while
           if process.poll() is not None:
               break # the process is dead
           time.sleep(.1)
        else: # no break, kill the process explicitly
           try:
               process.kill()    
           except OSError:
               pass
        sys.exit("Child exited with %d" % process.wait())

Python 2 doesn't restore signals for child processes e.g., if you SIG_IGN the SIGINT signal in the parent, you could reset the necessary hanlders explicitly using preexec_fn parameter.

Community
  • 1
  • 1
jfs
  • 399,953
  • 195
  • 994
  • 1,670
  • Would be cool to mention in this answer something about how signals are inherited by child processes (or not). – bennlich Aug 02 '17 at 21:17
  • @bennlich there would be no need to restore signals in preexec_fn if child processes were not inheriting them. Though there is no harm in mentioning it explicitly – jfs Aug 02 '17 at 21:29