How to pip install a package with min and max version range?
Asked Answered
H

4

424

I'm wondering if there's any way to tell pip, specifically in a requirements file, to install a package with both a minimum version (pip install package>=0.2) and a maximum version which should never be installed (theoretical api: pip install package<0.3).

I ask because I am using a third party library that's in active development. I'd like my pip requirements file to specify that it should always install the most recent minor release of the 0.5.x branch, but I don't want pip to ever try to install any newer major versions (like 0.6.x) since the API is different. This is important because even though the 0.6.x branch is available, the devs are still releasing patches and bugfixes to the 0.5.x branch, so I don't want to use a static package==0.5.9 line in my requirements file.

Is there any way to do that?

Hic answered 9/1, 2012 at 21:44 Comment(0)
P
579

You can do:

$ pip install "package>=0.2,<0.3"

And pip will look for the best match, assuming the version is at least 0.2, and less than 0.3.

This also applies to pip requirements files. See the full details on version specifiers in PEP 440.

Porshaport answered 10/1, 2012 at 22:25 Comment(6)
For the record, I think "package>=0.2,<=0.3" doesn't make a lot of sense: when would you be okay with both 0.2 and 0.3.0, but not with any of 0.3's bugfix releases? I think "package>=0.2,<0.3" is a much better example, because it reflects the common case of saying: "please give me the latest bugfix release of the current minor version, but don't automatically upgrade me to the next minor version, because I would like to do that explicitly, making sure that there are no functional changes affecting me."Unfetter
If you like this answer, you'll love Mortiz answer right down below! Be sure to check it out, ~=0.2 is (imho) a better solution than this.Jochebed
@BradRoot It's really unclear what ~=0.2.1 would do, for example. Being explicit as in >=0.2,<0.3 is a good thing because it's really clear what's happening.Comical
@Acumenus someone who understands the properties of the requirement format and how versioning works wouldn't write ~=0.2.1 in a requirements file. That's user error, not a disadvantage of the ~= prefix.Jochebed
@BradRoot Wouldn't ~=0.2.1 mean >=0.2.1,<0.3? How is it a user error?Comical
The quotes around the version are important to avoid the < or > being interpreted by the shell.Methane
I
193

An elegant method would be to use the ~= compatible release operator according to PEP 440. In your case this would amount to:

package~=0.5.0

As an example, if the following versions exist, it would choose 0.5.9:

  • 0.5.0
  • 0.5.9
  • 0.6.0

For clarification, each two lines in a pair are equivalent:

~= 0.5.0
>= 0.5.0, == 0.5.*

~= 0.5
>= 0.5, == 0.*
Ism answered 28/4, 2018 at 19:26 Comment(12)
How would you use this for truncated versions? E.g. if there's a 2.2 and a planned future 2.2.1, will ~=2.2.* match 2.2 despite there not being a tertiary number?Chemosh
@Mike'Pomax'Kamermans You should use ~=2.2.0 in that case(* operator will not work if you're using ~=). 2.2 and 2.2.0 (and 2.2.0.0, and so on) are internally handled as same thing when it comes to installing packages.Fervent
It is extremely unclear how this works for nested version numbering, e.g. ~=1.2.3. It is a lot more explicit and clearer to use the multi-clause form instead.Comical
By far the clearest is == 1.*. Couldn't be clearer! "Latest possible 1.x version". And if the package follows semantic versioning, ALL 1.x versions WILL be compatible with each other, since only major versions (2.x, 3.x) are allowed to break APIs.Interject
@MitchMcMabers That only works if the features you want happen to have been present in the first release of that major version, which isn't true in general. If you're relying on something that was added in v1.2.0, for example, == 1.* will improperly accept v1.1.0. The ~= (or >= plus < if you find that hard to read) operator is better because it encourages being correctly specific.Divisive
@Divisive That's a good point. So == 1.* would fail and do nothing if we need a 1.2 version feature but the user already had 1.1 installed. Your proposed ~= 1.2 is the same as saying >= 1.2, < 2.0 (or >= 1.2, == 1.*). So yeah you're right, ~= is the best operator since it lets you target the development version you used of a library, while allowing newer versions within the same major version. Thanks for that clarification!Interject
It seems really odd to me that numpy~=1.17.0 downloads 1.17.5, but numpy~=1.17 downloads 1.18.5.Amplifier
@Amplifier The mental model I'm trying to use is "significant figures" (like in high school science classes). The amount of info shown implies the specificity we're working with. If it's ~=1.17, we're only concerned with specificity at the minor semver level. But if it's 1.17.0, we want specificity down to the patch semver level. They're both the same "number", but how far down the decimals go specifies the accuracy level we're using. It's confusing, but I think it's a mnemonic that will help me. Hope it helps you remember too.Lachlan
@Erdős-Bacon: but 1.17.5 is not guaranteed to be feature compatible with 1.18.0, that makes no sense.Amplifier
@Amplifier Well, by semver standards, 1.18.0 should be feature compatible with 1.17.5. No dev is perfect, so it's certainly possible it won't be, but by the same token, it's also possible 1.17.6 will fail to be compatible too. If we assume that semver was correctly followed though, 1.18 will be backwards compatible. Regardless of it actually working, my point was that in the context of the ~= operator, decimal level is a way of specifying how flexible/accurate that package version requirement is.Lachlan
This does not work for me: Collecting matplotlib~=3.2 -> Successfully installed matplotlib-3.4.2Counterfactual
Worked as specified. To get a 3.2.X version use matplotlib~=3.2.0Ism
H
182

you can also use:

pip install package==0.5.*

which is more consistent and easy to read.

Halakah answered 3/1, 2018 at 9:34 Comment(4)
This is a much better way to manage requirements.txt IMO. Using package==1.* instead of package>=1.2 prevents pip from installing major version 2+ for the package, which is desirable since major version changes are often backwards incompatible.Brandiebrandise
Note, This doesn't upgrade an existing package. e.g. if you have 0.5.1 installed, but 0.5.2 is latest, and you run install 0.5.* it will say "already satisfied" and leave you with 0.5.1. Adding --upgrade solves it.Melodious
This does not work for me pip install matplotlib==3.2.* zsh: no matches found: matplotlib==3.2.*Counterfactual
@Tillus the star sign is being expanded by zsh (just like when you execute rm myfiles*. You need to escape the argument, e.g with single quotes pip install 'matplotlib==3.2.*'.Oribella
S
18

nok.github.io/pipdev is an interactive tool for developers to test defined specifiers for version handling.

enter image description here

Related to the question: nok.github.io/pipdev?spec=~=0.5.0&vers=0.6

Syreetasyria answered 14/6, 2022 at 23:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.