When 3rd-party MetaPathFinder are implemented using importlib.metadata, the return types in importlib_metadata are not respected, which causes the API to behave in an unexpected way (with unexpected errors).
This is an example similar to the one identified in pypa/pyproject-hooks#195 (comment) and pypa/setuptools#4338:
mkdir -p /tmp/stash/private_path/_test-0.0.1.dist-info
cat <<EOF > /tmp/stash/private_path/_test-0.0.1.dist-info/METADATAName: _testVersion: 0.0.1EOF
cat <<EOF > /tmp/stash/private_path/_test-0.0.1.dist-info/entry_points.txt[_test.importlib_metadata]hello = worldEOF
cat <<EOF > /tmp/stash/install_finder.pyimport sysfrom importlib.machinery import PathFinderfrom importlib.metadata import DistributionFinder, MetadataPathFinderclass _ExampleFinder: def __init__(self, private_path): self.private_path = private_path def find_spec(self, fullname, _path, _target=None): if "." in fullname: # Rely on importlib to find nested modules based on parent's path return None # Ignore other items in _path or sys.path and use private_path instead: return PathFinder.find_spec(fullname, path=self.private_path) def find_distributions(self, context=None): context = DistributionFinder.Context(path=self.private_path) return MetadataPathFinder.find_distributions(context=context)sys.meta_path.insert(0, _ExampleFinder(["/tmp/stash/private_path"]))EOFcd /tmp/stash/
python3.8 -m venv .venv
.venv/bin/python -m pip install -U importlib-metadata
.venv/bin/python
>>>importinstall_finder>>>fromimportlib_metadataimportdistribution>>>distribution("_test").entry_points.select(group="_test.importlib_metadata")
Traceback (mostrecentcalllast):
File"<stdin>", line1, in<module>AttributeError: 'list'objecthasnoattribute'select'The expected behaviour as per API documentation would be:
>>>distribution("_test").entry_points.select(group="_test.importlib_metadata")
EntryPoints((EntryPoint(name='hello', value='world', group='_test.importlib_metadata'),))It seems that the origin of this problem is a little "lie" in the API definition:
Instead of
importlib_metadata.Distribution.discover(...) -> Iterable[importlib_metadata.Distribution]
what actually happens is:
importlib_metadata.Distribution.discover(...) -> Iterable[importlib_metadata.Distribution | importlib.metadata.Distribution]
and that propagates throughout the whole API.
I haven't tested, but there is potential for other internal errors too, if internally importlib_metadata is relying that the objects will have type importlib_metadata.Distribution to call newer APIs.
It is probably worthy to change the return type of importlib_metadata.Distribution.discover(...) to Iterable[importlib_metadata.Distribution | importlib.metadata.Distribution] and then run the type checkers on the lowest Python supported (I suppose Python 3.8), to see if everything is OK.
It also means that consumers of importlib_metadata cannot rely on the newer APIs (unless they are sure that 3r-party packages installed in their environment are not using importlib.metadata).
When 3rd-party
MetaPathFinderare implemented usingimportlib.metadata, the return types inimportlib_metadataare not respected, which causes the API to behave in an unexpected way (with unexpected errors).This is an example similar to the one identified in pypa/pyproject-hooks#195 (comment) and pypa/setuptools#4338:
The expected behaviour as per API documentation would be:
It seems that the origin of this problem is a little "lie" in the API definition:
Instead of
importlib_metadata.Distribution.discover(...) -> Iterable[importlib_metadata.Distribution]what actually happens is:
importlib_metadata.Distribution.discover(...) -> Iterable[importlib_metadata.Distribution | importlib.metadata.Distribution]and that propagates throughout the whole API.
I haven't tested, but there is potential for other internal errors too, if internally
importlib_metadatais relying that the objects will have typeimportlib_metadata.Distributionto call newer APIs.It is probably worthy to change the return type of
importlib_metadata.Distribution.discover(...)toIterable[importlib_metadata.Distribution | importlib.metadata.Distribution]and then run the type checkers on the lowest Python supported (I suppose Python 3.8), to see if everything is OK.It also means that consumers of
importlib_metadatacannot rely on the newer APIs (unless they are sure that 3r-party packages installed in their environment are not usingimportlib.metadata).