Possible to guard installed packages from upgrade when installing new packages?

Imagine I have two packages foo and bar where foo is a dependency of bar. foo is already installed and I now want to pip install bar. Is it possible to do that without upgrading foo? In particular I want to install the latest version of bar that is satisfied with the already installed version of foo.

From pip help install

--upgrade-strategy <upgrade_strategy>
                    Determines how dependency upgrading should be handled
                    [default: only-if-needed]. "eager" - dependencies are
                    upgraded regardless of whether the currently installed
                    version satisfies the requirements of the upgraded
                    package(s). "only-if-needed" -  are upgraded only when
                    they do not satisfy the requirements of the upgraded
                    package(s).

As far as I see this only covers the cases “always update” and “only update if needed”, but not “never update” as I need it.

I believe pip install bar (no upgrade flags at all) should do what you want. pip should pick the currently-installed foo unless bar explicitly says that version is not compatible.

If you add --use-feature=2020-resolver, I believe pip would even try to find a version of bar that can work with the currently-installed foo. You can read more about the 2020 resolver here.

1 Like

@uranusjr

pip should pick the currently-installed foo unless bar explicitly says that version is not compatible.

That is true, but the whole problem arises because the latest version of bar requires a newer version of foo. foo is expensive to download and install and I don’t have a special requirement for bar. Thus, I just want the latest version of bar that I can run with foo.

If you add --use-feature=2020-resolver , I believe pip would even try to find a version of bar that can work with the currently-installed foo .

That kinda works. In that case the command would look like this:

$ pip install --use-feature=2020-resolver $(python -c "import foo; print(f'{foo.__name__}=={foo.__version__}')") bar

Unfortunately this downloads every single version of bar until it finds a match. Example: foo==chardet and bar==requests:

$ pip install chardet==3.0.0
[...]
$ pip install --use-feature=2020-resolver $(python -c "import chardet; print(f'{chardet.__name__}=={chardet.__version__}')") requests
Requirement already satisfied: chardet==3.0.0 in ./.venv/lib/python3.6/site-packages (3.0.0)
Collecting requests
  Using cached requests-2.24.0-py2.py3-none-any.whl (61 kB)
Collecting requests
  Using cached requests-2.23.0-py2.py3-none-any.whl (58 kB)
Collecting requests
  Using cached requests-2.22.0-py2.py3-none-any.whl (57 kB)
Collecting requests
  Using cached requests-2.21.0-py2.py3-none-any.whl (57 kB)
Collecting requests
  Using cached requests-2.20.1-py2.py3-none-any.whl (57 kB)
Collecting requests
  Using cached requests-2.20.0-py2.py3-none-any.whl (60 kB)
Collecting requests
  Using cached requests-2.19.1-py2.py3-none-any.whl (91 kB)
Collecting requests
  Using cached requests-2.19.0-py2.py3-none-any.whl (91 kB)
Collecting requests
  Using cached requests-2.18.4-py2.py3-none-any.whl (88 kB)
Collecting requests
  Using cached requests-2.18.3-py2.py3-none-any.whl (88 kB)
Collecting requests
  Using cached requests-2.18.2-py2.py3-none-any.whl (88 kB)
Collecting requests
  Using cached requests-2.18.1-py2.py3-none-any.whl (88 kB)
Collecting requests
  Using cached requests-2.18.0-py2.py3-none-any.whl (563 kB)
Collecting requests
  Using cached requests-2.17.3-py2.py3-none-any.whl (87 kB)
Collecting requests
  Using cached requests-2.17.2-py2.py3-none-any.whl (87 kB)
Collecting requests
  Using cached requests-2.17.1-py2.py3-none-any.whl (87 kB)
Collecting requests
  Using cached requests-2.17.0-py2.py3-none-any.whl (87 kB)
Collecting requests
  Using cached requests-2.16.5-py2.py3-none-any.whl (87 kB)
Collecting requests
  Using cached requests-2.16.4-py2.py3-none-any.whl (87 kB)
Collecting requests
  Using cached requests-2.16.3-py2.py3-none-any.whl (86 kB)
Collecting requests
  Using cached requests-2.16.2-py2.py3-none-any.whl (86 kB)
Collecting requests
  Using cached requests-2.16.1-py2.py3-none-any.whl (85 kB)
Collecting requests
  Using cached requests-2.16.0-py2.py3-none-any.whl (85 kB)
Collecting requests
  Using cached requests-2.15.1-py2.py3-none-any.whl (558 kB)
Installing collected packages: requests
Successfully installed requests-2.15.1

As I said before, in my actual case bar is expensive to download and thus this is not a feasible approach.

If you must avoid download, your only choice is to specify bar’s version yourself. Python packaging does not provide a way to discover version conflicts before downloading a package, so pip has no way to know what version of bar without trying to download various versions it can use unless you tell it explicitly.

2 Likes

Thanks for the insight! I’ve asked this question at Stack Overflow before I did here. Do you want to provide your answer there? If not, are you OK if I cite you and answer myself?

I’ve rewritten my responses above into an answer. I added some additional context as well to make the answer more future-proof.