@@ -7155,55 +7155,53 @@ def test_datetime_from_timestamp(self):
71557155
71567156 self .assertEqual (dt_orig , dt_rt )
71577157
7158- def assert_python_ok_in_subinterp (self , script ,
7159- setup = '_testcapi.test_datetime_capi()' ,
7160- mainsetup = '_testcapi.test_datetime_capi()' ,
7161- config = 'isolated' ):
7158+ def assert_python_ok_in_subinterp (self , script , init = '' , fini = '' ,
7159+ repeat = 1 , config = 'isolated' ):
71627160 # iOS requires the use of the custom framework loader,
71637161 # not the ExtensionFileLoader.
71647162 if sys .platform == "ios" :
71657163 extension_loader = "AppleFrameworkLoader"
71667164 else :
71677165 extension_loader = "ExtensionFileLoader"
71687166
7169- maincode = textwrap .dedent (f'''
7170- import textwrap
7171- from test import support
7172-
7173- subcode = textwrap.dedent("""
7174- if { _interpreters is None } :
7175- import _testcapi
7176- else:
7177- import importlib.machinery
7178- import importlib.util
7179- fullname = '_testcapi_datetime'
7180- origin = importlib.util.find_spec('_testcapi').origin
7181- loader = importlib.machinery.{ extension_loader } (fullname, origin)
7182- spec = importlib.util.spec_from_loader(fullname, loader)
7183- _testcapi = importlib.util.module_from_spec(spec)
7184- spec.loader.exec_module(_testcapi)
7185-
7186- $SETUP$
7187- $SCRIPT$
7188- """)
7189-
7190- import _testcapi
7191- $MAINSETUP$
7192-
7167+ code = textwrap .dedent (f'''
7168+ subinterp_code = """
71937169 if { _interpreters is None } :
7194- ret = support.run_in_subinterp(subcode)
7170+ import _testcapi
71957171 else:
7196- import _interpreters
7197- config = _interpreters.new_config('{ config } ').__dict__
7198- ret = support.run_in_subinterp_with_config(subcode, **config)
7199-
7200- assert ret == 0
7172+ import importlib.machinery
7173+ import importlib.util
7174+ fullname = '_testcapi_datetime'
7175+ origin = importlib.util.find_spec('_testcapi').origin
7176+ loader = importlib.machinery.{ extension_loader } (fullname, origin)
7177+ spec = importlib.util.spec_from_loader(fullname, loader)
7178+ _testcapi = importlib.util.module_from_spec(spec)
7179+ spec.loader.exec_module(_testcapi)
7180+ interp_index = $INTERP_INDEX$
7181+ setup = _testcapi.test_datetime_capi_newinterp # call it if needed
7182+ $SCRIPT$
7183+ """
72017184
7202- ''' ).replace ('$MAINSETUP$' , mainsetup )
7203- maincode = maincode .replace ('$SETUP$' , textwrap .indent (setup , '\x20 ' * 4 ))
7204- maincode = maincode .replace ('$SCRIPT$' , textwrap .indent (script , '\x20 ' * 4 ))
7185+ import _testcapi
7186+ from test import support
7187+ setup = _testcapi.test_datetime_capi_newinterp
7188+ $INIT$
72057189
7206- res = script_helper .assert_python_ok ('-c' , maincode )
7190+ for idx in range({ repeat } ):
7191+ subcode = subinterp_code.replace('$INTERP_INDEX$', str(idx))
7192+ if { _interpreters is None } :
7193+ ret = support.run_in_subinterp(subcode)
7194+ else:
7195+ import _interpreters
7196+ config = _interpreters.new_config('{ config } ').__dict__
7197+ ret = support.run_in_subinterp_with_config(subcode, **config)
7198+ assert ret == 0
7199+ $FINI$
7200+ ''' )
7201+ code = code .replace ('$INIT$' , init ).replace ('$FINI$' , fini )
7202+ code = code .replace ('$SCRIPT$' , script )
7203+
7204+ res = script_helper .assert_python_ok ('-c' , code )
72077205 return res
72087206
72097207 def test_type_check_in_subinterp (self ):
@@ -7212,18 +7210,18 @@ def run(type_checker, obj):
72127210 if not type_checker(obj, True):
72137211 raise TypeError(f'{{type(obj)}} is not C API type')
72147212
7213+ setup()
72157214 import _datetime
72167215 run(_testcapi.datetime_check_date, _datetime.date.today())
72177216 run(_testcapi.datetime_check_datetime, _datetime.datetime.now())
72187217 run(_testcapi.datetime_check_time, _datetime.time(12, 30))
72197218 run(_testcapi.datetime_check_delta, _datetime.timedelta(1))
72207219 run(_testcapi.datetime_check_tzinfo, _datetime.tzinfo())
7221- """ )
7222- self .assert_python_ok_in_subinterp (script , mainsetup = '' )
7220+ """ )
7221+ self .assert_python_ok_in_subinterp (script )
72237222 if _interpreters is not None :
7224- with self .subTest ('legacy' ):
7225- self .assert_python_ok_in_subinterp (script , mainsetup = '' ,
7226- config = 'legacy' )
7223+ with self .subTest (name := 'legacy' ):
7224+ self .assert_python_ok_in_subinterp (script , config = name )
72277225
72287226
72297227class ExtensionModuleTests (unittest .TestCase ):
@@ -7232,6 +7230,9 @@ def setUp(self):
72327230 if self .__class__ .__name__ .endswith ('Pure' ):
72337231 self .skipTest ('Not relevant in pure Python' )
72347232
7233+ def assert_python_ok_in_subinterp (self , * args , ** kwargs ):
7234+ return CapiTest .assert_python_ok_in_subinterp (self , * args , ** kwargs )
7235+
72357236 @support .cpython_only
72367237 def test_gh_120161 (self ):
72377238 with self .subTest ('simple' ):
@@ -7300,11 +7301,65 @@ def test_update_type_cache(self):
73007301 res = script_helper .assert_python_ok ('-c' , script )
73017302 self .assertFalse (res .err )
73027303
7303- def test_static_type_at_shutdown1 (self ):
7304+ def test_module_free (self ):
7305+ script = textwrap .dedent ("""
7306+ import sys
7307+ import gc
7308+ import weakref
7309+ ws = weakref.WeakSet()
7310+ for _ in range(3):
7311+ import _datetime
7312+ timedelta = _datetime.timedelta # static type
7313+ ws.add(_datetime)
7314+ del sys.modules['_datetime']
7315+ del _datetime
7316+ gc.collect()
7317+ assert len(ws) == 1 # only one remains
7318+ """ )
7319+ script_helper .assert_python_ok ('-c' , script )
7320+
7321+ @unittest .skipIf (not support .Py_DEBUG , "Debug builds only" )
7322+ def test_no_leak (self ):
7323+ script = textwrap .dedent ("""
7324+ import datetime
7325+ datetime.datetime.strptime('20000101', '%Y%m%d').strftime('%Y%m%d')
7326+ """ )
7327+ res = script_helper .assert_python_ok ('-X' , 'showrefcount' , '-c' , script )
7328+ self .assertIn (b'[0 refs, 0 blocks]' , res .err )
7329+
7330+ def test_static_type_on_subinterp (self ):
7331+ script = textwrap .dedent ("""
7332+ date = _testcapi.get_capi_types()['date']
7333+ date.today
7334+ """ )
7335+ with_setup = 'setup()' + script
7336+ with self .subTest ('[PyDateTime_IMPORT] main: no, sub: yes' ):
7337+ self .assert_python_ok_in_subinterp (with_setup )
7338+
7339+ with self .subTest ('[PyDateTime_IMPORT] main: yes, sub: yes' ):
7340+ # Fails if the setup() means test_datetime_capi() rather than
7341+ # test_datetime_capi_newinterp()
7342+ self .assert_python_ok_in_subinterp (with_setup , 'setup()' )
7343+ self .assert_python_ok_in_subinterp ('setup()' , fini = with_setup )
7344+ self .assert_python_ok_in_subinterp (with_setup , repeat = 2 )
7345+
7346+ with_import = 'import _datetime' + script
7347+ with self .subTest ('Explicit import' ):
7348+ self .assert_python_ok_in_subinterp (with_import , 'setup()' )
7349+
7350+ with_import = textwrap .dedent ("""
7351+ timedelta = _testcapi.get_capi_types()['timedelta']
7352+ timedelta(days=1)
7353+ """ ) + script
7354+ with self .subTest ('Implicit import' ):
7355+ self .assert_python_ok_in_subinterp (with_import , 'setup()' )
7356+
7357+ def test_static_type_at_shutdown (self ):
73047358 # gh-132413
73057359 script = textwrap .dedent ("""
73067360 import sys
73077361 import _datetime
7362+ timedelta = _datetime.timedelta
73087363
73097364 def gen():
73107365 try:
@@ -7314,85 +7369,45 @@ def gen():
73147369 assert not sys.modules
73157370 td = _datetime.timedelta(days=1)
73167371 assert td.days == 1
7372+ td = timedelta(days=1)
7373+ assert td.days == 1
73177374 assert not sys.modules
73187375
73197376 it = gen()
73207377 next(it)
7321- """ )
7322- res = script_helper .assert_python_ok ('-c' , script )
7323- self .assertFalse (res .err )
7378+ """ )
7379+ with self .subTest ('MainInterpreter' ):
7380+ res = script_helper .assert_python_ok ('-c' , script )
7381+ self .assertFalse (res .err )
7382+ with self .subTest ('Subinterpreter' ):
7383+ res = self .assert_python_ok_in_subinterp (script )
7384+ self .assertFalse (res .err )
73247385
7325- def test_static_type_at_shutdown2 (self ):
73267386 script = textwrap .dedent ("""
73277387 import sys
7328- from _datetime import timedelta
7388+ timedelta = _testcapi.get_capi_types()[' timedelta']
73297389
73307390 def gen():
73317391 try:
73327392 yield
73337393 finally:
7394+ # Exceptions are ignored here
73347395 assert not sys.modules
73357396 td = timedelta(days=1)
73367397 assert td.days == 1
73377398 assert not sys.modules
73387399
73397400 it = gen()
73407401 next(it)
7341- """ )
7342- res = script_helper .assert_python_ok ('-c' , script )
7343- self .assertFalse (res .err )
7344-
7345- def test_static_type_at_shutdown3 (self ):
7346- script = textwrap .dedent ("""
7347- timedelta = _testcapi.get_capi_types()['timedelta']
7348-
7349- def gen():
7350- try:
7351- yield
7352- finally:
7353- timedelta(days=1)
7354-
7355- it = gen()
7356- next(it)
7357- """ )
7358- res = CapiTest .assert_python_ok_in_subinterp (self , script , setup = '' )
7359- self .assertIn (b'ImportError: sys.meta_path is None' , res .err )
7360-
7361- def test_static_type_before_shutdown (self ):
7362- script = textwrap .dedent (f"""
7363- import sys
7364- assert '_datetime' not in sys.modules
7365- timedelta = _testcapi.get_capi_types()['timedelta']
7366- timedelta(days=1)
7367- assert '_datetime' in sys.modules
7368- """ )
7369- CapiTest .assert_python_ok_in_subinterp (self , script , setup = '' )
7370-
7371- def test_remain_only_one_module (self ):
7372- script = textwrap .dedent ("""
7373- import sys
7374- import gc
7375- import weakref
7376- ws = weakref.WeakSet()
7377- for _ in range(3):
7378- import _datetime
7379- timedelta = _datetime.timedelta
7380- ws.add(_datetime)
7381- del sys.modules['_datetime']
7382- del _datetime
7383- gc.collect()
7384- assert len(ws) == 1
7385- """ )
7386- script_helper .assert_python_ok ('-c' , script )
7387-
7388- @unittest .skipIf (not support .Py_DEBUG , "Debug builds only" )
7389- def test_no_leak (self ):
7390- script = textwrap .dedent ("""
7391- import datetime
7392- datetime.datetime.strptime('20000101', '%Y%m%d').strftime('%Y%m%d')
7393- """ )
7394- res = script_helper .assert_python_ok ('-X' , 'showrefcount' , '-c' , script )
7395- self .assertIn (b'[0 refs, 0 blocks]' , res .err )
7402+ """ )
7403+ with self .subTest ('[PyDateTime_IMPORT] main: yes, sub: no' ):
7404+ res = self .assert_python_ok_in_subinterp (script , 'setup()' )
7405+ self .assertIn (b'ImportError: sys.meta_path is None' , res .err )
7406+
7407+ with_import = 'setup()' + script
7408+ with self .subTest ('[PyDateTime_IMPORT] main: no, sub: yes' ):
7409+ res = self .assert_python_ok_in_subinterp (with_import )
7410+ self .assertFalse (res .err )
73967411
73977412
73987413def load_tests (loader , standard_tests , pattern ):
0 commit comments