Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 526ca4c

Browse files
gh-117953: Work Relative to Specific Extension Kinds in the Import Machinery (gh-118205)
This change will make some later changes simpler.
1 parent a524152 commit 526ca4c

File tree

3 files changed

+324
-66
lines changed

3 files changed

+324
-66
lines changed

Include/internal/pycore_importdl.h

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,15 @@ extern "C" {
1515
extern const char *_PyImport_DynLoadFiletab[];
1616

1717

18-
typedef PyObject *(*PyModInitFunction)(void);
18+
typedef enum ext_module_kind {
19+
_Py_ext_module_kind_UNKNOWN = 0,
20+
_Py_ext_module_kind_SINGLEPHASE = 1,
21+
_Py_ext_module_kind_MULTIPHASE = 2,
22+
_Py_ext_module_kind_INVALID = 3,
23+
} _Py_ext_module_kind;
24+
1925

26+
/* Input for loading an extension module. */
2027
struct _Py_ext_module_loader_info {
2128
PyObject *filename;
2229
#ifndef MS_WINDOWS
@@ -43,10 +50,33 @@ extern int _Py_ext_module_loader_info_init_from_spec(
4350
struct _Py_ext_module_loader_info *info,
4451
PyObject *spec);
4552

53+
/* The result from running an extension module's init function. */
4654
struct _Py_ext_module_loader_result {
4755
PyModuleDef *def;
4856
PyObject *module;
57+
_Py_ext_module_kind kind;
58+
struct _Py_ext_module_loader_result_error *err;
59+
struct _Py_ext_module_loader_result_error {
60+
enum _Py_ext_module_loader_result_error_kind {
61+
_Py_ext_module_loader_result_EXCEPTION = 0,
62+
_Py_ext_module_loader_result_ERR_MISSING = 1,
63+
_Py_ext_module_loader_result_ERR_UNREPORTED_EXC = 2,
64+
_Py_ext_module_loader_result_ERR_UNINITIALIZED = 3,
65+
_Py_ext_module_loader_result_ERR_NONASCII_NOT_MULTIPHASE = 4,
66+
_Py_ext_module_loader_result_ERR_NOT_MODULE = 5,
67+
_Py_ext_module_loader_result_ERR_MISSING_DEF = 6,
68+
} kind;
69+
PyObject *exc;
70+
} _err;
4971
};
72+
extern void _Py_ext_module_loader_result_clear(
73+
struct _Py_ext_module_loader_result *res);
74+
extern void _Py_ext_module_loader_result_apply_error(
75+
struct _Py_ext_module_loader_result *res,
76+
const char *name);
77+
78+
/* The module init function. */
79+
typedef PyObject *(*PyModInitFunction)(void);
5080
extern PyModInitFunction _PyImport_GetModInitFunc(
5181
struct _Py_ext_module_loader_info *info,
5282
FILE *fp);

Python/import.c

Lines changed: 114 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1193,26 +1193,63 @@ is_core_module(PyInterpreterState *interp, PyObject *name, PyObject *path)
11931193
}
11941194

11951195
#ifndef NDEBUG
1196-
static bool
1197-
is_singlephase(PyModuleDef *def)
1196+
static _Py_ext_module_kind
1197+
_get_extension_kind(PyModuleDef *def, bool check_size)
11981198
{
1199+
_Py_ext_module_kind kind;
11991200
if (def == NULL) {
12001201
/* It must be a module created by reload_singlephase_extension()
12011202
* from m_copy. Ideally we'd do away with this case. */
1202-
return true;
1203+
kind = _Py_ext_module_kind_SINGLEPHASE;
12031204
}
1204-
else if (def->m_slots == NULL) {
1205-
return true;
1205+
else if (def->m_slots != NULL) {
1206+
kind = _Py_ext_module_kind_MULTIPHASE;
12061207
}
1207-
else {
1208-
return false;
1208+
else if (check_size && def->m_size == -1) {
1209+
kind = _Py_ext_module_kind_SINGLEPHASE;
12091210
}
1210-
}
1211+
else if (def->m_base.m_init != NULL) {
1212+
kind = _Py_ext_module_kind_SINGLEPHASE;
1213+
}
1214+
else {
1215+
// This is probably single-phase init, but a multi-phase
1216+
// module *can* have NULL m_slots.
1217+
kind = _Py_ext_module_kind_UNKNOWN;
1218+
}
1219+
return kind;
1220+
}
1221+
1222+
/* The module might not be fully initialized yet
1223+
* and PyModule_FromDefAndSpec() checks m_size
1224+
* so we skip m_size. */
1225+
#define assert_multiphase_def(def) \
1226+
do { \
1227+
_Py_ext_module_kind kind = _get_extension_kind(def, false); \
1228+
assert(kind == _Py_ext_module_kind_MULTIPHASE \
1229+
/* m_slots can be NULL. */ \
1230+
|| kind == _Py_ext_module_kind_UNKNOWN); \
1231+
} while (0)
1232+
1233+
#define assert_singlephase_def(def) \
1234+
do { \
1235+
_Py_ext_module_kind kind = _get_extension_kind(def, true); \
1236+
assert(kind == _Py_ext_module_kind_SINGLEPHASE \
1237+
|| kind == _Py_ext_module_kind_UNKNOWN); \
1238+
} while (0)
1239+
1240+
#define assert_singlephase(def) \
1241+
assert_singlephase_def(def)
1242+
1243+
#else /* defined(NDEBUG) */
1244+
#define assert_multiphase_def(def)
1245+
#define assert_singlephase_def(def)
1246+
#define assert_singlephase(def)
12111247
#endif
12121248

12131249

12141250
struct singlephase_global_update {
12151251
PyObject *m_dict;
1252+
PyModInitFunction m_init;
12161253
};
12171254

12181255
static int
@@ -1226,10 +1263,24 @@ update_global_state_for_extension(PyThreadState *tstate,
12261263
assert(def->m_base.m_copy == NULL);
12271264
}
12281265
else {
1229-
assert(def->m_base.m_init != NULL
1230-
|| is_core_module(tstate->interp, name, path));
1231-
if (singlephase->m_dict == NULL) {
1266+
if (singlephase->m_init != NULL) {
1267+
assert(singlephase->m_dict == NULL);
1268+
assert(def->m_base.m_copy == NULL);
1269+
assert(def->m_size >= 0);
1270+
/* Remember pointer to module init function. */
1271+
// XXX If two modules share a def then def->m_base will
1272+
// reflect the last one added (here) to the global cache.
1273+
// We should prevent this somehow. The simplest solution
1274+
// is probably to store m_copy/m_init in the cache along
1275+
// with the def, rather than within the def.
1276+
def->m_base.m_init = singlephase->m_init;
1277+
}
1278+
else if (singlephase->m_dict == NULL) {
1279+
/* It must be a core builtin module. */
1280+
assert(is_core_module(tstate->interp, name, path));
1281+
assert(def->m_size == -1);
12321282
assert(def->m_base.m_copy == NULL);
1283+
assert(def->m_base.m_init == NULL);
12331284
}
12341285
else {
12351286
assert(PyDict_Check(singlephase->m_dict));
@@ -1316,7 +1367,7 @@ import_find_extension(PyThreadState *tstate,
13161367
if (def == NULL) {
13171368
return NULL;
13181369
}
1319-
assert(is_singlephase(def));
1370+
assert_singlephase(def);
13201371

13211372
/* It may have been successfully imported previously
13221373
in an interpreter that allows legacy modules
@@ -1369,11 +1420,26 @@ import_find_extension(PyThreadState *tstate,
13691420
}
13701421
struct _Py_ext_module_loader_result res;
13711422
if (_PyImport_RunModInitFunc(def->m_base.m_init, info, &res) < 0) {
1423+
_Py_ext_module_loader_result_apply_error(&res, name_buf);
13721424
return NULL;
13731425
}
13741426
assert(!PyErr_Occurred());
1427+
assert(res.err == NULL);
1428+
assert(res.kind == _Py_ext_module_kind_SINGLEPHASE);
13751429
mod = res.module;
1376-
// XXX __file__ doesn't get set!
1430+
/* Tchnically, the init function could return a different module def.
1431+
* Then we would probably need to update the global cache.
1432+
* However, we don't expect anyone to change the def. */
1433+
assert(res.def == def);
1434+
_Py_ext_module_loader_result_clear(&res);
1435+
1436+
/* Remember the filename as the __file__ attribute */
1437+
if (info->filename != NULL) {
1438+
if (PyModule_AddObjectRef(mod, "__file__", info->filename) < 0) {
1439+
PyErr_Clear(); /* Not important enough to report */
1440+
}
1441+
}
1442+
13771443
if (PyObject_SetItem(modules, info->name, mod) == -1) {
13781444
Py_DECREF(mod);
13791445
return NULL;
@@ -1398,78 +1464,89 @@ import_run_extension(PyThreadState *tstate, PyModInitFunction p0,
13981464
struct _Py_ext_module_loader_info *info,
13991465
PyObject *spec, PyObject *modules)
14001466
{
1467+
/* Core modules go through _PyImport_FixupBuiltin(). */
1468+
assert(!is_core_module(tstate->interp, info->name, info->path));
1469+
14011470
PyObject *mod = NULL;
14021471
PyModuleDef *def = NULL;
1472+
const char *name_buf = PyBytes_AS_STRING(info->name_encoded);
14031473

14041474
struct _Py_ext_module_loader_result res;
14051475
if (_PyImport_RunModInitFunc(p0, info, &res) < 0) {
14061476
/* We discard res.def. */
14071477
assert(res.module == NULL);
1408-
assert(PyErr_Occurred());
1409-
goto finally;
1478+
_Py_ext_module_loader_result_apply_error(&res, name_buf);
1479+
return NULL;
14101480
}
14111481
assert(!PyErr_Occurred());
1482+
assert(res.err == NULL);
14121483

14131484
mod = res.module;
14141485
res.module = NULL;
14151486
def = res.def;
14161487
assert(def != NULL);
14171488

1418-
if (mod == NULL) {
1419-
//assert(!is_singlephase(def));
1489+
if (res.kind == _Py_ext_module_kind_MULTIPHASE) {
1490+
assert_multiphase_def(def);
14201491
assert(mod == NULL);
14211492
mod = PyModule_FromDefAndSpec(def, spec);
14221493
if (mod == NULL) {
1423-
goto finally;
1494+
goto error;
14241495
}
14251496
}
14261497
else {
1427-
assert(is_singlephase(def));
1498+
assert(res.kind == _Py_ext_module_kind_SINGLEPHASE);
1499+
assert_singlephase_def(def);
14281500
assert(PyModule_Check(mod));
14291501

1430-
const char *name_buf = PyBytes_AS_STRING(info->name_encoded);
14311502
if (_PyImport_CheckSubinterpIncompatibleExtensionAllowed(name_buf) < 0) {
1432-
Py_CLEAR(mod);
1433-
goto finally;
1503+
goto error;
14341504
}
14351505

1436-
/* Remember pointer to module init function. */
1437-
def->m_base.m_init = p0;
1438-
1506+
/* Remember the filename as the __file__ attribute */
14391507
if (info->filename != NULL) {
1440-
/* Remember the filename as the __file__ attribute */
14411508
if (PyModule_AddObjectRef(mod, "__file__", info->filename) < 0) {
14421509
PyErr_Clear(); /* Not important enough to report */
14431510
}
14441511
}
14451512

1513+
/* Update global import state. */
14461514
struct singlephase_global_update singlephase = {0};
14471515
// gh-88216: Extensions and def->m_base.m_copy can be updated
14481516
// when the extension module doesn't support sub-interpreters.
1449-
if (def->m_size == -1
1450-
&& !is_core_module(tstate->interp, info->name, info->path))
1451-
{
1517+
if (def->m_size == -1) {
1518+
/* We will reload from m_copy. */
1519+
assert(def->m_base.m_init == NULL);
14521520
singlephase.m_dict = PyModule_GetDict(mod);
14531521
assert(singlephase.m_dict != NULL);
14541522
}
1523+
else {
1524+
/* We will reload via the init function. */
1525+
assert(def->m_size >= 0);
1526+
singlephase.m_init = p0;
1527+
}
14551528
if (update_global_state_for_extension(
14561529
tstate, info->path, info->name, def, &singlephase) < 0)
14571530
{
1458-
Py_CLEAR(mod);
1459-
goto finally;
1531+
goto error;
14601532
}
14611533

1534+
/* Update per-interpreter import state. */
14621535
PyObject *modules = get_modules_dict(tstate, true);
14631536
if (finish_singlephase_extension(
14641537
tstate, mod, def, info->name, modules) < 0)
14651538
{
1466-
Py_CLEAR(mod);
1467-
goto finally;
1539+
goto error;
14681540
}
14691541
}
14701542

1471-
finally:
1543+
_Py_ext_module_loader_result_clear(&res);
14721544
return mod;
1545+
1546+
error:
1547+
Py_XDECREF(mod);
1548+
_Py_ext_module_loader_result_clear(&res);
1549+
return NULL;
14731550
}
14741551

14751552

@@ -1532,7 +1609,7 @@ _PyImport_FixupBuiltin(PyThreadState *tstate, PyObject *mod, const char *name,
15321609
* module state, but we also don't populate def->m_base.m_copy
15331610
* for them. */
15341611
assert(is_core_module(tstate->interp, nameobj, nameobj));
1535-
assert(is_singlephase(def));
1612+
assert_singlephase_def(def);
15361613
assert(def->m_size == -1);
15371614
assert(def->m_base.m_copy == NULL);
15381615

@@ -1586,7 +1663,7 @@ create_builtin(PyThreadState *tstate, PyObject *name, PyObject *spec)
15861663
PyObject *mod = import_find_extension(tstate, &info);
15871664
if (mod != NULL) {
15881665
assert(!_PyErr_Occurred(tstate));
1589-
assert(is_singlephase(_PyModule_GetDef(mod)));
1666+
assert_singlephase(_PyModule_GetDef(mod));
15901667
goto finally;
15911668
}
15921669
else if (_PyErr_Occurred(tstate)) {
@@ -3940,7 +4017,7 @@ _imp_create_dynamic_impl(PyObject *module, PyObject *spec, PyObject *file)
39404017
mod = import_find_extension(tstate, &info);
39414018
if (mod != NULL) {
39424019
assert(!_PyErr_Occurred(tstate));
3943-
assert(is_singlephase(_PyModule_GetDef(mod)));
4020+
assert_singlephase(_PyModule_GetDef(mod));
39444021
goto finally;
39454022
}
39464023
else if (_PyErr_Occurred(tstate)) {

0 commit comments

Comments
 (0)