@@ -1982,9 +1982,7 @@ def parse(cls, text):
19821982 return self
19831983
19841984
1985- @requires_singlephase_init
1986- class SinglephaseInitTests (unittest .TestCase ):
1987-
1985+ class SinglephaseInitTestMixin :
19881986 NAME = '_testsinglephase'
19891987
19901988 @classmethod
@@ -2236,6 +2234,8 @@ def check_copied(self, loaded, base):
22362234 self .assertEqual (snap .state_initialized ,
22372235 base .snapshot .state_initialized )
22382236
2237+ @requires_singlephase_init
2238+ class SinglephaseInitTests (SinglephaseInitTestMixin , unittest .TestCase ):
22392239 #########################
22402240 # the tests
22412241
@@ -2499,8 +2499,8 @@ def test_basic_multiple_interpreters_main_no_reset(self):
24992499 # * module's global state was updated, not reset
25002500
25012501 @requires_subinterpreters
2502- def test_basic_multiple_interpreters_deleted_no_reset_skip_ref_leak_test (self ):
2503- # without resetting; already loaded in a deleted interpreter
2502+ def test_basic_multiple_interpreters_reset_each (self ):
2503+ # resetting between each interpreter
25042504
25052505 # At this point:
25062506 # * alive in 0 interpreters
@@ -2514,61 +2514,65 @@ def test_basic_multiple_interpreters_deleted_no_reset_skip_ref_leak_test(self):
25142514 interpid1 = self .add_subinterpreter ()
25152515 interpid2 = self .add_subinterpreter ()
25162516
2517- # First, load in the main interpreter but then completely clear it.
2518- loaded_main = self .load (self .NAME )
2519- loaded_main .module ._clear_globals ()
2520- _testinternalcapi .clear_extension (self .NAME , self .FILE )
2521-
2522- # At this point:
2523- # * alive in 0 interpreters
2524- # * module def loaded already
2525- # * module def was in _PyRuntime.imports.extensions, but cleared
2526- # * mod init func ran for the first time (since reset, at least)
2527- # * m_copy was set, but cleared (was NULL)
2528- # * module's global state was initialized but cleared
2529-
2530- # Start with an interpreter that gets destroyed right away.
2531- base = self .import_in_subinterp (postscript = '''
2517+ # Use an interpreter that gets destroyed right away.
2518+ loaded = self .import_in_subinterp (
2519+ postscript = '''
25322520 # Attrs set after loading are not in m_copy.
25332521 mod.spam = 'spam, spam, mash, spam, eggs, and spam'
2534- ''' )
2535- self .check_common (base )
2536- self .check_fresh (base )
2522+ ''' ,
2523+ postcleanup = True ,
2524+ )
2525+ self .check_common (loaded )
2526+ self .check_fresh (loaded )
25372527
25382528 # At this point:
25392529 # * alive in 0 interpreters
25402530 # * module def in _PyRuntime.imports.extensions
2541- # * mod init func ran again
2531+ # * mod init func ran for the first time (since reset, at least)
25422532 # * m_copy is NULL (claered when the interpreter was destroyed)
25432533 # * module's global state was initialized, not reset
25442534
25452535 # Use a subinterpreter that sticks around.
2546- loaded_interp1 = self .import_in_subinterp (interpid1 )
2547- self .check_common (loaded_interp1 )
2548- self .check_semi_fresh ( loaded_interp1 , loaded_main , base )
2536+ loaded = self .import_in_subinterp (interpid1 , postcleanup = True )
2537+ self .check_common (loaded )
2538+ self .check_fresh ( loaded )
25492539
25502540 # At this point:
25512541 # * alive in 1 interpreter (interp1)
25522542 # * module def still in _PyRuntime.imports.extensions
25532543 # * mod init func ran again
25542544 # * m_copy was copied from interp1 (was NULL)
2555- # * module's global state was updated , not reset
2545+ # * module's global state was initialized , not reset
25562546
25572547 # Use a subinterpreter while the previous one is still alive.
2558- loaded_interp2 = self .import_in_subinterp (interpid2 )
2559- self .check_common (loaded_interp2 )
2560- self .check_copied ( loaded_interp2 , loaded_interp1 )
2548+ loaded = self .import_in_subinterp (interpid2 , postcleanup = True )
2549+ self .check_common (loaded )
2550+ self .check_fresh ( loaded )
25612551
25622552 # At this point:
2563- # * alive in 2 interpreters (interp1 , interp2)
2553+ # * alive in 2 interpreters (interp2 , interp2)
25642554 # * module def still in _PyRuntime.imports.extensions
25652555 # * mod init func ran again
25662556 # * m_copy was copied from interp2 (was from interp1)
2567- # * module's global state was updated, not reset
2557+ # * module's global state was initialized, not reset
2558+
2559+
2560+ @requires_singlephase_init
2561+ class SinglephaseInitTestsNoRerun (SinglephaseInitTestMixin , unittest .TestCase ):
2562+ # Tests does not support rerunning are collected in this class
2563+ _has_run = False
2564+
2565+ @classmethod
2566+ def setUpClass (cls ):
2567+ super ().setUpClass ()
2568+ if cls ._has_run :
2569+ raise unittest .SkipTest ("requires single-phase init" )
2570+ cls ._has_run = True
25682571
25692572 @requires_subinterpreters
2570- def test_basic_multiple_interpreters_reset_each (self ):
2571- # resetting between each interpreter
2573+ def test_basic_multiple_interpreters_deleted_no_reset (self ):
2574+ # without resetting; already loaded in a deleted interpreter
2575+ # This test intentionally leaks references
25722576
25732577 # At this point:
25742578 # * alive in 0 interpreters
@@ -2582,47 +2586,57 @@ def test_basic_multiple_interpreters_reset_each(self):
25822586 interpid1 = self .add_subinterpreter ()
25832587 interpid2 = self .add_subinterpreter ()
25842588
2585- # Use an interpreter that gets destroyed right away.
2586- loaded = self .import_in_subinterp (
2587- postscript = '''
2589+ # First, load in the main interpreter but then completely clear it.
2590+ loaded_main = self .load (self .NAME )
2591+ loaded_main .module ._clear_globals ()
2592+ _testinternalcapi .clear_extension (self .NAME , self .FILE )
2593+
2594+ # At this point:
2595+ # * alive in 0 interpreters
2596+ # * module def loaded already
2597+ # * module def was in _PyRuntime.imports.extensions, but cleared
2598+ # * mod init func ran for the first time (since reset, at least)
2599+ # * m_copy was set, but cleared (was NULL)
2600+ # * module's global state was initialized but cleared
2601+
2602+ # Start with an interpreter that gets destroyed right away.
2603+ base = self .import_in_subinterp (postscript = '''
25882604 # Attrs set after loading are not in m_copy.
25892605 mod.spam = 'spam, spam, mash, spam, eggs, and spam'
2590- ''' ,
2591- postcleanup = True ,
2592- )
2593- self .check_common (loaded )
2594- self .check_fresh (loaded )
2606+ ''' )
2607+ self .check_common (base )
2608+ self .check_fresh (base )
25952609
25962610 # At this point:
25972611 # * alive in 0 interpreters
25982612 # * module def in _PyRuntime.imports.extensions
2599- # * mod init func ran for the first time (since reset, at least)
2613+ # * mod init func ran again
26002614 # * m_copy is NULL (claered when the interpreter was destroyed)
26012615 # * module's global state was initialized, not reset
26022616
26032617 # Use a subinterpreter that sticks around.
2604- loaded = self .import_in_subinterp (interpid1 , postcleanup = True )
2605- self .check_common (loaded )
2606- self .check_fresh ( loaded )
2618+ loaded_interp1 = self .import_in_subinterp (interpid1 )
2619+ self .check_common (loaded_interp1 )
2620+ self .check_semi_fresh ( loaded_interp1 , loaded_main , base )
26072621
26082622 # At this point:
26092623 # * alive in 1 interpreter (interp1)
26102624 # * module def still in _PyRuntime.imports.extensions
26112625 # * mod init func ran again
26122626 # * m_copy was copied from interp1 (was NULL)
2613- # * module's global state was initialized , not reset
2627+ # * module's global state was updated , not reset
26142628
26152629 # Use a subinterpreter while the previous one is still alive.
2616- loaded = self .import_in_subinterp (interpid2 , postcleanup = True )
2617- self .check_common (loaded )
2618- self .check_fresh ( loaded )
2630+ loaded_interp2 = self .import_in_subinterp (interpid2 )
2631+ self .check_common (loaded_interp2 )
2632+ self .check_copied ( loaded_interp2 , loaded_interp1 )
26192633
26202634 # At this point:
2621- # * alive in 2 interpreters (interp2 , interp2)
2635+ # * alive in 2 interpreters (interp1 , interp2)
26222636 # * module def still in _PyRuntime.imports.extensions
26232637 # * mod init func ran again
26242638 # * m_copy was copied from interp2 (was from interp1)
2625- # * module's global state was initialized , not reset
2639+ # * module's global state was updated , not reset
26262640
26272641
26282642@cpython_only
0 commit comments