Skip to content

Commit

Permalink
Add version class for pacman
Browse files Browse the repository at this point in the history
Signed-off-by: Shivam Sandbhor <[email protected]>
  • Loading branch information
sbs2001 committed Apr 16, 2021
1 parent c50b20c commit 63bd5ae
Show file tree
Hide file tree
Showing 6 changed files with 274 additions and 65 deletions.
144 changes: 144 additions & 0 deletions src/univers/arch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
# Copyright 2016-2020 Christoph Reiter
# SPDX-License-Identifier: MIT

import re
from itertools import zip_longest
from typing import List, Tuple, Optional, Dict, Set


def vercmp(v1: str, v2: str) -> int:
def cmp(a: int, b: int) -> int:
return (a > b) - (a < b)

def split(v: str) -> Tuple[str, str, Optional[str]]:
if ":" in v:
e, v = v.split(":", 1)
else:
e, v = ("0", v)

r: Optional[str] = None
if "-" in v:
v, r = v.rsplit("-", 1)
else:
v, r = (v, None)

return (e, v, r)

digit, alpha, other = range(3)

def get_type(c: str) -> int:
assert c
if c.isdigit():
return digit
elif c.isalpha():
return alpha
else:
return other

def parse(v: str) -> List[str]:
parts: List[str] = []
current = ""
for c in v:
if not current:
current += c
else:
if get_type(c) == get_type(current):
current += c
else:
parts.append(current)
current = c

if current:
parts.append(current)

return parts

def rpmvercmp(v1: str, v2: str) -> int:
for p1, p2 in zip_longest(parse(v1), parse(v2), fillvalue=None):
if p1 is None:
if get_type(p2) == alpha:
return 1
return -1
elif p2 is None:
if get_type(p1) == alpha:
return -1
return 1

t1 = get_type(p1)
t2 = get_type(p2)
if t1 != t2:
if t1 == digit:
return 1
elif t2 == digit:
return -1
elif t1 == other:
return 1
elif t2 == other:
return -1
elif t1 == other:
ret = cmp(len(p1), len(p2))
if ret != 0:
return ret
elif t1 == digit:
ret = cmp(int(p1), int(p2))
if ret != 0:
return ret
elif t1 == alpha:
ret = cmp(p1, p2)
if ret != 0:
return ret

return 0

e1, v1, r1 = split(v1)
e2, v2, r2 = split(v2)

ret = rpmvercmp(e1, e2)
if ret == 0:
ret = rpmvercmp(v1, v2)
if ret == 0 and r1 is not None and r2 is not None:
ret = rpmvercmp(r1, r2)

return ret


def extract_upstream_version(version: str) -> str:
return version.rsplit("-")[0].split("+", 1)[0].split("~", 1)[-1].split(":", 1)[-1]


def strip_vcs(package_name: str) -> str:
if package_name.endswith(("-cvs", "-svn", "-hg", "-darcs", "-bzr", "-git")):
return package_name.rsplit("-", 1)[0]
return package_name


def arch_version_to_msys(v: str) -> str:
return v.replace(":", "~")


def version_is_newer_than(v1: str, v2: str) -> bool:
return vercmp(v1, v2) == 1


def split_depends(deps: List[str]) -> Dict[str, Set[str]]:
r: Dict[str, Set[str]] = {}
for d in deps:
parts = re.split("([<>=]+)", d, 1)
first = parts[0].strip()
second = "".join(parts[1:]).strip()
r.setdefault(first, set()).add(second)
return r


def split_optdepends(deps: List[str]) -> Dict[str, Set[str]]:
r: Dict[str, Set[str]] = {}
for d in deps:
if ":" in d:
a, b = d.split(":", 1)
a, b = a.strip(), b.strip()
else:
a, b = d.strip(), ""
e = r.setdefault(a, set())
if b:
e.add(b)
return r
12 changes: 12 additions & 0 deletions src/univers/arch.py.ABOUT
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
about_resource: arch.py
package_url: pkg:github/msys2/msys2-web@3ed2dde45a8761523b749e42c201a737c7613e6e
copyright: |
Copyright 2016-2020 Christoph Reiter

license_expression: MIT
homepage_url: https://github.com/msys2/msys2-web/

notes: |
The version comparision utility is extracted from msys2 and further stripped down.

notice_file: arch.py.NOTICE
21 changes: 21 additions & 0 deletions src/univers/arch.py.NOTICE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
The MIT License

Copyright (c) 2017 Christoph Reiter

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.
24 changes: 24 additions & 0 deletions src/univers/versions.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from univers.rpm import vercmp as rpm_vercmp
from univers.gentoo import vercmp as gentoo_vercmp
from univers.gentoo import parse_version_and_revision as parse_gentoo_version_and_revision
from univers.arch import vercmp as arch_vercmp


class InvalidVersion(ValueError):
Expand Down Expand Up @@ -118,6 +119,29 @@ def __lt__(self, other):
return self.value.__lt__(other.value)


