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

gh-117953: Other Cleanups in the Extensions Machinery#118206

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
Show file tree
Hide file tree
Changes from1 commit
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
PrevPrevious commit
NextNext commit
Add notes about the _testsinglephase module.
  • Loading branch information
@ericsnowcurrently
ericsnowcurrently committedMay 2, 2024
commitd59a95ed83936cfdee5a032a86e8ca43fe92a627
101 changes: 101 additions & 0 deletionsLib/test/test_import/__init__.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -2285,6 +2285,107 @@ def test_disallowed_reimport(self):


class TestSinglePhaseSnapshot(ModuleSnapshot):
"""A representation of a single-phase init module for testing.

Fields from ModuleSnapshot:

* id - id(mod)
* module - mod or a SimpleNamespace with __file__ & __spec__
* ns - a shallow copy of mod.__dict__
* ns_id - id(mod.__dict__)
* cached - sys.modules[name] (or None if not there or not snapshotable)
* cached_id - id(sys.modules[name]) (or None if not there)

Extra fields:

* summed - the result of calling "mod.sum(1, 2)"
* lookedup - the result of calling "mod.look_up_self()"
* lookedup_id - the object ID of self.lookedup
* state_initialized - the result of calling "mod.state_initialized()"
* init_count - (optional) the result of calling "mod.initialized_count()"

Overridden methods from ModuleSnapshot:

* from_module()
* parse()

Other methods from ModuleSnapshot:

* build_script()
* from_subinterp()

----

There are 5 modules in Modules/_testsinglephase.c:

* _testsinglephase
* has global state
* extra loads skip the init function, copy def.m_base.m_copy
* counts calls to init function
* _testsinglephase_basic_wrapper
* _testsinglephase by another name (and separate init function symbol)
* _testsinglephase_basic_copy
* same as _testsinglephase but with own def (and init func)
* _testsinglephase_with_reinit
* has no global or module state
* mod.state_initialized returns None
* an extra load in the main interpreter calls the cached init func
* an extra load in legacy subinterpreters does a full load
* _testsinglephase_with_state
* has module state
* an extra load in the main interpreter calls the cached init func
* an extra load in legacy subinterpreters does a full load

(See Modules/_testsinglephase.c for more info.)

For all those modules, the snapshot after the initial load (not in
the global extensions cache) would look like the following:

* initial load
* id: ID of nww module object
* ns: exactly what the module init put there
* ns_id: ID of new module's __dict__
* cached_id: same as self.id
* summed: 3 (never changes)
* lookedup_id: same as self.id
* state_initialized: a timestamp between the time of the load
and the time of the snapshot
* init_count: 1 (None for _testsinglephase_with_reinit)

For the other scenarios it varies.

For the _testsinglephase, _testsinglephase_basic_wrapper, and
_testsinglephase_basic_copy modules, the snapshot should look
like the following:

* reloaded
* id: no change
* ns: matches what the module init function put there,
including the IDs of all contained objects,
plus any extra attributes added before the reload
* ns_id: no change
* cached_id: no change
* lookedup_id: no change
* state_initialized: no change
* init_count: no change
* already loaded
* (same as initial load except for ns and state_initialized)
* ns: matches the initial load, incl. IDs of contained objects
* state_initialized: no change from initial load

For _testsinglephase_with_reinit:

* reloaded: same as initial load (old module & ns is discarded)
* already loaded: same as initial load (old module & ns is discarded)

For _testsinglephase_with_state:

* reloaded
* (same as initial load (old module & ns is discarded),
except init_count)
* init_count: increase by 1
* already loaded: same as reloaded
"""

@classmethod
def from_module(cls, mod):
Expand Down
168 changes: 167 additions & 1 deletionModules/_testsinglephase.c
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,172 @@

/* Testing module for single-phase initialization of extension modules
*/

This file contains 5 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)
```

Here are the 5 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

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 1
#endif
Expand Down

[8]ページ先頭

©2009-2025 Movatter.jp