Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
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
7 changes: 7 additions & 0 deletions Lib/test/test_import/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2320,6 +2320,7 @@ def test_variants(self):
self.add_module_cleanup(name)
with self.subTest(name):
loaded = self.load(name)
self.addCleanup(loaded.module._clear_module_state)

self.check_common(loaded)
self.assertIsNot(loaded.snapshot.state_initialized, None)
Expand All @@ -2330,6 +2331,7 @@ def test_variants(self):
self.assertIs(basic.look_up_self(), basic_lookedup)
self.assertEqual(basic.initialized_count(), expected_init_count)


def test_basic_reloaded(self):
# m_copy is copied into the existing module object.
# Global state is not changed.
Expand Down Expand Up @@ -2388,6 +2390,10 @@ def test_with_reinit_reloaded(self):
loaded = self.load(name)
reloaded = self.re_load(name, loaded.module)

if name == f'{self.NAME}_with_state':
self.addCleanup(loaded.module._clear_module_state)
self.addCleanup(reloaded.module._clear_module_state)

self.check_common(loaded)
self.check_common(reloaded)

Expand All @@ -2413,6 +2419,7 @@ def test_with_reinit_reloaded(self):

self.assertIs(reloaded.snapshot.cached, reloaded.module)


# Currently, for every single-phrase init module loaded
# in multiple interpreters, those interpreters share a
# PyModuleDef for that object, which can be a problem.
Expand Down
22 changes: 21 additions & 1 deletion Modules/_testsinglephase.c
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,25 @@ basic__clear_globals(PyObject *self, PyObject *Py_UNUSED(ignored))
basic__clear_globals_doc}


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))
{
module_state *state = get_module_state(self);
if (state != NULL) {
clear_state(state);
}
Py_RETURN_NONE;
}

#define _CLEAR_MODULE_STATE_METHODDEF \
{"_clear_module_state", basic__clear_module_state, METH_NOARGS, \
basic__clear_module_state_doc}


/*********************************************/
/* the _testsinglephase module (and aliases) */
/*********************************************/
Expand Down Expand Up @@ -408,7 +427,7 @@ PyInit__testsinglephase_with_reinit(void)
/* the _testsinglephase_with_state module */
/******************************************/

/* This ia less typical of legacy extensions in the wild:
/* This is a less typical of legacy extensions in the wild:
- single-phase init (same as _testsinglephase above)
- has some module state
- supports repeated initialization
Expand All @@ -424,6 +443,7 @@ static PyMethodDef TestMethods_WithState[] = {
LOOK_UP_SELF_METHODDEF,
SUM_METHODDEF,
STATE_INITIALIZED_METHODDEF,
_CLEAR_MODULE_STATE_METHODDEF,
{NULL, NULL} /* sentinel */
};

Expand Down