@attr.s(frozen=True, init=False, order=False, eq=False, hash=True, repr=False)
@total_ordering
class ArchVersion(BaseVersion):
scheme = "arch"

def __init__(self, version_string):
version_string = version_string.lower()
version_string = remove_spaces(version_string)
object.__setattr__(self, "version_string", version_string)
object.__setattr__(self, "value", version_string)

@staticmethod
def validate(version_string):
pass

def __eq__(self, other):
# TBD: Should this verify the type of `other`
return arch_vercmp(self.value, other.value) == 0

def __lt__(self, other):
return arch_vercmp(self.value, other.value) == -1


@total_ordering
@attr.s(frozen=True, init=False, order=False, eq=False, hash=True, repr=False)
class DebianVersion(BaseVersion):
Expand Down
129 changes: 67 additions & 62 deletions tests/test_pacman_vercmp.py
Original file line number Diff line number Diff line change
@@ -1,91 +1,96 @@
from univers.versions import RPMVersion
from univers.versions import ArchVersion

# all similar length, no pkgrel
def test_same_length():
assert RPMVersion("1.5.0") == RPMVersion("1.5.0")
assert RPMVersion("1.5.1") > RPMVersion("1.5.0")
assert ArchVersion("1.5.0") == ArchVersion("1.5.0")
assert ArchVersion("1.5.1") > ArchVersion("1.5.0")


# mixed length
def test_mixed_length():
assert RPMVersion("1.5.1") > RPMVersion("1.5")
assert ArchVersion("1.5.1") > ArchVersion("1.5")


# with pkgrel, simple
def test_with_pkgrel_same_length():
assert RPMVersion("1.5.0-1") == RPMVersion("1.5.0-1")
assert RPMVersion("1.5.0-1") < RPMVersion("1.5.0-2")
assert RPMVersion("1.5.0-1") < RPMVersion("1.5.1-1")
assert RPMVersion("1.5.0-2") < RPMVersion("1.5.1-1")
assert ArchVersion("1.5.0-1") == ArchVersion("1.5.0-1")
assert ArchVersion("1.5.0-1") < ArchVersion("1.5.0-2")
assert ArchVersion("1.5.0-1") < ArchVersion("1.5.1-1")
assert ArchVersion("1.5.0-2") < ArchVersion("1.5.1-1")


# going crazy? alpha-dotted versions
def test_alpha_dotted():
assert RPMVersion("1.5.a") > RPMVersion("1.5 ")
assert RPMVersion("1.5.b") > RPMVersion("1.5.a")
assert RPMVersion("1.5.1") > RPMVersion("1.5.b")
assert ArchVersion("1.5.a") > ArchVersion("1.5")
assert ArchVersion("1.5.b") > ArchVersion("1.5.a")
assert ArchVersion("1.5.1") > ArchVersion("1.5.b")


# epoch included version comparisons
def test_with_epoch():
assert RPMVersion("0:1.0") == RPMVersion("0:1.0")
assert RPMVersion("0:1.0") < RPMVersion("0:1.1")
assert RPMVersion("1:1.0") > RPMVersion("0:1.0")
assert RPMVersion("1:1.0") > RPMVersion("0:1.1")
assert RPMVersion("1:1.0") < RPMVersion("2:1.1")
assert ArchVersion("0:1.0") == ArchVersion("0:1.0")
assert ArchVersion("0:1.0") < ArchVersion("0:1.1")
assert ArchVersion("1:1.0") > ArchVersion("0:1.0")
assert ArchVersion("1:1.0") > ArchVersion("0:1.1")
assert ArchVersion("1:1.0") < ArchVersion("2:1.1")


# epoch + sometimes present pkgrel
def test_with_epoch_mixed_pkgrel():
assert RPMVersion("1:1.0") > RPMVersion("0:1.0-1")
assert RPMVersion("1:1.0-1") > RPMVersion("0:1.1-1")
assert ArchVersion("1:1.0") > ArchVersion("0:1.0-1")
assert ArchVersion("1:1.0-1") > ArchVersion("0:1.1-1")


# # epoch included on one version
# def test_with_only_one_version_with_epoch():
# assert RPMVersion("0:1.0") == RPMVersion("1.0")
# assert RPMVersion("0:1.0") < RPMVersion("1.1")
# assert RPMVersion("0:1.1") > RPMVersion("1.0")
# assert RPMVersion("1:1.0") > RPMVersion("1.0")
# assert RPMVersion("1:1.0") > RPMVersion("1.1")
# assert RPMVersion("1:1.1") > RPMVersion("1.1")
# epoch included on one version
def test_with_only_one_version_with_epoch():
assert ArchVersion("0:1.0") == ArchVersion("1.0")
assert ArchVersion("0:1.0") < ArchVersion("1.1")
assert ArchVersion("0:1.1") > ArchVersion("1.0")
assert ArchVersion("1:1.0") > ArchVersion("1.0")
assert ArchVersion("1:1.0") > ArchVersion("1.1")
assert ArchVersion("1:1.1") > ArchVersion("1.1")


