cpython/Modules/_testsinglephase.c


/* 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)
{}