@@ -111,6 +111,24 @@ def require_frozen(module, *, skip=True):
111
111
def require_pure_python (module , * , skip = False ):
112
112
_require_loader (module , SourceFileLoader , skip )
113
113
114
+ def create_extension_loader (modname , filename ):
115
+ # Apple extensions must be distributed as frameworks. This requires
116
+ # a specialist loader.
117
+ if is_apple_mobile :
118
+ return AppleFrameworkLoader (modname , filename )
119
+ else :
120
+ return ExtensionFileLoader (modname , filename )
121
+
122
+ def import_extension_from_file (modname , filename , * , put_in_sys_modules = True ):
123
+ loader = create_extension_loader (modname , filename )
124
+ spec = importlib .util .spec_from_loader (modname , loader )
125
+ module = importlib .util .module_from_spec (spec )
126
+ loader .exec_module (module )
127
+ if put_in_sys_modules :
128
+ sys .modules [modname ] = module
129
+ return module
130
+
131
+
114
132
def remove_files (name ):
115
133
for f in (name + ".py" ,
116
134
name + ".pyc" ,
@@ -1913,6 +1931,37 @@ def test_absolute_circular_submodule(self):
1913
1931
str (cm .exception ),
1914
1932
)
1915
1933
1934
+ @requires_singlephase_init
1935
+ @unittest .skipIf (_testsinglephase is None , "test requires _testsinglephase module" )
1936
+ def test_singlephase_circular (self ):
1937
+ """Regression test for gh-123950
1938
+
1939
+ Import a single-phase-init module that imports itself
1940
+ from the PyInit_* function (before it's added to sys.modules).
1941
+ Manages its own cache (which is `static`, and so incompatible
1942
+ with multiple interpreters or interpreter reset).
1943
+ """
1944
+ name = '_testsinglephase_circular'
1945
+ helper_name = 'test.test_import.data.circular_imports.singlephase'
1946
+ with uncache (name , helper_name ):
1947
+ filename = _testsinglephase .__file__
1948
+ # We don't put the module in sys.modules: that the *inner*
1949
+ # import should do that.
1950
+ mod = import_extension_from_file (name , filename ,
1951
+ put_in_sys_modules = False )
1952
+
1953
+ self .assertEqual (mod .helper_mod_name , helper_name )
1954
+ self .assertIn (name , sys .modules )
1955
+ self .assertIn (helper_name , sys .modules )
1956
+
1957
+ self .assertIn (name , sys .modules )
1958
+ self .assertIn (helper_name , sys .modules )
1959
+ self .assertNotIn (name , sys .modules )
1960
+ self .assertNotIn (helper_name , sys .modules )
1961
+ self .assertIs (mod .clear_static_var (), mod )
1962
+ _testinternalcapi .clear_extension ('_testsinglephase_circular' ,
1963
+ mod .__spec__ .origin )
1964
+
1916
1965
def test_unwritable_module (self ):
1917
1966
self .addCleanup (unload , "test.test_import.data.unwritable" )
1918
1967
self .addCleanup (unload , "test.test_import.data.unwritable.x" )
@@ -1952,14 +2001,6 @@ def pipe(self):
1952
2001
os .set_blocking (r , False )
1953
2002
return (r , w )
1954
2003
1955
- def create_extension_loader (self , modname , filename ):
1956
- # Apple extensions must be distributed as frameworks. This requires
1957
- # a specialist loader.
1958
- if is_apple_mobile :
1959
- return AppleFrameworkLoader (modname , filename )
1960
- else :
1961
- return ExtensionFileLoader (modname , filename )
1962
-
1963
2004
def import_script (self , name , fd , filename = None , check_override = None ):
1964
2005
override_text = ''
1965
2006
if check_override is not None :
@@ -2176,11 +2217,7 @@ def test_multi_init_extension_compat(self):
2176
2217
def test_multi_init_extension_non_isolated_compat (self ):
2177
2218
modname = '_test_non_isolated'
2178
2219
filename = _testmultiphase .__file__
2179
- loader = self .create_extension_loader (modname , filename )
2180
- spec = importlib .util .spec_from_loader (modname , loader )
2181
- module = importlib .util .module_from_spec (spec )
2182
- loader .exec_module (module )
2183
- sys .modules [modname ] = module
2220
+ module = import_extension_from_file (modname , filename )
2184
2221
2185
2222
require_extension (module )
2186
2223
with self .subTest (f'{ modname } : isolated' ):
@@ -2195,11 +2232,7 @@ def test_multi_init_extension_non_isolated_compat(self):
2195
2232
def test_multi_init_extension_per_interpreter_gil_compat (self ):
2196
2233
modname = '_test_shared_gil_only'
2197
2234
filename = _testmultiphase .__file__
2198
- loader = self .create_extension_loader (modname , filename )
2199
- spec = importlib .util .spec_from_loader (modname , loader )
2200
- module = importlib .util .module_from_spec (spec )
2201
- loader .exec_module (module )
2202
- sys .modules [modname ] = module
2235
+ module = import_extension_from_file (modname , filename )
2203
2236
2204
2237
require_extension (module )
2205
2238
with self .subTest (f'{ modname } : isolated, strict' ):
0 commit comments