# alpha dots and dashes
def test_alpha_dot_dash():
assert ArchVersion("1.5.b-1") == ArchVersion("1.5.b")
assert ArchVersion("1.5-1") < ArchVersion("1.5.b")

# # alpha dots and dashes
# def test_alpha_dot_dash():
# assert RPMVersion("1.5.b-1") == RPMVersion("1.5.b")
# assert RPMVersion("1.5-1") < RPMVersion("1.5.b")

# # same/similar content, differing separators
# def test_same_content_different_separators():
# assert RPMVersion("2.0") == RPMVersion("2_0")
# assert RPMVersion("2.0_a") == RPMVersion("2_0.a")
# assert RPMVersion("2.0a ") < RPMVersion("2.0.a")
# assert RPMVersion("2___a") == RPMVersion("2_a")

# # with pkgrel, mixed lengths
# def test_with_pkgrel_mixed_length():
# assert RPMVersion("1.5-1") < RPMVersion("1.5.1-1")
# assert RPMVersion("1.5-2") < RPMVersion("1.5.1-1")
# assert RPMVersion("1.5-2") < RPMVersion("1.5.1-2")

# # mixed pkgrel inclusion
# def test_with_pkgrel_mixed():
# assert RPMVersion("1.5") == RPMVersion("1.5-1")
# assert RPMVersion("1.5-1") == RPMVersion("1.5 ")
# assert RPMVersion("1.1-1") == RPMVersion("1.1 ")
# assert RPMVersion("1.0-1") < RPMVersion("1.1 ")
# assert RPMVersion("1.1-1") > RPMVersion("1.0 ")

# # alphanumeric versions
# assert RPMVersion("1.5b-1") < RPMVersion("1.5-1")
# assert RPMVersion("1.5b ") < RPMVersion("1.5 ")
# assert RPMVersion("1.5b-1") < RPMVersion("1.5 ")
# assert RPMVersion("1.5b ") < RPMVersion("1.5.1")

# # from the manpage
# def test_manpage_cases():
# assert RPMVersion("1.0a") < RPMVersion("1.0alpha")
# assert RPMVersion("1.0alpha") < RPMVersion("1.0b")
# assert RPMVersion("1.0b") < RPMVersion("1.0beta")
# assert RPMVersion("1.0beta") < RPMVersion("1.0rc")
# assert RPMVersion("1.0rc") < RPMVersion("1.0")
# assert ArchVersion("2.0") == ArchVersion("2_0")
# assert ArchVersion("2.0_a") == ArchVersion("2_0.a")
# assert ArchVersion("2.0a ") < ArchVersion("2.0.a")
# assert ArchVersion("2___a") == ArchVersion("2_a")


# with pkgrel, mixed lengths
def test_with_pkgrel_mixed_length():
assert ArchVersion("1.5-1") < ArchVersion("1.5.1-1")
assert ArchVersion("1.5-2") < ArchVersion("1.5.1-1")
assert ArchVersion("1.5-2") < ArchVersion("1.5.1-2")


# mixed pkgrel inclusion
def test_with_pkgrel_mixed():
assert ArchVersion("1.5") == ArchVersion("1.5-1")
assert ArchVersion("1.5-1") == ArchVersion("1.5")
assert ArchVersion("1.1-1") == ArchVersion("1.1")
assert ArchVersion("1.0-1") < ArchVersion("1.1")
assert ArchVersion("1.1-1") > ArchVersion("1.0")


# alphanumeric versions
assert ArchVersion("1.5b-1") < ArchVersion("1.5-1")
assert ArchVersion("1.5b ") < ArchVersion("1.5 ")
assert ArchVersion("1.5b-1") < ArchVersion("1.5 ")
assert ArchVersion("1.5b ") < ArchVersion("1.5.1")

# from the manpage
def test_manpage_cases():
assert ArchVersion("1.0a") < ArchVersion("1.0alpha")
assert ArchVersion("1.0alpha") < ArchVersion("1.0b")
assert ArchVersion("1.0b") < ArchVersion("1.0beta")
assert ArchVersion("1.0beta") < ArchVersion("1.0rc")
assert ArchVersion("1.0rc") < ArchVersion("1.0")
9 changes: 6 additions & 3 deletions tests/test_pacman_vercmp.py.ABOUT
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
about_resource: test_pacman_vercmp.py
license_expression: apache-2.0
download_url: https://git.archlinux.org/pacman.git/plain/test/util/vercmptest.sh
copyright: Copyright (c) 2009-2020 by Pacman Development Team
copyright: |
Copyright (c) 2009-2020 by Pacman Development Team
Copyright (c) 2008 by Dan McGee <[email protected]>

package_url: pkg:pypi/[email protected]#tests/test_versioning.py
notes: this subset of tests has been modified to tests version comparison and parsing
homepage_url: https://github.com/nexB/pymaven
notice_file: test_maven_version.py.NOTICE
homepage_url: https://git.archlinux.org/pacman.git
notice_file: test_pacman_vercmp.py.NOTICE

0 comments on commit 63bd5ae

Please sign in to comment.