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