Unable to reference an imported module in __del__()
Asked Answered
X

2

6

I'm using an object's __del__() to unsubscribe it from an event (using an event scheme similar to this):

import my_enviroment
class MyClass():
    def __del__(self):
        my_environment.events.my_event -= self.event_handler_func

Oddly I received the following error at the end of the program's run:

Exception AttributeError: "'NoneType' object has no attribute 'events'" in <bound method MyClass.__del__ of <myclass.MyClass instance at 0x04C54580>> ignored

How could this be possible?! my_environment is a module I imported, how come it could be None? (events is a global object in it with event hooks such as my_event)

Xavier answered 21/12, 2011 at 13:3 Comment(4)
How would this normally get called (other than at program termination)? Wouldn't the event handler hold a reference to the object which would prevent it from being garbage collected?Choli
@Choli - Some of the MyClass objects live throughout the run and some are short lived. I added this event cleanup for the later case.Xavier
My point is that this will never be called for the short-lived objects because the event handler holds a reference to them.Choli
@Choli - good catch ;) I've created a deadlock in term of destructing these objects...Xavier
U
7

According to the python doc about __del__ :

[...] other globals referenced by the __del__() method may already have been deleted or in the process of being torn down (e.g. the import machinery shutting down). For this reason, __del__() methods should do the absolute minimum needed to maintain external invariants.

In other words, when the __del__ method is called on your object, the my_enviroment may have been 'deleted' by python, so it can be None...

Utgardloki answered 21/12, 2011 at 13:10 Comment(0)
B
4

If you check Python's documentation, you will see that at when objects are terminated, at the end of the program, the "machinery" is already being disassembled. That means that much of what you'd rely on is already gone - in this case, the global variable with a module reference.

What you have to do is to either ensure your objects that needs to have __del__ called are destroyed before the program ends - better yet, make then context managers, with __enter__ and __exit__ methods, and use then within with statements. Or just wrap you clauses in __del__ within try-except blocks so that they don't raise exceptions - if the execution of the code in there is not critical when the program is ending.

Bricebriceno answered 21/12, 2011 at 13:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.