/* Testing module for single-phase initialization of extension modules This file contains several distinct modules, meaning each as its own name and its own init function (PyInit_...). The default import system will only find the one matching the filename: _testsinglephase. To load the others you must do so manually. For example: ```python name = '_testsinglephase_base_wrapper' filename = _testsinglephase.__file__ loader = importlib.machinery.ExtensionFileLoader(name, filename) spec = importlib.util.spec_from_file_location(name, filename, loader=loader) mod = importlib._bootstrap._load(spec) loader.exec_module(module) sys.modules[modname] = module ``` (The last two lines are just for completeness.) Here are the modules: * _testsinglephase * def: _testsinglephase_basic, * m_name: "_testsinglephase" * m_size: -1 * state * process-global * <int> initialized_count (default to -1; will never be 0) * <module_state> module (see module state below) * module state: no * initial __dict__: see common initial __dict__ below * init function 1. create module 2. clear <global>.module 3. initialize <global>.module: see module state below 4. initialize module: set initial __dict__ 5. increment <global>.initialized_count * functions * (3 common, see below) * initialized_count() - return <global>.module.initialized_count * import system * caches * global extensions cache: yes * def.m_base.m_copy: yes * def.m_base.m_init: no * per-interpreter cache: yes (all single-phase init modules) * load in main interpreter * initial (not already in global cache) 1. get init function from shared object file 2. run init function 3. copy __dict__ into def.m_base.m_copy 4. set entry in global cache 5. set entry in per-interpreter cache 6. set entry in sys.modules * reload (already in sys.modules) 1. get def from global cache 2. get module from sys.modules 3. update module with contents of def.m_base.m_copy * already loaded in other interpreter (already in global cache) * same as reload, but create new module and update *it* * not in any sys.modules, still in global cache * same as already loaded * load in legacy (non-isolated) interpreter * same as main interpreter * unload: never (all single-phase init modules) * _testsinglephase_basic_wrapper * identical to _testsinglephase except module name * _testsinglephase_basic_copy * def: static local variable in init function * m_name: "_testsinglephase_basic_copy" * m_size: -1 * state: same as _testsinglephase * init function: same as _testsinglephase * functions: same as _testsinglephase * import system: same as _testsinglephase * _testsinglephase_with_reinit * def: _testsinglephase_with_reinit, * m_name: "_testsinglephase_with_reinit" * m_size: 0 * state * process-global state: no * module state: no * initial __dict__: see common initial __dict__ below * init function 1. create module 2. initialize temporary module state (local var): see module state below 3. initialize module: set initial __dict__ * functions: see common functions below * import system * caches * global extensions cache: only if loaded in main interpreter * def.m_base.m_copy: no * def.m_base.m_init: only if loaded in the main interpreter * per-interpreter cache: yes (all single-phase init modules) * load in main interpreter * initial (not already in global cache) * (same as _testsinglephase except step 3) 1. get init function from shared object file 2. run init function 3. set def.m_base.m_init to the init function 4. set entry in global cache 5. set entry in per-interpreter cache 6. set entry in sys.modules * reload (already in sys.modules) 1. get def from global cache 2. call def->m_base.m_init to get a new module object 3. replace the existing module in sys.modules * already loaded in other interpreter (already in global cache) * same as reload (since will only be in cache for main interp) * not in any sys.modules, still in global cache * same as already loaded * load in legacy (non-isolated) interpreter * initial (not already in global cache) * (same as main interpreter except skip steps 3 & 4 there) 1. get init function from shared object file 2. run init function ... 5. set entry in per-interpreter cache 6. set entry in sys.modules * reload (already in sys.modules) * same as initial (load from scratch) * already loaded in other interpreter (already in global cache) * same as initial (load from scratch) * not in any sys.modules, still in global cache * same as initial (load from scratch) * unload: never (all single-phase init modules) * _testsinglephase_with_state * def: _testsinglephase_with_state, * m_name: "_testsinglephase_with_state" * m_size: sizeof(module_state) * state * process-global: no * module state: see module state below * initial __dict__: see common initial __dict__ below * init function 1. create module 3. initialize module state: see module state below 4. initialize module: set initial __dict__ 5. increment <global>.initialized_count * functions: see common functions below * import system: same as _testsinglephase_basic_copy * _testsinglephase_check_cache_first * def: _testsinglepahse_check_cache_first * m_name: "_testsinglephase_check_cache_first" * m_size: -1 * state: none * init function: * tries PyState_FindModule() first * otherwise creates empty module * functions: none * import system: same as _testsinglephase * _testsinglephase_with_reinit_check_cache_first * def: _testsinglepahse_with_reinit_check_cache_first * m_name: "_testsinglephase_with_reinit_check_cache_first" * m_size: 0 * state: none * init function: same as _testsinglephase_check_cache_first * functions: none * import system: same as _testsinglephase_with_reinit * _testsinglephase_with_state_check_cache_first * def: _testsinglepahse_with_state_check_cache_first * m_name: "_testsinglephase_with_state_check_cache_first" * m_size: 42 * state: none * init function: same as _testsinglephase_check_cache_first * functions: none * import system: same as _testsinglephase_with_state * _testsinglephase_circular Regression test for gh-123880. Does not have the common attributes & methods. See test_singlephase_circular test.test_import.SinglephaseInitTests. Module state: * fields * <PyTime_t> initialized - when the module was first initialized * <PyObject> *error * <PyObject> *int_const * <PyObject> *str_const * initialization 1. set state.initialized to the current time 2. set state.error to a new exception class 3. set state->int_const to int(1969) 4. set state->str_const to "something different" Common initial __dict__: * error: state.error * int_const: state.int_const * str_const: state.str_const * _module_initialized: state.initialized Common functions: * look_up_self() - return the module from the per-interpreter "by-index" cache * sum() - return a + b * state_initialized() - return state->initialized (or None if m_size == 0) See Python/import.c, especially the long comments, for more about single-phase init modules. */ #ifndef Py_BUILD_CORE_BUILTIN #define Py_BUILD_CORE_MODULE … #endif //#include <time.h> #include "Python.h" #include "pycore_namespace.h" // _PyNamespace_New() module_state; /* Process-global state is only used by _testsinglephase since it's the only one that does not support re-init. */ static struct { … } global_state = …; static void clear_state(module_state *state); static void clear_global_state(void) { … } static inline module_state * get_module_state(PyObject *module) { … } static void clear_state(module_state *state) { … } static int _set_initialized(PyTime_t *initialized) { … } static int init_state(module_state *state) { … } static int init_module(PyObject *module, module_state *state) { … } PyDoc_STRVAR(common_state_initialized_doc, "state_initialized()\n\ \n\ Return the seconds-since-epoch when the module state was initialized."); static PyObject * common_state_initialized(PyObject *self, PyObject *Py_UNUSED(ignored)) { … } #define STATE_INITIALIZED_METHODDEF … PyDoc_STRVAR(common_look_up_self_doc, "look_up_self()\n\ \n\ Return the module associated with this module's def.m_base.m_index."); static PyObject * common_look_up_self(PyObject *self, PyObject *Py_UNUSED(ignored)) { … } #define LOOK_UP_SELF_METHODDEF … /* Function of two integers returning integer */ PyDoc_STRVAR(common_sum_doc, "sum(i,j)\n\ \n\ Return the sum of i and j."); static PyObject * common_sum(PyObject *self, PyObject *args) { … } #define SUM_METHODDEF … PyDoc_STRVAR(basic_initialized_count_doc, "initialized_count()\n\ \n\ Return how many times the module has been initialized."); static PyObject * basic_initialized_count(PyObject *self, PyObject *Py_UNUSED(ignored)) { … } #define INITIALIZED_COUNT_METHODDEF … PyDoc_STRVAR(basic__clear_globals_doc, "_clear_globals()\n\ \n\ Free all global state and set it to uninitialized."); static PyObject * basic__clear_globals(PyObject *self, PyObject *Py_UNUSED(ignored)) { … } #define _CLEAR_GLOBALS_METHODDEF … PyDoc_STRVAR(basic__clear_module_state_doc, "_clear_module_state()\n\ \n\ Free the module state and set it to uninitialized."); static PyObject * basic__clear_module_state(PyObject *self, PyObject *Py_UNUSED(ignored)) { … } #define _CLEAR_MODULE_STATE_METHODDEF … /*********************************************/ /* the _testsinglephase module (and aliases) */ /*********************************************/ /* This ia more typical of legacy extensions in the wild: - single-phase init - no module state - does not support repeated initialization (so m_copy is used) - the module def is cached in _PyRuntime.extensions (by name/filename) Also note that, because the module has single-phase init, it is cached in interp->module_by_index (using mod->md_def->m_base.m_index). */ static PyMethodDef TestMethods_Basic[] = …; static struct PyModuleDef _testsinglephase_basic = …; static PyObject * init__testsinglephase_basic(PyModuleDef *def) { … } PyMODINIT_FUNC PyInit__testsinglephase(void) { … } PyMODINIT_FUNC PyInit__testsinglephase_basic_wrapper(void) { … } PyMODINIT_FUNC PyInit__testsinglephase_basic_copy(void) { … } /*******************************************/ /* the _testsinglephase_with_reinit module */ /*******************************************/ /* This ia less typical of legacy extensions in the wild: - single-phase init (same as _testsinglephase above) - no module state - supports repeated initialization (so m_copy is not used) - the module def is not cached in _PyRuntime.extensions At this point most modules would reach for multi-phase init (PEP 489). However, module state has been around a while (PEP 3121), and most extensions predate multi-phase init. (This module is basically the same as _testsinglephase, but supports repeated initialization.) */ static PyMethodDef TestMethods_Reinit[] = …; static struct PyModuleDef _testsinglephase_with_reinit = …; PyMODINIT_FUNC PyInit__testsinglephase_with_reinit(void) { … } /******************************************/ /* the _testsinglephase_with_state module */ /******************************************/ /* This is less typical of legacy extensions in the wild: - single-phase init (same as _testsinglephase above) - has some module state - supports repeated initialization (so m_copy is not used) - the module def is not cached in _PyRuntime.extensions At this point most modules would reach for multi-phase init (PEP 489). However, module state has been around a while (PEP 3121), and most extensions predate multi-phase init. */ static PyMethodDef TestMethods_WithState[] = …; static struct PyModuleDef _testsinglephase_with_state = …; PyMODINIT_FUNC PyInit__testsinglephase_with_state(void) { … } /****************************************************/ /* the _testsinglephase_*_check_cache_first modules */ /****************************************************/ /* Each of these modules should only be freshly loaded. That means clearing the caches and each module def's m_base after each load. */ static struct PyModuleDef _testsinglephase_check_cache_first = …; PyMODINIT_FUNC PyInit__testsinglephase_check_cache_first(void) { … } static struct PyModuleDef _testsinglephase_with_reinit_check_cache_first = …; PyMODINIT_FUNC PyInit__testsinglephase_with_reinit_check_cache_first(void) { … } static struct PyModuleDef _testsinglephase_with_state_check_cache_first = …; PyMODINIT_FUNC PyInit__testsinglephase_with_state_check_cache_first(void) { … } /****************************************/ /* the _testsinglephase_circular module */ /****************************************/ static PyObject *static_module_circular; static PyObject * circularmod_clear_static_var(PyObject *self, PyObject *arg) { … } static struct PyModuleDef _testsinglephase_circular = …; PyMODINIT_FUNC PyInit__testsinglephase_circular(void) { … }