diff --git a/jenkins/plugins.py b/jenkins/plugins.py index 9709d5d..cdaada9 100644 --- a/jenkins/plugins.py +++ b/jenkins/plugins.py @@ -42,8 +42,6 @@ import operator import re -import pkg_resources - class Plugin(dict): '''Dictionary object containing plugin metadata.''' @@ -76,15 +74,13 @@ class PluginVersion(str): '''Parse plugin version and store it for comparison.''' self._version = version - self.parsed_version = pkg_resources.parse_version( - self.__convert_version(version)) + self._key = _legacy_cmpkey(self.__convert_version(version)) def __convert_version(self, version): return self._VERSION_RE.sub(r'\g<1>.preview', str(version)) def __compare(self, op, version): - return op(self.parsed_version, pkg_resources.parse_version( - self.__convert_version(version))) + return op(self._key, PluginVersion(version)._key) def __le__(self, version): return self.__compare(operator.le, version) @@ -109,3 +105,92 @@ class PluginVersion(str): def __repr__(self): return str(self._version) + + +############################################################################### +""" +The Python world has migrated to the versioning scheme defined in PEP 440, but +the versioning of Jenkins plugins is less strict than that. The code below was +salvaged from the implementation of the `LegacyVersion` class, which used to be +part of the `packaging` library prior to version 22.0. + +It is licensed as follows. + +Copyright (c) Donald Stufft and individual contributors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +""" + +_legacy_version_component_re = re.compile(r"(\d+ | [a-z]+ | \.| -)", re.VERBOSE) + +_legacy_version_replacement_map = { + "pre": "c", + "preview": "c", + "-": "final-", + "rc": "c", + "dev": "@", +} + + +def _parse_version_parts(s): + for part in _legacy_version_component_re.split(s): + part = _legacy_version_replacement_map.get(part, part) + + if not part or part == ".": + continue + + if part[:1] in "0123456789": + # pad for numeric comparison + yield part.zfill(8) + else: + yield "*" + part + + # ensure that alpha/beta/candidate are before final + yield "*final" + + +def _legacy_cmpkey(version): + + # We hardcode an epoch of -1 here. A PEP 440 version can only have a epoch + # greater than or equal to 0. This will effectively put the LegacyVersion, + # which uses the defacto standard originally implemented by setuptools, + # as before all PEP 440 versions. + epoch = -1 + + # This scheme is taken from pkg_resources.parse_version setuptools prior to + # it's adoption of the packaging library. + parts = [] + for part in _parse_version_parts(version.lower()): + if part.startswith("*"): + # remove "-" before a prerelease tag + if part < "*final": + while parts and parts[-1] == "*final-": + parts.pop() + + # remove trailing zeros from each series of numeric parts + while parts and parts[-1] == "00000000": + parts.pop() + + parts.append(part) + + return epoch, tuple(parts) diff --git a/requirements.txt b/requirements.txt index da41145..5c014cc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,3 @@ -# Setuptools removed support for PEP 440 non-conforming versions -setuptools<66 six>=1.3.0 pbr>=0.8.2 multi_key_dict