You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
importlib_metadata handles PathLike objects on sys.path when importlib.PathFinder does not #372
EntryPoint(name='name', value='module:function', group='group')
Traceback (most recent call last):
File "/home/graingert/projects/demo_importlib.py", line 33, in <module>
sys.exit(main())
^^^^^^
File "/usr/lib/python3.11/contextlib.py", line 155, in __exit__
self.gen.throw(typ, value, traceback)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/graingert/projects/demo_importlib.py", line 13, in _tmp_path
yield pathlib.Path(tmp_dir)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/graingert/projects/demo_importlib.py", line 29, in main
print(ep.load())
^^^^^^^^^
File "/home/graingert/.virtualenvs/testing311/lib/python3.11/site-packages/importlib_metadata/__init__.py", line 203, in load
module = import_module(match.group('module'))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/importlib/__init__.py", line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "<frozen importlib._bootstrap>", line 1206, in _gcd_import
File "<frozen importlib._bootstrap>", line 1178, in _find_and_load
File "<frozen importlib._bootstrap>", line 1142, in _find_and_load_unlocked
ModuleNotFoundError: No module named 'module'
however if I os.fsdecode the tmp_path - before appending it to the sys.path I get:
EntryPoint(name='name', value='module:function', group='group')
<function function at 0x7f2b12192700>
I've uploaded the repro as a gist so I can replicate the error with this one-liner:
$ http https://gist.githubusercontent.com/jaraco/835328403b381be186f15ae943183031/raw/c876455f3ad994660139e6e1c472e1a1a934d792/metadata-issue372.py | pip-run -q importlib_metadata -- -
EntryPoint(name='name', value='module:function', group='group')
Traceback (most recent call last):
File "<stdin>", line 33, in <module>
File "<stdin>", line 29, in main
File "/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-run-necvv5sn/importlib_metadata/__init__.py", line 203, in load
module = import_module(match.group('module'))
File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/importlib/__init__.py", line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "<frozen importlib._bootstrap>", line 1050, in _gcd_import
File "<frozen importlib._bootstrap>", line 1027, in _find_and_load
File "<frozen importlib._bootstrap>", line 1004, in _find_and_load_unlocked
ModuleNotFoundError: No module named 'module'
I can confirm the same issue exists in the stdlib version:
$ http https://gist.githubusercontent.com/jaraco/835328403b381be186f15ae94318303/raw/c876455f3ad994660139e6e1c472e1a1a934d792/metadata-issue372.py | sed -e 's/import importlib_metadata/import importlib.metadata as importlib_metadata/' | python -
EntryPoint(name='name', value='module:function', group='group')
Traceback (most recent call last):
File "<stdin>", line 33, in <module>
File "<stdin>", line 29, in main
File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/importlib/metadata/__init__.py", line 162, in load
module = import_module(match.group('module'))
File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/importlib/__init__.py", line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "<frozen importlib._bootstrap>", line 1050, in _gcd_import
File "<frozen importlib._bootstrap>", line 1027, in _find_and_load
File "<frozen importlib._bootstrap>", line 1004, in _find_and_load_unlocked
ModuleNotFoundError: No module named 'module'
I'm not sure there's much for importlib metadata to do here. Sure, it could exclude path entries that aren't strings.
Removing the cast to str doesn't change the error (importlib_metadata still finds the path):
$ http https://gist.githubusercontent.com/jaraco/835328403b381be186f15ae943183031/raw/c876455f3ad994660139e6e1c472e1a1a934d792/metadata-issue372.py | pip-run -q git+https://github.com/python/importlib_metadata@bugfix/372-no-cast-path -- -
EntryPoint(name='name', value='module:function', group='group')
Traceback (most recent call last):
File "<stdin>", line 33, in <module>
File "<stdin>", line 29, in main
File "/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pip-run-mqdr02l5/importlib_metadata/__init__.py", line 203, in load
module = import_module(match.group('module'))
File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/importlib/__init__.py", line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "<frozen importlib._bootstrap>", line 1050, in _gcd_import
File "<frozen importlib._bootstrap>", line 1027, in _find_and_load
File "<frozen importlib._bootstrap>", line 1004, in _find_and_load_unlocked
ModuleNotFoundError: No module named 'module'
afaik sys.path can have bytes in, which that str call would change to "b'/some/dir'"
Ugh. The docs are contradictory on the topic, stating that it's a list of strings, but implying that bytes are supported by saying that types other than bytes and str are ignored.
I'm not sure I want to add support for bytes unless a real world use case demonstrates the need.
I agree that would stop it from discovering metadata on paths indicated by Path objects. It also would affect discovery by paths not on sys.path, possibly a behavior worth keeping.
I'm not sure what is the right behavior here. I'm inclined to sit on this one for now until importlib has settled on what should happen about sys.path, but even if it decides to retain the behavior that only str/bytes are allowed on sys.path, I'd lean toward importlib metadata supporting any paths that are str or Path-like.
What was the use-case that triggered this report? Is it something that can't be easily worked around?
so it turns out bytes don't actually work in sys.path: ((although they used to in py3.2) unless they are zip files) see https://bugs.python.org/issue47025
Traceback (most recent call last):
File "<frozen importlib._bootstrap_external>", line 1363, in _path_importer_cache
KeyError: b'/tmp/tmpokxmswlq'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/graingert/projects/demo_importlib.py", line 35, in <module>
sys.exit(main())
File "/home/graingert/projects/demo_importlib.py", line 25, in main
import module
File "<frozen importlib._bootstrap>", line 1027, in _find_and_load
File "<frozen importlib._bootstrap>", line 1002, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 945, in _find_spec
File "<frozen importlib._bootstrap_external>", line 1430, in find_spec
File "<frozen importlib._bootstrap_external>", line 1399, in _get_spec
File "<frozen importlib._bootstrap_external>", line 1365, in _path_importer_cache
File "<frozen importlib._bootstrap_external>", line 1341, in _path_hooks
File "<frozen importlib._bootstrap_external>", line 1623, in path_hook_for_FileFinder
File "<frozen importlib._bootstrap_external>", line 1495, in __init__
File "<frozen importlib._bootstrap_external>", line 182, in _path_isabs
TypeError: startswith first arg must be bytes or a tuple of bytes, not str
In the referenced commit, I've documented the current behavior. It's my opinion that the other importlib machinery should support this behavior, but I'll be happy to revisit this issue at such a time that Python is more explicit about what types are allowed. In the meantime, I'd advise to just not put pathlib.Path objects on sys.path. Let me know if following that advice would be problemmatic.
Activity
jaraco commentedon Mar 12, 2022
I've uploaded the repro as a gist so I can replicate the error with this one-liner:
jaraco commentedon Mar 12, 2022
I can confirm the same issue exists in the stdlib version:
I'm not sure there's much for importlib metadata to do here. Sure, it could exclude path entries that aren't strings.
I do see where importlib_metadata resolves a pathlib.Path to str.
It looks like that cast was added to introduce compatibility on Python 2 (see #121 (comment)).
Now that Python 2 support is dropped, that cast can be dropped too. I wonder what that does to the repro.
Remove cast of path items to strings. Ref #372.
graingert commentedon Mar 12, 2022
afaik sys.path can have bytes in, which that str call would change to
"b'/some/dir'"jaraco commentedon Mar 12, 2022
Removing the cast to str doesn't change the error (importlib_metadata still finds the path):
graingert commentedon Mar 12, 2022
I think you have to skip paths that aren't
isinstance(path, (str, bytes))hereimportlib_metadata/importlib_metadata/__init__.py
Line 903 in 14cce75
jaraco commentedon Mar 12, 2022
Ugh. The docs are contradictory on the topic, stating that it's a list of strings, but implying that bytes are supported by saying that types other than bytes and str are ignored.
I'm not sure I want to add support for bytes unless a real world use case demonstrates the need.
jaraco commentedon Mar 12, 2022
I agree that would stop it from discovering metadata on paths indicated by Path objects. It also would affect discovery by paths not on
sys.path, possibly a behavior worth keeping.I'm not sure what is the right behavior here. I'm inclined to sit on this one for now until importlib has settled on what should happen about sys.path, but even if it decides to retain the behavior that only str/bytes are allowed on sys.path, I'd lean toward importlib metadata supporting any paths that are str or Path-like.
What was the use-case that triggered this report? Is it something that can't be easily worked around?
graingert commentedon Mar 12, 2022
I was writing a test for pandas that mutates sys.path https://github.com/pandas-dev/pandas/pull/46302/files#diff-a363ecff783b66b30c1c2ea2481145430bba2e2c7a2afc279a1db24912eab756R130 and noticed the discrepancy
graingert commentedon Mar 15, 2022
so it turns out bytes don't actually work in
sys.path: ((although they used to in py3.2) unless they are zip files) see https://bugs.python.org/issue47025gist here https://gist.github.com/graingert/3422e812b0aa243c9719884f86be52ff
jaraco commentedon Mar 19, 2022
In the referenced commit, I've documented the current behavior. It's my opinion that the other importlib machinery should support this behavior, but I'll be happy to revisit this issue at such a time that Python is more explicit about what types are allowed. In the meantime, I'd advise to just not put
pathlib.Pathobjects onsys.path. Let me know if following that advice would be problemmatic.