diff options
author | CoprDistGit <infra@openeuler.org> | 2023-03-23 02:28:22 +0000 |
---|---|---|
committer | CoprDistGit <infra@openeuler.org> | 2023-03-23 02:28:22 +0000 |
commit | 6d8e39fe5ab063ff2a4c2a918cb6ad5b5d4aec45 (patch) | |
tree | bd5138cc2ba1ebbbdbc52bd269b506114f8caa12 /pyproject_convert.py | |
parent | b0e529d9a17eaf6cc3ff98b78de743e401913baf (diff) |
automatic import of pyproject-rpm-macros
Diffstat (limited to 'pyproject_convert.py')
-rw-r--r-- | pyproject_convert.py | 171 |
1 files changed, 171 insertions, 0 deletions
diff --git a/pyproject_convert.py b/pyproject_convert.py new file mode 100644 index 0000000..ea01bc3 --- /dev/null +++ b/pyproject_convert.py @@ -0,0 +1,171 @@ +# Copyright 2019 Gordon Messmer <gordon.messmer@gmail.com> +# +# Upstream: https://github.com/gordonmessmer/pyreq2rpm +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from packaging.requirements import Requirement +from packaging.version import parse as parse_version + +class RpmVersion(): + def __init__(self, version_id): + version = parse_version(version_id) + if isinstance(version._version, str): + self.version = version._version + else: + self.epoch = version._version.epoch + self.version = list(version._version.release) + self.pre = version._version.pre + self.dev = version._version.dev + self.post = version._version.post + # version.local is ignored as it is not expected to appear + # in public releases + # https://www.python.org/dev/peps/pep-0440/#local-version-identifiers + + def is_legacy(self): + return isinstance(self.version, str) + + def increment(self): + self.version[-1] += 1 + self.pre = None + self.dev = None + self.post = None + return self + + def __str__(self): + if self.is_legacy(): + return self.version + if self.epoch: + rpm_epoch = str(self.epoch) + ':' + else: + rpm_epoch = '' + while len(self.version) > 1 and self.version[-1] == 0: + self.version.pop() + rpm_version = '.'.join(str(x) for x in self.version) + if self.pre: + rpm_suffix = '~{}'.format(''.join(str(x) for x in self.pre)) + elif self.dev: + rpm_suffix = '~~{}'.format(''.join(str(x) for x in self.dev)) + elif self.post: + rpm_suffix = '^post{}'.format(self.post[1]) + else: + rpm_suffix = '' + return '{}{}{}'.format(rpm_epoch, rpm_version, rpm_suffix) + +def convert_compatible(name, operator, version_id): + if version_id.endswith('.*'): + return 'Invalid version' + version = RpmVersion(version_id) + if version.is_legacy(): + # LegacyVersions are not supported in this context + return 'Invalid version' + if len(version.version) == 1: + return 'Invalid version' + upper_version = RpmVersion(version_id) + upper_version.version.pop() + upper_version.increment() + return '({} >= {} with {} < {})'.format( + name, version, name, upper_version) + +def convert_equal(name, operator, version_id): + if version_id.endswith('.*'): + version_id = version_id[:-2] + '.0' + return convert_compatible(name, '~=', version_id) + version = RpmVersion(version_id) + return '{} = {}'.format(name, version) + +def convert_arbitrary_equal(name, operator, version_id): + if version_id.endswith('.*'): + return 'Invalid version' + version = RpmVersion(version_id) + return '{} = {}'.format(name, version) + +def convert_not_equal(name, operator, version_id): + if version_id.endswith('.*'): + version_id = version_id[:-2] + version = RpmVersion(version_id) + if version.is_legacy(): + # LegacyVersions are not supported in this context + return 'Invalid version' + version_gt = RpmVersion(version_id).increment() + version_gt_operator = '>=' + # Prevent dev and pre-releases from satisfying a < requirement + version = '{}~~'.format(version) + else: + version = RpmVersion(version_id) + version_gt = version + version_gt_operator = '>' + return '({} < {} or {} {} {})'.format( + name, version, name, version_gt_operator, version_gt) + +def convert_ordered(name, operator, version_id): + if version_id.endswith('.*'): + # PEP 440 does not define semantics for prefix matching + # with ordered comparisons + # see: https://github.com/pypa/packaging/issues/320 + # and: https://github.com/pypa/packaging/issues/321 + # This style of specifier is officially "unsupported", + # even though it is processed. Support may be removed + # in version 21.0. + version_id = version_id[:-2] + version = RpmVersion(version_id) + if operator == '>': + # distutils will allow a prefix match with '>' + operator = '>=' + if operator == '<=': + # distutils will not allow a prefix match with '<=' + operator = '<' + else: + version = RpmVersion(version_id) + # For backwards compatibility, fallback to previous behavior with LegacyVersions + if not version.is_legacy(): + # Prevent dev and pre-releases from satisfying a < requirement + if operator == '<' and not version.pre and not version.dev and not version.post: + version = '{}~~'.format(version) + # Prevent post-releases from satisfying a > requirement + if operator == '>' and not version.pre and not version.dev and not version.post: + version = '{}.0'.format(version) + return '{} {} {}'.format(name, operator, version) + +OPERATORS = {'~=': convert_compatible, + '==': convert_equal, + '===': convert_arbitrary_equal, + '!=': convert_not_equal, + '<=': convert_ordered, + '<': convert_ordered, + '>=': convert_ordered, + '>': convert_ordered} + +def convert(name, operator, version_id): + return OPERATORS[operator](name, operator, version_id) + +def convert_requirement(req): + parsed_req = Requirement.parse(req) + reqs = [] + for spec in parsed_req.specs: + reqs.append(convert(parsed_req.project_name, spec[0], spec[1])) + if len(reqs) == 0: + return parsed_req.project_name + if len(reqs) == 1: + return reqs[0] + else: + reqs.sort() + return '({})'.format(' with '.join(reqs)) |