@@ -57,6 +57,10 @@ def tearDownModule():
5757 shutil .rmtree (TEMP_DIR )
5858
5959
60+ class CustomError (Exception ):
61+ pass
62+
63+
6064class TzPathUserMixin :
6165 """
6266 Adds a setUp() and tearDown() to make TZPATH manipulations thread-safe.
@@ -403,6 +407,25 @@ def test_time_fixed_offset(self):
403407 self .assertEqual (t .utcoffset (), offset .utcoffset )
404408 self .assertEqual (t .dst (), offset .dst )
405409
410+ def test_cache_exception (self ):
411+ class Incomparable (str ):
412+ eq_called = False
413+ def __eq__ (self , other ):
414+ self .eq_called = True
415+ raise CustomError
416+ __hash__ = str .__hash__
417+
418+ key = "America/Los_Angeles"
419+ tz1 = self .klass (key )
420+ key = Incomparable (key )
421+ try :
422+ tz2 = self .klass (key )
423+ except CustomError :
424+ self .assertTrue (key .eq_called )
425+ else :
426+ self .assertFalse (key .eq_called )
427+ self .assertIs (tz2 , tz1 )
428+
406429
407430class CZoneInfoTest (ZoneInfoTest ):
408431 module = c_zoneinfo
@@ -1506,6 +1529,26 @@ def test_clear_cache_two_keys(self):
15061529 self .assertIsNot (dub0 , dub1 )
15071530 self .assertIs (tok0 , tok1 )
15081531
1532+ def test_clear_cache_refleak (self ):
1533+ class Stringy (str ):
1534+ allow_comparisons = True
1535+ def __eq__ (self , other ):
1536+ if not self .allow_comparisons :
1537+ raise CustomError
1538+ return super ().__eq__ (other )
1539+ __hash__ = str .__hash__
1540+
1541+ key = Stringy ("America/Los_Angeles" )
1542+ self .klass (key )
1543+ key .allow_comparisons = False
1544+ try :
1545+ # Note: This is try/except rather than assertRaises because
1546+ # there is no guarantee that the key is even still in the cache,
1547+ # or that the key for the cache is the original `key` object.
1548+ self .klass .clear_cache (only_keys = "America/Los_Angeles" )
1549+ except CustomError :
1550+ pass
1551+
15091552
15101553class CZoneInfoCacheTest (ZoneInfoCacheTest ):
15111554 module = c_zoneinfo
0 commit comments