@@ -2204,6 +2204,257 @@ def test_module_state_shared_in_global(self):
22042204 self .assertEqual (main_attr_id , subinterp_attr_id )
22052205
22062206
2207+ class InterpreterConfigTests (unittest .TestCase ):
2208+
2209+ supported = {
2210+ 'isolated' : types .SimpleNamespace (
2211+ use_main_obmalloc = False ,
2212+ allow_fork = False ,
2213+ allow_exec = False ,
2214+ allow_threads = True ,
2215+ allow_daemon_threads = False ,
2216+ check_multi_interp_extensions = True ,
2217+ gil = 'own' ,
2218+ ),
2219+ 'legacy' : types .SimpleNamespace (
2220+ use_main_obmalloc = True ,
2221+ allow_fork = True ,
2222+ allow_exec = True ,
2223+ allow_threads = True ,
2224+ allow_daemon_threads = True ,
2225+ check_multi_interp_extensions = False ,
2226+ gil = 'shared' ,
2227+ ),
2228+ 'empty' : types .SimpleNamespace (
2229+ use_main_obmalloc = False ,
2230+ allow_fork = False ,
2231+ allow_exec = False ,
2232+ allow_threads = False ,
2233+ allow_daemon_threads = False ,
2234+ check_multi_interp_extensions = False ,
2235+ gil = 'default' ,
2236+ ),
2237+ }
2238+ gil_supported = ['default' , 'shared' , 'own' ]
2239+
2240+ def iter_all_configs (self ):
2241+ for use_main_obmalloc in (True , False ):
2242+ for allow_fork in (True , False ):
2243+ for allow_exec in (True , False ):
2244+ for allow_threads in (True , False ):
2245+ for allow_daemon in (True , False ):
2246+ for checkext in (True , False ):
2247+ for gil in ('shared' , 'own' , 'default' ):
2248+ yield types .SimpleNamespace (
2249+ use_main_obmalloc = use_main_obmalloc ,
2250+ allow_fork = allow_fork ,
2251+ allow_exec = allow_exec ,
2252+ allow_threads = allow_threads ,
2253+ allow_daemon_threads = allow_daemon ,
2254+ check_multi_interp_extensions = checkext ,
2255+ gil = gil ,
2256+ )
2257+
2258+ def assert_ns_equal (self , ns1 , ns2 , msg = None ):
2259+ # This is mostly copied from TestCase.assertDictEqual.
2260+ self .assertEqual (type (ns1 ), type (ns2 ))
2261+ if ns1 == ns2 :
2262+ return
2263+
2264+ import difflib
2265+ import pprint
2266+ from unittest .util import _common_shorten_repr
2267+ standardMsg = '%s != %s' % _common_shorten_repr (ns1 , ns2 )
2268+ diff = ('\n ' + '\n ' .join (difflib .ndiff (
2269+ pprint .pformat (vars (ns1 )).splitlines (),
2270+ pprint .pformat (vars (ns2 )).splitlines ())))
2271+ diff = f'namespace({ diff } )'
2272+ standardMsg = self ._truncateMessage (standardMsg , diff )
2273+ self .fail (self ._formatMessage (msg , standardMsg ))
2274+
2275+ def test_predefined_config (self ):
2276+ def check (name , expected ):
2277+ expected = self .supported [expected ]
2278+ args = (name ,) if name else ()
2279+
2280+ config1 = _testinternalcapi .new_interp_config (* args )
2281+ self .assert_ns_equal (config1 , expected )
2282+ self .assertIsNot (config1 , expected )
2283+
2284+ config2 = _testinternalcapi .new_interp_config (* args )
2285+ self .assert_ns_equal (config2 , expected )
2286+ self .assertIsNot (config2 , expected )
2287+ self .assertIsNot (config2 , config1 )
2288+
2289+ with self .subTest ('default' ):
2290+ check (None , 'isolated' )
2291+
2292+ for name in self .supported :
2293+ with self .subTest (name ):
2294+ check (name , name )
2295+
2296+ def test_update_from_dict (self ):
2297+ for name , vanilla in self .supported .items ():
2298+ with self .subTest (f'noop ({ name } )' ):
2299+ expected = vanilla
2300+ overrides = vars (vanilla )
2301+ config = _testinternalcapi .new_interp_config (name , ** overrides )
2302+ self .assert_ns_equal (config , expected )
2303+
2304+ with self .subTest (f'change all ({ name } )' ):
2305+ overrides = {k : not v for k , v in vars (vanilla ).items ()}
2306+ for gil in self .gil_supported :
2307+ if vanilla .gil == gil :
2308+ continue
2309+ overrides ['gil' ] = gil
2310+ expected = types .SimpleNamespace (** overrides )
2311+ config = _testinternalcapi .new_interp_config (
2312+ name , ** overrides )
2313+ self .assert_ns_equal (config , expected )
2314+
2315+ # Override individual fields.
2316+ for field , old in vars (vanilla ).items ():
2317+ if field == 'gil' :
2318+ values = [v for v in self .gil_supported if v != old ]
2319+ else :
2320+ values = [not old ]
2321+ for val in values :
2322+ with self .subTest (f'{ name } .{ field } ({ old !r} -> { val !r} )' ):
2323+ overrides = {field : val }
2324+ expected = types .SimpleNamespace (
2325+ ** dict (vars (vanilla ), ** overrides ),
2326+ )
2327+ config = _testinternalcapi .new_interp_config (
2328+ name , ** overrides )
2329+ self .assert_ns_equal (config , expected )
2330+
2331+ with self .subTest ('unsupported field' ):
2332+ for name in self .supported :
2333+ with self .assertRaises (ValueError ):
2334+ _testinternalcapi .new_interp_config (name , spam = True )
2335+
2336+ # Bad values for bool fields.
2337+ for field , value in vars (self .supported ['empty' ]).items ():
2338+ if field == 'gil' :
2339+ continue
2340+ assert isinstance (value , bool )
2341+ for value in [1 , '' , 'spam' , 1.0 , None , object ()]:
2342+ with self .subTest (f'unsupported value ({ field } ={ value !r} )' ):
2343+ with self .assertRaises (TypeError ):
2344+ _testinternalcapi .new_interp_config (** {field : value })
2345+
2346+ # Bad values for .gil.
2347+ for value in [True , 1 , 1.0 , None , object ()]:
2348+ with self .subTest (f'unsupported value(gil={ value !r} )' ):
2349+ with self .assertRaises (TypeError ):
2350+ _testinternalcapi .new_interp_config (gil = value )
2351+ for value in ['' , 'spam' ]:
2352+ with self .subTest (f'unsupported value (gil={ value !r} )' ):
2353+ with self .assertRaises (ValueError ):
2354+ _testinternalcapi .new_interp_config (gil = value )
2355+
2356+ @requires_subinterpreters
2357+ def test_interp_init (self ):
2358+ questionable = [
2359+ # strange
2360+ dict (
2361+ allow_fork = True ,
2362+ allow_exec = False ,
2363+ ),
2364+ dict (
2365+ gil = 'shared' ,
2366+ use_main_obmalloc = False ,
2367+ ),
2368+ # risky
2369+ dict (
2370+ allow_fork = True ,
2371+ allow_threads = True ,
2372+ ),
2373+ # ought to be invalid?
2374+ dict (
2375+ allow_threads = False ,
2376+ allow_daemon_threads = True ,
2377+ ),
2378+ dict (
2379+ gil = 'own' ,
2380+ use_main_obmalloc = True ,
2381+ ),
2382+ ]
2383+ invalid = [
2384+ dict (
2385+ use_main_obmalloc = False ,
2386+ check_multi_interp_extensions = False
2387+ ),
2388+ ]
2389+ def match (config , override_cases ):
2390+ ns = vars (config )
2391+ for overrides in override_cases :
2392+ if dict (ns , ** overrides ) == ns :
2393+ return True
2394+ return False
2395+
2396+ def check (config ):
2397+ script = 'pass'
2398+ rc = _testinternalcapi .run_in_subinterp_with_config (script , config )
2399+ self .assertEqual (rc , 0 )
2400+
2401+ for config in self .iter_all_configs ():
2402+ if config .gil == 'default' :
2403+ continue
2404+ if match (config , invalid ):
2405+ with self .subTest (f'invalid: { config } ' ):
2406+ with self .assertRaises (RuntimeError ):
2407+ check (config )
2408+ elif match (config , questionable ):
2409+ with self .subTest (f'questionable: { config } ' ):
2410+ check (config )
2411+ else :
2412+ with self .subTest (f'valid: { config } ' ):
2413+ check (config )
2414+
2415+ @requires_subinterpreters
2416+ def test_get_config (self ):
2417+ @contextlib .contextmanager
2418+ def new_interp (config ):
2419+ interpid = _testinternalcapi .new_interpreter (config )
2420+ try :
2421+ yield interpid
2422+ finally :
2423+ try :
2424+ _interpreters .destroy (interpid )
2425+ except _interpreters .InterpreterNotFoundError :
2426+ pass
2427+
2428+ with self .subTest ('main' ):
2429+ expected = _testinternalcapi .new_interp_config ('legacy' )
2430+ expected .gil = 'own'
2431+ interpid = _interpreters .get_main ()
2432+ config = _testinternalcapi .get_interp_config (interpid )
2433+ self .assert_ns_equal (config , expected )
2434+
2435+ with self .subTest ('isolated' ):
2436+ expected = _testinternalcapi .new_interp_config ('isolated' )
2437+ with new_interp ('isolated' ) as interpid :
2438+ config = _testinternalcapi .get_interp_config (interpid )
2439+ self .assert_ns_equal (config , expected )
2440+
2441+ with self .subTest ('legacy' ):
2442+ expected = _testinternalcapi .new_interp_config ('legacy' )
2443+ with new_interp ('legacy' ) as interpid :
2444+ config = _testinternalcapi .get_interp_config (interpid )
2445+ self .assert_ns_equal (config , expected )
2446+
2447+ with self .subTest ('custom' ):
2448+ orig = _testinternalcapi .new_interp_config (
2449+ 'empty' ,
2450+ use_main_obmalloc = True ,
2451+ gil = 'shared' ,
2452+ )
2453+ with new_interp (orig ) as interpid :
2454+ config = _testinternalcapi .get_interp_config (interpid )
2455+ self .assert_ns_equal (config , orig )
2456+
2457+
22072458@requires_subinterpreters
22082459class InterpreterIDTests (unittest .TestCase ):
22092460
0 commit comments