Skip to content

Commit 77cada3

Browse files
abnradoering
authored andcommitted
wip: pypa/packaging compliance
1 parent ed8c17d commit 77cada3

File tree

14 files changed

+484
-123
lines changed

14 files changed

+484
-123
lines changed

src/poetry/core/constraints/version/parser.py

+72-21
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,21 @@
66

77

88
if TYPE_CHECKING:
9+
from poetry.core.constraints.version.version import Version
910
from poetry.core.constraints.version.version_constraint import VersionConstraint
1011

1112

1213
def parse_constraint(constraints: str) -> VersionConstraint:
14+
return _parse_constraint(constraints=constraints)
15+
16+
17+
def parse_marker_version_constraint(constraints: str) -> VersionConstraint:
18+
return _parse_constraint(constraints=constraints, is_marker_constraint=True)
19+
20+
21+
def _parse_constraint(
22+
constraints: str, is_marker_constraint: bool = False
23+
) -> VersionConstraint:
1324
if constraints == "*":
1425
from poetry.core.constraints.version.version_range import VersionRange
1526

@@ -28,9 +39,13 @@ def parse_constraint(constraints: str) -> VersionConstraint:
2839

2940
if len(and_constraints) > 1:
3041
for constraint in and_constraints:
31-
constraint_objects.append(parse_single_constraint(constraint))
42+
constraint_objects.append(
43+
parse_single_constraint(constraint, is_marker_constraint)
44+
)
3245
else:
33-
constraint_objects.append(parse_single_constraint(and_constraints[0]))
46+
constraint_objects.append(
47+
parse_single_constraint(and_constraints[0], is_marker_constraint)
48+
)
3449

3550
if len(constraint_objects) == 1:
3651
constraint = constraint_objects[0]
@@ -49,7 +64,9 @@ def parse_constraint(constraints: str) -> VersionConstraint:
4964
return VersionUnion.of(*or_groups)
5065

5166

52-
def parse_single_constraint(constraint: str) -> VersionConstraint:
67+
def parse_single_constraint(
68+
constraint: str, is_marker_constraint: bool = False
69+
) -> VersionConstraint:
5370
from poetry.core.constraints.version.patterns import BASIC_CONSTRAINT
5471
from poetry.core.constraints.version.patterns import CARET_CONSTRAINT
5572
from poetry.core.constraints.version.patterns import TILDE_CONSTRAINT
@@ -95,26 +112,15 @@ def parse_single_constraint(constraint: str) -> VersionConstraint:
95112
m = X_CONSTRAINT.match(constraint)
96113
if m:
97114
op = m.group("op")
98-
major = int(m.group(2))
99-
minor = m.group(3)
100115

