Add support for version objects

I propose to add support for version objects in the stdlib. Version object is a namedtuple-like object with special attrbutes major, minor, etc for representing semantic version in the form used by most open source (and not only) software. The sys module already contains two version objects: sys.version_info and the result of sys.getwindowsversion(). There is a support of version objects in Perl [1] and .NET [2]. There are third-party Python implementations ([3], [4], [5]). Version objects can be used for representing: 1. Version of Python itself (sys.version_info). 2. Version of OS and system libraries. 3. Static and runtime versions of wrapped libraries (zlib, expat, sqlite, curses, Tcl/Tk, libffi, libmpdec). 4. Versions of third-party modules. The benefit of version objects against plain strings is that version objects are comparable with expected rules (1.2.3 < 1.10.1). The benefit of version objects against tuples is human readable string representation and named attributes for components. [1] http://search.cpan.org/perldoc?version [2] https://msdn.microsoft.com/en-us/library/system.version%28v=vs.110%29.aspx [3] https://pypi.python.org/pypi/semantic_version [4] https://pypi.python.org/pypi/versions [3] https://pypi.python.org/pypi/version https://pypi.python.org/pypi/SemVerPy

On Fri, May 27, 2016 at 10:04:34PM +0300, Serhiy Storchaka <storchaka@gmail.com> wrote:
Something like https://hg.python.org/cpython/file/tip/Lib/distutils/version.py https://hg.python.org/cpython/file/tip/Lib/distutils/versionpredicate.py ??? Oleg. -- Oleg Broytman http://phdru.name/ phd@phdru.name Programmers don't die, they just GOSUB without RETURN.

On 27.05.16 22:30, Oleg Broytman wrote:
Thank you. I was not aware of this class when wrote my message. This class provide a part of the feature. It doesn't provide nor tuple-like interface, nor attributes like major and minor, it doesn't allow to create a version object from components, and LooseVersion objects are not comparable in general case:

On May 27, 2016, at 3:04 PM, Serhiy Storchaka <storchaka@gmail.com> wrote:
I propose to add support for version objects in the stdlib. Version object is a namedtuple-like object with special attrbutes major, minor, etc for representing semantic version in the form used by most open source (and not only) software. The sys module already contains two version objects: sys.version_info and the result of sys.getwindowsversion().
If Python adds a version objection, it should not be one that implements SemVer, but one that implements PEP 440 which is what PyPI, pip, setuptools, etc all use. SemVer, while a nice idea, is too simplistic to represent the variations of versioning that the Python community uses. There’s also a pure Python implementation of it available [1]. To be honest though, I don’t see a lot of benefit to adding it to the standard library. [1] http://packaging.readthedocs.io/en/latest/version/ — Donald Stufft

On 27 May 2016 19:29, "Robert Collins" <robertc@robertcollins.net> wrote:
On 28 May 2016 at 07:37, Donald Stufft <donald@stufft.io> wrote:
On May 27, 2016, at 3:04 PM, Serhiy Storchaka <storchaka@gmail.com>
wrote:
The main advantage I'd see to stdlib inclusion is providing "one obvious way to do it" - it isn't immediately obvious to a newcomer that PEP 440 and the implementation in packaging are the preferred approach for SemVer-like version numbers in Python projects. Cheers, Nick.

On 28 May 2016 at 05:38, Ethan Furman <ethan@stoneleaf.us> wrote:
Agreed. Having a version object in the stdlib would be a good way of directing people to the standard approach. Some points: - It pretty much has to be PEP 440, as that's the standard in the Python packaging ecosystem. Adding a *different* standard to the stdlib would be a big problem IMO. - Packaging tools like pip will still need to bundle an external implementation (at the moment we use the "packaging" library) as they'll need to support older versions of Python for some time yet. So there would need to be a maintained backport version on PyPI (or the packaging library could be sanctioned as that backport). - Assuming a separate backport, it's not clear to me how we'd manage the transition between packaging and the backport (maintain the 2 in parallel? packaging falls back to the stdlib/backport if present? packaging gains a conditional dependency on the backport?) - PEP 440, and the implementation, is pretty stable. But are we happy for it to move to the development pace of the stdlib? The distutils version classes are pretty old, and fairly limited - the strict version is frequently *too* strict, but the loose version is rather too lenient... Paul

