Search code examples
pythonpython-3.xgarbage-collection

how to correctly handle objects deletion after an Exception


I'm trying to figure out how to correctly handle the deletion of an object when there is an error in a piece of code.

It seem that when there is an Exception raised the object is maintainded alive, while I need the object to be deleted. How do I release the object ?

with the following exemple code:

class A():
    def __init__(self) -> None:
        print(f"A[{id(self)}] created")
    def __del__(self):
        print(f"A[{id(self)}] deletted !!")

def foo(obj):
    raise ValueError

when I run foo(A()) I obtain:

A[1813071794504] created
[... traceback ...]
ValueError:

but I have no deletion

when I run a = A(); del a I obtain what I was expecting:

A[1813073658952] created
A[1813073658952] deletted !!

precision: I am running that code with py3.7 in the interactive window of VSCode (if that change anything)


Solution

  • This is because the exception keeps a traceback of the call stack frames and that can keep variables alive until the traceback itself is deleted. If you run from the python shell or other interactive tools, they likely keep the traceback alive for inspection until another exception occurs. Notice how calling foo(A()) again, causes the deletion as soon as the second exception happens.

    >>> foo(A())
    A[140015433609328] created
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 2, in foo
    ValueError
    >>> foo(A())
    A[140015432558352] created
    A[140015433609328] deletted !!
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 2, in foo
    ValueError
    >>> 
    

    If you add an exception handler and run from the command line you can see the deletion as the try/except clause ends.

    class A():
        def __init__(self) -> None:
            print(f"A[{id(self)}] created")
    
        def __del__(self):
            print(f"A[{id(self)}] deletted !!")
    
    def foo(obj):
        raise ValueError
    
    try:
        foo(A())
    except ValueError:
        print("handle exception")
    
    print("exiting")
    

    output

    A[139771605026992] created
    handle exception
    A[139771605026992] deletted !!
    exiting