python / importlib_resources

Backport of the importlib.resources module
Apache License 2.0
64 stars 44 forks source link

stock importlib.resources on python 3.9 importlib.resources.files does not work in zipapp #281

Closed franzhaas closed 1 year ago

franzhaas commented 1 year ago

Dear all,

This is not a bug of importlib_resources, but I still hope that the community here can help me.

I experience in my environment that importlib.resources.files works on python 3.10 and 3.11.

On python 3.6, 3.7 and 3.8 it does not exist -> therefore I use importlib_resources.

On python 3.9 it exists, and works in a regular environment, but not in a zipapp.

Again -> I use importlib_resources.

Is this to be expected? Am I misunderstanding something?

FFY00 commented 1 year ago

Hi, it is not expected. Can you share an example of how it doesn't work?

franzhaas commented 1 year ago

For me this example works in 3.10, 3.11 but not 3.9.13

from  importlib.resources import files

with files("res.res").joinpath("a.txt").open('r', encoding="utf-8") as f:
    print(f.read())

Thanks for giving it a look...

franzhaas commented 1 year ago

res.zip

Here is an example of a zipapp with stock importlib, which fails with 3.9 ...

pekkaklarck commented 1 year ago

I tested running the attached example directly like python res.zip. On Linux it passes with Python 3.9 and newer, but on Windows I get this the following error with Python 3.9.4. No problems with Python 3.10 or 3.11 there either.

KeyError: "There is no item named 'res\\res/a.txt' in the archive"

This looks like a Windows compatibility bug in importlib.resources for me, but I couldn't find a matching bug report from CPython issue tracker. Probably doesn't make sense submit an issue because the bug is already fixed, but it might make sense to add a note to importlib.resources.files() docs about it not working on Windows with Python 3.9.

pekkaklarck commented 1 year ago

Here's a workaround:

from importlib.resources import files

path = files("res.res").joinpath("a.txt")
if hasattr(path, 'at') and '\\' in path.at:
    path.at = path.at.replace('\\', '/')
with path.open(encoding='utf-8') as file:
    print(file.read())
jaraco commented 1 year ago

From the compatibility matrix in the readme, you can see that importlib_resources 1.3 was used for Python 3.9 and importlib_resources 5.0 was used in Python 3.10, so one of those changes probably addressed the shortcoming. I suspect it was the changes in v2.0.0. In Python 3.9, the files were resolved with fallback_resources, and by Python 3.10, the resources were supplied by a native reader that handled the path separators.

it might make sense to add a note to importlib.resources.files() docs about it not working on Windows with Python 3.9.

Since Python 3.9 only gets security fixes, I'm inclined to say it's not worth adding this detail to the docs (and may not be allowed).

Is this to be expected?

It is expected that using later versions of importlib_resources on older Pythons provides a newer experience, so that's the recommended workaround.