packaging icon indicating copy to clipboard operation
packaging copied to clipboard

utils.canonicalize_version produces an abnormal version

Open jaraco opened this issue 10 months ago • 7 comments

In https://github.com/pypa/setuptools/issues/4302, we learned that using packaging.utils.canonicalize_version to canonicalize a version produces an aberrant version.

 @ pip-run packaging -- -c 'import packaging.utils; print(packaging.utils.canonicalize_version("69.3.0"))'
69.3

Instead of matching the convention of wheels, which will produce a wheel with a version of "69.3.0", canonicalize_version strips the trailing zero and produces 69.3.

image

Moreover, semver indicates that the version number should include a trailing zero, so it's not possible to honor both semver and the canonicalized_version.

In https://github.com/pypa/setuptools/issues/3593#issuecomment-2052518260, @pfmoore says that wheels and sdists should use the same format.

What's the correct format?

Also, why does a function called canonicalize_version produce both canonical and non-canonical versions depending on a flag?

https://github.com/pypa/packaging/blob/32deafe8668a2130a3366b98154914d188f3718e/src/packaging/utils.py#L81-L83

I see that wheel implements its own safer_version function.

If packaging.utils.canonicalize_version isn't the right implementation for a PEP 625 canonicalized version, what is?

jaraco avatar Apr 12 '24 23:04 jaraco

In https://github.com/pypa/setuptools/issues/3593#issuecomment-2053639467, I confirmed that the spec actually considers 1.0 to be a canonical version. Therefore, the fact that canonicalize_version transforms it is operating against the spec.

jaraco avatar Apr 13 '24 13:04 jaraco

If packaging.utils.canonicalize_version isn't the right implementation for a PEP 625 canonicalized version, what is?

canonicalize_version(..., strip_trailing_zeros=False) does.

pradyunsg avatar Apr 22 '24 11:04 pradyunsg

It might be worthwhile to make that the default on this helper function.

pradyunsg avatar Apr 22 '24 11:04 pradyunsg

This was missing from the API doc, I made a PR #801.

Laurent-Dx avatar May 17 '24 10:05 Laurent-Dx

I feel like there should be two functions - one for users to use when normalizing a version and another to use when comparing two versions, so it's clear that there are two purposes here and can give the user clear guidance as to which form to use for a given circumstance. It's an oxymoron for a version to have multiple canonical forms.

jaraco avatar May 18 '24 14:05 jaraco

For comparison I think Version should be the preferred API.

uranusjr avatar May 20 '24 16:05 uranusjr

It might be worthwhile to make that the default on this helper function.

How would you want to handle the transition on this? The parameter is 2.5 years old, so you know there are people out there using this as-is. We could remove the default for a release or two and re-introduce it flipped.

Or we can document it and not worry about it.

It's an oxymoron for a version to have multiple canonical forms.

Not necessarily. If you think there's One True Representation For All Cases, then I would agree. But versions get used in so many cases for so many reasons, I think the canonical version for comparison and canonical version for display can be different. I can see how that would then make sense to have two functions, but we also already have the one function w/ a toggle, so I'm not if it's worth the hassle of adding two more functions for this.

brettcannon avatar Aug 08 '24 01:08 brettcannon