diff --git a/Include/internal/pycore_global_objects_fini_generated.h b/Include/internal/pycore_global_objects_fini_generated.h index 416448a74385b0..630730fe525f76 100644 --- a/Include/internal/pycore_global_objects_fini_generated.h +++ b/Include/internal/pycore_global_objects_fini_generated.h @@ -250,6 +250,7 @@ _PyStaticObjects_CheckAll(PyInterpreterState *interp) { _PyStaticObject_CheckUnicodeSingleton((PyObject *)&_Py_ID(_asyncio_future_blocking), "_asyncio_future_blocking", 24); _PyStaticObject_CheckUnicodeSingleton((PyObject *)&_Py_ID(_blksize), "_blksize", 8); _PyStaticObject_CheckUnicodeSingleton((PyObject *)&_Py_ID(_bootstrap), "_bootstrap", 10); + _PyStaticObject_CheckUnicodeSingleton((PyObject *)&_Py_ID(_bootstrap_external), "_bootstrap_external", 19); _PyStaticObject_CheckUnicodeSingleton((PyObject *)&_Py_ID(_check_retval_), "_check_retval_", 14); _PyStaticObject_CheckUnicodeSingleton((PyObject *)&_Py_ID(_dealloc_warn), "_dealloc_warn", 13); _PyStaticObject_CheckUnicodeSingleton((PyObject *)&_Py_ID(_feature_version), "_feature_version", 16); diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h index 7a6082035f5c97..321015ac7bce09 100644 --- a/Include/internal/pycore_global_strings.h +++ b/Include/internal/pycore_global_strings.h @@ -262,6 +262,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(_asyncio_future_blocking) STRUCT_FOR_ID(_blksize) STRUCT_FOR_ID(_bootstrap) + STRUCT_FOR_ID(_bootstrap_external) STRUCT_FOR_ID(_check_retval_) STRUCT_FOR_ID(_dealloc_warn) STRUCT_FOR_ID(_feature_version) diff --git a/Include/internal/pycore_importdl.h b/Include/internal/pycore_importdl.h index 9ed87a544234c5..e89686d4ae5884 100644 --- a/Include/internal/pycore_importdl.h +++ b/Include/internal/pycore_importdl.h @@ -51,9 +51,9 @@ typedef enum ext_module_kind { } _Py_ext_module_kind; typedef enum ext_module_origin { - _Py_ext_module_origin_CORE = 1, - _Py_ext_module_origin_BUILTIN = 2, - _Py_ext_module_origin_DYNAMIC = 3, + _Py_ext_module_origin_CORE = 1, // 'sys' & 'builtins' + _Py_ext_module_origin_BUILTIN = 2, // inittab or PyImport_CreateModuleFromInitfunc + _Py_ext_module_origin_DYNAMIC = 3, // loaded dynamically } _Py_ext_module_origin; struct hook_prefixes { @@ -79,21 +79,10 @@ struct _Py_ext_module_loader_info { extern void _Py_ext_module_loader_info_clear( struct _Py_ext_module_loader_info *info); extern int _Py_ext_module_loader_info_init( - struct _Py_ext_module_loader_info *info, + struct _Py_ext_module_loader_info *p_info, PyObject *name, - PyObject *filename, + PyObject *spec, _Py_ext_module_origin origin); -extern int _Py_ext_module_loader_info_init_for_core( - struct _Py_ext_module_loader_info *p_info, - PyObject *name); -extern int _Py_ext_module_loader_info_init_for_builtin( - struct _Py_ext_module_loader_info *p_info, - PyObject *name); -#ifdef HAVE_DYNAMIC_LOADING -extern int _Py_ext_module_loader_info_init_from_spec( - struct _Py_ext_module_loader_info *info, - PyObject *spec); -#endif /* The result from running an extension module's init function. * Not used for modules defined via PyModExport (slots array). diff --git a/Include/internal/pycore_runtime_init_generated.h b/Include/internal/pycore_runtime_init_generated.h index e9fce38ac8c4ac..69eb481472f2bc 100644 --- a/Include/internal/pycore_runtime_init_generated.h +++ b/Include/internal/pycore_runtime_init_generated.h @@ -1537,6 +1537,7 @@ extern "C" { INIT_ID(_asyncio_future_blocking), \ INIT_ID(_blksize), \ INIT_ID(_bootstrap), \ + INIT_ID(_bootstrap_external), \ INIT_ID(_check_retval_), \ INIT_ID(_dealloc_warn), \ INIT_ID(_feature_version), \ diff --git a/Include/internal/pycore_unicodeobject_generated.h b/Include/internal/pycore_unicodeobject_generated.h index ac814a8de84c3d..80473d20a42da3 100644 --- a/Include/internal/pycore_unicodeobject_generated.h +++ b/Include/internal/pycore_unicodeobject_generated.h @@ -214,6 +214,7 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { offsetof(struct _Py_global_strings, identifiers._py__asyncio_future_blocking._ascii.ob_base), offsetof(struct _Py_global_strings, identifiers._py__blksize._ascii.ob_base), offsetof(struct _Py_global_strings, identifiers._py__bootstrap._ascii.ob_base), + offsetof(struct _Py_global_strings, identifiers._py__bootstrap_external._ascii.ob_base), offsetof(struct _Py_global_strings, identifiers._py__check_retval_._ascii.ob_base), offsetof(struct _Py_global_strings, identifiers._py__dealloc_warn._ascii.ob_base), offsetof(struct _Py_global_strings, identifiers._py__feature_version._ascii.ob_base), diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py index 8ef2bcc3249b8b..6c51b79bc44a0f 100644 --- a/Lib/test/test_embed.py +++ b/Lib/test/test_embed.py @@ -293,6 +293,12 @@ def test_create_module_from_initfunc(self): "sp_pkg.sp_submod.__name__='sp_pkg.sp_submod'\n" "sys.modules[\"sp_pkg.sp_submod\"] is sp_pkg.sp_submod=True\n" "\"sp_submod\" in sys.modules=False\n" + # Module with non-ASCII name. (The SystemError shows the internal + # punycode-encoded name: not ideal, but also not horrible.) + "created nonascii_mp_\N{SNAKE}: in sys.modules=False\n" + "ascii(mp.__name__)=\"'nonascii_mp_\\\\U0001f40d'\" mp.executed='yes'\n" + "SystemError: 'initialization of nonascii_sp__rf17k " + "did not return PyModuleDef'\n" ) def test_inittab_submodule_multiphase(self): diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py index 17b1e03470dd04..6ff688aea61d6a 100644 --- a/Lib/test/test_import/__init__.py +++ b/Lib/test/test_import/__init__.py @@ -1264,6 +1264,13 @@ class Spec2: origin = "a\x00b" _imp.create_dynamic(Spec2()) + + def test_create_dynamic_no_origin(self): + with self.assertRaisesRegex(AttributeError, "no attribute 'origin'"): + class Spec: + name = "abc" + _imp.create_dynamic(Spec()) + def test_create_builtin(self): class Spec: pass @@ -1273,7 +1280,7 @@ class Spec: self.assertIs(_imp.create_builtin(spec), sys) spec.name = None - with self.assertRaisesRegex(TypeError, 'name must be string, not NoneType'): + with self.assertRaisesRegex(TypeError, 'module name must be a string, not NoneType'): _imp.create_builtin(spec) # gh-142029 @@ -1283,7 +1290,7 @@ class Spec: # gh-142029 spec.name = "" - with self.assertRaisesRegex(ValueError, 'name must not be empty'): + with self.assertRaisesRegex(ValueError, 'module name must not be empty'): _imp.create_builtin(spec) def test_filter_syntax_warnings_by_module(self): diff --git a/Misc/NEWS.d/next/C_API/2026-09-12-18-37-22.gh-issue-157384.uFbdle.rst b/Misc/NEWS.d/next/C_API/2026-09-12-18-37-22.gh-issue-157384.uFbdle.rst new file mode 100644 index 00000000000000..0ba43518705c50 --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2026-09-12-18-37-22.gh-issue-157384.uFbdle.rst @@ -0,0 +1,3 @@ +:c:func:`PyImport_CreateModuleFromInitfunc` now accepts non-ASCII module +names for multi-phase init modules, instead of raising +:exc:`UnicodeEncodeError`. diff --git a/Programs/_testembed.c b/Programs/_testembed.c index 2c5940a0755398..08bbcd4cfec154 100644 --- a/Programs/_testembed.c +++ b/Programs/_testembed.c @@ -2460,6 +2460,40 @@ PyInit_cmfi_sp_pkg(void) return PyModuleDef_Init(&cmfi_sp_pkg_def); } + +// Modules with non-ASCII names: multi-phase init is supported, +// single-phase init is not. +static PyModuleDef_Slot cmfi_nonascii_mp_slots[] = { + {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, + {0, NULL}, +}; + +static PyModuleDef cmfi_nonascii_mp_def = { + PyModuleDef_HEAD_INIT, + .m_name = "nonascii_mp_\xf0\x9f\x90\x8d", + .m_size = 0, + .m_slots = cmfi_nonascii_mp_slots, +}; + +static PyObject* +PyInit_cmfi_nonascii_mp(void) +{ + return PyModuleDef_Init(&cmfi_nonascii_mp_def); +} + +static PyModuleDef cmfi_nonascii_sp_def = { + PyModuleDef_HEAD_INIT, + .m_name = "nonascii_sp_\xf0\x9f\x90\x8d", + .m_size = -1, +}; + +static PyObject* +PyInit_cmfi_nonascii_sp(void) +{ + return PyModule_Create(&cmfi_nonascii_sp_def); +} + static PyObject* create_module(PyObject* self, PyObject* spec) { @@ -2487,6 +2521,14 @@ create_module(PyObject* self, PyObject* spec) Py_DECREF(name); return PyImport_CreateModuleFromInitfunc(spec, PyInit_cmfi_sp_submod); } + if (PyUnicode_EqualToUTF8(name, "nonascii_mp_\xf0\x9f\x90\x8d")) { + Py_DECREF(name); + return PyImport_CreateModuleFromInitfunc(spec, PyInit_cmfi_nonascii_mp); + } + if (PyUnicode_EqualToUTF8(name, "nonascii_sp_\xf0\x9f\x90\x8d")) { + Py_DECREF(name); + return PyImport_CreateModuleFromInitfunc(spec, PyInit_cmfi_nonascii_sp); + } PyErr_Format(PyExc_LookupError, "static module %R not found", name); Py_DECREF(name); return NULL; @@ -2548,6 +2590,11 @@ test_create_module_from_initfunc(void) L"print(f'{sp_pkg.sp_submod.__name__=}');" L"print(f'{sys.modules[\"sp_pkg.sp_submod\"] is sp_pkg.sp_submod=}');" L"print(f'{\"sp_submod\" in sys.modules=}');" + // Non-ASCII names: multi-phase init works, single-phase init doesn't + L"import importlib;" + L"mp = importlib.import_module('nonascii_mp_\\N{SNAKE}');" + L"print(f'{ascii(mp.__name__)=} {mp.executed=}');" + L"try_import('nonascii_sp_\\N{SNAKE}');" }; PyConfig config; if (PyImport_AppendInittab("create_static_module", @@ -2568,7 +2615,10 @@ test_create_module_from_initfunc(void) " @classmethod\n" " def find_spec(cls, fullname, path, target=None):\n" " if fullname in {'my_test_extension', 'embedded_ext',\n" - " 'sp_pkg', 'sp_pkg.sp_submod'}:\n" + " 'sp_pkg', 'sp_pkg.sp_submod',\n" + " 'nonascii_mp_\\N{SNAKE}',\n" + " 'nonascii_sp_\\N{SNAKE}',\n" + " }:\n" " return spec_from_loader(fullname, cls, origin=cls._ORIGIN,\n" " is_package=(fullname == 'sp_pkg'))\n" " return None\n" @@ -2583,6 +2633,13 @@ test_create_module_from_initfunc(void) " create_static_module.exec_module(module)\n" " module.executed = 'yes'\n" "sys.meta_path.append(StaticExtensionImporter)\n" + "def try_import(name):\n" + " try:\n" + " importlib.import_module(name)\n" + " except SystemError as exc:\n" + " print(f'SystemError: {ascii(str(exc))}')\n" + " else:\n" + " print(f'no SystemError for {ascii(name)}!')\n" ); if (result < 0) { fprintf(stderr, "PyRun_SimpleString() failed\n"); diff --git a/Python/import.c b/Python/import.c index 88d4980991f16a..8f4cec08ef765c 100644 --- a/Python/import.c +++ b/Python/import.c @@ -2507,27 +2507,16 @@ lookup_inittab_entry(const struct _Py_ext_module_loader_info* info) static PyObject* create_builtin( - PyThreadState *tstate, PyObject *name, + PyThreadState *tstate, + PyObject *name, /* if NULL, get from spec */ PyObject *spec, PyModInitFunction initfunc) { struct _Py_ext_module_loader_info info; - if (_Py_ext_module_loader_info_init_for_builtin(&info, name) < 0) { + _Py_ext_module_origin origin = _Py_ext_module_origin_BUILTIN; + if (_Py_ext_module_loader_info_init(&info, name, spec, origin) < 0) { return NULL; } - if (initfunc != NULL) { - /* An explicitly provided init function (see - * PyImport_CreateModuleFromInitfunc()) may belong to a submodule - * whose single-phase init creates the module using only the last - * component of the name (as e.g. pybind11 does). Set the package - * context so that PyModule_Create() resolves the full name, the - * same as for dynamically loaded extensions. */ - info.newcontext = PyUnicode_AsUTF8(info.name); - if (info.newcontext == NULL) { - _Py_ext_module_loader_info_clear(&info); - return NULL; - } - } struct extensions_cache_value *cached = NULL; PyObject *mod = import_find_extension(tstate, &info, &cached); @@ -2558,7 +2547,7 @@ create_builtin( struct _inittab *entry = lookup_inittab_entry(&info); if (entry == NULL) { mod = NULL; - _PyErr_SetModuleNotFoundError(name); + _PyErr_SetModuleNotFoundError(info.name); goto finally; } @@ -2610,20 +2599,7 @@ PyImport_CreateModuleFromInitfunc( PyThreadState *tstate = _PyThreadState_GET(); - PyObject *name = PyObject_GetAttr(spec, &_Py_ID(name)); - if (name == NULL) { - return NULL; - } - - if (!PyUnicode_Check(name)) { - PyErr_Format(PyExc_TypeError, - "spec name must be string, not %T", name); - Py_DECREF(name); - return NULL; - } - - PyObject *mod = create_builtin(tstate, name, spec, initfunc); - Py_DECREF(name); + PyObject *mod = create_builtin(tstate, NULL, spec, initfunc); return mod; } @@ -2835,8 +2811,8 @@ PyImport_ExecCodeModuleWithPathnames(const char *name, PyObject *co, Py_FatalError("no current interpreter"); } - external= PyObject_GetAttrString(IMPORTLIB(interp), - "_bootstrap_external"); + external= PyObject_GetAttr(IMPORTLIB(interp), + &_Py_ID(_bootstrap_external)); if (external != NULL) { pathobj = PyObject_CallMethodOneArg( external, &_Py_ID(_get_sourcefile), cpathobj); @@ -2920,8 +2896,8 @@ PyImport_ExecCodeModuleObject(PyObject *name, PyObject *co, PyObject *pathname, if (pathname == NULL) { pathname = ((PyCodeObject *)co)->co_filename; } - external = PyObject_GetAttrString(IMPORTLIB(tstate->interp), - "_bootstrap_external"); + external = PyObject_GetAttr(IMPORTLIB(tstate->interp), + &_Py_ID(_bootstrap_external)); if (external == NULL) { Py_DECREF(d); return NULL; @@ -3499,8 +3475,8 @@ PyObject * _PyImport_GetImportlibExternalLoader(PyInterpreterState *interp, const char *loader_name) { - PyObject *bootstrap = PyObject_GetAttrString(IMPORTLIB(interp), - "_bootstrap_external"); + PyObject *bootstrap = PyObject_GetAttr(IMPORTLIB(interp), + &_Py_ID(_bootstrap_external)); if (bootstrap == NULL) { return NULL; } @@ -3513,8 +3489,8 @@ _PyImport_GetImportlibExternalLoader(PyInterpreterState *interp, PyObject * _PyImport_BlessMyLoader(PyInterpreterState *interp, PyObject *module_globals) { - PyObject *external = PyObject_GetAttrString(IMPORTLIB(interp), - "_bootstrap_external"); + PyObject *external = PyObject_GetAttr(IMPORTLIB(interp), + &_Py_ID(_bootstrap_external)); if (external == NULL) { return NULL; } @@ -5085,27 +5061,7 @@ _imp_create_builtin(PyObject *module, PyObject *spec) { PyThreadState *tstate = _PyThreadState_GET(); - PyObject *name = PyObject_GetAttrString(spec, "name"); - if (name == NULL) { - return NULL; - } - - if (!PyUnicode_Check(name)) { - PyErr_Format(PyExc_TypeError, - "name must be string, not %.200s", - Py_TYPE(name)->tp_name); - Py_DECREF(name); - return NULL; - } - - if (PyUnicode_GetLength(name) == 0) { - PyErr_Format(PyExc_ValueError, "name must not be empty"); - Py_DECREF(name); - return NULL; - } - - PyObject *mod = create_builtin(tstate, name, spec, NULL); - Py_DECREF(name); + PyObject *mod = create_builtin(tstate, NULL, spec, NULL); return mod; } @@ -5437,7 +5393,8 @@ _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file) PyThreadState *tstate = _PyThreadState_GET(); struct _Py_ext_module_loader_info info; - if (_Py_ext_module_loader_info_init_from_spec(&info, spec) < 0) { + _Py_ext_module_origin origin = _Py_ext_module_origin_DYNAMIC; + if (_Py_ext_module_loader_info_init(&info, NULL, spec, origin) < 0) { return NULL; } diff --git a/Python/importdl.c b/Python/importdl.c index 537e8d869dc93c..dbcf5c98ceb69b 100644 --- a/Python/importdl.c +++ b/Python/importdl.c @@ -8,6 +8,7 @@ #include "pycore_moduleobject.h" // _PyModule_GetDefOrNull() #include "pycore_pyerrors.h" // _PyErr_FormatFromCause() #include "pycore_runtime.h" // _Py_ID() +#include "pycore_global_objects.h" // _Py_SINGLETON() /***********************************/ @@ -28,8 +29,6 @@ static const struct hook_prefixes nonascii_prefixes = { static PyObject * get_encoded_name(PyObject *name, const struct hook_prefixes **hook_prefixes) { PyObject *tmp; - PyObject *encoded = NULL; - PyObject *modname = NULL; Py_ssize_t name_len, lastdot; /* Get the short name (substring after last dot) */ @@ -51,33 +50,38 @@ get_encoded_name(PyObject *name, const struct hook_prefixes **hook_prefixes) { } /* Encode to ASCII or Punycode, as needed */ - encoded = PyUnicode_AsEncodedString(name, "ascii", NULL); - if (encoded != NULL) { + PyObject *encoded; + if (PyUnicode_IS_ASCII(name)) { *hook_prefixes = &ascii_only_prefixes; + encoded = PyUnicode_AsASCIIString(name); } else { - if (PyErr_ExceptionMatches(PyExc_UnicodeEncodeError)) { - PyErr_Clear(); - encoded = PyUnicode_AsEncodedString(name, "punycode", NULL); - if (encoded == NULL) { - goto error; - } - *hook_prefixes = &nonascii_prefixes; - } else { - goto error; - } + *hook_prefixes = &nonascii_prefixes; + encoded = PyUnicode_AsEncodedString(name, "punycode", NULL); + } + if (encoded == NULL) { + goto error; } - /* Replace '-' by '_' */ - modname = _PyObject_CallMethod(encoded, &_Py_ID(replace), "cc", '-', '_'); - if (modname == NULL) + /* Call encoded.replace('-', '_') */ + PyObject *modname = PyObject_VectorcallMethod( + &_Py_ID(replace), + (PyObject*[3]) { + encoded, + (PyObject *)&_Py_SINGLETON(bytes_characters)['-'], + (PyObject *)&_Py_SINGLETON(bytes_characters)['_'], + }, + 3 | PY_VECTORCALL_ARGUMENTS_OFFSET, + NULL); + Py_CLEAR(encoded); + + if (modname == NULL) { goto error; + } Py_DECREF(name); - Py_DECREF(encoded); return modname; error: Py_DECREF(name); - Py_XDECREF(encoded); return NULL; } @@ -94,122 +98,81 @@ _Py_ext_module_loader_info_clear(struct _Py_ext_module_loader_info *info) int _Py_ext_module_loader_info_init(struct _Py_ext_module_loader_info *p_info, - PyObject *name, PyObject *filename, + PyObject *name, + PyObject *spec, _Py_ext_module_origin origin) { struct _Py_ext_module_loader_info info = { .origin=origin, }; - assert(name != NULL); - if (!PyUnicode_Check(name)) { - PyErr_SetString(PyExc_TypeError, - "module name must be a string"); - _Py_ext_module_loader_info_clear(&info); - return -1; + if (name == NULL) { + assert(spec != NULL); + info.name = PyObject_GetAttr(spec, &_Py_ID(name)); + if (info.name == NULL) { + goto error; + } + } + else { + info.name = Py_NewRef(name); + } + + if (!PyUnicode_Check(info.name)) { + PyErr_Format(PyExc_TypeError, + "module name must be a string, not %T", + info.name); + goto error; + } + + if (PyUnicode_GetLength(info.name) == 0) { + PyErr_Format(PyExc_ValueError, "module name must not be empty"); + goto error; } - assert(PyUnicode_GetLength(name) > 0); - info.name = Py_NewRef(name); info.name_encoded = get_encoded_name(info.name, &info.hook_prefixes); if (info.name_encoded == NULL) { - _Py_ext_module_loader_info_clear(&info); - return -1; + goto error; } info.newcontext = PyUnicode_AsUTF8(info.name); if (info.newcontext == NULL) { - _Py_ext_module_loader_info_clear(&info); - return -1; + goto error; } - if (filename != NULL) { - if (!PyUnicode_Check(filename)) { + if (origin == _Py_ext_module_origin_DYNAMIC) { + assert(spec != NULL); + info.filename = PyObject_GetAttr(spec, &_Py_ID(origin)); + if (info.filename == NULL) { + goto error; + } + + if (!PyUnicode_Check(info.filename)) { PyErr_SetString(PyExc_TypeError, "module filename must be a string"); - _Py_ext_module_loader_info_clear(&info); - return -1; + goto error; } - info.filename = Py_NewRef(filename); #ifndef MS_WINDOWS info.filename_encoded = PyUnicode_EncodeFSDefault(info.filename); if (info.filename_encoded == NULL) { - _Py_ext_module_loader_info_clear(&info); - return -1; + goto error; } #endif info.path = info.filename; } else { + info.filename = NULL; info.path = info.name; } *p_info = info; return 0; -} -int -_Py_ext_module_loader_info_init_for_builtin( - struct _Py_ext_module_loader_info *info, - PyObject *name) -{ - assert(PyUnicode_Check(name)); - assert(PyUnicode_GetLength(name) > 0); - - PyObject *name_encoded = PyUnicode_AsEncodedString(name, "ascii", NULL); - if (name_encoded == NULL) { - return -1; - } - - *info = (struct _Py_ext_module_loader_info){ - .name=Py_NewRef(name), - .name_encoded=name_encoded, - /* We won't need filename. */ - .path=name, - .origin=_Py_ext_module_origin_BUILTIN, - .hook_prefixes=&ascii_only_prefixes, - .newcontext=NULL, - }; - return 0; -} - -int -_Py_ext_module_loader_info_init_for_core( - struct _Py_ext_module_loader_info *info, - PyObject *name) -{ - if (_Py_ext_module_loader_info_init_for_builtin(info, name) < 0) { - return -1; - } - info->origin = _Py_ext_module_origin_CORE; - return 0; -} - -#ifdef HAVE_DYNAMIC_LOADING -int -_Py_ext_module_loader_info_init_from_spec( - struct _Py_ext_module_loader_info *p_info, - PyObject *spec) -{ - PyObject *name = PyObject_GetAttrString(spec, "name"); - if (name == NULL) { - return -1; - } - PyObject *filename = PyObject_GetAttrString(spec, "origin"); - if (filename == NULL) { - Py_DECREF(name); - return -1; - } - /* We could also accommodate builtin modules here without much trouble. */ - _Py_ext_module_origin origin = _Py_ext_module_origin_DYNAMIC; - int err = _Py_ext_module_loader_info_init(p_info, name, filename, origin); - Py_DECREF(name); - Py_DECREF(filename); - return err; +error: + _Py_ext_module_loader_info_clear(&info); + return -1; } -#endif /* HAVE_DYNAMIC_LOADING */ /********************************/