packaging icon indicating copy to clipboard operation
packaging copied to clipboard

packaging.version as a CLI tool

Open mgaitan opened this issue 10 months ago • 3 comments

I want to propose this simple CLI tool in the packaging.version module to perform semantic version comparisons directly from the command line. This utility is modeled after dpkg --compare-versions but designed to be platform-agnostic, filling a gap for non-Debian users and providing a Python-native solution.

Version comparison is a common requirement in deployment scripts, package management, and development workflows. Currently, developers resort to complex shell scripts or third-party tools to compare versions. Examples of community solutions include intricate bash functions and snippets found on Stack Overflow and GitHub Gists that, while functional for basic cases, vary in reliability and can be unnecessarily complex 1, 2, 3.

My proposal leverages the robustness of the packaging.version.Version class for parsing and comparing semantic versions, supporting standard comparison operators (e.g., lt, gt, eq) and provide straightforward syntax that resembles the mentioned dpkg command

Usage

$ python -m packaging.version --help
usage: version.py [-h] version1 {lt,le,eq,ne,ge,gt,lt-nl,le-nl,ge-nl,gt-nl,<,<<,<=,=,>=,>>,>} version2

Compare two semantic versions.

positional arguments:
  version1              First version to compare
  {lt,le,eq,ne,ge,gt,lt-nl,le-nl,ge-nl,gt-nl,<,<<,<=,=,>=,>>,>}
                        Comparison operator
  version2              Second version to compare

options:
  -h, --help            show this help message and exit

$ python -m packaging.version 1.0b gt 0.9   
$ echo $?
0

$ python -m packaging.version 1.0b eq 0.9   # Should exit with status 1
$ echo $?
1

$ python -m packaging.version 1.0b foo 0.9
usage: version.py [-h] version1 {lt,le,eq,ne,ge,gt,lt-nl,le-nl,ge-nl,gt-nl,<,<<,<=,=,>=,>>,>} version2
version.py: error: argument operator: invalid choice: 'foo' (choose from 'lt', 'le', 'eq', 'ne', 'ge', 'gt', 'lt-nl', 'le-nl', 'ge-nl', 'gt-nl', '<', '<<', '<=', '=', '>=', '>>', '>')
$ echo $?
2

$ python -m packaging.version non-version eq 0.9
usage: version.py [-h] version1 {lt,le,eq,ne,ge,gt,lt-nl,le-nl,ge-nl,gt-nl,<,<<,<=,=,>=,>>,>} version2
version.py: error: argument version1: invalid Version value: 'non-version'
$ echo $?
2

mgaitan avatar Apr 16 '24 18:04 mgaitan

What does "nl" mean for those variant comparators? And why do they mean the left-hand side is less than the right-hand no matter what? And what no e.g., eq-nl? And why both e.g., lt and < supported?

brettcannon avatar Aug 08 '24 00:08 brettcannon

Hi @brettcannon,

The "nl" in those variant comparators comes from dpkg --compare-versions, which I originally intended to support as a drop-in replacement. From the manpage:

There are two groups of operators, which differ in how they treat an empty ver1 or ver2. These treat an empty version as earlier than any version: lt le eq ne ge gt. These treat an empty version as later than any version: lt-nl le-nl ge-nl gt-nl.

Regarding the lack of an eq-nl operator, it's likely because version equality doesn't depend on whether one of the versions is empty—if both versions are non-empty, eq handles it naturally.

However, I decided to remove support for *-nl in this tool because the version arguments are mandatory. If an invalid version is provided, the tool will exit with code 2. If needed, this can still be converted to exit code 0 using shell tools.

I also removed the non-textual operators <, <<, <=, =, >=, >>, >.

So, while the tool is still inspired by dpkg, it’s not a compatible API anymore, but it is simpler and still useful.

mgaitan avatar Sep 09 '24 22:09 mgaitan

I don’t really mind, but aren’t non-textual operators more readable than lt ge etc?

uranusjr avatar Sep 11 '24 19:09 uranusjr