101-
if minor is not None:
102-
version = Version.from_parts(major, int(minor), 0)
103-
result: VersionConstraint = VersionRange(
104-
version, version.next_minor(), include_min=True
116+
try:
117+
return _make_x_constraint_range(
118+
version=Version.parse(m.group("version")),
119+
invert=op == "!=",
120+
is_marker_constraint=is_marker_constraint,
105121
)
106-
else:
107-
if major == 0:
108-
result = VersionRange(max=Version.from_parts(1, 0, 0))
109-
else:
110-
version = Version.from_parts(major, 0, 0)
111-
112-
result = VersionRange(version, version.next_major(), include_min=True)
113-
114-
if op == "!=":
115-
result = VersionRange().difference(result)
116-
117-
return result
122+
except ValueError:
123+
raise ValueError(f"Could not parse version constraint: {constraint}")
118124

119125
# Basic comparator
120126
m = BASIC_CONSTRAINT.match(constraint)
@@ -138,10 +144,55 @@ def parse_single_constraint(constraint: str) -> VersionConstraint:
138144
return VersionRange(min=version)
139145
if op == ">=":
140146
return VersionRange(min=version, include_min=True)
147+
148+
if m.group("wildcard") is not None:
149+
return _make_x_constraint_range(
150+
version=version,
151+
invert=op == "!=",
152+
is_marker_constraint=is_marker_constraint,
153+
)
154+
141155
if op == "!=":
142156
return VersionUnion(VersionRange(max=version), VersionRange(min=version))
157+
143158
return version
144159

145160
from poetry.core.constraints.version.exceptions import ParseConstraintError
146161

147162
raise ParseConstraintError(f"Could not parse version constraint: {constraint}")
163+
164+
165+
def _make_x_constraint_range(
166+
version: Version, invert: bool = False, is_marker_constraint: bool = False
167+
) -> VersionConstraint:
168+
from poetry.core.constraints.version.version_range import VersionRange
169+
170+
_is_zero_major = version.major == 0 and version.precision == 1
171+
172+
if version.is_postrelease():
173+
_next = version.next_postrelease()
174+
_is_zero_major = False
175+
elif version.is_stable():
176+
_next = version.next_stable()
177+
elif version.is_prerelease():
178+
_next = version.next_prerelease()
179+
_is_zero_major = False
180+
elif version.is_devrelease():
181+
_next = version.next_devrelease()
182+
_is_zero_major = False
183+
184+
if _is_zero_major:
185+
result = VersionRange(max=_next.with_precision(3))
186+
else:
187+
_min = version.with_precision(max(version.precision, 3))
188+
189+
if not is_marker_constraint and not _next.is_unstable():
190+
_min = _min.next_devrelease()
191+
192+
_max = _next.with_precision(max(version.precision, 3))
193+
result = VersionRange(_min, _max, include_min=True)
194+
195+
if invert:
196+
return VersionRange().difference(result)
197+
198+
return result

src/poetry/core/constraints/version/patterns.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@
1717
rf"^~=\s*(?P<version>{VERSION_PATTERN})$", re.VERBOSE | re.IGNORECASE
1818
)
1919
X_CONSTRAINT = re.compile(
20-
r"^(?P<op>!=|==)?\s*v?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.[xX*])+$"
20+
r"^(?P<op>!=|==)?\s*v?(?P<version>(\d+)(?:\.(\d+))?(?:\.(\d+))?)(?:\.[xX*])+$"
2121
)
2222

2323
# note that we also allow technically incorrect version patterns with astrix (eg: 3.5.*)
2424
# as this is supported by pip and appears in metadata within python packages
2525
BASIC_CONSTRAINT = re.compile(
26-
rf"^(?P<op><>|!=|>=?|<=?|==?)?\s*(?P<version>{VERSION_PATTERN}|dev)(\.\*)?$",
26+
rf"^(?P<op><>|!=|>=?|<=?|==?)?\s*(?P<version>{VERSION_PATTERN}|dev)(?P<wildcard>\.\*)?$",
2727
re.VERBOSE | re.IGNORECASE,
2828
)

src/poetry/core/constraints/version/version_range.py

+18-27
Original file line numberDiff line numberDiff line change
@@ -21,22 +21,9 @@ def __init__(
2121
max: Version | None = None,
2222
include_min: bool = False,
2323
include_max: bool = False,
24-
always_include_max_prerelease: bool = False,
2524
) -> None:
26-
full_max = max
27-
if (
28-
not always_include_max_prerelease
29-
and not include_max
30-
and full_max is not None
31-
and full_max.is_stable()
32-
and not full_max.is_postrelease()
33-
and (min is None or min.is_stable() or min.release != full_max.release)
34-
):
35-
full_max = full_max.first_prerelease()
36-
37-
self._min = min
3825
self._max = max
39-
self._full_max = full_max
26+
self._min = min
4027
self._include_min = include_min
4128
self._include_max = include_max
4229

@@ -48,10 +35,6 @@ def min(self) -> Version | None:
4835
def max(self) -> Version | None:
4936
return self._max
5037

51-
@property
52-
def full_max(self) -> Version | None:
53-
return self._full_max
54-
5538
@property
5639
def include_min(self) -> bool:
5740
return self._include_min
@@ -71,27 +54,35 @@ def is_simple(self) -> bool:
7154

7255
def allows(self, other: Version) -> bool:
7356
if self._min is not None:
74-
if other < self._min:
57+
_this, _other = self.allowed_min, other
58+
59+
assert _this is not None
60+
61+
if not _this.is_postrelease() and _other.is_postrelease():
62+
_other = _other.without_postrelease()
63+
64+
if not _this.is_local() and _other.is_local():
65+
_other = other.without_local()
66+
67+
if _other < _this:
7568
return False
7669

