-
-
Notifications
You must be signed in to change notification settings - Fork 30.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix Refleak in test_import #102251
Comments
FWIW, I've spent quite a bit of time over the last week trying to find the leak, without success. The following test method simplifies things to make it easier to chase the leak: def test_singlephase_cleared_globals(self):
name = '_testsinglephase'
fileobj, filename, _ = imp.find_module(name)
fileobj.close()
# Load the module.
mod = imp.load_dynamic(name, filename)
# Clear the module's process-global state.
mod._clear_globals()
self.assertEqual(mod.initialized(), 0)
self.assertEqual(mod.initialized_count(), -1)
# Clean up all traces of the module in the import state.
del sys.modules[name]
_testinternalcapi.clear_extension(name, filename) Run:
(UPDATE: That's basically what I later added in test_imp.SinglephaseInitTests.test_cleared_globals, Observations:
The following demonstrates that the leak does not exist between def test_singlephase_not_loaded(self):
name = '_testsinglephase'
fileobj, filename, _ = imp.find_module(name)
fileobj.close()
_testinternalcapi.clear_extension(name, filename) |
This is related to fixing the refleaks introduced by commit 096d009. I haven't been able to find the leak yet, but these changes are a consequence of that effort. This includes some cleanup, some tweaks to the existing tests, and a bunch of new test cases. The only change here that might have impact outside the tests in question is in imp.py, where I update imp.load_dynamic() to use spec_from_file_location() instead of creating a ModuleSpec directly. Also note that I've updated the tests to only skip if we're checking for refleaks (regrtest's --huntrleaks), whereas in gh-101969 I had skipped the tests entirely. The tests will be useful for some upcoming work and I'd rather the refleaks not hold that up. (It isn't clear how quickly we'll be able to fix the leaking code, though it will certainly be done in the short term.) #102251
I'm unassigning myself, but we'll make sure this gets addressed within the next few weeks. |
Just be aware that I am working on removing |
* main: (67 commits) pythongh-99108: Add missing md5/sha1 defines to Modules/Setup (python#102308) pythongh-100227: Move _str_replace_inf to PyInterpreterState (pythongh-102333) pythongh-100227: Move the dtoa State to PyInterpreterState (pythongh-102331) pythonGH-102305: Expand some macros in generated_cases.c.h (python#102309) Migrate to new PSF mailgun account (python#102284) pythongh-102192: Replace PyErr_Fetch/Restore etc by more efficient alternatives (in Python/) (python#102193) pythonGH-90744: Fix erroneous doc links in the sys module (python#101319) pythongh-87092: Make jump target label equal to the offset of the target in the instructions sequence (python#102093) pythongh-101101: Unstable C API tier (PEP 689) (pythonGH-101102) IDLE: Simplify DynOptionsMenu __init__code (python#101371) pythongh-101561: Add typing.override decorator (python#101564) pythongh-101825: Clarify that as_integer_ratio() output is always normalized (python#101843) pythongh-101773: Optimize creation of Fractions in private methods (python#101780) pythongh-102251: Updates to test_imp Toward Fixing Some Refleaks (pythongh-102254) pythongh-102296 Document that inspect.Parameter kinds support ordering (pythonGH-102297) pythongh-102250: Fix double-decref in COMPARE_AND_BRANCH error case (pythonGH-102287) pythongh-101100: Fix sphinx warnings in `types` module (python#102274) pythongh-91038: Change default argument value to `False` instead of `0` (python#31621) pythongh-101765: unicodeobject: use Py_XDECREF correctly (python#102283) [doc] Improve grammar/fix missing word (pythonGH-102060) ...
This is related to fixing the refleaks introduced by commit 096d009. I haven't been able to find the leak yet, but these changes are a consequence of that effort. This includes some cleanup, some tweaks to the existing tests, and a bunch of new test cases. The only change here that might have impact outside the tests in question is in imp.py, where I update imp.load_dynamic() to use spec_from_file_location() instead of creating a ModuleSpec directly. Also note that I've updated the tests to only skip if we're checking for refleaks (regrtest's --huntrleaks), whereas in gh-101969 I had skipped the tests entirely. The tests will be useful for some upcoming work and I'd rather the refleaks not hold that up. (It isn't clear how quickly we'll be able to fix the leaking code, though it will certainly be done in the short term.) python/cpython#102251
@sunmy2019 has some analysis of these refleaks in #103879 (comment) and nearby comments. |
I opened a PR for the ref leaks. |
…5082) Correctly decref 'initialized' in init_module() Co-authored-by: Erlend E. Aasland <erlend.aasland@protonmail.com>
pythonGH-105082) Correctly decref 'initialized' in init_module() (cherry picked from commit d14eb34) Co-authored-by: sunmy2019 <59365878+sunmy2019@users.noreply.github.com> Co-authored-by: Erlend E. Aasland <erlend.aasland@protonmail.com>
… test_import (pythonGH-105085) (cherry picked from commit a99b9d9) Co-authored-by: sunmy2019 <59365878+sunmy2019@users.noreply.github.com> Co-authored-by: Erlend E. Aasland <erlend.aasland@protonmail.com>
…import (#105085) Co-authored-by: Erlend E. Aasland <erlend.aasland@protonmail.com>
test_basic_multiple_interpreters_main_no_reset() does not support rerunning
Cc @vstinner |
On Python 3.12, test_import.test_concurrency() leaks on Windows:
|
I think this is an essentially new issue. See #104796 (comment) I would suggest opening a new one and closing this. |
@sunmy2019 created issue #106176. |
Commit 096d009 (for gh-101758) resulted in refleak failures. It's unclear if the leak is new or if the test simply exposed it. In 984f8ab, I skipped the leaking tests until we can fix the leak.
UPDATE: I've updates the tests to only skip when checking for refleaks. I've also added a few more tests.
Context
(expand)
(Also see gh-101758.)
Loading an extension module involves meaningfully different code paths depending on the content of the
PyModuleDef
. There's the difference between legacy (single-phase-init, PEP 3121) and modern (multi-phase-init, PEP 489) modules. For single-phase init, there are those that support being loaded more than once (m_size
>= 0) and those that can't (m_size
== -1). I've added several long comments in import.c explaining about single-phase init modules in detail. I also added some tests to verify the behavior of the single-phase-init cases more thoroughly. Those are the leaking tests.Relevant state:
PyModuleDef.m_size
- the size of the module's per-interpreter state (PyModuleObject.md_state
)PyModuleDef.m_base.m_index
- the index intoPyInterpreterState.imports.modules_by_index
(same index used for every interpreter)PyModuleDef_Init()
(e.g. when the module is created)PyModuleDef.m_base.m_copy
- a copy of the__dict__
of the last time a module was loaded using this def (only single-phase init wherem_size
is -1)fix_up_extension()
import_find_extension()
fix_up_extension()
ifm_copy
was already set (e.g. multiple interpreters, multiple modules in same file using same def)_PyImport_ClearModulesByIndex()
during interpreter finalization_PyRuntime.imports.extensions
- a dict mapping(filename, name)
toPyModuleDef
fix_up_extension()
(only for single-phase init modules)import_find_extension()
_PyImport_Fini()
at runtime finalizationinterp->imports.modules_by_index
- a list of single-phase-init modules loaded in this interpreter; lookups (e.g.PyState_FindModule()
) usePyModuleDef.m_base.m_index
fix_up_extension()
andimport_find_extension()
(only single-phase init modules)PyState_AddModule()
)PyState_FindModule()
_PyImport_ClearModulesByIndex()
during interpreter finalizationPyState_RemoveModule()
)Code path when loading a single-phase-init module for the first time (in import.c, unless otherwise noted):
imp.load_dynamic()
importlib._boostrap._load()
(using a spec withExtensionFileLoader
)ExtensionFileLoader.create_module()
(in _boostrap_external.py)_imp.create_dynamic()
(_imp_create_dynamic_impl()
)import_find_extension()
(not found in_PyRuntime.imports.extensions
)_PyImport_LoadDynamicModuleWithSpec()
(importdl.c)_PyImport_FindSharedFuncptr()
<module init func from loaded binary>()
m_size == -1
)PyModule_Create()
m_size > 0
)__dict__
)def->m_base.m_init
(only needed for single-phase-init wherem_size >=0
)_PyImport_FixupExtensionObject()
sys.modules[spec.name]
fix_up_extension()
interp->imports.modules_by_index[def->m_base.m_index]
def->m_base.m_copy
(only if set and only ifm_size == -1
)def->m_base.m_copy
to a copy of the module's__dict__
(only ifm_size == -1
)_PyRuntime.imports.extensions[(filename, name)]
__file__
)During testing we use a helper to erase (nearly) any evidence of the module having been imported before. That means clearing the state described above.
Here's the code path:
_testinternalcapi.clear_extension()
(Modules/_testinternalcapi.c)_PyImport_ClearExtension()
clear_singlephase_extension()
_PyRuntime.imports.extensions
entry)m_copy
interp->imports.modules_by_index
entry withPy_None
_PyRuntime.imports.extensions
entryLinked PRs
test_import
#104796The text was updated successfully, but these errors were encountered: