Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ Changelog
=========


Version v30.9.1
----------------

- Add invert function to VersionRange.


Version v30.9.0
----------------

Expand Down
22 changes: 22 additions & 0 deletions src/univers/version_constraint.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,28 @@ def __lt__(self, other):
# we compare tuples, version first
return (self.version, self.comparator).__lt__((other.version, other.comparator))

def is_star(self):
return self.comparator == "*"

def invert(self):
INVERTED_COMPARATORS = {
Comment thread
pombredanne marked this conversation as resolved.
">=": "<",
"<=": ">",
"!=": "=",
"<": ">=",
">": "<=",
"=": "!=",
}

if self.is_star():
return None

inverted_comparator = INVERTED_COMPARATORS[self.comparator]
return self.__class__(
comparator=inverted_comparator,
version=self.version,
)

@classmethod
def from_string(cls, string, version_class):
"""
Expand Down
31 changes: 31 additions & 0 deletions src/univers/version_range.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@ class InvalidVersionRange(Exception):
"""


INVERTED_COMPARATORS = {
Comment thread
TG1999 marked this conversation as resolved.
">=": "<",
"<=": ">",
"!=": "=",
"<": ">=",
">": "<=",
"=": "!=",
}


@attr.s(frozen=True, order=False, eq=True, hash=True)
class VersionRange:
"""
Expand Down Expand Up @@ -164,6 +174,27 @@ def from_versions(cls, sequence):
constraints.append(constraint)
return cls(constraints=constraints)

def is_star(self):
return len(self.constraints) == 1 and self.constraints[0].is_star()

def invert(self):
"""
Return the inverse or complement of this VersionRange. For example, if this range is
">=1.0.0", the inverse is "<1.0.0".
>>> str(VersionRange.from_string("vers:npm/>=1.0.0").invert())
'vers:npm/<1.0.0'
"""
inverted_constraints = []

if self.is_star():
# The inverse of "*" is an empty range.
return None

for constraint in self.constraints:
inverted_constraints.append(constraint.invert())

return self.__class__(constraints=inverted_constraints)

def __str__(self):
constraints = "|".join(str(c) for c in sorted(self.constraints))
return f"vers:{self.scheme}/{constraints}"
Expand Down
17 changes: 17 additions & 0 deletions tests/test_version_range.py
Original file line number Diff line number Diff line change
Expand Up @@ -393,3 +393,20 @@ def test_npm_advisory_version_range_parse(test_case):
string=test_case["npm_native"],
)
assert str(result) == test_case["expected_vers"]


def test_inverse():

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

vers_with_equal_operator = VersionRange.from_string("vers:gem/1.0")
assert str(vers_with_equal_operator.invert()) == "vers:gem/!=1.0"
assert VersionRange.from_string("vers:gem/!=1.0").invert() == vers_with_equal_operator

vers_with_less_than_operator = VersionRange.from_string("vers:gem/<1.0")
assert str(vers_with_less_than_operator.invert()) == "vers:gem/>=1.0"
assert VersionRange.from_string("vers:gem/>=1.0").invert() == vers_with_less_than_operator

vers_with_greater_than_operator = VersionRange.from_string("vers:gem/>1.0")
assert str(vers_with_greater_than_operator.invert()) == "vers:gem/<=1.0"
assert VersionRange.from_string("vers:gem/<=1.0").invert() == vers_with_greater_than_operator

vers_with_complex_constraints = VersionRange.from_string("vers:gem/<=1.0|>=3.0|<4.0|!=5.0")
assert str(vers_with_complex_constraints.invert()) == "vers:gem/>1.0|<3.0|>=4.0|5.0"