On 28.05.16 05:28, Robert Collins wrote:
My use case is the need of testing versions of libraries, mainly for testing, but not only. For example Tkinter provides version in two forms: _tkinter.TK_VERSION is a string ('8.6') and tkinter.TkVersion is a decimal number (8.6). Both forms wouldn't work for Tk 8.10. We need to convert string version to a tuple of integers. In Lib/tkinter/test/support.py: tcl_version = tuple(map(int, _tkinter.TCL_VERSION.split('.'))) But two digits is not enough, because the behavior often depends on the pathlevel number. We need to retrieve and parse runtime version. In Lib/idlelib/macosxSupport.py: tkversion = root.tk.eval('info patchlevel') if tuple(map(int, tkversion.split('.'))) < (8, 4, 14): ... This code doesn't work with alpha, beta and rc versions. There is more correct complex code in Lib/tkinter/test/support.py: tcl = tkinter.Tcl() patchlevel = tcl.call('info', 'patchlevel') m = re.fullmatch(r'(\d+)\.(\d+)([ab.])(\d+)', patchlevel) major, minor, releaselevel, serial = m.groups() major, minor, serial = int(major), int(minor), int(serial) releaselevel = {'a':'alpha','b':'beta','.':'final'}[releaselevel] if releaselevel == 'final': _tk_patchlevel = major, minor, serial, releaselevel, 0 else: _tk_patchlevel = major, minor, 0, releaselevel, serial I think it is worth to provide structured comparable version in the tkinter module. But this is just one library. There are similar problems with other libraries. zlib tests contain following complicated code: v = (zlib.ZLIB_RUNTIME_VERSION + ".0").split(".", 4) supports_wbits_0 = int(v[0]) > 1 or int(v[0]) == 1 \ and (int(v[1]) > 2 or int(v[1]) == 2 and (int(v[2]) > 3 or int(v[2]) == 3 and int(v[3]) >= 5)) It is so complex because zlib.ZLIB_RUNTIME_VERSION can contain not only digits (like "1.2.8.1-motley"). See also complex _requires_unix_version() and requires_mac_ver() in Lib/test/support/__init__.py. There is other code that parses string representation to a tuple of numbers. While parsing code can be library specific, I think it is worth to provide standard type for representing the result. Libraries provide versions as string at Python level, but they often provide separate version components, thus we can create version object without parsing string representation.

On Fri, May 27, 2016 at 10:04:34PM +0300, Serhiy Storchaka <storchaka@gmail.com> wrote:
Something like https://hg.python.org/cpython/file/tip/Lib/distutils/version.py https://hg.python.org/cpython/file/tip/Lib/distutils/versionpredicate.py ??? Oleg. -- Oleg Broytman http://phdru.name/ phd@phdru.name Programmers don't die, they just GOSUB without RETURN.

On 27.05.16 22:30, Oleg Broytman wrote:
Thank you. I was not aware of this class when wrote my message. This class provide a part of the feature. It doesn't provide nor tuple-like interface, nor attributes like major and minor, it doesn't allow to create a version object from components, and LooseVersion objects are not comparable in general case:

On May 27, 2016, at 3:04 PM, Serhiy Storchaka <storchaka@gmail.com> wrote:
I propose to add support for version objects in the stdlib. Version object is a namedtuple-like object with special attrbutes major, minor, etc for representing semantic version in the form used by most open source (and not only) software. The sys module already contains two version objects: sys.version_info and the result of sys.getwindowsversion().
If Python adds a version objection, it should not be one that implements SemVer, but one that implements PEP 440 which is what PyPI, pip, setuptools, etc all use. SemVer, while a nice idea, is too simplistic to represent the variations of versioning that the Python community uses. There’s also a pure Python implementation of it available [1]. To be honest though, I don’t see a lot of benefit to adding it to the standard library. [1] http://packaging.readthedocs.io/en/latest/version/ — Donald Stufft

