Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Upgrading `pip` removes other python's pips

Tags:

python

linux

pip

On a CentOS 7 system, I have multiple versions of Python installed, each with their own version of 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

When I ask pip3.8 to upgrade itself, it removes the installed 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

Why is it doing this, and how can I prevent it?

UPDATES:

  • The lib paths are different for the two installations, as shown here:
# 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']
  • It is not bidirectional - upgrading pip3.7 does not remove pip3.8.

  • I believe the library gets upgraded correctly and leaves the version 3.7 library in place, it's just the shell wrapper script that's deleted. Here's after the pip3.8 upgrade:

# 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)
  • Doing pip3.7 install --upgrade pip does not remove /usr/local/bin/pip3.6, so it's not the case that it always removes previous versions.

  • For full reproducibility, and to show that I'm starting with a fairly pristine system, here's a Gist containing my Dockerfile text: https://gist.github.com/kenahoo/a1104f9cb84694fbd5ec9d6d560a885e . It fails on the RUN pip3.7 install setuptools numpy pandas line because pip3.7 has gone missing.

  • It doesn't matter whether I upgrade using python3.8 -m pip install --upgrade pip or pip3.8 install --upgrade pip, both of them end up removing the /usr/local/bin/pip3.7 wrapper script.

like image 407
Ken Williams Avatar asked Oct 27 '22 23:10

Ken Williams


People also ask

What does upgrading pip do?

New software releases can bring bug fixes, new features, and faster performance. For example, NumPy 1.20 added type annotations, and improved performance by using SIMD when possible. If you're installing NumPy, you might want to install the newest version.

Does upgrading pip upgrade Python?

pip is a package manager, upgrading it will not have any effect on your pre-existing packages. Save this answer.

What is Python pip install -- upgrade pip?

Python pip install –user –upgrade is used to update a package. We can also upgrade any package to a specific version using the below command.


2 Answers

I believe I found the issue.

In short, the pipX.Y console script is set to the version of the Python interpreter used to build the pip's wheel, instead of the version of the Python interpreter used to install it.

  • https://github.com/pypa/pip/issues/8010

For example take any pip installed in any Python that is not 3.8 (in my case it's Python 3.6) and use it to download pip itself:

$ /path/to/pythonX.Y -m pip download pip

This should give you a wheel file for example pip-20.0.2-py2.py3-none-any.whl, now unzip it:

$ /path/to/pythonX.Y -m zipfile -e pip-20.0.2-py2.py3-none-any.whl .

Now look at the content of 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

So there is an entry for a console script pip3.8 even though I have Python 3.6. This is obviously wrong. And for example if I indeed had an actual pip3.8 script then this file would be deleted when uninstalling the pip associated with the Python 3.6, for example to upgrade it.

The root of the issue can be seen here for example:

  • https://github.com/pypa/pip/blob/19.2.3/setup.py#L76
    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],
        ],
    },

This line pip%s.%s=pip._internal:main" % sys.version_info[:2] gets actually written down definitely when building the wheel, and I assume the wheel we downloaded earlier was built with Python 3.8.


That bug is (at least partially) known to pip's maintainers, and not sure it will get fixed (probably not worth it).

Anyway, one should always use the explicit /path/to/pythonX.Y -m pip instead. The pip* scripts are just shortcuts that are here for convenience. They are somewhat useful from an interactive command line to save some keystrokes and be able to work faster. But in a file, anything from documentation, to shell scripts, or Dockerfiles, I am the opinion that one should always use the explicit expanded versions. For example I always write rm --recursive instead of rm -r, etc.

Additionally in the one particular case of Python's pip, it makes sense no matter what:

  • https://snarky.ca/why-you-should-use-python-m-pip/
like image 171
sinoroc Avatar answered Nov 15 '22 05:11

sinoroc


Update

You can use pip's target command to tell pip where it is allowed to look for pip and do an update.

$ pip3.8 install --upgrade --target /usr/local/lib/python3./site-packages/ pip

To upgrade just pip3.8 which will leave pip 3.7 intact.

When I ran

...

RUN pip3.5 install --upgrade --target /usr/local/lib/python3.5/site-packages/ pip
RUN pip3.6 install --upgrade --target /usr/local/lib/python3.6/site-packages/ pip
RUN pip3.7 install --upgrade --target /usr/local/lib/python3.7/site-packages/ pip
RUN pip3.8 install --upgrade --target /usr/local/lib/python3.8/site-packages/ pip

...

pip was still in the site-packages for python3.5/3.6 but pip3.5 & 3.6 did not show up in /usr/local/bin. So to install packages globally to python3.5/3.6 one would have to use python3.5 -m pip install <package>

