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