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 &lt;(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

python下安装第三方插件

win7 64位安装python的pip和easy_install

解决pip安装太慢问题及pip/setup.py的源

安装setuptools与pip

关于 cmdclass 参数的 pip3 和 `python3 setup.py install` 之间的区别