babel icon indicating copy to clipboard operation
babel copied to clipboard

Crash with double-slashes in /etc/localtime on Python 3.9

Open torfsen opened this issue 2 years ago • 8 comments

Overview Description

Importing babel.localtime crashes on Python 3.9 if /etc/localtime contains double-slashes.

Steps to Reproduce

ls -lh /etc/localtime 
lrwxrwxrwx 1 root root 24 Mar  2 03:25 /etc/localtime -> /usr/share/zoneinfo//UTC

Note the double-slash, //UTC.

# python
Python 3.9.16 (main, Mar 12 2023, 19:18:41) 
[GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import babel.localtime
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.9/site-packages/babel/localtime/__init__.py", line 41, in <module>
    LOCALTZ = get_localzone()
  File "/usr/local/lib/python3.9/site-packages/babel/localtime/__init__.py", line 37, in get_localzone
    return _get_localzone()
  File "/usr/local/lib/python3.9/site-packages/babel/localtime/_unix.py", line 49, in _get_localzone
    tzinfo = _get_tzinfo(zone_name)
  File "/usr/local/lib/python3.9/site-packages/babel/localtime/_helpers.py", line 21, in _get_tzinfo
    return zoneinfo.ZoneInfo(tzenv)
  File "/usr/local/lib/python3.9/zoneinfo/_tzpath.py", line 67, in find_tzfile
    _validate_tzfile_path(key)
  File "/usr/local/lib/python3.9/zoneinfo/_tzpath.py", line 81, in _validate_tzfile_path
    raise ValueError(
ValueError: ZoneInfo keys may not be absolute paths, got: /UTC
>>> import babel
>>> babel.__version__
'2.12.1'

Actual Results

Crash.

Expected Results

No crash.

Reproducibility

Crashes for me on Python 3.9 with babel 2.12.1. Works on Python 3.8.

The problem is that babel.localtime._unix._get_localzone has naive path handling.

Additional Information

I'm not sure whether babel is to blame here, but since this is working on Python 3.8 it would be nice to make babel more robust here. As far as I can tell, double slashes are valid in POSIX and should be treated as single slashes.

torfsen avatar Mar 15 '23 10:03 torfsen

Using babel version v2.11.0 with Python 3.9 does not give the error, so this becomes an issue in the latest versions.

alexmaragko avatar Mar 23 '23 17:03 alexmaragko

I just encountered this with Babel 2.12.1 running with Python 3.9 inside an Ubuntu 22.04 (Jammy) container. The problem is definitely the double slash in the /etc/localtime link. I don't know why the Ubuntu installer creates an irregular link like that, but it is indeed valid.

It looks like the problem is in localtime/_unix.py. The os.readlink() function is used to get the target of /etc/localtime. This does not normalize the path, so the time zone ID is being parsed incorrectly as /UTC instead of UTC.


def _get_localzone(_root: str = '/') -> datetime.tzinfo:
    """Tries to find the local timezone configuration.
    This method prefers finding the timezone name and passing that to
    zoneinfo or pytz, over passing in the localtime file, as in the later
    case the zoneinfo name is unknown.
    The parameter _root makes the function look for files like /etc/localtime
    beneath the _root directory. This is primarily used by the tests.
    In normal usage you call the function without parameters.
    """
    # <snip>

    # This is actually a pretty reliable way to test for the local time
    # 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')
    except OSError:
        pass

    # <snip>

mdklatt avatar May 31 '23 19:05 mdklatt

I think the solution to this is to replace os.readlink() with pathlib.Path().resolve(), which will normalize the path and remove the double slashes. I created a fork, but coming up with a test for this is tricky.

mdklatt avatar May 31 '23 22:05 mdklatt

Confirmed this is also a problem with Python 3.10 on Ubuntu.

natescherer avatar Aug 04 '23 16:08 natescherer

I submitted https://github.com/python-babel/babel/pull/1006 for this back in June. The PR could not be automatically merged because some unrelated broken tests were failing. It looks like it was manually merged by a maintainer, but I'm not sure what the current status is.

mdklatt avatar Oct 12 '23 19:10 mdklatt

The broken unit tests have been fixed upstream. I rebased my fixes onto master and submitted https://github.com/python-babel/babel/pull/1035. This should pass all automated checks, which will hopefully streamline the merge process and speed up the resolution of this issue.

mdklatt avatar Oct 12 '23 20:10 mdklatt

For anybody that needs a temporary workaround, I was able to fix this in an Ubuntu Docker image by recreating the /etc/localtime link to remove the double slashes:

rm -f /etc/localtime
ln -s /usr/share/zoneinfo/Etc/UTC /etc/localtime

mdklatt avatar Oct 12 '23 20:10 mdklatt

I'm having this issue with Ubuntu 22.04, python 3.10 and babel 2.13.

Traceback (most recent call last):
  File "/usr/local/bin/sphinx-build", line 5, in <module>
    from sphinx.cmd.build import main
  File "/usr/local/lib/python3.10/dist-packages/sphinx/cmd/build.py", line 25, in <module>
    from sphinx.application import Sphinx
  File "/usr/local/lib/python3.10/dist-packages/sphinx/application.py", line 32, in <module>
    from sphinx.config import Config
  File "/usr/local/lib/python3.10/dist-packages/sphinx/config.py", line 22, in <module>
    from sphinx.util.i18n import format_date
  File "/usr/local/lib/python3.10/dist-packages/sphinx/util/i18n.py", line 17, in <module>
    import babel.dates
  File "/usr/local/lib/python3.10/dist-packages/babel/dates.py", line 34, in <module>
    from babel import localtime
  File "/usr/local/lib/python3.10/dist-packages/babel/localtime/__init__.py", line 41, in <module>
    LOCALTZ = get_localzone()
  File "/usr/local/lib/python3.10/dist-packages/babel/localtime/__init__.py", line 37, in get_localzone
    return _get_localzone()
  File "/usr/local/lib/python3.10/dist-packages/babel/localtime/_unix.py", line 49, in _get_localzone
    tzinfo = _get_tzinfo(zone_name)
  File "/usr/local/lib/python3.10/dist-packages/babel/localtime/_helpers.py", line 21, in _get_tzinfo
    return zoneinfo.ZoneInfo(tzenv)
  File "/usr/lib/python3.10/zoneinfo/_tzpath.py", line 67, in find_tzfile
    _validate_tzfile_path(key)
  File "/usr/lib/python3.10/zoneinfo/_tzpath.py", line 81, in _validate_tzfile_path
    raise ValueError(
ValueError: ZoneInfo keys may not be absolute paths, got: /UTC

The strange thing is that my /etc/localtime does not have any slash... but it is quite strange anyway:

$ cat /etc/localtime 
TZif2UTCTZif2UTC
UTC0

Don't know how to fix it.

[EDIT] Sorry! Now I noticed that the double slash is on the filepath itself...

$ ls -la /etc/localtime
/etc/localtime -> /usr/share/zoneinfo//UTC

So this solution indeed fixed my error:

$ rm -f /etc/localtime
$ ln -s /usr/share/zoneinfo/UTC /etc/localtime

igormcoelho avatar Oct 13 '23 20:10 igormcoelho

Because other people might hit the same problem:

In an Ubuntu 22.04 container I hat to execute this in addition to the above mentioned fixes:

sed -i "s/\///g" /etc/timezone

falkephi avatar Jan 09 '25 10:01 falkephi