Skip to content

Commit ef00dde

Browse files
committed
[mypyc] Fix object finalization
1 parent e852829 commit ef00dde

File tree

1 file changed

+13
-18
lines changed

1 file changed

+13
-18
lines changed

mypyc/codegen/emitclass.py

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -842,10 +842,15 @@ def generate_dealloc_for_class(
842842
emitter.emit_line("static void")
843843
emitter.emit_line(f"{dealloc_func_name}({cl.struct_name(emitter.names)} *self)")
844844
emitter.emit_line("{")
845+
emitter.emit_line("int finalize_error = 0;")
846+
emitter.emit_line("PyObject *type, *value, *traceback;")
847+
emitter.emit_line("PyErr_Fetch(&type, &value, &traceback);")
845848
if has_tp_finalize:
846849
emitter.emit_line("if (!PyObject_GC_IsFinalized((PyObject *)self)) {")
847-
emitter.emit_line("Py_TYPE(self)->tp_finalize((PyObject *)self);")
850+
emitter.emit_line("PyObject_CallFinalizerFromDealloc((PyObject *)self);")
848851
emitter.emit_line("}")
852+
emitter.emit_line("if (PyErr_Occurred() != NULL) finalize_error = 1;")
853+
emitter.emit_line("PyErr_Restore(type, value, traceback);")
849854
emitter.emit_line("PyObject_GC_UnTrack(self);")
850855
if cl.reuse_freed_instance:
851856
emit_reuse_dealloc(cl, emitter)
@@ -854,6 +859,13 @@ def generate_dealloc_for_class(
854859
emitter.emit_line(f"{clear_func_name}(self);")
855860
emitter.emit_line("Py_TYPE(self)->tp_free((PyObject *)self);")
856861
emitter.emit_line("CPy_TRASHCAN_END(self)")
862+
# # CPython interpreter uses PyErr_WriteUnraisable: https://docs.python.org/3/c-api/exceptions.html#c.PyErr_WriteUnraisable
863+
# # However, the message is slightly different due to the way mypyc compiles classes.
864+
# # CPython interpreter prints: Exception ignored in: <function F.__del__ at 0x100aed940>
865+
# # mypyc prints: Exception ignored in: <slot wrapper '__del__' of 'F' objects>
866+
emitter.emit_line("if (finalize_error == 1) {")
867+
emitter.emit_line("PyErr_WriteUnraisable((PyObject *)self);")
868+
emitter.emit_line("}")
857869
emitter.emit_line("}")
858870

859871

@@ -884,30 +896,13 @@ def generate_finalize_for_class(
884896
emitter.emit_line("static void")
885897
emitter.emit_line(f"{finalize_func_name}(PyObject *self)")
886898
emitter.emit_line("{")
887-
emitter.emit_line("PyObject *type, *value, *traceback;")
888-
emitter.emit_line("PyErr_Fetch(&type, &value, &traceback);")
889899
emitter.emit_line(
890900
"{}{}{}(self);".format(
891901
emitter.get_group_prefix(del_method.decl),
892902
NATIVE_PREFIX,
893903
del_method.cname(emitter.names),
894904
)
895905
)
896-
emitter.emit_line("if (PyErr_Occurred() != NULL) {")
897-
emitter.emit_line('PyObject *del_str = PyUnicode_FromString("__del__");')
898-
emitter.emit_line(
899-
"PyObject *del_method = (del_str == NULL) ? NULL : _PyType_Lookup(Py_TYPE(self), del_str);"
900-
)
901-
# CPython interpreter uses PyErr_WriteUnraisable: https://docs.python.org/3/c-api/exceptions.html#c.PyErr_WriteUnraisable
902-
# However, the message is slightly different due to the way mypyc compiles classes.
903-
# CPython interpreter prints: Exception ignored in: <function F.__del__ at 0x100aed940>
904-
# mypyc prints: Exception ignored in: <slot wrapper '__del__' of 'F' objects>
905-
emitter.emit_line("PyErr_WriteUnraisable(del_method);")
906-
emitter.emit_line("Py_XDECREF(del_method);")
907-
emitter.emit_line("Py_XDECREF(del_str);")
908-
emitter.emit_line("}")
909-
# PyErr_Restore also clears exception raised in __del__.
910-
emitter.emit_line("PyErr_Restore(type, value, traceback);")
911906
emitter.emit_line("}")
912907

913908

0 commit comments

Comments
 (0)