]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.13] gh-120782: Update internal type cache when reloading datetime (GH-120829)...
authorneonene <53406459+neonene@users.noreply.github.com>
Wed, 3 Jul 2024 08:22:51 +0000 (17:22 +0900)
committerGitHub <noreply@github.com>
Wed, 3 Jul 2024 08:22:51 +0000 (13:52 +0530)
* [3.13] gh-120782: Update internal type cache when reloading datetime

When reloading _datetime module, the single-phase version did not invoke the PyInit__datetime function, whereas the current multi-phase version updates the static types through the module init. The outdated static type cache in the interpreter state needs to be invalidated at the end of reloading the multi-phase module.

Lib/test/datetimetester.py
Misc/NEWS.d/next/Library/2024-06-21-12-00-16.gh-issue-120782.LOE8tj.rst [new file with mode: 0644]
Modules/_datetimemodule.c

index 3f67c6990b652e307a7b81e0fc3574c8e1791f8c..00819fda45289f6620120800db25e43b0f20539d 100644 (file)
@@ -52,25 +52,6 @@ except ImportError:
     pass
 #
 
-# This is copied from test_import/__init__.py.
-def no_rerun(reason):
-    """Skip rerunning for a particular test.
-
-    WARNING: Use this decorator with care; skipping rerunning makes it
-    impossible to find reference leaks. Provide a clear reason for skipping the
-    test using the 'reason' parameter.
-    """
-    def deco(func):
-        _has_run = False
-        def wrapper(self):
-            nonlocal _has_run
-            if _has_run:
-                self.skipTest(reason)
-            func(self)
-            _has_run = True
-        return wrapper
-    return deco
-
 pickle_loads = {pickle.loads, pickle._loads}
 
 pickle_choices = [(pickle, pickle, proto)
@@ -6420,7 +6401,6 @@ class IranTest(ZoneInfoTest):
 
 
 @unittest.skipIf(_testcapi is None, 'need _testcapi module')
-@no_rerun("the encapsulated datetime C API does not support reloading")
 class CapiTest(unittest.TestCase):
     def setUp(self):
         # Since the C API is not present in the _Pure tests, skip all tests
@@ -6889,6 +6869,38 @@ class ExtensionModuleTests(unittest.TestCase):
                 """)
             script_helper.assert_python_ok('-c', script)
 
+    def test_update_type_cache(self):
+        # gh-120782
+        script = textwrap.dedent("""
+            import sys
+            for i in range(5):
+                import _datetime
+                assert _datetime.date.max > _datetime.date.min
+                assert _datetime.time.max > _datetime.time.min
+                assert _datetime.datetime.max > _datetime.datetime.min
+                assert _datetime.timedelta.max > _datetime.timedelta.min
+                assert _datetime.date.__dict__["min"] is _datetime.date.min
+                assert _datetime.date.__dict__["max"] is _datetime.date.max
+                assert _datetime.date.__dict__["resolution"] is _datetime.date.resolution
+                assert _datetime.time.__dict__["min"] is _datetime.time.min
+                assert _datetime.time.__dict__["max"] is _datetime.time.max
+                assert _datetime.time.__dict__["resolution"] is _datetime.time.resolution
+                assert _datetime.datetime.__dict__["min"] is _datetime.datetime.min
+                assert _datetime.datetime.__dict__["max"] is _datetime.datetime.max
+                assert _datetime.datetime.__dict__["resolution"] is _datetime.datetime.resolution
+                assert _datetime.timedelta.__dict__["min"] is _datetime.timedelta.min
+                assert _datetime.timedelta.__dict__["max"] is _datetime.timedelta.max
+                assert _datetime.timedelta.__dict__["resolution"] is _datetime.timedelta.resolution
+                assert _datetime.timezone.__dict__["min"] is _datetime.timezone.min
+                assert _datetime.timezone.__dict__["max"] is _datetime.timezone.max
+                assert _datetime.timezone.__dict__["utc"] is _datetime.timezone.utc
+                assert isinstance(_datetime.timezone.min, _datetime.tzinfo)
+                assert isinstance(_datetime.timezone.max, _datetime.tzinfo)
+                assert isinstance(_datetime.timezone.utc, _datetime.tzinfo)
+                del sys.modules['_datetime']
+            """)
+        script_helper.assert_python_ok('-c', script)
+
 
 def load_tests(loader, standard_tests, pattern):
     standard_tests.addTest(ZoneInfoCompleteTest())
diff --git a/Misc/NEWS.d/next/Library/2024-06-21-12-00-16.gh-issue-120782.LOE8tj.rst b/Misc/NEWS.d/next/Library/2024-06-21-12-00-16.gh-issue-120782.LOE8tj.rst
new file mode 100644 (file)
index 0000000..02acbd2
--- /dev/null
@@ -0,0 +1 @@
+Fix wrong references of the :mod:`datetime` types after reloading the module.
index 6c1ae29636ec47cb0634d43cbce968c7abfb7e68..f7d6912ab41ce4af650da4e7ecf929f12a3e0cbf 100644 (file)
@@ -7339,6 +7339,12 @@ _datetime_exec(PyObject *module)
     static_assert(DI100Y == 25 * DI4Y - 1, "DI100Y");
     assert(DI100Y == days_before_year(100+1));
 
+    if (reloading) {
+        for (size_t i = 0; i < Py_ARRAY_LENGTH(capi_types); i++) {
+            PyType_Modified(capi_types[i]);
+        }
+    }
+
     if (set_current_module(interp, module) < 0) {
         goto error;
     }