Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
gh-116146: Add new C-API to create builtin from spec and initfunc
  • Loading branch information
itamaro committed Nov 8, 2025
commit 403c7e21182ada18cfe5ce544eb3e2641981257f
9 changes: 9 additions & 0 deletions Include/cpython/import.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,15 @@ struct _inittab {
PyAPI_DATA(struct _inittab *) PyImport_Inittab;
PyAPI_FUNC(int) PyImport_ExtendInittab(struct _inittab *newtab);

// Custom importers may use this API to initialize statically linked
// extension modules directly from a spec and init function,
// without needing to go through inittab
PyAPI_FUNC(PyObject *)
PyImport_CreateBuiltinFromSpecAndInitfunc(
PyObject *spec,
PyObject* (*initfunc)(void)
);

struct _frozen {
const char *name; /* ASCII encoded string */
const unsigned char *code;
Expand Down
76 changes: 57 additions & 19 deletions Python/import.c
Original file line number Diff line number Diff line change
Expand Up @@ -2362,8 +2362,27 @@ is_builtin(PyObject *name)
return 0;
}

static PyModInitFunction
lookup_inittab_initfunc(const struct _Py_ext_module_loader_info* info)
{
struct _inittab *found = NULL;
for (struct _inittab *p = INITTAB; p->name != NULL; p++) {
if (_PyUnicode_EqualToASCIIString(info->name, p->name)) {
found = p;
}
}
if (found == NULL) {
// not found
return NULL;
}
return (PyModInitFunction)found->initfunc;
}

static PyObject*
create_builtin(PyThreadState *tstate, PyObject *name, PyObject *spec)
create_builtin_ex(
PyThreadState *tstate, PyObject *name,
PyObject *spec,
PyModInitFunction initfunc)
{
struct _Py_ext_module_loader_info info;
if (_Py_ext_module_loader_info_init_for_builtin(&info, name) < 0) {
Expand Down Expand Up @@ -2394,25 +2413,15 @@ create_builtin(PyThreadState *tstate, PyObject *name, PyObject *spec)
_extensions_cache_delete(info.path, info.name);
}

struct _inittab *found = NULL;
for (struct _inittab *p = INITTAB; p->name != NULL; p++) {
if (_PyUnicode_EqualToASCIIString(info.name, p->name)) {
found = p;
break;
}
}
if (found == NULL) {
// not found
mod = Py_NewRef(Py_None);
goto finally;
}

PyModInitFunction p0 = (PyModInitFunction)found->initfunc;
PyModInitFunction p0 = initfunc;
if (p0 == NULL) {
/* Cannot re-init internal module ("sys" or "builtins") */
assert(is_core_module(tstate->interp, info.name, info.path));
mod = import_add_module(tstate, info.name);
goto finally;
p0 = lookup_inittab_initfunc(&info);
if (p0 == NULL) {
/* Cannot re-init internal module ("sys" or "builtins") */
assert(is_core_module(tstate->interp, info.name, info.path));
mod = import_add_module(tstate, info.name);
goto finally;
}
}

#ifdef Py_GIL_DISABLED
Expand All @@ -2438,6 +2447,35 @@ create_builtin(PyThreadState *tstate, PyObject *name, PyObject *spec)
return mod;
}

static PyObject*
create_builtin(PyThreadState *tstate, PyObject *name, PyObject *spec)
{
return create_builtin_ex(tstate, name, spec, NULL);
}

PyObject*
PyImport_CreateBuiltinFromSpecAndInitfunc(
PyObject *spec, PyObject* (*initfunc)(void))
{
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;
}

PyObject *mod = create_builtin_ex(tstate, name, spec, initfunc);
Py_DECREF(name);
return mod;
}

/*****************************/
/* the builtin modules table */
Expand Down