]> git.ipfire.org Git - thirdparty/babel.git/commitdiff
Avoid crashing on importing `babel.localtime` when TZ envvar is malformed (#1100)
authorAarni Koskela <akx@iki.fi>
Tue, 23 Jul 2024 07:10:08 +0000 (10:10 +0300)
committerGitHub <noreply@github.com>
Tue, 23 Jul 2024 07:10:08 +0000 (10:10 +0300)
When `pytz` is _not_ installed, importing `babel.localtime` could fail
(repeatedly) when the `TZ` environment variable is malformed enough to be
caught by `_validate_tzfile_path`, which might throw a certain `ValueError`.

(When `pytz` is installed, it would raise an `UnknownTimeZoneError` we
already catch and ignore for the same sort of input.)

Fixes #1092

babel/localtime/_helpers.py
tests/test_localtime.py [new file with mode: 0644]

index f27b31522a49b6884707220e5a9592157b6f2828..e7e67052c1fbdb7e88761fd6e8f08e97d54cb54e 100644 (file)
@@ -2,7 +2,11 @@ try:
     import pytz
 except ModuleNotFoundError:
     pytz = None
+
+try:
     import zoneinfo
+except ModuleNotFoundError:
+    zoneinfo = None
 
 
 def _get_tzinfo(tzenv: str):
@@ -19,6 +23,16 @@ def _get_tzinfo(tzenv: str):
     else:
         try:
             return zoneinfo.ZoneInfo(tzenv)
+        except ValueError as ve:
+            # This is somewhat hacky, but since _validate_tzfile_path() doesn't
+            # raise a specific error type, we'll need to check the message to be
+            # one we know to be from that function.
+            # If so, we pretend it meant that the TZ didn't exist, for the benefit
+            # of `babel.localtime` catching the `LookupError` raised by
+            # `_get_tzinfo_or_raise()`.
+            # See https://github.com/python-babel/babel/issues/1092
+            if str(ve).startswith("ZoneInfo keys "):
+                return None
         except zoneinfo.ZoneInfoNotFoundError:
             pass
 
diff --git a/tests/test_localtime.py b/tests/test_localtime.py
new file mode 100644 (file)
index 0000000..723ffa0
--- /dev/null
@@ -0,0 +1,29 @@
+import sys
+
+import pytest
+
+from babel.localtime import _helpers, get_localzone
+
+
+@pytest.mark.skipif(
+    sys.platform == "win32",
+    reason="Issue 1092 is not applicable on Windows",
+)
+def test_issue_1092_without_pytz(monkeypatch):
+    pytest.importorskip("zoneinfo", reason="zoneinfo is not available")
+    monkeypatch.setenv("TZ", "/UTC")  # Malformed timezone name.
+    # In case pytz _is_ also installed, we want to pretend it's not, so patch it out...
+    monkeypatch.setattr(_helpers, "pytz", None)
+    with pytest.raises(LookupError):
+        get_localzone()
+
+
+@pytest.mark.skipif(
+    sys.platform == "win32",
+    reason="Issue 1092 is not applicable on Windows",
+)
+def test_issue_1092_with_pytz(monkeypatch):
+    pytest.importorskip("pytz", reason="pytz is not installed")
+    monkeypatch.setenv("TZ", "/UTC")  # Malformed timezone name.
+    with pytest.raises(LookupError):
+        get_localzone()