@@ -58,6 +58,10 @@ def tearDownModule():
58
58
shutil .rmtree (TEMP_DIR )
59
59
60
60
61
+ class CustomError (Exception ):
62
+ pass
63
+
64
+
61
65
class TzPathUserMixin :
62
66
"""
63
67
Adds a setUp() and tearDown() to make TZPATH manipulations thread-safe.
@@ -404,6 +408,25 @@ def test_time_fixed_offset(self):
404
408
self .assertEqual (t .utcoffset (), offset .utcoffset )
405
409
self .assertEqual (t .dst (), offset .dst )
406
410
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
+
407
430
408
431
class CZoneInfoTest (ZoneInfoTest ):
409
432
module = c_zoneinfo
@@ -1507,6 +1530,26 @@ def test_clear_cache_two_keys(self):
1507
1530
self .assertIsNot (dub0 , dub1 )
1508
1531
self .assertIs (tok0 , tok1 )
1509
1532
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
+
1510
1553
1511
1554
class CZoneInfoCacheTest (ZoneInfoCacheTest ):
1512
1555
module = c_zoneinfo
0 commit comments