pip 下载而不执行 setup.py
Posted
技术标签:
【中文标题】pip 下载而不执行 setup.py【英文标题】:pip download without executing setup.py 【发布时间】:2019-02-28 10:00:31 【问题描述】:如何在不执行setup.py
文件(可能包含恶意代码)的情况下下载分发包(可能是 sdist)?
我不想递归获取依赖,只为指定的发行版下载一个文件。尝试无效:
pip download --no-deps mydist
这是一个可重现的示例,演示了setup.py
在上述情况下仍然执行:
$ docker run --rm -it python:3.8-alpine sh
/ # pip --version
pip 20.0.2 from /usr/local/lib/python3.8/site-packages/pip (python 3.8)
/ # pip download --no-deps suds
Collecting suds
Downloading suds-0.4.tar.gz (104 kB)
|████████████████████████████████| 104 kB 13.4 MB/s
ERROR: Command errored out with exit status 1:
command: /usr/local/bin/python -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-download-yqfdz35d/suds/setup.py'"'"'; __file__='"'"'/tmp/pip-download-yqfdz35d/suds/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' egg_info --egg-base /tmp/pip-download-yqfdz35d/suds/pip-egg-info
cwd: /tmp/pip-download-yqfdz35d/suds/
Complete output (7 lines):
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "/tmp/pip-download-yqfdz35d/suds/setup.py", line 20, in <module>
import suds
File "/tmp/pip-download-yqfdz35d/suds/suds/__init__.py", line 154, in <module>
import client
ModuleNotFoundError: No module named 'client'
----------------------------------------
ERROR: Command errored out with exit status 1: python setup.py egg_info Check the logs for full command output.
我不能使用--no-binary
选项,因为我不想排除源分发。我只是想避免执行他们的源代码。
【问题讨论】:
没有明显的解决方案浮现在脑海。但一个人可能会足够接近,这取决于最终目标是什么。例如,我想知道通过确保 setuptools 未安装、猴子修补/覆盖 distutils 以使其无害等操作能走多远。否则我会尝试使用pip download --no-deps --require-hashes --requirement <(echo 'suds==0.4 --hash=sha256:')
并故意提供错误的哈希值,以便在下载后全部停止;但要找到分发的最终位置并不容易(也许使用--cache-dir
选项?)。
【参考方案1】:
我一直在研究pip
,遗憾的是那里的代码相当复杂。目前似乎没有办法做到这一点,并且根据link provided by @doctaphred 没有计划在这个方向上取得进展。
下一步取决于您的情况;例如,如果您需要这个“包下载器”进行生产,我建议您编写自己的 pypi 客户端。编写起来非常简单,您可以根据自己的需要对其进行优化,使其比pip
更快、更简单。为此,您可以尝试使用 pip
中的一些现有代码,但我认为这可能会非常困难(在看到该代码之后)。
否则,我会考虑使用更快、更简单的方法来完成工作。想到的第一个解决方案是在尝试运行egg_info
命令时停止pip
。为此,您可以使用各种方法在运行时修补pip
的代码。我最喜欢的是使用usercutomize
文件。
例如,创建一个包含以下内容的补丁文件并将其放置在您选择的目录中:
/pypatches/pip_pure_download/usercustomize.py
:
from pip._internal.req.req_install import InstallRequirement
print('Applying pure download patch!')
def override_run_egg_info(*args, **kwargs):
raise KeyboardInterrupt # Joke's on you, evil hackers! :P
InstallRequirement.run_egg_info = override_run_egg_info
现在要将补丁应用到python执行中,只需将补丁的目录添加到PYTHONPATH
,例如:
PYTHONPATH=/pypatches/pip_pure_download:$PYTHONPATH pip download --no-deps suds
【讨论】:
好吧,这是一个令人作呕的猴子补丁,而且由于pip._internal.req.req_install
是私有实现,因此不能保证它可以跨 pip 版本工作。但是没有人想出更好的方法,所以我想你默认赢了!我不同意创建自己的 PyPI 客户端“编写起来非常简单”,如果你正确地考虑the grammar for parsing environment markers and version specifications,那对我来说看起来很重要。
哈哈,你是对的,但我想可怕的代码就像派对上的食物 - 如果你想享受其他人的食物,你必须自己做一些:) 关于一个新的 pypi 客户端,我想你可能会不需要 pip 的全部功能,也许大多数复杂的东西都会包含在一些你可以复制的代码中。但我没有检查,你可能是对的......
这太可怕了,我喜欢它。感谢您进行必要的挖掘!
我不得不接受这个答案,因为这在更新的 pip 版本中已经停止工作。【参考方案2】:
从 pip 19.3.1 开始,这似乎是不可能的 :(
见https://github.com/pypa/pip/issues/1884
【讨论】:
以上是关于pip 下载而不执行 setup.py的主要内容,如果未能解决你的问题,请参考以下文章
在执行 pip install 时遇到错误:python setup.py egg_info Check the logs for full command output