python / python/importlib_metadata
entry_points doesn't handle empty .dist-info files well
还没有人认领这个 Issue。
- 主要语言
- Python
- 星标
- 142
- 派生
- 97
- PR 合并指标
- 30 天内没有已合并 PR
描述
This is related to https://github.com/python/importlib_metadata/issues/489 but with a different way to reach it.
The background is that our build process will delete files when packages are upgraded but will leave directories behind, because it doesn't track who created the directory and whether it should remain or not.
This means that over time you can end up with a site-packages that has a number of directories in, such as setuptools (with the code), setuptools-82.0.1.dist-info (with the 82.0.1 metadata) and also setuptools-82.0.0.dist-info which is empty.
If the on-disk order is returning the empty 82.0.0 before 82.0.1 then importlib_metadata.entry_points() will return a subset of what is expected.
For example, with a simple test case:
for x in metadata.entry_points():
print(x)
A fresh venv that has setuptools/build/pip installed finds 48 entry points:
EntryPoint(name='alias', value='setuptools.command.alias:alias', group='distutils.commands')
EntryPoint(name='bdist_egg', value='setuptools.command.bdist_egg:bdist_egg', group='distutils.commands')
EntryPoint(name='bdist_rpm', value='setuptools.command.bdist_rpm:bdist_rpm', group='distutils.commands')
EntryPoint(name='bdist_wheel', value='setuptools.command.bdist_wheel:bdist_wheel', group='distutils.commands')
EntryPoint(name='build', value='setuptools.command.build:build', group='distutils.commands')
...
EntryPoint(name='pip', value='pip._internal.cli.main:main', group='console_scripts')
EntryPoint(name='pip3', value='pip._internal.cli.main:main', group='console_scripts')
EntryPoint(name='pyproject-build', value='build.__main__:entrypoint', group='console_scripts')
EntryPoint(name='build', value='build.__main__:entrypoint', group='pipx.run')
But by creating setuptools-n.dist-info directories with different values of n until ls -U shows that it appears before the actual metadata has different behaviour and only finds 4 entry points:
EntryPoint(name='pip', value='pip._internal.cli.main:main', group='console_scripts')
EntryPoint(name='pip3', value='pip._internal.cli.main:main', group='console_scripts')
EntryPoint(name='pyproject-build', value='build.__main__:entrypoint', group='console_scripts')
EntryPoint(name='build', value='build.__main__:entrypoint', group='pipx.run')
Note that none of the setuptools EPs were listed.
My theory: the entry_points() method is listing all distributions and then doing a unique() on it, which will remove duplicate distributions based on the name with the simple logic that it takes the first one seen. With the broken setup I've described above this means it just returns the first, broken, dist and ignores the one with actual content.
I've verified locally that adding another _prefer_valid() call to Distribution.discover() resolves this by sorting valid dists first:
context = context or DistributionFinder.Context(**kwargs)
return cls._prefer_valid(itertools.chain.from_iterable(
resolver(context) for resolver in cls._discover_resolvers()
))
This feels like a bit of a heavy hammer though and possibly better logic in entry_points() would be preferable?
贡献指南
这个仓库没有索引到贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
调研方向
通过将一个空的 setuptools-*.dist-info 目录放在包含完整元数据的目录之前来复现该问题,然后比较 entry_points() 的结果。检查 Distribution.discover() 和 entry_points() 路径,包括现有的 _prefer_valid() 行为。当空元数据不再掩盖包含完整元数据的 distribution,并返回预期的 entry points 时,即表示完成。
由索引模型根据 Issue 内容生成。
评估
- 技术栈
- python
- 领域
- tooling
- Issue 类型
- 缺陷
- 难度
- 4/5
- 预计耗时
- 3-5 天
- 活跃度
- 冷清
- 描述清晰度
- 基本清楚
- 新手友好度
- 48/100