pypa / pip

The Python package installer
https://pip.pypa.io/
MIT License
9.47k stars 3.01k forks source link

pip sometimes installs release candidates instead of stable releases #4969

Closed hyperknot closed 6 years ago

hyperknot commented 6 years ago

Description:

When running the following command:

pip install 'webob >= 1.7.0rc2'

WebOb 1.8.0rc1 gets installed, even thought the latest stable release is 1.7.4.

Collecting webob>=1.7.0rc2
  Using cached WebOb-1.8.0rc1-py2.py3-none-any.whl

I think this bug seems to be triggered when there is rc in the version name.

benoit-pierre commented 6 years ago

I think this is normal: since the requirement version specifier explicitely allow a pre-release, then pre-releases are allowed for this requirement.

hyperknot commented 6 years ago

This doesn't seem logical to me. The command specified a minimum version, why would it change the maximum version? The maximum should always be the same as when nothing is specified, which is the latest stable release.

benoit-pierre commented 6 years ago

Pre-releases are not considered, unless --pre is used, or a requirement ask for it. IMHO, it does make sense, if you're asking for a pre-release (not stable code) to consider all pre-releases.

benoit-pierre commented 6 years ago

Basically, if one of the version specifiers match a pre-release, then all pre-releases are considered.

hyperknot commented 6 years ago

But you are setting a minimum specifier for totally different reasons then a maximum specifier.

Minimum specifier in a dependency means that you want to have at least a good working version. Sometimes that happens to be an rc version, it doesn't matter. You just want to make sure that you are not on an old dependency.

Why would this mean that you want to use non-stable release of the dependency?

benoit-pierre commented 6 years ago

It's >=, not >, you're explicly saying: "I'm okay with a non-stable release"... So why not a more recent RC? See PEP 440.

hyperknot commented 6 years ago

Because you are jumping a minor version here! Allowing an rc of 1.7 doesn't mean that it's ok to use an rc of 1.8.

Anyway, from PEP 440 it seems clear to me that this is a bug:

By default, dependency resolution tools SHOULD:

accept already installed pre-releases for all version specifiers - not applying here accept remotely available pre-releases for version specifiers where there is no final or post release that satisfies the version specifier - there is, 1.7.4 exclude all other pre-releases from consideration - should exclude 1.8 rc releases

benoit-pierre commented 6 years ago

I think this fall into the explicitly requested by the user category.

hyperknot commented 6 years ago

OK, this is in the docs:

If a Requirement specifier includes a pre-release or development version (e.g. >=0.0.dev0) then pip will allow pre-release and development versions for that requirement. This does not include the != flag.

Still, I consider it to be questionable behaviour.

For me, if a command line tool has an explicit --pre switch, that is what I'd call explicitly requested by the user, everything else is not explicit.

In this regards I'd say that pip is not following PEP 440.

pfmoore commented 6 years ago

Note that PEP 440 says "SHOULD" not "MUST", so pip's behaviour is acceptable on that score.

Having said that, I can see your argument. But I doubt it's something that's likely to get changed unless a 3rd party submits a PR, so if you want to take this further I would suggest doing that. You'll also need to include documentation changes and tests to confirm that the new behaviour works as expected. Whether the feature then gets accepted will depend on whether the core pip developers feel that tighter conformance with PEP 440 is worth the breakage that will inevitably happen for our users who rely on the current behaviour (there are bound to be some).

The fact that the current behaviour is clearly documented is also a strong argument against your proposal.

For the record, I'm of the opinion that if we were writing the code now, I'd be willing to go with your interpretation. But I'm not convinced it's important enough to change existing behaviour. (Unless there are other tools implementing PEP 440 that already implement the behaviour you suggest, it's arguably just as easy to get the PEP modified to reflect pip's behaviour).

hyperknot commented 6 years ago

I don't have hopes that a PR submitted by me would have any chance in changing pip's default behaviour.

Finally, here is the relevant part of npm's semver handling, mirroring my points: https://github.com/npm/node-semver#prerelease-tags

Prerelease Tags

If a version has a prerelease tag (for example, 1.2.3-alpha.3) then it will only be allowed to satisfy comparator sets if at least one comparator with the same [major, minor, patch] tuple also has a prerelease tag.

