kevbonham
kevbonham

Reputation: 1040

How to kill a process reliably while testing?

I have a couple of different scripts that require opening a MongoDB instance that go something like this:

mongod = Popen(
        ["mongod", "--dbpath", '/path/to/db'],
    )

#Do some stuff

mongod.terminate()

And this works great when the code I'm executing works, but while I'm tinkering, errors inevitably arise. Then the Mongod instance remains running, and the next time I attempt to run the script, it detects that and doesn't open a new one.

I can terminate the process from the command line, but this is somewhat tedious. Or I can wrap everything in a try loop, but for some of the scripts, I have to do this a bunch, since every function depends on every other one. Is there a more elegant way to force close the process even in the event of an error somewhere else in the code?

EDIT: Did some testing based on tdelaney's comment, it looks like when I run these scripts in Sublime text and en error is generated, the script doesn't actually finish - it hits the error and then waits with the mongod instance open... i think. Once I kill the process in the terminal, sublime text tells me "finished in X seconds with exit code1"

EDIT2: On Kirby's suggestion, tried:

def testing():   
    mongod = Popen(
            ["mongod", "--dbpath", '/Users/KBLaptop/computation/db/'],
        )

    #Stuff that generates error

    mongod.terminate()

def cleanup():
    for proc in subprocess._active[:]:
        try: proc.terminate()
        except: pass

atexit.register(cleanup)
testing()

The error in testing() seems to prevent anything from continuing, so the atexit never registers and the process keeps running. Am I missing something obvious?

Upvotes: 3

Views: 152

Answers (1)

kirbyfan64sos
kirbyfan64sos

Reputation: 10727

If you're running under CPython, you can cheat and take advantage of Python's destructors:

class PopenWrapper(object):
    def __del__(self):
        if self._child_created:
            self.terminate()

This is slightly ucky, though. My preference would be to atexit:

import atexit
mongod = Popen(...)
def cleanup():
    for proc in subprocess._active[:]:
        try: proc.terminate()
        except: pass
atexit.register(cleanup)

Still slightly hack-ish, though.

EDIT: Try this:

from subprocess import Popen
import atexit

started = []

def auto_popen(*args, **kw):
    p = Popen(*args, **kw)
    started.append(p)
    return p

def testing():
    mongod = auto_popen(['blah blah'], shell=True)
    assert 0

    #Stuff that generates error

    mongod.terminate()

def cleanup():
    for proc in started:
        if proc.poll() is None:
            try: proc.kill()
           except: pass

atexit.register(cleanup)
testing()

Upvotes: 3

Related Questions