Skip to content

Commit

Permalink
Avoid crashing on importing babel.localtime when TZ envvar is malfo…
Browse files Browse the repository at this point in the history
…rmed (#1100)

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
  • Loading branch information
akx authored Jul 23, 2024
1 parent 2f87363 commit ad0fabe
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 0 deletions.
14 changes: 14 additions & 0 deletions babel/localtime/_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
import pytz
except ModuleNotFoundError:
pytz = None

try:
import zoneinfo
except ModuleNotFoundError:
zoneinfo = None


def _get_tzinfo(tzenv: str):
Expand All @@ -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

Expand Down
29 changes: 29 additions & 0 deletions tests/test_localtime.py
Original file line number Diff line number Diff line change
@@ -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()

0 comments on commit ad0fabe

Please sign in to comment.