Skip to content

Commit

Permalink
Fixed localtime._unix._get_localzone() (python-babel#990)
Browse files Browse the repository at this point in the history
In certain OS installations, the target of /etc/localtime contains
double slashes. This is a valid path, but not a valid zoneinfo key.
This fix replaces `os.readlink` with `os.path.realpath`, which is
guaranteed to return a normalized path.

Fixes python-babel#990
  • Loading branch information
mdklatt committed Jun 1, 2023
1 parent 1a4e658 commit 9c1af0c
Show file tree
Hide file tree
Showing 3 changed files with 18 additions and 7 deletions.
5 changes: 4 additions & 1 deletion babel/localtime/_unix.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@ def _get_localzone(_root: str = '/') -> datetime.tzinfo:
# zone on operating systems like OS X. On OS X especially this is the
# only one that actually works.
try:
link_dst = os.readlink('/etc/localtime')
# Make sure the path is normalized so that it will be parsed correctly
# below, e.g. 'Etc//UTC' is a valid path but not a valid zoneinfo key.
# <https://github.com/python-babel/babel/issues/990>
link_dst = os.path.realpath('/etc/localtime')
except OSError:
pass
else:
Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ def run(self):
'dev': [
'pytest>=6.0',
'pytest-cov',
'pyfakefs~=5.0',
'freezegun~=1.0',
],
},
Expand Down
19 changes: 13 additions & 6 deletions tests/test_localtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
"""
import datetime
import pytest
import sys
import os.path
import sys
from pathlib import Path

from babel.localtime import get_localzone, LOCALTZ
import pytest

from babel.localtime import LOCALTZ, get_localzone

_timezones = {
"Etc/UTC": "UTC",
Expand Down Expand Up @@ -60,9 +60,16 @@ def timezone(self, zoneinfo, request) -> str:
# standard-ish Unix implementation where `/etc/localtime` can be used
# to set the time zone.
# <https://www.unix.com/man-page/linux/4/zoneinfo>
#
# Note the double slash in the symlink source. Because os.symlink()
# does not normalize paths, this will appear on the file system as
# e.g. `zoneinfo//Etc/UTC`. Although irregular, this is a valid link
# and has been observed in certain Ubuntu installations, so make sure
# 'localtime' can handle them.
# <https://github.com/python-babel/babel/issues/990>.
key, name = request.param
os.remove("/etc/localtime")
os.symlink(f"{zoneinfo}/{key}", "/etc/localtime")
os.symlink(f"{zoneinfo}//{key}", "/etc/localtime") # double slash OK
return name

def test_get_localzone(self, zoneinfo, timezone):
Expand All @@ -76,7 +83,7 @@ def test_localtz(self, zoneinfo):
""" Test the LOCALTZ module attribute.
"""
assert LOCALTZ == get_localzone()
assert get_localzone() == LOCALTZ


@pytest.mark.skipif(sys.platform != "win32", reason="Win32 tests")
Expand All @@ -90,4 +97,4 @@ def test_localtz(self):
""" Test the LOCALTZ module attribute.
"""
assert LOCALTZ == get_localzone()
assert get_localzone() == LOCALTZ

0 comments on commit 9c1af0c

Please sign in to comment.