question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Exception being ignored in __dealloc__ (and in C++ class destructor)

See original GitHub issue

Hi, I have a code that is like the following simplified test case:

cdef class RaiseOnDealloc:
    def __cinit__(self):
        print('Entered RaiseOnDealloc.__cinit__')

    def __dealloc__(self):
        print('Entered RaiseOnDealloc.__dealloc__')
        raise RuntimeError('Something bad')
        print('After raising')

def test_raise_on_dealloc():
    print('Entered test_raise_on_dealloc')
    cdef RaiseOnDealloc r = RaiseOnDealloc()
    print('Body of test_raise_on_dealloc')

the output of calling test_raise_on_dealloc() is this:

Entered test_raise_on_dealloc
Entered RaiseOnDealloc.__cinit__
Body of test_raise_on_dealloc
Entered RaiseOnDealloc.__dealloc__
Exception ignored in: 'RaiseOnDealloc.__dealloc__'
RuntimeError: Something bad

The RuntimeError is not being propagated by test_raise_on_dealloc. Is there a way for it to not be ignored? Unfortunately __dealloc__ is only valid as a def function, and it doesn’t accept except +.

Even if I move the class to C++ and declare it with cdef cppclass, it’s still not possible to propagate exceptions from its destructor, since there seems to be no way to declare destructors on cdef cppclass declarations.

Issue Analytics

  • State:closed
  • Created 7 years ago
  • Reactions:3
  • Comments:10 (1 by maintainers)

github_iconTop GitHub Comments

1reaction
da-woodscommented, Mar 9, 2021

Is there really anything to work around? Ultimately your options are:

  • Do nothing - it only prints an error message
  • Swallow the exception (e.g. put contents of __dealloc__ in a try-catch block)
  • Skip the destruction on shutting down (what your proposal does)
  • Actually fix the error that’s causing the exception to be raised

All of the first three options seem equally unsatisfactory to me. But ultimately it is only an warning message - it doesn’t break your program.

1reaction
da-woodscommented, Jan 11, 2021

As a general rule you shouldn’t be raising exceptions from destructors (either from C++ or Python). The basic problem is that destructors are often called while handling other exceptions and you can’t have two exceptions at once.

It isn’t completely clear from the documentation what the rules are for exceptions in tp_dealloc. tp_finalize (which we don’t yet support) definitely requires the exception state to be restored on exit.

So I’m not really convinced this is a bug. Swallowing the exception is definitely a sensible implementation choice (and probably the most sensible one)


https://docs.python.org/3/extending/newtypes.html#finalization-and-de-allocation:

One important requirement of the deallocator function is that it leaves any pending exceptions alone. This is important since deallocators are frequently called as the interpreter unwinds the Python stack; when the stack is unwound due to an exception (rather than normal returns), nothing is done to protect the deallocators from seeing that an exception has already been set. Any actions which a deallocator performs which may cause additional Python code to be executed may detect that an exception has been set. This can lead to misleading errors from the interpreter. The proper way to protect against this is to save a pending exception before performing the unsafe action, and restoring it when done. This can be done using the PyErr_Fetch() and PyErr_Restore() functions

That, to me, suggests you pretty much have to clear the exception state in the destructor.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Are destructors called after a throw in C++? - Stack Overflow
Yes, destructors are guaranteed to be called on stack unwinding, including unwinding due to exception being thrown. There are ...
Read more >
Exception Handling in C++ - Saylor Academy
The class Botch not only throws an exception inside f( ), but also in its destructor. This causes a call to terminate( ),...
Read more >
1: Exception handling
The problem with C++ is that longjmp( ) doesn't respect objects; in particular it doesn't call destructors when it jumps out of a...
Read more >
Finalize Instead Of Proper Destructor - C2 wiki
A better replacement is "with" in C#, but to make it safe, "with" should also ... Useful: pass 'abort/retry/ignore' continuations as part of...
Read more >
Exception specification in derived class - C++ Forum
It sounds like it is not aware of C++11. Nowadays it is assumed that the destructor never throws even if there is no...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found