This is because pip3.5 and pip3.6 should be stored in /usr/bin not /usr/local/bin. You can use

...

RUN pip3.5 install --upgrade pip
RUN pip3.6 install --upgrade pip
RUN pip3.7 install --upgrade --target /usr/local/lib/python3.7/site-packages/ pip
RUN pip3.8 install --upgrade --target /usr/local/lib/python3.8/site-packages/ pip

...

And pip3.5-8 will all exist and work. It is still advisable to use virtual environments with python.

Warning

Pip seems to be issuing a warning that is caused by calling pip directly. The warning suggestions calling pip as a python module moving forward python -m pip <command>

[root@93e6e7373eff /]# pip3.8 -V
WARNING: pip is being invoked by an old script wrapper. This will fail in a future version of pip.
Please see https://github.com/pypa/pip/issues/5599 for advice on fixing the underlying issue.
To avoid this problem you can invoke Python with '-m pip' instead of running pip directly.
pip 20.0.2 from /usr/local/lib/python3.8/site-packages/pip (python 3.8)

[root@93e6e7373eff /]# pip3.7 -V
WARNING: pip is being invoked by an old script wrapper. This will fail in a future version of pip.
Please see https://github.com/pypa/pip/issues/5599 for advice on fixing the underlying issue.
To avoid this problem you can invoke Python with '-m pip' instead of running pip directly.
pip 20.0.2 from /usr/local/lib/python3.7/site-packages/pip (python 3.7)

[root@93e6e7373eff /]# pip3.6 -V
pip 20.0.2 from /usr/local/lib/python3.6/site-packages/pip (python 3.6)

[root@93e6e7373eff /]# pip3.5 -V
pip 20.0.2 from /usr/lib/python3.5/site-packages/pip (python 3.5)


Workaround

I was able to get it all to work by changing the order in which you install. Since the only issue seems to be that pip3.8 looks for older versions and deletes them, I installed and upgraded before anything else. I just built this on my laptop and it ran (lol it worked on my machine).

# -*- dockerfile -*-
FROM centos:7.7.1908
RUN yum update -y
RUN yum install -y epel-release
RUN yum install -y https://centos7.iuscommunity.org/ius-release.rpm

RUN yum install -y python35u python35u-pip
RUN yum install -y python36u python36u-pip

RUN yum install -y gcc gcc-c++
RUN yum install -y make openssl-devel bzip2-devel libffi-devel

# Python3.8 is not currently available from RHEL, EPEL, or IUS repos so download and compile it
ARG PY38_VERSION=3.8.2
RUN cd /usr/src && curl https://www.python.org/ftp/python/${PY38_VERSION}/Python-${PY38_VERSION}.tgz | tar -xz &&\
  cd Python-${PY38_VERSION} && ./configure --enable-optimizations && make -j4 altinstall &&\
  rm -rf /usr/src/Python-${PY38_VERSION}

RUN pip3.8 install --upgrade pip
RUN pip3.8 install setuptools numpy pandas

# Python3.7 is not currently available from RHEL, EPEL, or IUS repos so download and compile it
RUN yum install -y gcc openssl-devel bzip2-devel libffi-devel make sqlite-devel
ARG PY37_VERSION=3.7.6
RUN cd /usr/src && curl https://www.python.org/ftp/python/${PY37_VERSION}/Python-${PY37_VERSION}.tgz | tar -xz &&\
  cd Python-${PY37_VERSION} && ./configure --enable-optimizations && make -j4 altinstall &&\
  rm -rf /usr/src/Python-${PY37_VERSION}

RUN pip3.7 install --upgrade pip
RUN pip3.7 install setuptools numpy pandas

RUN pip3.5 install --upgrade pip
RUN pip3.5 install setuptools numpy pandas
RUN pip3.6 install --upgrade pip
RUN pip3.6 install setuptools numpy pandas


RUN yum install -y python35u-devel python36u-devel python37u-devel python38u-devel


CMD /bin/bash

outputs from console:

[root@e3b166a8b479 /]# python3.7 -m pip -V     
pip 20.0.2 from /usr/local/lib/python3.7/site-packages/pip (python 3.7)
[root@e3b166a8b479 /]# python3.6 -m pip -V
pip 20.0.2 from /usr/local/lib/python3.6/site-packages/pip (python 3.6)
[root@e3b166a8b479 /]# python3.8 -m pip -V
pip 20.0.2 from /usr/local/lib/python3.8/site-packages/pip (python 3.8)
[root@e3b166a8b479 /]# ls /usr/local/bin | grep pip
pip
pip3
pip3.6
pip3.7
pip3.8
like image 38
lwileczek Avatar answered Nov 15 '22 07:11

lwileczek