Skip to content

Commit 097efaf

Browse files
committed
Fixed handling of exceptions raised in __del__ methods.
1 parent 6cf1116 commit 097efaf

File tree

2 files changed

+31
-2
lines changed

2 files changed

+31
-2
lines changed

mypyc/codegen/emitclass.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -819,9 +819,19 @@ def generate_finalize_for_class(
819819
del_method.cname(emitter.names),
820820
)
821821
)
822-
emitter.emit_line("if (type != NULL) {")
823-
emitter.emit_line("PyErr_Restore(type, value, traceback);")
822+
emitter.emit_line("if (PyErr_Occurred() != NULL) {")
823+
emitter.emit_line('PyObject *class_obj = PyObject_GetAttrString(self, "__class__");')
824+
emitter.emit_line('PyObject *del_method = PyObject_GetAttrString(class_obj, "__del__");')
825+
# CPython interpreter uses PyErr_WriteUnraisable: https://docs.python.org/3/c-api/exceptions.html#c.PyErr_WriteUnraisable
826+
# However, the message is slightly different due to the way mypyc compiles classes.
827+
# CPython interpreter prints: Exception ignored in: <function F.__del__ at 0x100aed940>
828+
# mypyc prints: Exception ignored in: <slot wrapper '__del__' of 'F' objects>
829+
emitter.emit_line("PyErr_WriteUnraisable(del_method);")
830+
emitter.emit_line("Py_DECREF(class_obj);")
831+
emitter.emit_line("Py_XDECREF(del_method);")
824832
emitter.emit_line("}")
833+
# PyErr_Restore also clears exception raised in __del__.
834+
emitter.emit_line("PyErr_Restore(type, value, traceback);")
825835
emitter.emit_line("}")
826836

827837

mypyc/test-data/run-classes.test

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2781,3 +2781,22 @@ gc.collect()
27812781
[out]
27822782
deleting C1...
27832783
deleting C2...
2784+
2785+
[case testDelException]
2786+
# The error message in the expected output of this test does not match CPython's error message due to the way mypyc compiles Python classes. If the error message is fixed, the expected output of this test will also change.
2787+
class F:
2788+
def __del__(self):
2789+
if True:
2790+
raise Exception("e2")
2791+
2792+
[file driver.py]
2793+
import native
2794+
f = native.F()
2795+
del f
2796+
2797+
[out]
2798+
Exception ignored in: <slot wrapper '__del__' of 'F' objects>
2799+
Traceback (most recent call last):
2800+
File "native.py", line 5, in __del__
2801+
raise Exception("e2")
2802+
Exception: e2

0 commit comments

Comments
 (0)