Skip to content
31 changes: 31 additions & 0 deletions Lib/test/test_zoneinfo/test_zoneinfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -1577,6 +1577,37 @@ class EvilZoneInfo(self.klass):
class CZoneInfoCacheTest(ZoneInfoCacheTest):
module = c_zoneinfo

def test_inconsistent_weak_cache_get(self):
class Cache:
def get(self, key, default=None):
return 1337

class ZI(self.klass):
pass
# Class attribute must be set after class creation
# to override zoneinfo.Zoneinfo.__init_subclass__.
Comment thread
StanFromIreland marked this conversation as resolved.
Outdated
ZI._weak_cache = Cache()

with self.assertRaises(TypeError) as te:
ZI("America/Los_Angeles")
self.assertEqual(str(te.exception), "expected ZI, got int")

def test_inconsistent_weak_cache_setdefault(self):
class Cache:
def get(self, key, default=None):
return default
def setdefault(self, key, value):
return 1337

class ZI(self.klass):
pass
# Class attribute must be set after class creation
# to override zoneinfo.Zoneinfo.__init_subclass__.
Comment thread
StanFromIreland marked this conversation as resolved.
Outdated
ZI._weak_cache = Cache()

with self.assertRaises(TypeError) as te:
ZI("America/Los_Angeles")
self.assertEqual(str(te.exception), "expected ZI, got int")

class ZoneInfoPickleTest(TzPathUserMixin, ZoneInfoTestBase):
module = py_zoneinfo
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
:mod:`zoneinfo`: fix a crash when instantiating :class:`~zoneinfo.ZoneInfo`
objects for which the internal class-level cache is inconsistent.
15 changes: 15 additions & 0 deletions Modules/_zoneinfo.c
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,22 @@ zoneinfo_ZoneInfo_impl(PyTypeObject *type, PyObject *key)
Py_DECREF(weak_cache);
return NULL;
}

if (!PyObject_TypeCheck(instance, type)) {
PyErr_Format(PyExc_TypeError, "expected %s, got %s",
type->tp_name, Py_TYPE(instance)->tp_name);
Py_DECREF(instance);
Py_DECREF(weak_cache);
return NULL;
}

((PyZoneInfo_ZoneInfo *)instance)->source = SOURCE_CACHE;
Comment thread
superboy-zjc marked this conversation as resolved.
Outdated
} else if (!PyObject_TypeCheck(instance, type)) {
Comment thread
superboy-zjc marked this conversation as resolved.
Outdated
PyErr_Format(PyExc_TypeError, "expected %s, got %s",
Comment thread
superboy-zjc marked this conversation as resolved.
Outdated
type->tp_name, Py_TYPE(instance)->tp_name);
Py_DECREF(instance);
Py_DECREF(weak_cache);
return NULL;
}

update_strong_cache(state, type, key, instance);
Expand Down
Loading