405

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?

Tomasz Gandor
  • 8,235
  • 2
  • 60
  • 55
coredumperror
  • 8,471
  • 6
  • 33
  • 42

4 Answers4

550

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.

Leif Arne Storset
  • 899
  • 1
  • 8
  • 19
Hugo Lopes Tavares
  • 28,528
  • 5
  • 47
  • 45
  • 82
    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." – Henrik Heimbuerger May 29 '14 at 10:19
  • 2
    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. – Brad Root Apr 22 '19 at 17:24
  • 4
    @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. – Asclepius Nov 01 '19 at 16:06
  • @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. – Brad Root Nov 07 '19 at 18:15
  • 1
    @BradRoot Wouldn't `~=0.2.1` mean `>=0.2.1,<0.3`? How is it a user error? – Asclepius Nov 07 '19 at 21:27
  • 4
    The quotes around the version are important to avoid the `<` or `>` being interpreted by the shell. – phoenix Feb 10 '20 at 13:23
176

you can also use:

pip install package==0.5.*

which is more consistent and easy to read.

lowrin
  • 2,155
  • 2
  • 13
  • 10
  • 20
    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. – Michael Hays Jan 18 '18 at 18:41
  • 29
    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. – scipilot Apr 06 '18 at 08:05
  • 1
    This does not work for me ```pip install matplotlib==3.2.*``` ```zsh: no matches found: matplotlib==3.2.*``` – tbrodbeck Jun 16 '21 at 06:32
  • 6
    @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.*'`. – Adir Jul 01 '21 at 08:52
174

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.*
tanius
  • 14,003
  • 3
  • 51
  • 63
Moritz
  • 2,987
  • 3
  • 21
  • 34
  • 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? – Mike 'Pomax' Kamermans Apr 29 '19 at 17:34
  • 2
    @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. – ik1ne May 23 '19 at 08:15
  • 2
    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. – Asclepius Nov 01 '19 at 16:05
  • 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. – Mitch McMabers Nov 06 '19 at 11:13
  • 5
    @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. – Maxpm Feb 15 '20 at 01:56
  • 5
    @Maxpm 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! – Mitch McMabers Feb 17 '20 at 18:44
  • 3
    It seems really odd to me that `numpy~=1.17.0` downloads 1.17.5, but `numpy~=1.17` downloads 1.18.5. – naught101 Jun 15 '20 at 05:50
  • 2
    @naught101 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. – Erdős-Bacon Aug 30 '20 at 18:11
  • 1
    @Erdős-Bacon: but 1.17.5 is not guaranteed to be feature compatible with 1.18.0, that makes no sense. – naught101 Aug 31 '20 at 06:47
  • 1
    @naught101 Well, by [semver standards](https://semver.org/), 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. – Erdős-Bacon Aug 31 '20 at 17:37
  • This does not work for me: `Collecting matplotlib~=3.2` -> `Successfully installed matplotlib-3.4.2` – tbrodbeck Jun 16 '21 at 06:31
  • 2
    Worked as specified. To get a 3.2.X version use matplotlib~=3.2.0 – Moritz Jun 16 '21 at 12:08
13

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

Darius
  • 10,762
  • 2
  • 29
  • 50