Originally reported by: mklein0 (Bitbucket: mklein0, GitHub: mklein0)
Could an option be added to package builds to disable the use of normalized version identifiers in the final package file name?
Under previous versions of setup tools a version identifier of 20140510.003458 would generate a package name of:
dated_release-20140510.003458-cp27-none-macosx_10_9_intel.whl
After setuptools 8:
dated_release-20140510.3458-cp27-none-macosx_10_9_intel.whl
My primary concern is for older installation tool/devpy versions, the package version matching will fail if I start using the new normalized package names. And I do not want to have to test it.
Aesthetics is another aspect as in this case, the version identifier is actually an encoding of an ISO8601 timestamp.:
date -u +'%Y%m%d.%H%M%S'
This request is derived from issue #302
I also noticed that, if I set the version number as, say, 1.2.3-rc1, setuptools normalizes it to 1.2.3rc1. This created several problems for me when generating debian packages. Allowing such kinds of normalization do be disabled would be a major improvement.
Would also like to be able to disable normalization, 1.2.3-rc1 being normalized to 1.2.3rc1 is causing problems for packaging for me as well.
@dstufft, do you have any opinion on this? IIRC, it was your contribution that added the normalization. What would be the ramifications of allowing bypassing of this normalization?
@jaraco I think as long as you still validate that it is a PEP 440 version (or warn if not.. I don't think we have it set to hard fail), not doing the normalization shouldn't be a big deal. I think my only reason to normalize was just to get everyone using the normal forms as the default since that makes things a bit saner I think. However allowing a flag to bypass normalization should not have any negative affect.
Thanks @dstufft. Glad to hear this wouldn't be a blocker. @mklein0 or others, would you consider putting together a pull request?
Hi there, I was wondering if there was a status update or further discussion on this issue. I've just run into this when our CI pipeline built a new package (which appends a short git has to the version string) was changed to a different hash.
Version is: 2.4.5.5513c055
/usr/local/lib/python2.7/site-packages/setuptools/dist.py:332: UserWarning: Normalizing '2.4.5.5513c055' to '2.4.5.5513rc55'
It would seem to my quick analysis that there is an overly greedy regex munging version strings...
Not nice, but a workaround that works for me:
add this to your setup.py
import pkg_resources
pkg_resources.extern.packaging.version.Version = pkg_resources.SetuptoolsLegacyVersion
Just ran into this myself. I've already disabled the PEP 440 warnings and now I have to work around this also :(
We're packaging pre-releases following the semver.org standard, entirely for internal use only. I can maybe understand PyPI rejecting "internal only" versions at registration/upload time, but rejecting it outside of a public arena seems a little heavy-handed.
~My solution was to modify our version strings from "2.2.0-a046027" to "2.2.0-pre.a046027" - I couldn't figure a reasonable way to disable the normalization code through monkey-patching.~
I am using this monkeypatch to disable the normalization:
from setuptools.extern.packaging import version
version.Version = version.LegacyVersion
I threw together a quick proof of concept for adding a normalize_version=False parameter to setup() that would allow a project to disable version normalization. It does not prevent the warning about non PEP 440 versions (because it is true that using those versions are likely to cause issues), but it does provide a path for people who need a very specific version string.
That branch can be found at https://github.com/pypa/setuptools/compare/dont-normalize, it's only two lines of code plus re-indenting a block of code to be guarded by an if statement. I don't have time right now to develop it further, it would presumably require some tests and someone to shepherd it through the contribution process, but if someone does have the time, that code there is working.
Thanks, Donald!
@dstufft do you need a hand to make this a bona fide PR ?
I doubt I'm going to have the time to finish up the work that's needed to make that proof of concept into a real PR. Feel free to take it and run with it.
@dstufft code has changed a little since but this patch (even updated for the latest code) does not seem to help correctly: for instance I cannot create a valid semver version such as 1.1.0-alpha1 .... it will be normalized at all times as 1.1.0a1 for an sdist and a bdist_wheel which is no longer semver compliant. At least the sdist should be left untouched IMHO.
Will you support this feature in the next release? Any plans if next release don't support the feature? Thank you.
@an-sush if you feel this is "unfair", would you consider submitting a patch then to help right this unfair wrong? :innocent: ;)
I created https://github.com/ccrisan/setup-no-ver-normalize to "fix" this issue in my projects.
@ccrisan that's simple and clean. Monkey patching really allows awesome things
Is there any update on this issue? I would like to use -alpha prefixes in my versioning as suggested by semver standard. @ccrisan your library works for the most part but converts - to _ in the wheel file.
I threw together a quick proof of concept for adding a
normalize_version=Falseparameter tosetup()
I took a look at this, and while it's a good proof of concept (thanks Donald), I think I'd prefer an implementation that avoided a boolean parameter alongside the version parameter, but instead have some API where the version specified carries with it the fact that it shouldn't be normalized, either by way of wrapping it in an alternate class (such as ccrisan's NoNormalizeVersion) or by setting an attribute on the str that's passed in or by accepting a function (by name or value) for normalizing.
On the other hand, I'd also like something that if it's officially supported, would also work with declarative config. I'm considering this approach:
version='1.2..3 (sic)', where the (sic) appended acknowledges the non-normal usage.(sic) is present, setuptools will strip the indicator and skip normalization.While I sort-of like this approach, it also causes the value to be passed in-band (in the same string as the intended value), which I dislike.
I'm also considering:
sic() function, which if called on a string would indicate to Setuptools not to use normalization. e.g. version=setuptools.sic('1.2..3').Thoughts?
I threw together a quick proof of concept for adding a
normalize_version=Falseparameter tosetup()I took a look at this, and while it's a good proof of concept (thanks Donald), I think I'd prefer an implementation that avoided a boolean parameter alongside the version parameter, but instead have some API where the version specified carries with it the fact that it shouldn't be normalized, either by way of wrapping it in an alternate class (such as ccrisan's
NoNormalizeVersion) or by setting an attribute on thestrthat's passed in or by accepting a function (by name or value) for normalizing.On the other hand, I'd also like something that if it's officially supported, would also work with declarative config. I'm considering this approach:
- a user could supply a version as
version='1.2..3 (sic)', where the(sic)appended acknowledges the non-normal usage.- if
(sic)is present, setuptools will strip the indicator and skip normalization.While I sort-of like this approach, it also causes the value to be passed in-band (in the same string as the intended value), which I dislike.
I'm also considering:
- Setuptools would supply a
sic()function, which if called on a string would indicate to Setuptools not to use normalization. e.g.version=setuptools.sic('1.2..3').- This approach would not be as portable to declarative config.
Thoughts?
How about providing an option to pass an implementation for the normalize function when calling setup, if parameter is not passed then you use the default implementation, if passed as None then you return the version as it is. You could also pass a custom implementation if you like.
@KishanKishore you wrote:
How about providing an option to pass an implementation for the normalize function when calling setup, if parameter is not passed then you use the default implementation, if passed as None then you return the version as it is. You could also pass a custom implementation if you like.
can you elaborate and be more explicit and also include an example?
I don't think that version='1.2..3 (sic)' is any different than a boolean parameter, except instead of using a normal Python boolean it's using some weird setuptools version specific boolean. Notably this would fail really hard if someone is using an older version of setuptools.
Honestly, all of the options seem to just conceptually be a boolean argument, just with different ways of passing it, EXCEPT for the ability to pass your own normalization function.
In #2026, I've drafted the second approach for consideration. Test release available here.
I'm leaning strongly toward this approach as it's minimally invasive and allows the special-consideration to be indicated through the same mechanism as version but while maintaining compatibility for the nominal case.
Please give it a try.
Not nice, but a workaround that works for me:
add this to your
setup.py
import pkg_resources
pkg_resources.extern.packaging.version.Version = pkg_resources.SetuptoolsLegacyVersion
I tried this, but I am getting the following error:
Traceback (most recent call last):
File "setup.py", line 16, in <module>
pkg_resources.extern.packaging.version.Version = pkg_resources.SetuptoolsLegacyVersion
AttributeError: 'module' object has no attribute 'SetuptoolsLegacyVersion'
Not nice, but a workaround that works for me:
add this to yoursetup.py
import pkg_resources
pkg_resources.extern.packaging.version.Version = pkg_resources.SetuptoolsLegacyVersionI tried this, but I am getting the following error:
Traceback (most recent call last): File "setup.py", line 16, in <module> pkg_resources.extern.packaging.version.Version = pkg_resources.SetuptoolsLegacyVersion AttributeError: 'module' object has no attribute 'SetuptoolsLegacyVersion'
@oxr463 Use the newer monkey patch mentioned in a subsequent comment:
from setuptools.extern.packaging import version
version.Version = version.LegacyVersion
It worked for me with the latest setuptools version (50.3.2). I did not verify how far back this works in the setuptools versions.
As an update to my previous comment, and for people that run additional tests with well-defined minimum versions of dependent packages, here is an approach that works for versions of setuptools at least back to 20.2.2:
# Disable version normalization performed by setuptools.setup()
try:
# Try the approach of using sic(), added in setuptools 46.1.0
from setuptools import sic
except ImportError:
# Try the approach of replacing packaging.version.Version
sic = lambda v: v
try:
# setuptools >=39.0.0 uses packaging from setuptools.extern
from setuptools.extern import packaging
except ImportError:
# setuptools <39.0.0 uses packaging from pkg_resources.extern
from pkg_resources.extern import packaging
packaging.version.Version = packaging.version.LegacyVersion
setuptools.setup(
. . .
version=sic('1.0.0-dev1')
. . .
)
$ python setup.py --version
1.0.0-dev1
Most helpful comment
I created https://github.com/ccrisan/setup-no-ver-normalize to "fix" this issue in my projects.