On 27 May 2016 19:29, "Robert Collins" <robertc@robertcollins.net> wrote:
On 28 May 2016 at 07:37, Donald Stufft <donald@stufft.io> wrote:
On May 27, 2016, at 3:04 PM, Serhiy Storchaka <storchaka@gmail.com>
wrote:
The main advantage I'd see to stdlib inclusion is providing "one obvious way to do it" - it isn't immediately obvious to a newcomer that PEP 440 and the implementation in packaging are the preferred approach for SemVer-like version numbers in Python projects. Cheers, Nick.

On 28 May 2016 at 05:38, Ethan Furman <ethan@stoneleaf.us> wrote:
Agreed. Having a version object in the stdlib would be a good way of directing people to the standard approach. Some points: - It pretty much has to be PEP 440, as that's the standard in the Python packaging ecosystem. Adding a *different* standard to the stdlib would be a big problem IMO. - Packaging tools like pip will still need to bundle an external implementation (at the moment we use the "packaging" library) as they'll need to support older versions of Python for some time yet. So there would need to be a maintained backport version on PyPI (or the packaging library could be sanctioned as that backport). - Assuming a separate backport, it's not clear to me how we'd manage the transition between packaging and the backport (maintain the 2 in parallel? packaging falls back to the stdlib/backport if present? packaging gains a conditional dependency on the backport?) - PEP 440, and the implementation, is pretty stable. But are we happy for it to move to the development pace of the stdlib? The distutils version classes are pretty old, and fairly limited - the strict version is frequently *too* strict, but the loose version is rather too lenient... Paul

On 28.05.16 05:28, Robert Collins wrote:
My use case is the need of testing versions of libraries, mainly for testing, but not only. For example Tkinter provides version in two forms: _tkinter.TK_VERSION is a string ('8.6') and tkinter.TkVersion is a decimal number (8.6). Both forms wouldn't work for Tk 8.10. We need to convert string version to a tuple of integers. In Lib/tkinter/test/support.py: tcl_version = tuple(map(int, _tkinter.TCL_VERSION.split('.'))) But two digits is not enough, because the behavior often depends on the pathlevel number. We need to retrieve and parse runtime version. In Lib/idlelib/macosxSupport.py: tkversion = root.tk.eval('info patchlevel') if tuple(map(int, tkversion.split('.'))) < (8, 4, 14): ... This code doesn't work with alpha, beta and rc versions. There is more correct complex code in Lib/tkinter/test/support.py: tcl = tkinter.Tcl() patchlevel = tcl.call('info', 'patchlevel') m = re.fullmatch(r'(\d+)\.(\d+)([ab.])(\d+)', patchlevel) major, minor, releaselevel, serial = m.groups() major, minor, serial = int(major), int(minor), int(serial) releaselevel = {'a':'alpha','b':'beta','.':'final'}[releaselevel] if releaselevel == 'final': _tk_patchlevel = major, minor, serial, releaselevel, 0 else: _tk_patchlevel = major, minor, 0, releaselevel, serial I think it is worth to provide structured comparable version in the tkinter module. But this is just one library. There are similar problems with other libraries. zlib tests contain following complicated code: v = (zlib.ZLIB_RUNTIME_VERSION + ".0").split(".", 4) supports_wbits_0 = int(v[0]) > 1 or int(v[0]) == 1 \ and (int(v[1]) > 2 or int(v[1]) == 2 and (int(v[2]) > 3 or int(v[2]) == 3 and int(v[3]) >= 5)) It is so complex because zlib.ZLIB_RUNTIME_VERSION can contain not only digits (like "1.2.8.1-motley"). See also complex _requires_unix_version() and requires_mac_ver() in Lib/test/support/__init__.py. There is other code that parses string representation to a tuple of numbers. While parsing code can be library specific, I think it is worth to provide standard type for representing the result. Libraries provide versions as string at Python level, but they often provide separate version components, thus we can create version object without parsing string representation.
participants (7)
-
Donald Stufft
-
Ethan Furman
-
Nick Coghlan
-
Oleg Broytman
-
Paul Moore
-
Robert Collins
-
Serhiy Storchaka