升级`pip`会删除其他python的pips

Ken*_*ams 5 python linux pip

在 CentOS 7 系统上,我安装了多个版本的 Python,每个版本都有自己的以下版本pip

# head -n1 /usr/local/bin/pip3.*
==> /usr/local/bin/pip3.6 <==
#!/usr/bin/python3

==> /usr/local/bin/pip3.7 <==
#!/usr/local/bin/python3.7

==> /usr/local/bin/pip3.8 <==
#!/usr/local/bin/python3.8
Run Code Online (Sandbox Code Playgroud)

当我要求pip3.8升级自己时,它会删除已安装的pip3.7

# pip3.8 install --upgrade pip
Collecting pip
  Using cached https://files.pythonhosted.org/packages/54/0c/d01aa759fdc501a58f431eb594a17495f15b88da142ce14b5845662c13f3/pip-20.0.2-py2.py3-none-any.whl
Installing collected packages: pip
  Found existing installation: pip 19.2.3
    Uninstalling pip-19.2.3:
      Successfully uninstalled pip-19.2.3
Successfully installed pip-20.0.2


# head -n1 /usr/local/bin/pip3.*
==> /usr/local/bin/pip3.6 <==
#!/usr/bin/python3

==> /usr/local/bin/pip3.8 <==
#!/usr/local/bin/python3.8
Run Code Online (Sandbox Code Playgroud)

为什么会这样,我该如何预防?

更新

  • 两个安装的 lib 路径不同,如下所示:
# python3.7 -c 'import sys; print(sys.path)'
['', '/usr/local/lib/python37.zip', '/usr/local/lib/python3.7', '/usr/local/lib/python3.7/lib-dynload', '/usr/local/lib/python3.7/site-packages']
# python3.8 -c 'import sys; print(sys.path)'
['', '/usr/local/lib/python38.zip', '/usr/local/lib/python3.8', '/usr/local/lib/python3.8/lib-dynload', '/usr/local/lib/python3.8/site-packages']
Run Code Online (Sandbox Code Playgroud)
  • 它不是双向升级pip3.7不删除pip3.8

  • 我相信该会正确升级并保留 3.7 版库,只是删除了 shell 包装器脚本。pip3.8升级后是这样的:

# python3.7 -m pip --version
pip 20.0.2 from /usr/local/lib/python3.7/site-packages/pip (python 3.7)
# python3.8 -m pip --version
pip 20.0.2 from /usr/local/lib/python3.8/site-packages/pip (python 3.8)
# pip3.7 --version
bash: pip3.7: command not found
# pip3.8 --version
pip 20.0.2 from /usr/local/lib/python3.8/site-packages/pip (python 3.8)
Run Code Online (Sandbox Code Playgroud)
  • 这样pip3.7 install --upgrade pip做不会删除/usr/local/bin/pip3.6,因此它并不总是删除以前的版本。

  • 为了完全重现,并表明我从一个相当原始的系统开始,这里有一个包含我的 Dockerfile 文本的 Gist:https ://gist.github.com/kenahoo/a1104f9cb84694fbd5ec9d6d560a885e 。它上RUN pip3.7 install setuptools numpy pandas线失败,因为pip3.7丢失了。

  • 我是否使用python3.8 -m pip install --upgrade pip或升级都没有关系pip3.8 install --upgrade pip,它们最终都会删除/usr/local/bin/pip3.7包装器脚本。

sin*_*roc 2

我相信我发现了这个问题。

简而言之,pipX.Y控制台脚本设置为用于构建pip轮子的 Python 解释器版本,而不是用于安装它的 Python 解释器版本。

例如,获取任何非3.8版本的Python中安装的任何pip(在我的例子中是 Python 3.6)并使用它来下载自身:pip

$ /path/to/pythonX.Y -m pip download pip
Run Code Online (Sandbox Code Playgroud)

例如,这应该给你一个wheelpip-20.0.2-py2.py3-none-any.whl文件,现在解压它:

$ /path/to/pythonX.Y -m zipfile -e pip-20.0.2-py2.py3-none-any.whl .
Run Code Online (Sandbox Code Playgroud)

现在看一下内容pip-20.0.2.dist-info/entry_points.txt

$ cat pip-20.0.2.dist-info/entry_points.txt 
[console_scripts]
pip = pip._internal.cli.main:main
pip3 = pip._internal.cli.main:main
pip3.8 = pip._internal.cli.main:main
Run Code Online (Sandbox Code Playgroud)

pip3.8因此,即使我有 Python 3.6,也有一个控制台脚本的条目。这显然是错误的。例如,如果我确实有一个实际的脚本,那么在卸载与 Python 3.6 关联的pip时(例如升级它),pip3.8该文件将被删除。

问题的根源可以在这里看到,例如:

    entry_points={
        "console_scripts": [
            "pip=pip._internal:main",
            "pip%s=pip._internal:main" % sys.version_info[:1],
            "pip%s.%s=pip._internal:main" % sys.version_info[:2],
        ],
    },
Run Code Online (Sandbox Code Playgroud)

这行代码实际上是在构建轮子pip%s.%s=pip._internal:main" % sys.version_info[:2]时明确写下来的,我假设我们之前下载的轮子是用Python 3.8构建的。


pip的维护者(至少部分)知道该错误,并且不确定它是否会得到修复(可能不值得)。

无论如何,人们应该总是使用显式/path/to/pythonX.Y -m pip的。这些pip*脚本只是为了方便起见而设置的快捷方式。它们在交互式命令行中有点有用,可以节省一些击键并能够更快地工作。但在文件中,从文档到shell脚本或Dockerfiles的任何内容,我认为应该始终使用显式扩展版本。例如,我总是写rm --recursive而不是rm -r等等。

另外,在Pythonpip的一种特殊情况下,无论如何它都是有意义的: