pull/613/head
Tim Head 2019-03-07 23:53:01 +01:00
rodzic 6e2a718d15
commit b3451d3bc7
1 zmienionych plików z 75 dodań i 84 usunięć

Wyświetl plik

@ -1,15 +1,16 @@
# This file implements the julia-specific logic for handling SemVer (Semantic """
# Versioning) strings in .toml files. Julia specific handling of SemVer strings
#
# It uses the python "semver" package to do most version string comparisons, but It uses the python "semver" package to do most version string comparisons, but
# the places where julia's SemVer handling differs from the semver package have the places where julia's SemVer handling differs from the semver package have
# been implemented directly. been implemented directly.
# """
# Here, we use tuples to represent a Version, and functors as "matchers". The matcher
# functors take a version string and return true if it passes its constraints. # We use tuples to represent a Version, and functors as "matchers". The
# matcher functors take a version string and return True if it passes its
# constraints.
import re import re
from enum import Enum
import semver import semver
@ -29,70 +30,6 @@ def str_to_version(vstr):
return tuple([int(n) for n in vstr.split(".")]) return tuple([int(n) for n in vstr.split(".")])
# --- Matcher interface -------------------------------------------
class AbstractMatcher:
def match(self, v):
pass
class SemverMatcher(AbstractMatcher):
""" Match a version tuple to a given constraint_str using the `semver` package. """
def __init__(self, constraint_str):
self.constraint_str = constraint_str
def match(self, v):
while len(v) < 3:
v = v + (0,)
v_str = ".".join(map(str, v))
return semver.match(v_str, self.constraint_str)
def __eq__(self, rhs):
return self.constraint_str == rhs.constraint_str
def __repr__(self):
return self.constraint_str
# --- Custom matcher for julia-specific SemVer handling: ---------
class Exclusivity(Enum):
EXCLUSIVE = 1
INCLUSIVE = 2
class VersionRange(AbstractMatcher):
"""Match a version tuple between lower and upper bounds"""
def __init__(self, lower, upper, upper_exclusivity):
self.lower = lower
self.upper = upper
self.upper_exclusivity = upper_exclusivity
def match(self, v):
if self.upper_exclusivity == Exclusivity.EXCLUSIVE:
return self.lower <= v < self.upper
else:
return self.lower <= v <= self.upper
def __eq__(self, rhs):
return (
self.lower == rhs.lower
and self.upper == rhs.upper
and self.upper_exclusivity == rhs.upper_exclusivity
)
def __repr__(self):
return (
"["
+ ".".join(map(str, self.lower))
+ "-"
+ ".".join(map(str, self.upper))
+ (")" if self.upper_exclusivity == Exclusivity.EXCLUSIVE else "]")
)
# Helpers # Helpers
def major(v): def major(v):
return v[0] return v[0]
@ -106,13 +43,10 @@ def patch(v):
return v[2] if len(v) >= 3 else 0 return v[2] if len(v) >= 3 else 0
# --- main constraint parser function ------------------------------------
def create_semver_matcher(constraint_str): def create_semver_matcher(constraint_str):
""" """Create a matcher that can be used to match version tuples
Returns a derived-class instance of AbstractMatcher that matches version
tuples against the provided constraint_str. Version tuples are matched against the provided `constraint_str`.
""" """
constraint_str = constraint_str.strip() constraint_str = constraint_str.strip()
first_digit = re.search(r"\d", constraint_str) first_digit = re.search(r"\d", constraint_str)
@ -129,17 +63,18 @@ def create_semver_matcher(constraint_str):
# Also, julia treats pre-1.0 releases specially, as if the first # Also, julia treats pre-1.0 releases specially, as if the first
# non-zero number is actually a major number: # non-zero number is actually a major number:
# https://docs.julialang.org/en/latest/stdlib/Pkg/#Caret-specifiers-1 # https://docs.julialang.org/en/latest/stdlib/Pkg/#Caret-specifiers-1
# So we need to handle it separately by bumping the first non-zero number. # So we need to handle it separately by bumping the first non-zero
# enumber.
for i, n in enumerate(constraint): for i, n in enumerate(constraint):
if ( if (
n != 0 or i == len(constraint) - 1 n != 0 or i == len(constraint) - 1
): # (using the last existing number handles situations like "^0.0" or "^0") ): # (using the last existing number handles situations like "^0.0" or "^0")
upper = constraint[0:i] + (n + 1,) upper = constraint[0:i] + (n + 1,)
break break
return VersionRange(constraint, upper, Exclusivity.EXCLUSIVE) return VersionRange(constraint, upper, True)
else: else:
return VersionRange( return VersionRange(
constraint, (major(constraint) + 1,), Exclusivity.EXCLUSIVE constraint, (major(constraint) + 1,), True
) )
# '~' matching (only allowed to bump the last present number by one) # '~' matching (only allowed to bump the last present number by one)
@ -147,7 +82,7 @@ def create_semver_matcher(constraint_str):
return VersionRange( return VersionRange(
constraint, constraint,
constraint[:-1] + (constraint[-1] + 1,), constraint[:-1] + (constraint[-1] + 1,),
Exclusivity.INCLUSIVE, upper_exclusivity=False
) )
# Use semver package's comparisons for everything else: # Use semver package's comparisons for everything else:
@ -165,3 +100,59 @@ def create_semver_matcher(constraint_str):
constraint_str = re.sub(r"(^|\b)=\b", "==", constraint_str) constraint_str = re.sub(r"(^|\b)=\b", "==", constraint_str)
return SemverMatcher(constraint_str) return SemverMatcher(constraint_str)
class SemverMatcher:
"""Match a version tuple to a given `constraint_str`
The matching is handled via the `semver` package.
"""
def __init__(self, constraint_str):
self.constraint_str = constraint_str
def match(self, v):
while len(v) < 3:
v = v + (0,)
v_str = ".".join(map(str, v))
return semver.match(v_str, self.constraint_str)
def __eq__(self, rhs):
return self.constraint_str == rhs.constraint_str
def __repr__(self):
return self.constraint_str
class VersionRange:
"""Match a version tuple between lower and upper bounds
`upper_exclusivity` specifies if the upper bound is inclusive or exclusive.
"""
def __init__(self, lower, upper, upper_exclusivity):
self.lower = lower
self.upper = upper
self.upper_exclusivity = upper_exclusivity
def match(self, v):
if self.upper_exclusivity:
return self.lower <= v < self.upper
else:
return self.lower <= v <= self.upper
def __eq__(self, rhs):
return (
self.lower == rhs.lower
and self.upper == rhs.upper
and self.upper_exclusivity == rhs.upper_exclusivity
)
def __repr__(self):
return (
"["
+ ".".join(map(str, self.lower))
+ "-"
+ ".".join(map(str, self.upper))
+ (")" if self.upper_exclusivity else "]")
)