How to stop SIGINT being passed to subprocess in python?
Asked Answered
F

3

16

My python script intercepts the SIGINT signal with the signal process module to prevent premature exit, but this signal is passed to a subprocess that I open with Popen. is there some way to prevent passing this signal to the subprocess so that it also is not exited prematurely when the user presses ctrl-c?

Fizzy answered 12/7, 2010 at 21:57 Comment(1)
Does this answer your question? Run a program from python, and have it continue to run after the script is killedVick
S
2

You are able to re-assign the role of ctrl-c using the tty module, which allows you to manipulate the assignment of signals. Be warned, however, that unless you put them back the way they were before you modified them, they will persist for the shell's entire session, even after the program exits.

Here is a simple code snippet to get you started that stores your old tty settings, re-assigns ctrl-c to ctrl-x, and then restores your previous tty settings upon exit.

import sys
import tty

# Back up previous tty settings
stdin_fileno = sys.stdin.fileno()
old_ttyattr = tty.tcgetattr(stdin_fileno)

try:
    print 'Reassigning ctrl-c to ctrl-x'

    # Enter raw mode on local tty
    tty.setraw(stdin_fileno)
    raw_ta = tty.tcgetattr(stdin_fileno)
    raw_ta[tty.LFLAG] |= tty.ISIG
    raw_ta[tty.OFLAG] |= tty.OPOST | tty.ONLCR

    # ^X is the new ^C, set this to 0 to disable it entirely
    raw_ta[tty.CC][tty.VINTR] = '\x18'  

    # Set raw tty as active tty
    tty.tcsetattr(stdin_fileno, tty.TCSANOW, raw_ta)

    # Dummy program loop
    import time
    for _ in range(5):
        print 'doing stuff'
        time.sleep(1)

finally:
    print 'Resetting ctrl-c'
    # Restore previous tty no matter what
    tty.tcsetattr(stdin_fileno, tty.TCSANOW, old_ttyattr)
Saxena answered 12/7, 2010 at 22:52 Comment(0)
Y
16

Signal handlers are inherited when you start a subprocess, so if you use the signal module to ignore SIGINT (signal.signal(signal.SIGINT, signal.SIG_IGN)), then your child process automatically will also.

There are two important caveats, though:

  • You have to set the ignore handler before you spawn the child process
  • Custom signal handlers are reset to the default handlers, since the child process won't have access to the handler code to run it.

So if you need to customise your handling of SIGINT rather than just ignoring it, you probably want to temporarily ignore SIGINT while you spawn your child process, then (re)set your custom signal handler.

If you're trying to catch SIGINT and set a flag so you can exit at a safe point rather than immediately, remember that when you get to that safe point your code will have to manually clean up its descendants, since your child process and any processes it starts will be ignoring the SIGINT.

Yoakum answered 17/9, 2010 at 0:42 Comment(4)
If this works (which it won't always), it is a better option than mucking with the terminal.Guss
If using fork()/exec() instead of popen(), you can do the SIG_IGN after fork() before exec(). That avoids temporarily disabling the handler in the parent. Seems like there are going to be race conditions no matter what you do, but the fork()/exec() way seems a little safer. Of course, the OP was asking about python's Popen, but this question really seems to be about Posix; that's why I got here.Novanovaculite
@thejoshwolfe: To avoid race conditions, you use sigprocmask() to defer signals, set the handler, fork, set the handler back, and then use sigprocmask again to re-enable signals. (And if any signals arrived in between the calls to sigprocmask, they will be delivered at this point.)Fargone
Note that SIGINT might still be received by child processes of the child processes (e.g. apt-get started as subprocess in python might spawn dpkg or gpgv). In this case one needs to create a new process group by passing preexec_fn=os.setpgrp to subprocess.Popen and handle everything (including manual signal forwarding to subprocesses) in the parent signal handler.Ky
S
2

You are able to re-assign the role of ctrl-c using the tty module, which allows you to manipulate the assignment of signals. Be warned, however, that unless you put them back the way they were before you modified them, they will persist for the shell's entire session, even after the program exits.

Here is a simple code snippet to get you started that stores your old tty settings, re-assigns ctrl-c to ctrl-x, and then restores your previous tty settings upon exit.

import sys
import tty

# Back up previous tty settings
stdin_fileno = sys.stdin.fileno()
old_ttyattr = tty.tcgetattr(stdin_fileno)

try:
    print 'Reassigning ctrl-c to ctrl-x'

    # Enter raw mode on local tty
    tty.setraw(stdin_fileno)
    raw_ta = tty.tcgetattr(stdin_fileno)
    raw_ta[tty.LFLAG] |= tty.ISIG
    raw_ta[tty.OFLAG] |= tty.OPOST | tty.ONLCR

    # ^X is the new ^C, set this to 0 to disable it entirely
    raw_ta[tty.CC][tty.VINTR] = '\x18'  

    # Set raw tty as active tty
    tty.tcsetattr(stdin_fileno, tty.TCSANOW, raw_ta)

    # Dummy program loop
    import time
    for _ in range(5):
        print 'doing stuff'
        time.sleep(1)

finally:
    print 'Resetting ctrl-c'
    # Restore previous tty no matter what
    tty.tcsetattr(stdin_fileno, tty.TCSANOW, old_ttyattr)
Saxena answered 12/7, 2010 at 22:52 Comment(0)
P
0

For python 2 codebase: subprocess is broken.

The right thing is

import subprocess32 as subprocess

See subprocess32

This is a backport of the Python 3 subprocess module for use on Python 2. This code has not been tested on Windows or other non-POSIX platforms.

Pilaf answered 11/1, 2021 at 11:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.