packaging
packaging copied to clipboard
packaging.version as a CLI tool
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
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?
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.
I don’t really mind, but aren’t non-textual operators more readable than lt
ge
etc?