For example, the range >1.2.3-alpha.3 would be allowed to match the version 1.2.3-alpha.7, but it would not be satisfied by 3.4.5-alpha.9, even though 3.4.5-alpha.9 is technically "greater than" 1.2.3-alpha.3 according to the SemVer sort rules. The version range only accepts prerelease tags on the 1.2.3 version. The version 3.4.5 would satisfy the range, because it does not have a prerelease flag, and 3.4.5 is greater than 1.2.3-alpha.7.

The purpose for this behavior is twofold. First, prerelease versions frequently are updated very quickly, and contain many breaking changes that are (by the author's design) not yet fit for public consumption. Therefore, by default, they are excluded from range matching semantics.

Second, a user who has opted into using a prerelease version has clearly indicated the intent to use that specific set of alpha/beta/rc versions. By including a prerelease tag in the range, the user is indicating that they are aware of the risk. However, it is still not appropriate to assume that they have opted into taking a similar risk on the next set of prerelease versions.

The last two paragraph has a better explanation about what I am trying to say.

dstufft commented 6 years ago

As one of the authors of PEP 440 and the author of the packaging library, I can say that the current handling accurately represents the intent of what was documented in PEP 440. The thinking here is that we cannot assume anything about the version numbers because, unlike npm, we do not mandate any particular version scheme. One possible answer for how things get versioned is a datebased version also known as CalVer) where 18.1 was released in January of 2018, 18.2 February, etc. 18.2 may be a very minor bug fix, or it may introduce several new fixes, but it's impossible to tell from just looking at the version number.

So implicitly, what you're asking is to special case semver-esque versions in a way that will make other versioning schemes less useful with our pre-release handling. That isn't necessarily the wrong thing to do, semver-esque is certainly the most popular versioning scheme so it'd likely be generally useful to the larger popularion, but I don't think we'd be able to do that without discussion on distutils-sig and an amendment made to PEP 440 to clarify an errata that we're changing the way pre-release handling should work.

dstufft commented 6 years ago

Given the above, I'm going to go ahead and close this issue, because pip is working as intended here and the next steps to pursue this issue is to go through the standards process first before there is any work to be done here.

mmerickel commented 6 years ago

This still seems quite odd to me. PEP 440 definitely defines some ordering to version numbers for sorting and knows what is a pre versus stable build. It seems that knowing the "stable" part of a "pre" specifier should be quite straightfoward and then those pins could be treated as only "pre" pins for that range versus "any pre ever".

There doesn't seem to be any way right now (please correct me if I'm wrong) to specify a version as requiring "at least this pre-release, but not any pre-release on newer / non-matching stable versions".

dstufft commented 6 years ago

It defines ordering yes, and a boolean flag whether something is a pre-release or not. It doesn't however define that 1.8 and 1.9 are different release series, they could be as related as 1.8.0 and 1.8.1.

mmerickel commented 6 years ago

The stable part must match between two pre-release versions to be considered part of the same pre-release series. For example, 1.8rc1 and 1.8.1rc1 are clearly different series, or 18.1rc1 versus 18.2rc3 in your CalVer example.

This is clear, isn't it? ISTM pip could split the version into (stable version, pre-release-stuff) and then group by version via only the requirements in PEP 440 as far as what is considered a pre-release and how to sort things.

mmerickel commented 6 years ago

Sorry for the spam, I just find it so hard to grasp that Pyramid pinning to an unstable dependency version could be considered a request by the "user" to use unstable versions forever. I'm having trouble wrapping my head around that one as an "invalid" concern that requires the PEP process to fix. It just seems like a clear bug in pip that PEP 440 should have helped a lot with by stabilizing the version number format, even if it isn't semver exactly.

yarikoptic commented 5 years ago

FWIW, if I got it all right, I am as confused as @mmerickel on the logic of opening the doors for all future pre-releases by stating that I need at least the release candidate of an upcoming release. Is there any other distribution which adopts similar specification logic?

lock[bot] commented 5 years ago

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

uranusjr commented 4 years ago

Cross-linking #7579 where this was broght up again.