cpython icon indicating copy to clipboard operation
cpython copied to clipboard

zoneinfo: ZoneInfo raises IsADirectoryError instead of ZoneInfoNotFoundError

Open fc57fb39-0a7a-4ca9-8996-e973ba0d61a9 opened this issue 5 years ago • 12 comments

BPO 41530
Nosy @vstinner, @pganssle, @bijij
PRs
  • python/cpython#21839
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = None
    closed_at = None
    created_at = <Date 2020-08-12.11:42:16.788>
    labels = ['type-bug', 'library', '3.9', '3.10']
    title = 'zoneinfo: ZoneInfo raises IsADirectoryError instead of ZoneInfoNotFoundError'
    updated_at = <Date 2020-08-12.14:46:45.318>
    user = 'https://github.com/bijij'
    

    bugs.python.org fields:

    activity = <Date 2020-08-12.14:46:45.318>
    actor = 'josh.ja.butt'
    assignee = 'none'
    closed = False
    closed_date = None
    closer = None
    components = ['Library (Lib)']
    creation = <Date 2020-08-12.11:42:16.788>
    creator = 'josh.ja.butt'
    dependencies = []
    files = []
    hgrepos = []
    issue_num = 41530
    keywords = ['patch']
    message_count = 10.0
    messages = ['375225', '375226', '375228', '375233', '375234', '375235', '375238', '375239', '375240', '375251']
    nosy_count = 4.0
    nosy_names = ['vstinner', 'python-dev', 'p-ganssle', 'josh.ja.butt']
    pr_nums = ['21839']
    priority = 'normal'
    resolution = None
    stage = 'patch review'
    status = 'open'
    superseder = None
    type = 'behavior'
    url = 'https://bugs.python.org/issue41530'
    versions = ['Python 3.9', 'Python 3.10']
    

    Linked PRs

    • gh-131333

    Attempting to parse specific keys in zoneinfo.ZoneInfo with tzdata installed will raise unhandled exceptions

    e.g. on windows

    >>> import zoneinfo
    >>> zoneinfo.ZoneInfo('Pacific')
    PermissionError: [Errno 13] Permission denied: 'C:\\Program Files\\Python39\\lib\\site-packages\\tzdata\\zoneinfo\\Pacific'
    
    >>> import zoneinfo
    >>> zoneinfo.ZoneInfo('__init__.py')
    ValueError: Invalid TZif file: magic not found
    

    This happens when non TZif files or directories in the tzdata.zoneinfo module are used as keys.

    Hi. It seems like you are running Windows.

    About the permission error, how did you install tzdata? Did you install it as an admin user and do you run Python as a different user?

    vstinner avatar Aug 12 '20 11:08 vstinner

    tzdata was installed as an admin user, however this behaviour was reproducible on a linux installation where for the ZoneInfo.('Pacific') instance an IsADirectoryError would instead be raised.

    On Wed, Aug 12, 2020 at 9:50 PM STINNER Victor <[email protected]> wrote:

    STINNER Victor <[email protected]> added the comment:

    Hi. It seems like you are running Windows.

    About the permission error, how did you install tzdata? Did you install it as an admin user and do you run Python as a different user?

    ---------- nosy: +vstinner


    Python tracker <[email protected]> <https://bugs.python.org/issue41530>


    Oh right, 'Pacific' is a directory, not a valid zone, and so ZoneInfoNotFoundError should be raised. I see.

    Example:

    $ ./python -m venv env
    $ env/bin/python -m pip install tzdata
    $ env/bin/python 
    
    # ZoneInfoNotFoundError expected, get IsADirectoryError
    >>> import zoneinfo; zoneinfo.ZoneInfo('Pacific')
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/home/vstinner/python/master/Lib/zoneinfo/_common.py", line 12, in load_tzdata
        return importlib.resources.open_binary(package_name, resource_name)
      File "/home/vstinner/python/master/Lib/importlib/resources.py", line 40, in open_binary
        return reader.open_resource(resource)
      File "/home/vstinner/python/master/Lib/importlib/abc.py", line 419, in open_resource
        return self.files().joinpath(resource).open('rb')
      File "/home/vstinner/python/master/Lib/pathlib.py", line 1238, in open
        return io.open(self, mode, buffering, encoding, errors, newline,
    IsADirectoryError: [Errno 21] Is a directory: '/home/vstinner/python/master/env/lib/python3.10/site-packages/tzdata/zoneinfo/Pacific'
    
    # valid zone
    >>> import zoneinfo; zoneinfo.ZoneInfo('Pacific/Noumea')
    zoneinfo.ZoneInfo(key='Pacific/Noumea')
    
    # raise ZoneInfoNotFoundError as expected (from FileNotFoundError)
    >>> import zoneinfo; zoneinfo.ZoneInfo('xxx')
    Traceback (most recent call last):
      File "/home/vstinner/python/master/Lib/zoneinfo/_common.py", line 12, in load_tzdata
        return importlib.resources.open_binary(package_name, resource_name)
      File "/home/vstinner/python/master/Lib/importlib/resources.py", line 40, in open_binary
        return reader.open_resource(resource)
      File "/home/vstinner/python/master/Lib/importlib/abc.py", line 419, in open_resource
        return self.files().joinpath(resource).open('rb')
      File "/home/vstinner/python/master/Lib/pathlib.py", line 1238, in open
        return io.open(self, mode, buffering, encoding, errors, newline,
      File "/home/vstinner/python/master/Lib/pathlib.py", line 1106, in _opener
        return self._accessor.open(self, flags, mode)
    FileNotFoundError: [Errno 2] No such file or directory: '/home/vstinner/python/master/env/lib/python3.10/site-packages/tzdata/zoneinfo/xxx'
    
    During handling of the above exception, another exception occurred:
    
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/home/vstinner/python/master/Lib/zoneinfo/_common.py", line 24, in load_tzdata
        raise ZoneInfoNotFoundError(f"No time zone found with key {key}")
    zoneinfo._common.ZoneInfoNotFoundError: 'No time zone found with key xxx'
    

    vstinner avatar Aug 12 '20 13:08 vstinner

    On Linux, converting IsADirectoryError to ZoneInfoNotFoundError is easy.

    But on Windows, I don't think that converting any PermissionError into a ZoneInfoNotFoundError is a good idea. Maybe if PermissionError happens, we should check if the path is a directory.

    Pseudo-code:

    try: <open and read file> except OSError as exc: if os.path.isdir(zone_path): raise ZoneInfoNotFoundError else: raise

    vstinner avatar Aug 12 '20 13:08 vstinner

    Oh, I dislike Roundup UI. I didn't notice that a PR was submitted: PR 21839 (now closed).

    vstinner avatar Aug 12 '20 13:08 vstinner

    I had opened a PR which just caught IsADirectoryError and PermissionError but closed it as you'd mentioned that handling PermissionError that way probably would not be the best, and I agree. I can reopen and modify to check path on OSerror if you'd like otherwise feel free to handle however you see fit.

    I think that ZoneInfo('__init__.py') is also a problem, but a slightly different one. That comes from the fact that in order to support importlib.resources, each of the zoneinfo subdirectories needs an __init__.py, but the ZoneInfo constructor should probably ignore those, since they are added by tzdata and not actually part of a tzdata distribution.

    pganssle avatar Aug 12 '20 13:08 pganssle

    By the way, it might be easiest to start with a PR against backports.zoneinfo, because I have a lot more linting, coverage and format checks set up there: https://github.com/pganssle/zoneinfo

    pganssle avatar Aug 12 '20 13:08 pganssle

    I'll reopen the PR with an attempted resolution of the OSError related issues however. In my quick testing it seems at least on Linux backports.zoneinfo segfaults when trying ZoneInfo('__init__.py') so that might be a larger issue for Paul to look into.

    This is still an issue with python3.13:

    >>> from zoneinfo._common import load_tzdata
    >>> load_tzdata("Europe")
    Traceback (most recent call last):
      File "<python-input-1>", line 1, in <module>
        load_tzdata("Europe")
        ~~~~~~~~~~~^^^^^^^^^^
      File "/usr/lib/python3.13/zoneinfo/_common.py", line 12, in load_tzdata
        return resources.files(package_name).joinpath(resource_name).open("rb")
               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
      File "/usr/lib/python3.13/pathlib/_local.py", line 537, in open
        return io.open(self, mode, buffering, encoding, errors, newline)
               ~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    IsADirectoryError: [Errno 21] Is a directory: '/path/to/python3.13/site-packages/tzdata/zoneinfo/Europe'
    

    fabien-michel avatar Nov 26 '24 20:11 fabien-michel

    IsADirectoryError has been fixed, now just the windows specific case of PermissionError remains. We should not catch it outright, but maybe we could check if the error is for a file in tzdata, and then raise ZoneInfoNotFoundError?

    StanFromIreland avatar Jun 29 '25 11:06 StanFromIreland

    IsADirectoryError has been fixed, now just the windows specific case of PermissionError remains. We should not catch it outright, but maybe we could check if the error is for a file in tzdata, and then raise ZoneInfoNotFoundError?

    I created https://github.com/python/cpython/pull/136117 for the PermissionError on Windows.

    vstinner avatar Jun 30 '25 09:06 vstinner

    Windows pr and backports have been merged. I do not know of anything else to do here, if other os-specific cases pop up in the future we can always reopen.

    StanFromIreland avatar Jul 07 '25 14:07 StanFromIreland