0

We have some API that should be shut down (e.g. api.shutdown()) just once per python process and specific only to a particular class (e.g. ControllerA) from a hierarchy of controllers (e.g. Controller inherited by ControllerA, ..., ControllerZ). Can I add a "destructor logic" in python in some reasonable way when destroying the class itself and not just any of its instances? I understand that in Python classes are not explicitly destroyed in the same way as objects but rather garbage collected when there are no existing references to them but perhaps there is some way to achieve the above effect? What I want is to perform the api.shutdown() call once for all instances but not explicitly as it should not be done for instances of ControllerB ..., ControllerZ. Could using metaclasses or something like metaclass destructors achieve anything like it?

2 Answers 2

0

You could use a shutdown hook with atexit. Inside the class definition, add the appropriate function for atexit.

However, note that it is hard to design your software around when such "cleanup" methods, like atexit or __del__, will be called.

It can be a better design to have an explicit lifecycle in your application, so that at shutdown, a list of registered callback functions is called.

Sign up to request clarification or add additional context in comments.

1 Comment

While this could definitely handle some cases and therefore could be a viable answer, in our case unfortunately it is not going to work - the python process will not exit or trigger any exit procedure as it waits for the api.shutdown() that we have to call first. So essentially this solution is turning into a chicken-or-egg problem.
0

You don't need metaclasses for that - just a classmethod.

metaclasses actually cant even help - since their __del__ method won't be called ever: any created class in Python is kept forever while the process is alive. (probably it should be possible to track all the references down and erase them, but it is not done by default with the class simply going out of scope)

Just have a class level registry - a list as a class attribute - and append all crrated instances there. this could be done in __init__ but also in a metaclass __call__ method, if you'd really use a metaclass.

and any code now could just go through this registry and close whatever is needed in all instances. There could be a classmethod to do it, called from your shutdown itself:

class Base:
    def __init__(self):
        cls = type(self)
        if not "_instances" in cls.__dict__: 
            # check cls.__dict__ instead of using "hasattr" as we want
            # one registry in each subclass. 
            # "hasattr" would return `True` for any superclass having a "._instances" attribute.
            cls._instances = set()
        cls._instances.add(self)

    @classmethod
    def shutdown(cls):
         for instance in cls._instances:
             instance.close()
 
    def close(self):
        raise NotImplementedError()

...
class ControllerA(Base):
    ...
    def close(self):
       # example:
       try:
           self.connection.close()
       except Exception as error:
           logger.info(f"Error closing connection: {error}") 
           # maybe it is already closed, etc...
           # so we just make some noise to ensure it is visible, if needed. 

9 Comments

I need to do the call api.shutdown() only once per python process and not once for each instance.
The code above works when the call is made once per class. If you know which classes you want to shutdown when you close the API, that is your code.
I find it strange that you accept the answer recommending the use of atexit as you explicit mention that other classes than the one of interest should not be closed. atexit hooks are called just at interpreter shutdown, so no other classes will possibly continue to keep anything open.
In your reply I should call Base.shutdown() at exactly once at the end of a program while my preference was to not have to call anything and find a natural destructor-like way for the cleanup to happen implicitly when it is time without such an explicit call. Note that if one accepts having the explicit call, we could just call a base class method close once at the end of the program (which will do api,shutdown() for a particular sublcass or nothing for the rest) without tracking any instances (something like type(controller).close().
It is each time harder to understand what you want. You should include some code snippet to indicate how do you want things to work. I think my answer is extenseviley clear, as are any Python docs on __del__ that it is not reliable (and for the class case, it won't even work not even occasionally). If you want lifecycle events without having to call some explicit .close method, you have to implement the "Context manager protocol" by having your objects have the __enter__ and __exit__ methods, and use a with statement block.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.