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 @@ -1575,6 +1575,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
# Must set AFTER class creation to override __init_subclass__
ZI._weak_cache = Cache()

with self.assertRaises(TypeError) as te:
ZI("America/Los_Angeles")
# Heap type objects' tp_name should just be the type name, i.e. ZI
Comment thread
superboy-zjc marked this conversation as resolved.
Outdated
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
# Must set AFTER class creation to override __init_subclass__
Comment thread
superboy-zjc marked this conversation as resolved.
Outdated
ZI._weak_cache = Cache()

with self.assertRaises(TypeError) as te:
ZI("America/Los_Angeles")
# Heap type objects' tp_name should just be the type name, i.e. ZI
Comment thread
superboy-zjc marked this conversation as resolved.
Outdated
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.
16 changes: 16 additions & 0 deletions Modules/_zoneinfo.c
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,14 @@ zoneinfo_ZoneInfo_impl(PyTypeObject *type, PyObject *key)
return NULL;
}

if (instance != Py_None && !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;
}
Comment thread
superboy-zjc marked this conversation as resolved.
Outdated

if (instance == Py_None) {
Py_DECREF(instance);
PyObject *tmp = zoneinfo_new_instance(state, type, key);
Expand All @@ -342,6 +350,14 @@ 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
}

Expand Down
Loading