@@ -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" ,
@@ -1894,6 +1912,37 @@ def test_absolute_circular_submodule(self):
1894
1912
str (cm .exception ),
1895
1913
)
1896
1914
1915
+ @requires_singlephase_init
1916
+ @unittest .skipIf (_testsinglephase is None , "test requires _testsinglephase module" )
1917
+ def test_singlephase_circular (self ):
1918
+ """Regression test for gh-123950
1919
+
1920
+ Import a single-phase-init module that imports itself
1921
+ from the PyInit_* function (before it's added to sys.modules).
1922
+ Manages its own cache (which is `static`, and so incompatible
1923
+ with multiple interpreters or interpreter reset).
1924
+ """
1925
+ name = '_testsinglephase_circular'
1926
+ helper_name = 'test.test_import.data.circular_imports.singlephase'
1927
+ with uncache (name , helper_name ):
1928
+ filename = _testsinglephase .__file__
1929
+ # We don't put the module in sys.modules: that the *inner*
1930
+ # import should do that.
1931
+ mod = import_extension_from_file (name , filename ,
1932
+ put_in_sys_modules = False )
1933
+
1934
+ self .assertEqual (mod .helper_mod_name , helper_name )
1935
+ self .assertIn (name , sys .modules )
1936
+ self .assertIn (helper_name , sys .modules )
1937
+
1938
+ self .assertIn (name , sys .modules )
1939
+ self .assertIn (helper_name , sys .modules )
1940
+ self .assertNotIn (name , sys .modules )
1941
+ self .assertNotIn (helper_name , sys .modules )
1942
+ self .assertIs (mod .clear_static_var (), mod )
1943
+ _testinternalcapi .clear_extension ('_testsinglephase_circular' ,
1944
+ mod .__spec__ .origin )
1945
+
1897
1946
def test_unwritable_module (self ):
1898
1947
self .addCleanup (unload , "test.test_import.data.unwritable" )
1899
1948
self .addCleanup (unload , "test.test_import.data.unwritable.x" )
@@ -1933,14 +1982,6 @@ def pipe(self):
1933
1982
os .set_blocking (r , False )
1934
1983
return (r , w )
1935
1984
1936
- def create_extension_loader (self , modname , filename ):
1937
- # Apple extensions must be distributed as frameworks. This requires
1938
- # a specialist loader.
1939
- if is_apple_mobile :
1940
- return AppleFrameworkLoader (modname , filename )
1941
- else :
1942
- return ExtensionFileLoader (modname , filename )
1943
-
1944
1985
def import_script (self , name , fd , filename = None , check_override = None ):
1945
1986
override_text = ''
1946
1987
if check_override is not None :
@@ -2157,11 +2198,7 @@ def test_multi_init_extension_compat(self):
2157
2198
def test_multi_init_extension_non_isolated_compat (self ):
2158
2199
modname = '_test_non_isolated'
2159
2200
filename = _testmultiphase .__file__
2160
- loader = self .create_extension_loader (modname , filename )
2161
- spec = importlib .util .spec_from_loader (modname , loader )
2162
- module = importlib .util .module_from_spec (spec )
2163
- loader .exec_module (module )
2164
- sys .modules [modname ] = module
2201
+ module = import_extension_from_file (modname , filename )
2165
2202
2166
2203
require_extension (module )
2167
2204
with self .subTest (f'{ modname } : isolated' ):
@@ -2176,11 +2213,7 @@ def test_multi_init_extension_non_isolated_compat(self):
2176
2213
def test_multi_init_extension_per_interpreter_gil_compat (self ):
2177
2214
modname = '_test_shared_gil_only'
2178
2215
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
2216
+ module = import_extension_from_file (modname , filename )
2184
2217
2185
2218
require_extension (module )
2186
2219
with self .subTest (f'{ modname } : isolated, strict' ):
0 commit comments