From 727d90759383941dd32ce11c46fd256b1e128098 Mon Sep 17 00:00:00 2001 From: Jim Crist-Harif Date: Wed, 4 Oct 2023 22:47:47 -0500 Subject: [PATCH] Fix module init for Python 3.12 Python 3.12 changed module init slightly (this may actually be a CPython 3.12 bug, it's not 100% clear). Before Python 3.12 the module wouldn't be available via `PyState_FindModule` until it was fully initialized. Now the module is immediately available, but in an invalid state leading to segfaults. The workaround is to manually call `PyState_AddModule` to ensure the module is always available. In the long run we should move to using multiphase module init which should avoid this problem entirely. --- msgspec/_core.c | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/msgspec/_core.c b/msgspec/_core.c index 27ae54da..1582695f 100644 --- a/msgspec/_core.c +++ b/msgspec/_core.c @@ -6043,16 +6043,11 @@ StructMeta_new_inner( /* Fill in struct offsets */ if (structmeta_construct_offsets(&info, cls) < 0) goto cleanup; - /* Cache access to __post_init__ (if defined). - * XXX: When StructMeta is defined, the module hasn't finished initializing - * yet so `mod` will be NULL here. */ - if (mod != NULL) { - cls->post_init = PyObject_GetAttr((PyObject *)cls, mod->str___post_init__); + /* Cache access to __post_init__ (if defined). */ + cls->post_init = PyObject_GetAttr((PyObject *)cls, mod->str___post_init__); + if (cls->post_init == NULL) { PyErr_Clear(); } - else { - cls->post_init = NULL; - } cls->nkwonly = info.nkwonly; cls->n_trailing_defaults = info.n_trailing_defaults; @@ -21067,17 +21062,6 @@ PyInit__core(void) if (PyModule_AddObject(m, "UNSET", UNSET) < 0) return NULL; - /* Initialize the Struct Type */ - st->StructType = PyObject_CallFunction( - (PyObject *)&StructMetaType, "s(O){ssss}", "Struct", &StructMixinType, - "__module__", "msgspec", "__doc__", Struct__doc__ - ); - if (st->StructType == NULL) - return NULL; - Py_INCREF(st->StructType); - if (PyModule_AddObject(m, "Struct", st->StructType) < 0) - return NULL; - /* Initialize the exceptions. */ st->MsgspecError = PyErr_NewExceptionWithDoc( "msgspec.MsgspecError", @@ -21245,5 +21229,15 @@ PyInit__core(void) CACHED_STRING(str_int, "int"); CACHED_STRING(str_is_safe, "is_safe"); + /* Initialize the Struct Type */ + PyState_AddModule(m, &msgspecmodule); + st->StructType = PyObject_CallFunction( + (PyObject *)&StructMetaType, "s(O){ssss}", "Struct", &StructMixinType, + "__module__", "msgspec", "__doc__", Struct__doc__ + ); + if (st->StructType == NULL) return NULL; + Py_INCREF(st->StructType); + if (PyModule_AddObject(m, "Struct", st->StructType) < 0) return NULL; + return m; }