"Fire and forget" a process from a Python script

Using asyncio you can write a simple decorator as @background

import asyncio
import time

def background(f):
    def wrapped(*args, **kwargs):
        return asyncio.get_event_loop().run_in_executor(None, f, *args, *kwargs)

    return wrapped

@background
def foo():
    time.sleep(1)
    print("foo() completed")

print("Hello")
foo()
print("I didn't wait for foo()")

Produces

>>> Hello
>>> I didn't wait for foo()
>>> foo() completed

Since you mentioned os.system, I think it's worth to mention that you should have used os.spawn* with mode P_NOWAIT to achieve the "forget" part.

But subprocess module provides replacements for os.system, os,spawn*,etc so you should use that instead like so

import subprocess
p = subprocess.Popen("./child.py")
print "pid = ", p.pid

See Replacing os.spawn with subprocess.Popen

As I explained in the comments both processes parent.py and child.py are still on the same process group and therefore the terminal will forward signals (like Ctrl-C) to all process in the foreground process group so both will get killed when you Ctrl-C. So if you don't want that you can force child.py to be in a new process group with the following:

#!/usr/bin/env python
import subprocess
import time
import os
p = subprocess.Popen("./child.py", preexec_fn=os.setsid)
print "pid = ", p.pid
time.sleep(30) # Ctrl-C at this point will not kill child.py
print "parent exit"

Answering my own question: I ended up simply using os.system with & at the end of command as suggested by @kevinsa. This allows the parent process to be terminated without the child being terminated.

Here's some code:

child.py

#!/usr/bin/python

import time
print "Child started"
time.sleep(10)
print "Child finished"

parent.py, using subprocess.Popen:

#!/usr/bin/python

import subprocess
import time

print "Parent started"
subprocess.Popen("./child.py")
print "(child started, sleeping)"

time.sleep(5)

print "Parent finished"

Output:

$ ./parent.py
Parent started
(child started, sleeping)
Child started
^CTraceback (most recent call last):
Traceback (most recent call last):
  File "./child.py", line 5, in <module>
  File "./parent.py", line 13, in <module>
        time.sleep(10)
time.sleep(5)
KeyboardInterrupt
KeyboardInterrupt
  • note how the child never finishes if the parent is interrupted with Ctrl-C

parent.py, using os.system and &

#!/usr/bin/python

import os
import time

print "Parent started"
os.system("./child.py &")
print "(child started, sleeping)"

time.sleep(5)

print "Parent finished"

Output:

$ ./parent.py
Parent started
(child started, sleeping)
Child started
^CTraceback (most recent call last):
  File "./parent.py", line 12, in <module>
    time.sleep(5)
KeyboardInterrupt

$ Child finished

Note how the child lives beyond the Ctrl-C.