How can I stop a python dependency from exiting?

Viewed 26

I'm calling a 3rd party python library from within my code, which in response to an exceptional condition simply exits rather than raiseing an exception. I think the exit itself is not coming from within python itself but from a linked C file. using strace on linux definitely reveals that it is calling exit.

Unfortunately, this causes my entire program to exit. What I want is to be able to capture this "exit" event and raise an exception, then have my handling code make some changes to the parameters and call again. I'm looking at ways to capture an exit event, and so far I've seen atexit, which doesn't actually seem to allow you to continue execution, and also I could isolate the call in a subprocess to firewall myself from the exit event. This seems like too inelegant of a solution to me, so I'm wondering if anyone else can weigh in on what can be done here.

I haven't tried anything concrete at this point, I'm just looking for possible solutions to this problem.

1 Answers

As noted in the comments above, there isn't really a way to do this without creating a subprocess. In the end, I ended up (ab)using the python multiprocessing library to solve this problem. The solution looks like:

import multiprocessing as mp
import unsafe_module  # my external dependency that hard exits unexpectedly


def access_unsafe_module_safely(unsafe_args, pipe):
    unsafe_obj = unsafe_module.UnsafeClass()
    
    # the next line causes the process to exit when certain args are passed
    results = unsafe_obj.do_unsafe_thing(unsafe_args)
    
    # report the results using multiprocessing.Pipe
    pipe.send(results)


# unsafe_args are passed in from the user
def main(unsafe_args):
    (receive_end, send_end) = mp.Pipe(False)  # False gives non-duplex mode

    # the target callable is invoked with args, which must be iterable
    process = mp.Process(target=access_unsafe_module_safely, args=(unsafe_args, send_end))
    process.start()
    process.join()  # waits until the subprocess is complete

    # if you know your module's exit codes, you can be smarter here
    if process.exitcode != 0:  # generally signals error
        raise RuntimeError("something bad happened in unsafe_module")
    
    # gets the returned results from the subprocess
    results = receive_end.recv()

    # (Python 3.7+) cleans up the subprocess resources
    process.close()
    
    # continue on with results from here...

Unfortunately there isn't much of a point in going to the library maintainers; it's Python bindings for a scientific C/C++ application. exit makes perfect sense for their design case.

Related