1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
|
import re
from .empty_constraint import EmptyConstraint
from .patterns import (
BASIC_CONSTRAINT,
CARET_CONSTRAINT,
TILDE_CONSTRAINT,
TILDE_PEP440_CONSTRAINT,
X_CONSTRAINT,
)
from .version import Version
from .version_constraint import VersionConstraint
from .version_range import VersionRange
from .version_union import VersionUnion
__version__ = "0.1.0"
def parse_constraint(constraints: str) -> VersionConstraint:
if constraints == "*":
return VersionRange()
or_constraints = re.split(r"\s*\|\|?\s*", constraints.strip())
or_groups = []
for constraints in or_constraints:
and_constraints = re.split("(?<!^)(?<![=>< ,]) *(?<!-)[, ](?!-) *(?!,|$)", constraints)
constraint_objects = []
if len(and_constraints) > 1:
for constraint in and_constraints:
constraint_objects.append(parse_single_constraint(constraint))
else:
constraint_objects.append(parse_single_constraint(and_constraints[0]))
if len(constraint_objects) == 1:
constraint = constraint_objects[0]
else:
constraint = constraint_objects[0]
for next_constraint in constraint_objects[1:]:
constraint = constraint.intersect(next_constraint)
or_groups.append(constraint)
if len(or_groups) == 1:
return or_groups[0]
else:
return VersionUnion.of(*or_groups)
def parse_single_constraint(constraint: str) -> VersionConstraint:
m = re.match(r"(?i)^v?[xX*](\.[xX*])*$", constraint)
if m:
return VersionRange()
# Tilde range
m = TILDE_CONSTRAINT.match(constraint)
if m:
version = Version.parse(m.group(1))
high = version.stable.next_minor
if len(m.group(1).split(".")) == 1:
high = version.stable.next_major
return VersionRange(version, high, include_min=True, always_include_max_prerelease=True)
# PEP 440 Tilde range (~=)
m = TILDE_PEP440_CONSTRAINT.match(constraint)
if m:
precision = 1
if m.group(3):
precision += 1
if m.group(4):
precision += 1
version = Version.parse(m.group(1))
if precision == 2:
low = version
high = version.stable.next_major
else:
low = Version(version.major, version.minor, version.patch)
high = version.stable.next_minor
return VersionRange(low, high, include_min=True, always_include_max_prerelease=True)
# Caret range
m = CARET_CONSTRAINT.match(constraint)
if m:
version = Version.parse(m.group(1))
return VersionRange(
version,
version.next_breaking,
include_min=True,
always_include_max_prerelease=True,
)
# X Range
m = X_CONSTRAINT.match(constraint)
if m:
op = m.group(1)
major = int(m.group(2))
minor = m.group(3)
if minor is not None:
version = Version(major, int(minor), 0)
result = VersionRange(
version,
version.next_minor,
include_min=True,
always_include_max_prerelease=True,
)
else:
if major == 0:
result = VersionRange(max=Version(1, 0, 0))
else:
version = Version(major, 0, 0)
result = VersionRange(
version,
version.next_major,
include_min=True,
always_include_max_prerelease=True,
)
if op == "!=":
result = VersionRange().difference(result)
return result
# Basic comparator
m = BASIC_CONSTRAINT.match(constraint)
if m:
op = m.group(1)
version = m.group(2)
if version == "dev":
version = "0.0-dev"
try:
version = Version.parse(version)
except ValueError:
raise ValueError("Could not parse version constraint: {}".format(constraint))
if op == "<":
return VersionRange(max=version)
elif op == "<=":
return VersionRange(max=version, include_max=True)
elif op == ">":
return VersionRange(min=version)
elif op == ">=":
return VersionRange(min=version, include_min=True)
elif op == "!=":
return VersionUnion(VersionRange(max=version), VersionRange(min=version))
else:
return version
raise ValueError("Could not parse version constraint: {}".format(constraint))
|