diff --git a/Lib/test/test_zoneinfo/test_zoneinfo.py b/Lib/test/test_zoneinfo/test_zoneinfo.py index 581072d0701d65..af1b2be7cea724 100644 --- a/Lib/test/test_zoneinfo/test_zoneinfo.py +++ b/Lib/test/test_zoneinfo/test_zoneinfo.py @@ -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__. + 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__. + 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 diff --git a/Misc/NEWS.d/next/Library/2025-12-18-00-14-16.gh-issue-142781.gcOeYF.rst b/Misc/NEWS.d/next/Library/2025-12-18-00-14-16.gh-issue-142781.gcOeYF.rst new file mode 100644 index 00000000000000..772e05766c5c69 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-12-18-00-14-16.gh-issue-142781.gcOeYF.rst @@ -0,0 +1,2 @@ +:mod:`zoneinfo`: fix a crash when instantiating :class:`~zoneinfo.ZoneInfo` +objects for which the internal class-level cache is inconsistent. diff --git a/Modules/_zoneinfo.c b/Modules/_zoneinfo.c index e07dfd19efa06d..60b8608764a79f 100644 --- a/Modules/_zoneinfo.c +++ b/Modules/_zoneinfo.c @@ -342,8 +342,24 @@ 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; } + else 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; + } update_strong_cache(state, type, key, instance); Py_DECREF(weak_cache);