@@ -741,6 +741,38 @@ def test_empty_zone(self):
741741 with self .assertRaises (ValueError ):
742742 self .klass .from_file (zf )
743743
744+ def test_invalid_transition_index (self ):
745+ STD = ZoneOffset ("STD" , ZERO )
746+ DST = ZoneOffset ("DST" , ONE_H , ONE_H )
747+
748+ zf = self .construct_zone ([
749+ ZoneTransition (datetime (2026 , 3 , 1 , 2 ), STD , DST ),
750+ ZoneTransition (datetime (2026 , 11 , 1 , 2 ), DST , STD ),
751+ ], after = "" , version = 1 )
752+
753+ data = bytearray (zf .read ())
754+ timecnt = struct .unpack_from (">l" , data , 32 )[0 ]
755+ idx_offset = 44 + timecnt * 4
756+ data [idx_offset + 1 ] = 2 # typecnt is 2, so index 2 is OOB
757+ f = io .BytesIO (bytes (data ))
758+
759+ with self .assertRaises (ValueError ):
760+ self .klass .from_file (f )
761+
762+ def test_transition_lookahead_out_of_bounds (self ):
763+ STD = ZoneOffset ("STD" , ZERO )
764+ DST = ZoneOffset ("DST" , ONE_H , ONE_H )
765+ EXT = ZoneOffset ("EXT" , ONE_H )
766+
767+ zf = self .construct_zone ([
768+ ZoneTransition (datetime (2026 , 3 , 1 ), STD , DST ),
769+ ZoneTransition (datetime (2026 , 6 , 1 ), DST , EXT ),
770+ ZoneTransition (datetime (2026 , 9 , 1 ), EXT , DST ),
771+ ], after = "" )
772+
773+ zi = self .klass .from_file (zf )
774+ self .assertIsNotNone (zi )
775+
744776 def test_zone_very_large_timestamp (self ):
745777 """Test when a transition is in the far past or future.
746778
@@ -1577,6 +1609,59 @@ class EvilZoneInfo(self.klass):
15771609class CZoneInfoCacheTest (ZoneInfoCacheTest ):
15781610 module = c_zoneinfo
15791611
1612+ @unittest .expectedFailure # TODO: RUSTPYTHON; AssertionError: RuntimeError not raised
1613+ def test_inconsistent_weak_cache_get (self ):
1614+ class Cache :
1615+ def get (self , key , default = None ):
1616+ return 1337
1617+
1618+ class ZI (self .klass ):
1619+ pass
1620+ # Class attribute must be set after class creation
1621+ # to override zoneinfo.ZoneInfo.__init_subclass__.
1622+ ZI ._weak_cache = Cache ()
1623+
1624+ with self .assertRaises (RuntimeError ) as te :
1625+ ZI ("America/Los_Angeles" )
1626+ self .assertEqual (
1627+ str (te .exception ),
1628+ "Unexpected instance of int in ZI weak cache for key 'America/Los_Angeles'"
1629+ )
1630+
1631+ @unittest .expectedFailure # TODO: RUSTPYTHON; AssertionError: AttributeError not raised
1632+ def test_deleted_weak_cache (self ):
1633+ class ZI (self .klass ):
1634+ pass
1635+ delattr (ZI , '_weak_cache' )
1636+
1637+ # These should not segfault
1638+ with self .assertRaises (AttributeError ):
1639+ ZI ("UTC" )
1640+
1641+ with self .assertRaises (AttributeError ):
1642+ ZI .clear_cache ()
1643+
1644+ @unittest .expectedFailure # TODO: RUSTPYTHON; AttributeError: 'int' object has no attribute '_from_cache'
1645+ def test_inconsistent_weak_cache_setdefault (self ):
1646+ class Cache :
1647+ def get (self , key , default = None ):
1648+ return default
1649+ def setdefault (self , key , value ):
1650+ return 1337
1651+
1652+ class ZI (self .klass ):
1653+ pass
1654+ # Class attribute must be set after class creation
1655+ # to override zoneinfo.ZoneInfo.__init_subclass__.
1656+ ZI ._weak_cache = Cache ()
1657+
1658+ with self .assertRaises (RuntimeError ) as te :
1659+ ZI ("America/Los_Angeles" )
1660+ self .assertEqual (
1661+ str (te .exception ),
1662+ "Unexpected instance of int in ZI weak cache for key 'America/Los_Angeles'"
1663+ )
1664+
15801665
15811666class ZoneInfoPickleTest (TzPathUserMixin , ZoneInfoTestBase ):
15821667 module = py_zoneinfo
0 commit comments