@@ -2285,6 +2285,107 @@ def test_disallowed_reimport(self):
2285
2285
2286
2286
2287
2287
class TestSinglePhaseSnapshot (ModuleSnapshot ):
2288
+ """A representation of a single-phase init module for testing.
2289
+
2290
+ Fields from ModuleSnapshot:
2291
+
2292
+ * id - id(mod)
2293
+ * module - mod or a SimpleNamespace with __file__ & __spec__
2294
+ * ns - a shallow copy of mod.__dict__
2295
+ * ns_id - id(mod.__dict__)
2296
+ * cached - sys.modules[name] (or None if not there or not snapshotable)
2297
+ * cached_id - id(sys.modules[name]) (or None if not there)
2298
+
2299
+ Extra fields:
2300
+
2301
+ * summed - the result of calling "mod.sum(1, 2)"
2302
+ * lookedup - the result of calling "mod.look_up_self()"
2303
+ * lookedup_id - the object ID of self.lookedup
2304
+ * state_initialized - the result of calling "mod.state_initialized()"
2305
+ * init_count - (optional) the result of calling "mod.initialized_count()"
2306
+
2307
+ Overridden methods from ModuleSnapshot:
2308
+
2309
+ * from_module()
2310
+ * parse()
2311
+
2312
+ Other methods from ModuleSnapshot:
2313
+
2314
+ * build_script()
2315
+ * from_subinterp()
2316
+
2317
+ ----
2318
+
2319
+ There are 5 modules in Modules/_testsinglephase.c:
2320
+
2321
+ * _testsinglephase
2322
+ * has global state
2323
+ * extra loads skip the init function, copy def.m_base.m_copy
2324
+ * counts calls to init function
2325
+ * _testsinglephase_basic_wrapper
2326
+ * _testsinglephase by another name (and separate init function symbol)
2327
+ * _testsinglephase_basic_copy
2328
+ * same as _testsinglephase but with own def (and init func)
2329
+ * _testsinglephase_with_reinit
2330
+ * has no global or module state
2331
+ * mod.state_initialized returns None
2332
+ * an extra load in the main interpreter calls the cached init func
2333
+ * an extra load in legacy subinterpreters does a full load
2334
+ * _testsinglephase_with_state
2335
+ * has module state
2336
+ * an extra load in the main interpreter calls the cached init func
2337
+ * an extra load in legacy subinterpreters does a full load
2338
+
2339
+ (See Modules/_testsinglephase.c for more info.)
2340
+
2341
+ For all those modules, the snapshot after the initial load (not in
2342
+ the global extensions cache) would look like the following:
2343
+
2344
+ * initial load
2345
+ * id: ID of nww module object
2346
+ * ns: exactly what the module init put there
2347
+ * ns_id: ID of new module's __dict__
2348
+ * cached_id: same as self.id
2349
+ * summed: 3 (never changes)
2350
+ * lookedup_id: same as self.id
2351
+ * state_initialized: a timestamp between the time of the load
2352
+ and the time of the snapshot
2353
+ * init_count: 1 (None for _testsinglephase_with_reinit)
2354
+
2355
+ For the other scenarios it varies.
2356
+
2357
+ For the _testsinglephase, _testsinglephase_basic_wrapper, and
2358
+ _testsinglephase_basic_copy modules, the snapshot should look
2359
+ like the following:
2360
+
2361
+ * reloaded
2362
+ * id: no change
2363
+ * ns: matches what the module init function put there,
2364
+ including the IDs of all contained objects,
2365
+ plus any extra attributes added before the reload
2366
+ * ns_id: no change
2367
+ * cached_id: no change
2368
+ * lookedup_id: no change
2369
+ * state_initialized: no change
2370
+ * init_count: no change
2371
+ * already loaded
2372
+ * (same as initial load except for ns and state_initialized)
2373
+ * ns: matches the initial load, incl. IDs of contained objects
2374
+ * state_initialized: no change from initial load
2375
+
2376
+ For _testsinglephase_with_reinit:
2377
+
2378
+ * reloaded: same as initial load (old module & ns is discarded)
2379
+ * already loaded: same as initial load (old module & ns is discarded)
2380
+
2381
+ For _testsinglephase_with_state:
2382
+
2383
+ * reloaded
2384
+ * (same as initial load (old module & ns is discarded),
2385
+ except init_count)
2386
+ * init_count: increase by 1
2387
+ * already loaded: same as reloaded
2388
+ """
2288
2389
2289
2390
@classmethod
2290
2391
def from_module (cls , mod ):
@@ -2901,17 +3002,18 @@ def test_basic_multiple_interpreters_deleted_no_reset(self):
2901
3002
# * module's global state was initialized but cleared
2902
3003
2903
3004
# Start with an interpreter that gets destroyed right away.
2904
- base = self .import_in_subinterp (postscript = '''
2905
- # Attrs set after loading are not in m_copy.
2906
- mod.spam = 'spam, spam, mash, spam, eggs, and spam'
2907
- ''' )
3005
+ base = self .import_in_subinterp (
3006
+ postscript = '''
3007
+ # Attrs set after loading are not in m_copy.
3008
+ mod.spam = 'spam, spam, mash, spam, eggs, and spam'
3009
+ ''' )
2908
3010
self .check_com
F438
mon (base )
2909
3011
self .check_fresh (base )
2910
3012
2911
3013
# At this point:
2912
3014
# * alive in 0 interpreters
2913
3015
# * module def in _PyRuntime.imports.extensions
2914
- # * mod init func ran again
3016
+ # * mod init func ran for the first time (since reset)
2915
3017
# * m_copy is NULL (claered when the interpreter was destroyed)
2916
3018
# * module's global state was initialized, not reset
2917
3019
@@ -2923,7 +3025,7 @@ def test_basic_multiple_interpreters_deleted_no_reset(self):
2923
3025
# At this point:
2924
3026
# * alive in 1 interpreter (interp1)
2925
3027
# * module def still in _PyRuntime.imports.extensions
2926
- # * mod init func ran again
3028
+ # * mod init func ran for the second time (since reset)
2927
3029
# * m_copy was copied from interp1 (was NULL)
2928
3030
# * module's global state was updated, not reset
2929
3031
@@ -2935,7 +3037,7 @@ def test_basic_multiple_interpreters_deleted_no_reset(self):
2935
3037
# At this point:
2936
3038
# * alive in 2 interpreters (interp1, interp2)
2937
3039
# * module def still in _PyRuntime.imports.extensions
2938
- # * mod init func ran again
3040
+ # * mod init func did not run again
2939
3041
# * m_copy was copied from interp2 (was from interp1)
2940
3042
# * module's global state was updated, not reset
2941
3043
0 commit comments