pip: pip install -U breaks dependencies

Environment

  • pip version: 20.0.2
  • Python version: 3.8.2
  • OS: MacOS
  • python installed by brew to avoid conflict with OS’s python.
$ uname -a

Darwin illbebachs-iMac 19.4.0 Darwin Kernel Version 19.4.0: Wed Mar  4 22:28:40 PST 2020; root:xnu-6153.101.6~15/RELEASE_X86_64 x86_64

Related Issue

#7744 Handle conflicts from installed packages when updating other packages

Description

pip install -U breaks dependencies on installed packages, even when --upgrade-strategy=only-if-needed is specified on the command line. In the script below, I create a clean environment using venv and install pylint. pip install -U breaks dependencies of installed packages.

pip recognizes and reports the error regarding the dependency, but performs the upgrade anyway.

Expected behavior

pip should report the error and not perform the upgrade.

How to Reproduce

I ran the following script to demonstrate the error

#!/bin/bash

which python3.8
python3.8 --version

# setup a clean environment for testing
rm -rf clean_slate
python3.8 -m venv clean_slate
. clean_slate/bin/activate
which pip
which python

# upgrade basic packages to latest version
pip install --upgrade pip setuptools
pip --version
pip list

pip install pylint

pip list
pip check
pip list --outdated

# now perform the upgrade, where pip should fail
pip install --upgrade --upgrade-strategy=only-if-needed wrapt
pip check


Output

$ which python3.8
/usr/local/bin/python3.8

$ python3.8 --version
Python 3.8.2

$ rm -rf clean_slate

$ python3.8 -m venv clean_slate

$ . clean_slate/bin/activate

$ which pip
/Users/illbebach/clean_slate/bin/pip

$ which python
/Users/illbebach/clean_slate/bin/python

$ pip install --upgrade pip setuptools
... verbose output omitted ...
Successfully installed pip-20.0.2 setuptools-46.1.3

$ pip --version
pip 20.0.2 from /Users/illbebach/clean_slate/lib/python3.8/site-packages/pip (python 3.8)

$ pip list
Package    Version
---------- -------
pip        20.0.2 
setuptools 46.1.3 

$ pip install pylint
... verbose output omitted ...
Successfully installed astroid-2.3.3 isort-4.3.21 lazy-object-proxy-1.4.3 mccabe-0.6.1 pylint-2.4.4 six-1.14.0 wrapt-1.11.2

$ pip list
Package           Version
----------------- -------
astroid           2.3.3  
isort             4.3.21 
lazy-object-proxy 1.4.3  
mccabe            0.6.1  
pip               20.0.2 
pylint            2.4.4  
setuptools        46.1.3 
six               1.14.0 
wrapt             1.11.2 

$ pip check
No broken requirements found.

$ pip list --outdated
Package Version Latest Type 
------- ------- ------ -----
wrapt   1.11.2  1.12.1 sdist

$ pip install --upgrade --upgrade-strategy=only-if-needed wrapt
Collecting wrapt
  Using cached wrapt-1.12.1.tar.gz (27 kB)
ERROR: astroid 2.3.3 has requirement wrapt==1.11.*, but you'll have wrapt 1.12.1 which is incompatible.
Installing collected packages: wrapt
  Attempting uninstall: wrapt
    Found existing installation: wrapt 1.11.2
    Uninstalling wrapt-1.11.2:
      Successfully uninstalled wrapt-1.11.2
    Running setup.py install for wrapt: started
    Running setup.py install for wrapt: finished with status 'done'
Successfully installed wrapt-1.12.1

$ pip check
astroid 2.3.3 has requirement wrapt==1.11.*, but you have wrapt 1.12.1.

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 15 (12 by maintainers)

Most upvoted comments

Forgive the lateness of my reply. During the discussion, it quickly became apparent I do not have a full context of the history of this issue. That said, several times above, others stated “pip does not know what the user wants”. To me, “what the user wants” can be stated in this simplistic statement:

Update everything to the newest version possible, without breaking anything.

If I were king, I would specify the proper behavior be much as the description of poetry, as mentioned by @WSLUser, behaves. Without other inputs, “pip install -U” should upgrade all installed packages, then give a note stating what happened. “pip install -U” is my typical use-case where I periodically want to update my system. A contrived example output would be

$ pip install -U
package update complete
NOTE: the following packages could not be updated to the latest available version, due to version conflicts with other installed packages:
package1 - updated to 1.2.3, even though 1.3.4 is available
package2 - updated to 3.4, even though 3.4.8 is available
...

Perhaps if —verbose is specified, for each package1,2,… above, print the newly installed version, the latest available version, and a possibly long list of packages which caused the conflict.

Even if the user specifies “pip install —update A B”, it is possible that A or B cannot be updated to the latest available, due to conflicts with installed packages. In that case, it seems to me, the proper thing to do is still “Update A and B to the newest version possible, without breaking anything”.

I recently became aware of the pipdeptree package, which I find helpful in seeing dependencies among packages.

Thank you all for adding so much context to the discussion. I was not aware of the longstanding issues or the broader context.

I think both of those concerns are in-scope for #7744.

Honestly, this is a known issue with pip’s current resolver (see #988) and grant funded work is being undertaken right now to fix the broader issue.

I’m personally inclined to close this issue and classify it as “will be tackled as part of funded work on #988” + duplicate of #7744.

@uranusjr I think @sbidoul is suggesting augmenting/improving the REQUESTED file, to include that information – about what the user requested – to serve as a distributed manifest.

This was (incidentally) discussed quite extensively in discussions on implementing the new dependency resolver (notes from last week, search for installed packages for the relevant parts; follow-up discussion is around here). The opinions are conflicting, unfortunately, and has a lot of technical and UX implications.