diff --git a/Lib/test/test_zoneinfo/test_zoneinfo.py b/Lib/test/test_zoneinfo/test_zoneinfo.py index 44e87e71c8ee7b..c6495398b8b9d0 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..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}" ) @@ -156,6 +156,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 key = key.replace(os.sep, "/") 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