From 97c8a36ae5e9ad4b9b0085bfa39b0ce3486d09a6 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 16 Aug 2025 09:02:15 +0300 Subject: [PATCH 1/4] gh-137754: Fix import of zoneinfo if _datetime is not available Both modules should use the Python implementation in that case. --- Lib/test/test_zoneinfo/test_zoneinfo.py | 16 ++++++++++++++++ Lib/zoneinfo/__init__.py | 5 ++++- ...025-08-16-09-02-11.gh-issue-137754.mCev1Y.rst | 2 ++ 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2025-08-16-09-02-11.gh-issue-137754.mCev1Y.rst diff --git a/Lib/test/test_zoneinfo/test_zoneinfo.py b/Lib/test/test_zoneinfo/test_zoneinfo.py index 44e87e71c8ee7b..97f869cd33e89d 100644 --- a/Lib/test/test_zoneinfo/test_zoneinfo.py +++ b/Lib/test/test_zoneinfo/test_zoneinfo.py @@ -12,6 +12,7 @@ import re import shutil import struct +import sys import tempfile import unittest from datetime import date, datetime, time, timedelta, timezone @@ -1946,6 +1947,21 @@ class CTestModule(TestModule): module = c_zoneinfo +class MiscTests(unittest.TestCase): + def test_pydatetime(self): + with CleanImport('zoneinfo', 'zoneinfo._tzpath', 'zoneinfo._zoneinfo', + '_zoneinfo', 'datetime', '_pydatetime', '_datetime'): + sys.modules['_datetime'] = None + import datetime + import zoneinfo + zoneinfo.ZoneInfo.clear_cache() + tzinfo = zoneinfo.ZoneInfo('Europe/Paris') + datetime.datetime(2025, 10, 26, 2, 0, tzinfo=tzinfo) + self.assertIn('_pydatetime', sys.modules) + self.assertNotIn('_zoneinfo', sys.modules) + self.assertIn('zoneinfo._zoneinfo', sys.modules) + + class ExtensionBuiltTest(unittest.TestCase): """Smoke test to ensure that the C and Python extensions are both tested. diff --git a/Lib/zoneinfo/__init__.py b/Lib/zoneinfo/__init__.py index f5510ee0497513..df2ae909f53cf2 100644 --- a/Lib/zoneinfo/__init__.py +++ b/Lib/zoneinfo/__init__.py @@ -12,7 +12,10 @@ try: from _zoneinfo import ZoneInfo -except ImportError: # pragma: nocover +except (ImportError, AttributeError): # pragma: nocover + # AttributeError: module 'datetime' has no attribute 'datetime_CAPI'. + # This happens when the '_datetime' module is not available and the + # pure Python implementation is used instead. from ._zoneinfo import ZoneInfo reset_tzpath = _tzpath.reset_tzpath diff --git a/Misc/NEWS.d/next/Library/2025-08-16-09-02-11.gh-issue-137754.mCev1Y.rst b/Misc/NEWS.d/next/Library/2025-08-16-09-02-11.gh-issue-137754.mCev1Y.rst new file mode 100644 index 00000000000000..323870afd97106 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-08-16-09-02-11.gh-issue-137754.mCev1Y.rst @@ -0,0 +1,2 @@ +Fix import of the :mod:`zoneinfo` module if the C implementation of the +:mod:`datetime` module is not available. From 312a09c7777fbbdb89f81f76fe705626a969bc20 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 17 Aug 2025 09:06:14 +0300 Subject: [PATCH 2/4] Run the testing code in subprocess. --- Lib/test/test_zoneinfo/test_zoneinfo.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_zoneinfo/test_zoneinfo.py b/Lib/test/test_zoneinfo/test_zoneinfo.py index 97f869cd33e89d..955202abed3f37 100644 --- a/Lib/test/test_zoneinfo/test_zoneinfo.py +++ b/Lib/test/test_zoneinfo/test_zoneinfo.py @@ -12,7 +12,6 @@ import re import shutil import struct -import sys import tempfile import unittest from datetime import date, datetime, time, timedelta, timezone @@ -23,6 +22,7 @@ from test.test_zoneinfo import _support as test_support from test.test_zoneinfo._support import TZPATH_TEST_LOCK, ZoneInfoTestBase from test.support.import_helper import import_module, CleanImport +from test.support.script_helper import assert_python_ok lzma = import_module('lzma') py_zoneinfo, c_zoneinfo = test_support.get_modules() @@ -1949,17 +1949,14 @@ class CTestModule(TestModule): class MiscTests(unittest.TestCase): def test_pydatetime(self): - with CleanImport('zoneinfo', 'zoneinfo._tzpath', 'zoneinfo._zoneinfo', - '_zoneinfo', 'datetime', '_pydatetime', '_datetime'): + assert_python_ok('-c', '''if 1: + import sys sys.modules['_datetime'] = None import datetime import zoneinfo - zoneinfo.ZoneInfo.clear_cache() tzinfo = zoneinfo.ZoneInfo('Europe/Paris') datetime.datetime(2025, 10, 26, 2, 0, tzinfo=tzinfo) - self.assertIn('_pydatetime', sys.modules) - self.assertNotIn('_zoneinfo', sys.modules) - self.assertIn('zoneinfo._zoneinfo', sys.modules) + ''') class ExtensionBuiltTest(unittest.TestCase): From ee7e8c0c3cfca3d142319bf2ad3f792dded09c86 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 21 Aug 2025 21:08:46 +0300 Subject: [PATCH 3/4] Fix test on Windows. --- Lib/test/test_zoneinfo/test_zoneinfo.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_zoneinfo/test_zoneinfo.py b/Lib/test/test_zoneinfo/test_zoneinfo.py index 955202abed3f37..a64311b836d2e9 100644 --- a/Lib/test/test_zoneinfo/test_zoneinfo.py +++ b/Lib/test/test_zoneinfo/test_zoneinfo.py @@ -1954,9 +1954,10 @@ def test_pydatetime(self): sys.modules['_datetime'] = None import datetime import zoneinfo - tzinfo = zoneinfo.ZoneInfo('Europe/Paris') + tzinfo = zoneinfo.ZoneInfo('Europe/London') datetime.datetime(2025, 10, 26, 2, 0, tzinfo=tzinfo) - ''') + ''', + PYTHONTZPATH=str(ZONEINFO_DATA.tzpath)) class ExtensionBuiltTest(unittest.TestCase): From e9c16d3403b1dd02997496690dd6f73c452d49df Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 22 Aug 2025 12:19:45 +0300 Subject: [PATCH 4/4] Add an explanation comment. --- Lib/test/test_zoneinfo/test_zoneinfo.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Lib/test/test_zoneinfo/test_zoneinfo.py b/Lib/test/test_zoneinfo/test_zoneinfo.py index a64311b836d2e9..addc905af5ede9 100644 --- a/Lib/test/test_zoneinfo/test_zoneinfo.py +++ b/Lib/test/test_zoneinfo/test_zoneinfo.py @@ -1949,6 +1949,13 @@ class CTestModule(TestModule): class MiscTests(unittest.TestCase): def test_pydatetime(self): + # Test that zoneinfo works if the C implementation of datetime + # is not available and the Python implementation of datetime is used. + # The Python implementation of zoneinfo should be used in thet case. + # + # Run the test in a subprocess, as importing _zoneinfo with + # _datettime disabled causes crash in the previously imported + # _zoneinfo. assert_python_ok('-c', '''if 1: import sys sys.modules['_datetime'] = None