3131 import _testmultiphase
3232except ImportError :
3333 _testmultiphase = None
34+ try :
35+ import _testsinglephase
36+ except ImportError :
37+ _testsinglephase = None
3438
3539# Skip this test if the _testcapi module isn't available.
3640_testcapi = import_helper .import_module ('_testcapi' )
@@ -1297,17 +1301,20 @@ def test_configured_settings(self):
12971301 """
12981302 import json
12991303
1304+ EXTENSIONS = 1 << 8
13001305 THREADS = 1 << 10
13011306 DAEMON_THREADS = 1 << 11
13021307 FORK = 1 << 15
13031308 EXEC = 1 << 16
13041309
1305- features = ['fork' , 'exec' , 'threads' , 'daemon_threads' ]
1310+ features = ['fork' , 'exec' , 'threads' , 'daemon_threads' , 'extensions' ]
13061311 kwlist = [f'allow_{ n } ' for n in features ]
1312+ kwlist [- 1 ] = 'check_multi_interp_extensions'
13071313 for config , expected in {
1308- (True , True , True , True ): FORK | EXEC | THREADS | DAEMON_THREADS ,
1309- (False , False , False , False ): 0 ,
1310- (False , False , True , False ): THREADS ,
1314+ (True , True , True , True , True ):
1315+ FORK | EXEC | THREADS | DAEMON_THREADS | EXTENSIONS ,
1316+ (False , False , False , False , False ): 0 ,
1317+ (False , False , True , False , True ): THREADS | EXTENSIONS ,
13111318 }.items ():
13121319 kwargs = dict (zip (kwlist , config ))
13131320 expected = {
@@ -1322,12 +1329,93 @@ def test_configured_settings(self):
13221329 json.dump(settings, stdin)
13231330 ''' )
13241331 with os .fdopen (r ) as stdout :
1325- support .run_in_subinterp_with_config (script , ** kwargs )
1332+ ret = support .run_in_subinterp_with_config (script , ** kwargs )
1333+ self .assertEqual (ret , 0 )
13261334 out = stdout .read ()
13271335 settings = json .loads (out )
13281336
13291337 self .assertEqual (settings , expected )
13301338
1339+ @unittest .skipIf (_testsinglephase is None , "test requires _testsinglephase module" )
1340+ @unittest .skipUnless (hasattr (os , "pipe" ), "requires os.pipe()" )
1341+ def test_overridden_setting_extensions_subinterp_check (self ):
1342+ """
1343+ PyInterpreterConfig.check_multi_interp_extensions can be overridden
1344+ with PyInterpreterState.override_multi_interp_extensions_check.
1345+ This verifies that the override works but does not modify
1346+ the underlying setting.
1347+ """
1348+ import json
1349+
1350+ EXTENSIONS = 1 << 8
1351+ THREADS = 1 << 10
1352+ DAEMON_THREADS = 1 << 11
1353+ FORK = 1 << 15
1354+ EXEC = 1 << 16
1355+ BASE_FLAGS = FORK | EXEC | THREADS | DAEMON_THREADS
1356+ base_kwargs = {
1357+ 'allow_fork' : True ,
1358+ 'allow_exec' : True ,
1359+ 'allow_threads' : True ,
1360+ 'allow_daemon_threads' : True ,
1361+ }
1362+
1363+ def check (enabled , override ):
1364+ kwargs = dict (
1365+ base_kwargs ,
1366+ check_multi_interp_extensions = enabled ,
1367+ )
1368+ flags = BASE_FLAGS | EXTENSIONS if enabled else BASE_FLAGS
1369+ settings = {
1370+ 'feature_flags' : flags ,
1371+ }
1372+
1373+ expected = {
1374+ 'requested' : override ,
1375+ 'override__initial' : 0 ,
1376+ 'override_after' : override ,
1377+ 'override_restored' : 0 ,
1378+ # The override should not affect the config or settings.
1379+ 'settings__initial' : settings ,
1380+ 'settings_after' : settings ,
1381+ 'settings_restored' : settings ,
1382+ # These are the most likely values to be wrong.
1383+ 'allowed__initial' : not enabled ,
1384+ 'allowed_after' : not ((override > 0 ) if override else enabled ),
1385+ 'allowed_restored' : not enabled ,
1386+ }
1387+
1388+ r , w = os .pipe ()
1389+ script = textwrap .dedent (f'''
1390+ from test.test_capi.check_config import run_singlephase_check
1391+ run_singlephase_check({ override } , { w } )
1392+ ''' )
1393+ with os .fdopen (r ) as stdout :
1394+ ret = support .run_in_subinterp_with_config (script , ** kwargs )
1395+ self .assertEqual (ret , 0 )
1396+ out = stdout .read ()
1397+ results = json .loads (out )
1398+
1399+ self .assertEqual (results , expected )
1400+
1401+ self .maxDiff = None
1402+
1403+ # setting: check disabled
1404+ with self .subTest ('config: check disabled; override: disabled' ):
1405+ check (False , - 1 )
1406+ with self .subTest ('config: check disabled; override: use config' ):
1407+ check (False , 0 )
1408+ with self .subTest ('config: check disabled; override: enabled' ):
1409+ check (False , 1 )
1410+
1411+ # setting: check enabled
1412+ with self .subTest ('config: check enabled; override: disabled' ):
1413+ check (True , - 1 )
1414+ with self .subTest ('config: check enabled; override: use config' ):
1415+ check (True , 0 )
1416+ with self .subTest ('config: check enabled; override: enabled' ):
1417+ check (True , 1 )
1418+
13311419 def test_mutate_exception (self ):
13321420 """
13331421 Exceptions saved in global module state get shared between
0 commit comments