Skip to content

Commit 4daa526

Browse files
authored
Update tzinfo to 3.14.5 (#7934)
* Update `tzinfo` to 3.14.5 * Mark failing tests
1 parent d073964 commit 4daa526

3 files changed

Lines changed: 95 additions & 2 deletions

File tree

Lib/test/test_zoneinfo/test_zoneinfo.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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):
15771609
class 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

15811666
class ZoneInfoPickleTest(TzPathUserMixin, ZoneInfoTestBase):
15821667
module = py_zoneinfo

Lib/zoneinfo/_common.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ def load_data(fobj):
6767
f">{timecnt}{time_type}", fobj.read(timecnt * time_size)
6868
)
6969
trans_idx = struct.unpack(f">{timecnt}B", fobj.read(timecnt))
70+
71+
if max(trans_idx) >= typecnt:
72+
raise ValueError("Invalid transition index found while reading TZif: "
73+
f"{max(trans_idx)}")
7074
else:
7175
trans_list_utc = ()
7276
trans_idx = ()

Lib/zoneinfo/_zoneinfo.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,11 @@ def __new__(cls, key):
4747
cls._strong_cache[key] = cls._strong_cache.pop(key, instance)
4848

4949
if len(cls._strong_cache) > cls._strong_cache_size:
50-
cls._strong_cache.popitem(last=False)
50+
try:
51+
cls._strong_cache.popitem(last=False)
52+
except KeyError:
53+
# another thread may have already emptied the cache
54+
pass
5155

5256
return instance
5357

@@ -334,7 +338,7 @@ def _utcoff_to_dstoff(trans_idx, utcoffsets, isdsts):
334338
if not isdsts[comp_idx]:
335339
dstoff = utcoff - utcoffsets[comp_idx]
336340

337-
if not dstoff and idx < (typecnt - 1):
341+
if not dstoff and idx < (typecnt - 1) and i + 1 < len(trans_idx):
338342
comp_idx = trans_idx[i + 1]
339343

340344
# If the following transition is also DST and we couldn't

0 commit comments

Comments
 (0)