From c36acc9eaaf398089a85c50117e4d944341e8fbb Mon Sep 17 00:00:00 2001 From: Sviatoslav Sydorenko Date: Thu, 19 Nov 2020 19:38:25 +0100 Subject: [PATCH] Add a versioning helper module --- .../galaxy/dependency_resolution/__init__.py | 7 ++ .../dependency_resolution/versioning.py | 67 +++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 lib/ansible/galaxy/dependency_resolution/__init__.py create mode 100644 lib/ansible/galaxy/dependency_resolution/versioning.py diff --git a/lib/ansible/galaxy/dependency_resolution/__init__.py b/lib/ansible/galaxy/dependency_resolution/__init__.py new file mode 100644 index 00000000000..7d0020691bb --- /dev/null +++ b/lib/ansible/galaxy/dependency_resolution/__init__.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- +# Copyright: (c) 2020, Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +"""Dependency resolution machinery.""" + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type diff --git a/lib/ansible/galaxy/dependency_resolution/versioning.py b/lib/ansible/galaxy/dependency_resolution/versioning.py new file mode 100644 index 00000000000..c5c5ae85ab7 --- /dev/null +++ b/lib/ansible/galaxy/dependency_resolution/versioning.py @@ -0,0 +1,67 @@ +# -*- coding: utf-8 -*- +# Copyright: (c) 2019-2020, Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +"""Version comparison helpers.""" + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import operator +from distutils.version import LooseVersion + +from ansible.utils.version import SemanticVersion + + +def is_pre_release(version): + # type: (str) -> bool + """Figure out if a given version is a pre-release.""" + return SemanticVersion(version).is_prerelease + + +def meets_requirements(version, requirements): + # type: (str, str) -> bool + """Verify if a given version satisfies all the requirements. + + Supported version identifiers are: + * '==' + * '!=' + * '>' + * '>=' + * '<' + * '<=' + * '*' + + Each requirement is delimited by ','. + """ + op_map = { + '!=': operator.ne, + '==': operator.eq, + '=': operator.eq, + '>=': operator.ge, + '>': operator.gt, + '<=': operator.le, + '<': operator.lt, + } + + for req in requirements.split(','): + op_pos = 2 if len(req) > 1 and req[1] == '=' else 1 + op = op_map.get(req[:op_pos]) + + requirement = req[op_pos:] + if not op: + requirement = req + op = operator.eq + + if requirement == '*' or version == '*': + continue + + if not op( + SemanticVersion(version), + SemanticVersion.from_loose_version(LooseVersion(requirement)), + ): + break + else: + return True + + # The loop was broken early, it does not meet all the requirements + return False