Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Fix Refleak in test_import #102251

Closed
Closed
Labels
3.12only security fixesextension-modulesC modules in the Modules dirtype-featureA feature request or enhancement
@ericsnowcurrently

Description

@ericsnowcurrently

Commit096d009 (forgh-101758) resulted in refleak failures. It's unclear if the leak is new or if the test simply exposed it. In984f8ab, 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 seegh-101758.)

Loading an extension module involves meaningfully different code paths depending on the content of thePyModuleDef. 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)
    • single-phase init and multi-phase init modules can have a value of 0 or greater
    • such modules must not have any process-global state
    • only single-phase init modules can have a value of -1, which means the module does not support being loaded more than once
    • such modules may have process-global state
  • PyModuleDef.m_base.m_index - the index intoPyInterpreterState.imports.modules_by_index (same index used for every interpreter)
    • set byPyModuleDef_Init() (e.g. when the module is created)
  • PyModuleDef.m_base.m_copy - a copy of the__dict__ of the last time a module wasloaded using this def (only single-phase init wherem_size is -1)
    • set exclusively byfix_up_extension()
    • used exclusively byimport_find_extension()
    • cleared by (replaced in)fix_up_extension() ifm_copy was already set (e.g. multiple interpreters, multiple modules in same file using same def)
    • cleared by_PyImport_ClearModulesByIndex() during interpreter finalization
  • _PyRuntime.imports.extensions - a dict mapping(filename, name) toPyModuleDef
    • entry set exclusively byfix_up_extension() (only for single-phase init modules)
    • entry used exclusively byimport_find_extension()
    • entry cleared by_PyImport_Fini() at runtime finalization
  • interp->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
    • entry set normally only byfix_up_extension() andimport_find_extension() (only single-phase init modules)
    • (entry also set byPyState_AddModule())
    • used exclusively byPyState_FindModule()
    • entry cleared normally by_PyImport_ClearModulesByIndex() during interpreter finalization
    • (entry also cleared byPyState_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>()
              • sets process-global state (only wherem_size == -1)
              • PyModule_Create()
              • populates per-interpreter module state (only wherem_size > 0)
              • sets module attrs (on__dict__)
            • setsdef->m_base.m_init (only needed for single-phase-init wherem_size >=0)
            • _PyImport_FixupExtensionObject()
              • setssys.modules[spec.name]
              • fix_up_extension()
                • setinterp->imports.modules_by_index[def->m_base.m_index]
                • cleardef->m_base.m_copy (only if set and only ifm_size == -1)
                • setdef->m_base.m_copy to a copy of the module's__dict__ (only ifm_size == -1)
                • set_PyRuntime.imports.extensions[(filename, name)]
      • sets missing module attrs (e.g.__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()
        • (only if there's a_PyRuntime.imports.extensions entry)
          • clear the module def'sm_copy
          • replace theinterp->imports.modules_by_index entry withPy_None
          • delete the_PyRuntime.imports.extensions entry

Linked PRs

Metadata

Metadata

Assignees

No one assigned

    Labels

    3.12only security fixesextension-modulesC modules in the Modules dirtype-featureA feature request or enhancement

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions


      [8]ページ先頭

      ©2009-2025 Movatter.jp