Skip to content

Commit

Permalink
Add function to parse bracket notation constraints
Browse files Browse the repository at this point in the history
Signed-off-by: Keshav Priyadarshi <[email protected]>
  • Loading branch information
keshav-space committed Jul 24, 2024
1 parent c833b97 commit 8f0d727
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 27 deletions.
60 changes: 36 additions & 24 deletions src/univers/version_range.py
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,7 @@ def from_native(cls, string):
return cls(constraints=constraints)


def split_req(string, comparators, comparators_rear={}, default=None, strip=""):
def split_req(string, comparators, default=None, strip=""):
"""
Return a tuple of (vers comparator, version) strings given an common version
requirement``string`` such as "> 2.3" or "<= 2.3" using the ``comparators``
Expand All @@ -501,8 +501,6 @@ def split_req(string, comparators, comparators_rear={}, default=None, strip=""):
>>> assert split_req(">= 2.3", comparators=comps) == (">=", "2.3",)
>>> assert split_req("<= 2.3", comparators=comps) == ("<=", "2.3",)
>>> assert split_req("(< = 2.3 )", comparators=comps, strip=")(") == ("<=", "2.3",)
>>> comps_rear = {")": "<", "]": "<="}
>>> assert split_req(" 2.3 ]", comparators=comps, comparators_rear=comps_rear) == ("<=", "2.3",)
With a default, we return the default comparator::
Expand All @@ -523,12 +521,6 @@ def split_req(string, comparators, comparators_rear={}, default=None, strip=""):
version = constraint_string.lstrip(native_comparator)
return vers_comparator, version

# Some bracket notation comparators starts from end.
for native_comparator, vers_comparator in comparators_rear.items():
if constraint_string.endswith(native_comparator):
version = constraint_string.rstrip(native_comparator)
return vers_comparator, version

if default:
return default, constraint_string

Expand Down Expand Up @@ -1300,14 +1292,35 @@ def build_range_from_github_advisory_constraint(scheme: str, string: Union[str,
">=": ">=",
"<": "<",
">": ">",
"(": ">",
"[": ">=",
}

vers_by_snyk_native_comparators_rear = {
")": "<",
"]": "<=",
}

def split_req_bracket_notation(string):
"""
Return a tuple of (vers comparator, version) strings given an bracket notation
version requirement ``string`` such as "(2.3" or "3.9]"
For example::
>>> assert split_req_bracket_notation(" 2.3 ]") == ("<=", "2.3")
>>> assert split_req_bracket_notation("( 3.9") == (">", "3.9")
"""
comparators_front = {"(": ">", "[": ">="}
comparators_rear = {")": "<", "]": "<="}

constraint_string = remove_spaces(string).strip()

for native_comparator, vers_comparator in comparators_front.items():
if constraint_string.startswith(native_comparator):
version = constraint_string.lstrip(native_comparator)
return vers_comparator, version

for native_comparator, vers_comparator in comparators_rear.items():
if constraint_string.endswith(native_comparator):
version = constraint_string.rstrip(native_comparator)
return vers_comparator, version

raise ValueError(f"Unknown comparator in version requirement: {string!r} ")


def build_range_from_snyk_advisory_string(scheme: str, string: Union[str, List]):
Expand Down Expand Up @@ -1346,11 +1359,13 @@ def build_range_from_snyk_advisory_string(scheme: str, string: Union[str, List])
constraints = snyk_constraints.split(" ")

for constraint in constraints:
comparator, version = split_req(
string=constraint,
comparators=vers_by_snyk_native_comparators,
comparators_rear=vers_by_snyk_native_comparators_rear,
)
if any(comp in constraint for comp in "[]()"):
comparator, version = split_req_bracket_notation(string=constraint)
else:
comparator, version = split_req(
string=constraint,
comparators=vers_by_snyk_native_comparators,
)
if comparator and version:
version = vrc.version_class(version)
version_constraints.append(
Expand Down Expand Up @@ -1382,10 +1397,7 @@ def build_range_from_discrete_version_string(scheme: str, string: Union[str, Lis
string = [string]

for item in string:
version = item.strip().lstrip("vV")
if version == "0":
continue

version = item.strip()
version = vrc.version_class(version)
version_constraints.append(
VersionConstraint(
Expand Down
45 changes: 42 additions & 3 deletions tests/test_version_range.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
from univers.version_range import OpensslVersionRange
from univers.version_range import PypiVersionRange
from univers.version_range import VersionRange
from univers.version_range import build_range_from_discrete_version_string
from univers.version_range import build_range_from_snyk_advisory_string
from univers.version_range import from_gitlab_native
from univers.versions import InvalidVersion
from univers.versions import NugetVersion
Expand Down Expand Up @@ -85,6 +87,11 @@ def test_VersionRange_contains_version_in_between(self):
version_range = VersionRange.from_string(vers)
assert version_range.contains(PypiVersion("1.5"))

def test_VersionRange_contains_filterd_constraint_edge_case(self):
vers = "vers:pypi/<=1.3.0|3.0.0"
version_range = VersionRange.from_string(vers)
assert version_range.contains(PypiVersion("1.0.0"))

def test_VersionRange_from_string_pypi(self):
vers = "vers:pypi/0.0.2|0.0.6|0.0.0|0.0.1|0.0.4|0.0.5|0.0.3"
version_range = VersionRange.from_string(vers)
Expand Down Expand Up @@ -458,8 +465,40 @@ def test_mattermost_version_range():
) == VersionRange.from_string("vers:mattermost/>=5.0")


def test_build_range_from_snyk_advisory_string():
expression = [">=4.0.0, <4.0.10", ">7.0.0, <8.0.1"]
vr = build_range_from_snyk_advisory_string("pypi", expression)
expected = "vers:pypi/>=4.0.0|<4.0.10|>7.0.0|<8.0.1"

assert str(vr) == expected


def test_build_range_from_snyk_advisory_string_bracket():
expression = ["[3.0.0,3.1.25)", "[1.0.0,1.0.5)"]
vr = build_range_from_snyk_advisory_string("nuget", expression)
expected = "vers:nuget/>=1.0.0|<1.0.5|>=3.0.0|<3.1.25"

assert str(vr) == expected


def test_build_range_from_snyk_advisory_string_spaced():
expression = [">=4.1.0 <4.4.1", ">2.1.0 <=3.2.7"]
vr = build_range_from_snyk_advisory_string("composer", expression)
expected = "vers:composer/>2.1.0|<=3.2.7|>=4.1.0|<4.4.1"

assert str(vr) == expected


def test_build_range_from_discrete_version_string():
expression = ["4.1.0", " 4.4.1", "2.1.0 ", " 3.2.7 "]
vr = build_range_from_discrete_version_string("pypi", expression)
expected = "vers:pypi/2.1.0|3.2.7|4.1.0|4.4.1"

assert str(vr) == expected


def test_version_range_normalize_case1():
known_versions = ["3.0.0", "1.0.0", "2.0.0", "1.3.0", "1.1.0", "1.2.0"]
known_versions = ["4.0.0", "3.0.0", "1.0.0", "2.0.0", "1.3.0", "1.1.0", "1.2.0"]

vr = VersionRange.from_string("vers:pypi/<=1.1.0|>=1.2.0|<=1.3.0|3.0.0")
nvr = vr.normalize(known_versions=known_versions)
Expand All @@ -468,7 +507,7 @@ def test_version_range_normalize_case1():


def test_version_range_normalize_case2():
known_versions = ["3.0.0", "1.0.0", "2.0.0", "1.3.0", "1.1.0", "1.2.0"]
known_versions = ["4.0.0", "3.0.0", "1.0.0", "2.0.0", "1.3.0", "1.1.0", "1.2.0"]

vr = VersionRange.from_string("vers:pypi/<=1.3.0|3.0.0")
nvr = vr.normalize(known_versions=known_versions)
Expand All @@ -477,7 +516,7 @@ def test_version_range_normalize_case2():


def test_version_range_normalize_case3():
known_versions = ["3.0.0", "1.0.0", "2.0.0", "1.3.0", "1.1.0", "1.2.0"]
known_versions = ["4.0.0", "3.0.0", "1.0.0", "2.0.0", "1.3.0", "1.1.0", "1.2.0"]

vr = VersionRange.from_string("vers:pypi/<2.0.0|3.0.0")
nvr = vr.normalize(known_versions=known_versions)
Expand Down

0 comments on commit 8f0d727

Please sign in to comment.