#103 Import pyreq2rpm requirement conversion functions
Closed 3 years ago by gordonmessmer. Opened 3 years ago by gordonmessmer.
rpms/ gordonmessmer/pyproject-rpm-macros master  into  master

file modified
+3 -9
@@ -10,6 +10,7 @@ 

  import re

  import tempfile

  import email.parser

+ from pyproject_convert import convert

  

  print_err = functools.partial(print, file=sys.stderr)

  
@@ -95,20 +96,13 @@ 

                      f'Unknown character in version: {specifier.version}. '

                      + '(This is probably a bug in pyproject-rpm-macros.)',

                  )

-             if specifier.operator == '!=':

-                 lower = python3dist(name, '<', version)

-                 higher = python3dist(name, '>', f'{version}.0')

-                 together.append(

-                     f'({lower} or {higher})'

-                 )

-             else:

-                 together.append(python3dist(name, specifier.operator, version))

+             together.append(convert(python3dist(name), specifier.operator, version))

          if len(together) == 0:

              print(python3dist(name))

          elif len(together) == 1:

              print(together[0])

          else:

-             print(f"({' and '.join(together)})")

+             print(f"({' with '.join(together)})")

  

      def check(self, *, source=None):

          """End current pass if any unsatisfied dependencies were output"""

@@ -87,12 +87,12 @@ 

      ]

    expected: |

      python3dist(foo)

-     (python3dist(ne) < 1 or python3dist(ne) > 1.0)

+     (python3dist(ne) < 1 or python3dist(ne) > 1)

      python3dist(ge) >= 1.2

      python3dist(le) <= 1.2.3

      python3dist(lt) < 1.2.3.4

      python3dist(gt) > 1.2.3.4.5

-     ((python3dist(combo) < 3 or python3dist(combo) > 3.0) and python3dist(combo) < 5 and python3dist(combo) > 2)

+     ((python3dist(combo) < 3 or python3dist(combo) > 3) with python3dist(combo) < 5 with python3dist(combo) > 2)

      python3dist(py3)

      python3dist(pkg)

      python3dist(setuptools) >= 40.8
@@ -108,7 +108,7 @@ 

      setup(

          name='test',

          version='0.1',

-         setup_requires=['foo', 'bar!=2'],

+         setup_requires=['foo', 'bar!=2', 'baz~=1.1.1'],

          install_requires=['inst'],

      )

    expected: |
@@ -116,7 +116,8 @@ 

      python3dist(wheel)

      python3dist(wheel)

      python3dist(foo)

-     (python3dist(bar) < 2 or python3dist(bar) > 2.0)

+     (python3dist(bar) < 2 or python3dist(bar) > 2)

+     (python3dist(baz) >= 1.1.1 with python3dist(baz) < 1.2)

    result: 0

  

  Default build system, run dependencies in setup.py:

file added
+141
@@ -0,0 +1,141 @@ 

+ #!/usr/bin/env python3

+ 

+ # Copyright 2019 Gordon Messmer <gordon.messmer@gmail.com>

+ #

+ # 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 pkg_resources import Requirement, 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

+ 

+     def increment(self):

+         self.version[-1] += 1

+         self.pre = None

+         self.dev = None

+         self.post = None

+         return self

+ 

+     def __str__(self):

+         if isinstance(self.version, str):

+             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 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)

+         lower_version = RpmVersion(version_id).increment()

+     else:

+         version = RpmVersion(version_id)

+         lower_version = version

+     return '({} < {} or {} > {})'.format(

+         name, version, name, lower_version)

+ 

+ def convert_ordered(name, operator, version_id):

+     if version_id.endswith('.*'):

+         # PEP 440 does not define semantics for prefix matching

+         # with ordered comparisons

+         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)

+     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))

This change introduces code from pyreq2rpm, a tested set of
requirement conversion functions used in pyp2rpm and rpm's
pythondistdeps. This adds support for the '~=' operator and
wildcards.

Merge Failed.

This change or one of its cross-repo dependencies was unable to be automatically merged with the current state of its repository. Please rebase the change and upload a new patchset.

Pull-Request has been closed by gordonmessmer

3 years ago