From 9267754cbccfa9daf9679cc79c7235599cb1c1aa Mon Sep 17 00:00:00 2001 From: MengAiDev <3463526515@qq.com> Date: Wed, 20 Aug 2025 13:25:58 +0800 Subject: [PATCH 1/3] bpo-XXXXX: Exclude symlinks from zoneinfo.available_timezones() results\n\nSometimes available_timezones() was including 'localtime' in its results\nbecause it wasn't properly handling symlinks. This change ensures that\nsymlinks like 'localtime' are excluded from the available timezones list,\nproviding consistent results across different systems.\n\nAdded a test case to verify that symlinks are properly excluded. --- Lib/test/test_zoneinfo/test_zoneinfo.py | 21 +++++++++++++++++++++ Lib/zoneinfo/_tzpath.py | 4 ++++ 2 files changed, 25 insertions(+) diff --git a/Lib/test/test_zoneinfo/test_zoneinfo.py b/Lib/test/test_zoneinfo/test_zoneinfo.py index 44e87e71c8ee7b..65c1ad5eba8a9d 100644 --- a/Lib/test/test_zoneinfo/test_zoneinfo.py +++ b/Lib/test/test_zoneinfo/test_zoneinfo.py @@ -1941,6 +1941,27 @@ def test_exclude_posixrules(self): actual = self.module.available_timezones() self.assertEqual(actual, expected) + def test_exclude_symlinks(self): + expected = { + "America/New_York", + "Europe/London", + } + + tree = list(expected) + ["localtime"] + + with tempfile.TemporaryDirectory() as td: + # Create regular timezone files + for key in expected: + self.touch_zone(key, td) + + # Create a symlink named "localtime" pointing to one of the timezone files + os.symlink("America/New_York", os.path.join(td, "localtime")) + + with self.tzpath_context([td]): + actual = self.module.available_timezones() + self.assertEqual(actual, expected) + self.assertNotIn("localtime", actual) + class CTestModule(TestModule): module = c_zoneinfo diff --git a/Lib/zoneinfo/_tzpath.py b/Lib/zoneinfo/_tzpath.py index 78fa6f00a8590a..093e89ab8ec844 100644 --- a/Lib/zoneinfo/_tzpath.py +++ b/Lib/zoneinfo/_tzpath.py @@ -155,6 +155,10 @@ def valid_key(fpath): for file in files: fpath = os.path.join(root, file) + + # Skip symlinks to avoid including files like 'localtime' + if os.path.islink(fpath): + continue key = os.path.relpath(fpath, start=tz_root) if os.sep != "/": # pragma: nocover From 0bcbf447c8fd38ce68c8ff846922e9655a89db3f Mon Sep 17 00:00:00 2001 From: MengAiDev <3463526515@qq.com> Date: Wed, 20 Aug 2025 14:14:25 +0800 Subject: [PATCH 2/3] bpo-XXXXX: Exclude symlinks from zoneinfo.available_timezones() results\n\nSometimes available_timezones() was including 'localtime' in its results\nbecause it wasn't properly handling symlinks. This change ensures that\nsymlinks like 'localtime' are excluded from the available timezones list,\nproviding consistent results across different systems.\n\nAdded a test case to verify that symlinks are properly excluded. --- .../next/Library/2025-08-20-14-01-13.gh-issue-137976.Ql3ZCr.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2025-08-20-14-01-13.gh-issue-137976.Ql3ZCr.rst diff --git a/Misc/NEWS.d/next/Library/2025-08-20-14-01-13.gh-issue-137976.Ql3ZCr.rst b/Misc/NEWS.d/next/Library/2025-08-20-14-01-13.gh-issue-137976.Ql3ZCr.rst new file mode 100644 index 00000000000000..b033e36d4a50f8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-08-20-14-01-13.gh-issue-137976.Ql3ZCr.rst @@ -0,0 +1 @@ +exclude symlinks from zoneinfo.available_timezones() results From dee7d4b93f32b37c094d2dc5b76f910d368443a5 Mon Sep 17 00:00:00 2001 From: MengAiDev <3463526515@qq.com> Date: Wed, 20 Aug 2025 14:22:00 +0800 Subject: [PATCH 3/3] fix precommit --- Lib/test/test_zoneinfo/test_zoneinfo.py | 2 +- Lib/zoneinfo/_tzpath.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_zoneinfo/test_zoneinfo.py b/Lib/test/test_zoneinfo/test_zoneinfo.py index 65c1ad5eba8a9d..c6495398b8b9d0 100644 --- a/Lib/test/test_zoneinfo/test_zoneinfo.py +++ b/Lib/test/test_zoneinfo/test_zoneinfo.py @@ -1953,7 +1953,7 @@ def test_exclude_symlinks(self): # Create regular timezone files for key in expected: self.touch_zone(key, td) - + # Create a symlink named "localtime" pointing to one of the timezone files os.symlink("America/New_York", os.path.join(td, "localtime")) diff --git a/Lib/zoneinfo/_tzpath.py b/Lib/zoneinfo/_tzpath.py index 093e89ab8ec844..eb040a3c4b77ec 100644 --- a/Lib/zoneinfo/_tzpath.py +++ b/Lib/zoneinfo/_tzpath.py @@ -9,7 +9,7 @@ def _reset_tzpath(to=None, stacklevel=4): if tzpaths is not None: if isinstance(tzpaths, (str, bytes)): raise TypeError( - f"tzpaths must be a list or tuple, " + "tzpaths must be a list or tuple, " + f"not {type(tzpaths)}: {tzpaths!r}" ) @@ -155,7 +155,7 @@ def valid_key(fpath): for file in files: fpath = os.path.join(root, file) - + # Skip symlinks to avoid including files like 'localtime' if os.path.islink(fpath): continue