]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-87298: Add tests for find_in_strong_cache() bug in _zoneinfo (GH-24829)
authorZackery Spytz <zspytz@gmail.com>
Tue, 1 Jul 2025 14:55:24 +0000 (07:55 -0700)
committerGitHub <noreply@github.com>
Tue, 1 Jul 2025 14:55:24 +0000 (14:55 +0000)
Co-authored-by: Paul Ganssle <p.ganssle@gmail.com>
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
Lib/test/test_zoneinfo/test_zoneinfo.py

index f313e394f49e9b2021bd10411c9843b0e97a104f..44e87e71c8ee7b86e1a0dff38896e1a3f3618dcf 100644 (file)
@@ -58,6 +58,10 @@ def tearDownModule():
     shutil.rmtree(TEMP_DIR)
 
 
+class CustomError(Exception):
+    pass
+
+
 class TzPathUserMixin:
     """
     Adds a setUp() and tearDown() to make TZPATH manipulations thread-safe.
@@ -404,6 +408,25 @@ class ZoneInfoTest(TzPathUserMixin, ZoneInfoTestBase):
                 self.assertEqual(t.utcoffset(), offset.utcoffset)
                 self.assertEqual(t.dst(), offset.dst)
 
+    def test_cache_exception(self):
+        class Incomparable(str):
+            eq_called = False
+            def __eq__(self, other):
+                self.eq_called = True
+                raise CustomError
+            __hash__ = str.__hash__
+
+        key = "America/Los_Angeles"
+        tz1 = self.klass(key)
+        key = Incomparable(key)
+        try:
+            tz2 = self.klass(key)
+        except CustomError:
+            self.assertTrue(key.eq_called)
+        else:
+            self.assertFalse(key.eq_called)
+            self.assertIs(tz2, tz1)
+
 
 class CZoneInfoTest(ZoneInfoTest):
     module = c_zoneinfo
@@ -1507,6 +1530,26 @@ class ZoneInfoCacheTest(TzPathUserMixin, ZoneInfoTestBase):
         self.assertIsNot(dub0, dub1)
         self.assertIs(tok0, tok1)
 
+    def test_clear_cache_refleak(self):
+        class Stringy(str):
+            allow_comparisons = True
+            def __eq__(self, other):
+                if not self.allow_comparisons:
+                    raise CustomError
+                return super().__eq__(other)
+            __hash__ = str.__hash__
+
+        key = Stringy("America/Los_Angeles")
+        self.klass(key)
+        key.allow_comparisons = False
+        try:
+            # Note: This is try/except rather than assertRaises because
+            # there is no guarantee that the key is even still in the cache,
+            # or that the key for the cache is the original `key` object.
+            self.klass.clear_cache(only_keys="America/Los_Angeles")
+        except CustomError:
+            pass
+
 
 class CZoneInfoCacheTest(ZoneInfoCacheTest):
     module = c_zoneinfo