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
Answers:
You can try using atexit
import atexit
def stufftodowhenpythonquits():
# do some stuff
atexit.register(stufftodowhenpythonquits)
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. 🙂
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
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
You can try using atexit
import atexit
def stufftodowhenpythonquits():
# do some stuff
atexit.register(stufftodowhenpythonquits)
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. 🙂
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