Determine if python is exiting

Question:

Is there a way to determine if python is shutting down?

Basically:

def Foo(object):
  def __del__(self):
    if PYTHON_IS_EXITING:
      do_this
    else:
      do_that

foo1 = Foo()
del foo1 # calls do_that
foo2 # calls do_this when python exits

The context is multiprocessing.ThreadPool doesn’t work when python is exiting and do_that will execute in parallel while do_this will execute in series.

Thanks

Asked By: Brian

||

Answers:

You can try using atexit

import atexit

def stufftodowhenpythonquits():
    # do some stuff

atexit.register(stufftodowhenpythonquits)
Answered By: Adam Smith

Building on what Adam Smith said…

If you arrange for atexit to tear down your object, the chances are good that you don’t need to do anything different. Because when atexit runs its registered functions, the ThreadPool you wanted to do something with will not have been torn down yet. So the atexit-registered function can (probably) do exactly what the destructor would have done if called before exit.

But wait, there’s more.

Consider the following slightly ill advised attempt to deal with object tear down before exit vs at exit:

#!/usr/bin/python3

import atexit

class Foo:
    def __init__(self):
        self.dead = False
        atexit.register(self.atexit)

    def __del__(self):
        print("%s: in destructor, self.dead is %s"%(id(self), self.dead))
        if not self.dead: self.atexit()

    def atexit(self):
        print("%s: in atexit"%id(self))
        self.dead = True
        atexit.unregister(self.atexit)
        # Do whatever end-of-life processing you need here.  Whether
        # we got here from our destructor or the atexit modules
        # processing, whatever resources we need should (probably)
        # still be available.

foo1 = Foo()
foo2 = Foo()

print("foo1: %s, foo2: %s"%(id(foo1), id(foo2)))
del foo1

If you run this, you’ll see that both objects have already had their atexit() method called by the time the destructor is invoked. That’s because, since the object’s atexit() method is registered with the atexit module, the atexit module is holding a reference to the object. So even after the del, the object lingers until exit.

That may be okay, if you don’t have a great need for quick garbage collection. (In which case, you can just get rid of the self.dead flag and the destructor. Because the destructor will never be called before atexit-invoked object tear down anyway, so there will always be nothing left to do when the destructor is called.)

If you do have a need for the object to go away before exit — well, working that out is left as an exercise for the reader. 🙂

Answered By: Phil

Building on what @Phil said:

If you do have a need for the object to go away before exit

Then this is the textbook solution:

import atexit


PYTHON_EXITING = False


def _exit():
    print("SETTING PYTHON AT EXIT")
    global PYTHON_EXITING
    PYTHON_EXITING = True


atexit.register(_exit)


class DummyThread:
    def __init__(self, name) -> None:
        self.name = name

    def __del__(self):
        if PYTHON_EXITING:
            print("PYTHON AT EXIT")
        print("__del__", self.name)


foo1 = DummyThread("1")
foo2 = DummyThread("2")

del foo1

Output:

__del__ 1
SETTING PYTHON AT EXIT
PYTHON AT EXIT
__del__ 2
Answered By: root-11
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.