Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 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/libregrtest/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,13 @@ def parse_args(self, kwargs):
if ns.tempdir:
ns.tempdir = os.path.expanduser(ns.tempdir)

# introduce a way to skip the ref leak tests
if ns.huntrleaks:
if ns.ignore_tests is None:
ns.ignore_tests = ["*skip_ref_leak_test"]
else:
ns.ignore_tests.append("*skip_ref_leak_test")

self.ns = ns

def find_tests(self, tests):
Expand Down
13 changes: 8 additions & 5 deletions Lib/test/test_import/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1980,10 +1980,6 @@ class SinglephaseInitTests(unittest.TestCase):

@classmethod
def setUpClass(cls):
if '-R' in sys.argv or '--huntrleaks' in sys.argv:
# https://github.com/python/cpython/issues/102251
raise unittest.SkipTest('unresolved refleaks (see gh-102251)')
Comment on lines -1992 to -1994
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This trick cannot be used with ./python -m test -j n, thus causing failures on CI.


spec = importlib.util.find_spec(cls.NAME)
from importlib.machinery import ExtensionFileLoader
cls.FILE = spec.origin
Expand Down Expand Up @@ -2330,6 +2326,8 @@ def test_variants(self):
self.assertIs(basic.look_up_self(), basic_lookedup)
self.assertEqual(basic.initialized_count(), expected_init_count)

loaded.module._clear_module_state()

def test_basic_reloaded(self):
# m_copy is copied into the existing module object.
# Global state is not changed.
Expand Down Expand Up @@ -2413,6 +2411,11 @@ def test_with_reinit_reloaded(self):

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

if hasattr(loaded.module, '_clear_module_state'):
loaded.module._clear_module_state()
if hasattr(reloaded.module, '_clear_module_state'):
reloaded.module._clear_module_state()

# 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 Expand Up @@ -2488,7 +2491,7 @@ def test_basic_multiple_interpreters_main_no_reset(self):
# * module's global state was updated, not reset

@requires_subinterpreters
def test_basic_multiple_interpreters_deleted_no_reset(self):
def test_basic_multiple_interpreters_deleted_no_reset_skip_ref_leak_test(self):
# without resetting; already loaded in a deleted interpreter

# At this point:
Expand Down
26 changes: 21 additions & 5 deletions Modules/_testsinglephase.c
Original file line number Diff line number Diff line change
Expand Up @@ -137,13 +137,10 @@ init_module(PyObject *module, module_state *state)

double d = _PyTime_AsSecondsDouble(state->initialized);
PyObject *initialized = PyFloat_FromDouble(d);
if (initialized == NULL) {
return -1;
}
if (PyModule_AddObjectRef(module, "_module_initialized", initialized) != 0) {
return -1;
}

Py_XDECREF(initialized);
return 0;
}

Expand Down Expand Up @@ -246,6 +243,24 @@ 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 @@ -406,7 +421,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 @@ -422,6 +437,7 @@ static PyMethodDef TestMethods_WithState[] = {
LOOK_UP_SELF_METHODDEF,
SUM_METHODDEF,
STATE_INITIALIZED_METHODDEF,
_CLEAR_MODULE_STATE_METHODDEF,
{NULL, NULL} /* sentinel */
};

Expand Down