77-
if not self._include_min and other == self._min:
70+
if not self._include_min and (_other == self._min or _other == _this):
7871
return False
7972

80-
if self.full_max is not None:
81-
_this, _other = self.full_max, other
73+
if self.max is not None:
74+
_this, _other = self.allowed_max, other
75+
76+
assert _this is not None
8277

8378
if not _this.is_local() and _other.is_local():
8479
# allow weak equality to allow `3.0.0+local.1` for `<=3.0.0`
8580
_other = _other.without_local()
8681

87-
if not _this.is_postrelease() and _other.is_postrelease():
88-
# allow weak equality to allow `3.0.0-1` for `<=3.0.0`
89-
_other = _other.without_postrelease()
90-
9182
if _other > _this:
9283
return False
9384

94-
if not self._include_max and _other == _this:
85+
if not self._include_max and (_other == self._max or _other == _this):
9586
return False
9687

9788
return True

src/poetry/core/constraints/version/version_range_constraint.py

+47-18
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,6 @@ def min(self) -> Version | None:
2121
def max(self) -> Version | None:
2222
raise NotImplementedError()
2323

24-
@property
25-
@abstractmethod
26-
def full_max(self) -> Version | None:
27-
raise NotImplementedError()
28-
2924
@property
3025
@abstractmethod
3126
def include_min(self) -> bool:
@@ -36,44 +31,78 @@ def include_min(self) -> bool:
3631
def include_max(self) -> bool:
3732
raise NotImplementedError()
3833

39-
def allows_lower(self, other: VersionRangeConstraint) -> bool:
34+
@property
35+
def allowed_min(self) -> Version | None:
4036
if self.min is None:
41-
return other.min is not None
37+
return None
38+
39+
if not self.include_min or self.min.is_unstable():
40+
return self.min
41+
42+
if self.min == self.max and (self.include_min or self.include_max):
43+
# this is an equality range
44+
return self.min
4245

43-
if other.min is None:
46+
return self.min
47+
48+
@property
49+
def allowed_max(self) -> Version | None:
50+
if self.max is None:
51+
return None
52+
53+
if self.include_max or self.max.is_unstable():
54+
return self.max
55+
56+
if self.min == self.max and (self.include_min or self.include_max):
57+
# this is an equality range
58+
return self.max
59+
60+
return self.max.next_devrelease()
61+
62+
def allows_lower(self, other: VersionRangeConstraint) -> bool:
63+
_this, _other = self.allowed_min, other.allowed_min
64+
65+
if _this is None:
66+
return _other is not None
67+
68+
if _other is None:
4469
return False
4570

46-
if self.min < other.min:
71+
if _this < _other:
4772
return True
4873

49-
if self.min > other.min:
74+
if _this > _other:
5075
return False
5176

5277
return self.include_min and not other.include_min
5378

5479
def allows_higher(self, other: VersionRangeConstraint) -> bool:
55-
if self.full_max is None:
56-
return other.max is not None
80+
_this, _other = self.allowed_max, other.allowed_max
5781

58-
if other.full_max is None:
82+
if _this is None:
83+
return _other is not None
84+
85+
if _other is None:
5986
return False
6087

61-
if self.full_max < other.full_max:
88+
if _this < _other:
6289
return False
6390

64-
if self.full_max > other.full_max:
91+
if _this > _other:
6592
return True
6693

6794
return self.include_max and not other.include_max
6895

6996
def is_strictly_lower(self, other: VersionRangeConstraint) -> bool:
70-
if self.full_max is None or other.min is None:
97+
_this, _other = self.allowed_max, other.allowed_min
98+
99+
if _this is None or _other is None:
71100
return False
72101

73-
if self.full_max < other.min:
102+
if _this < _other:
74103
return True
75104

76-
if self.full_max > other.min:
105+
if _this > _other:
77106
return False
78107

79108
return not self.include_max or not other.include_min

0 commit comments

Comments
 (0)