Commit 670d31c7 by Sébastien Eustace

Fix prereleases being selected when they should trigger a conflict

parent 76801fd2
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
### Fixed ### Fixed
- Fixed the `--no-dev` option in `install` not working properly. - Fixed the `--no-dev` option in `install` not working properly.
- Fixed prereleases being selected even if another constraint conflicted with them.
## [0.12.2] - 2018-10-17 ## [0.12.2] - 2018-10-17
......
...@@ -59,7 +59,9 @@ def parse_single_constraint(constraint): # type: (str) -> VersionConstraint ...@@ -59,7 +59,9 @@ def parse_single_constraint(constraint): # type: (str) -> VersionConstraint
if len(m.group(1).split(".")) == 1: if len(m.group(1).split(".")) == 1:
high = version.stable.next_major high = version.stable.next_major
return VersionRange(version, high, include_min=True) return VersionRange(
version, high, include_min=True, always_include_max_prerelease=True
)
# PEP 440 Tilde range (~=) # PEP 440 Tilde range (~=)
m = TILDE_PEP440_CONSTRAINT.match(constraint) m = TILDE_PEP440_CONSTRAINT.match(constraint)
...@@ -80,14 +82,21 @@ def parse_single_constraint(constraint): # type: (str) -> VersionConstraint ...@@ -80,14 +82,21 @@ def parse_single_constraint(constraint): # type: (str) -> VersionConstraint
low = Version(version.major, version.minor, 0) low = Version(version.major, version.minor, 0)
high = version.stable.next_minor high = version.stable.next_minor
return VersionRange(low, high, include_min=True) return VersionRange(
low, high, include_min=True, always_include_max_prerelease=True
)
# Caret range # Caret range
m = CARET_CONSTRAINT.match(constraint) m = CARET_CONSTRAINT.match(constraint)
if m: if m:
version = Version.parse(m.group(1)) version = Version.parse(m.group(1))
return VersionRange(version, version.next_breaking, include_min=True) return VersionRange(
version,
version.next_breaking,
include_min=True,
always_include_max_prerelease=True,
)
# X Range # X Range
m = X_CONSTRAINT.match(constraint) m = X_CONSTRAINT.match(constraint)
...@@ -99,14 +108,24 @@ def parse_single_constraint(constraint): # type: (str) -> VersionConstraint ...@@ -99,14 +108,24 @@ def parse_single_constraint(constraint): # type: (str) -> VersionConstraint
if minor is not None: if minor is not None:
version = Version(major, int(minor), 0) version = Version(major, int(minor), 0)
result = VersionRange(version, version.next_minor, include_min=True) result = VersionRange(
version,
version.next_minor,
include_min=True,
always_include_max_prerelease=True,
)
else: else:
if major == 0: if major == 0:
result = VersionRange(max=Version(1, 0, 0)) result = VersionRange(max=Version(1, 0, 0))
else: else:
version = Version(major, 0, 0) version = Version(major, 0, 0)
result = VersionRange(version, version.next_major, include_min=True) result = VersionRange(
version,
version.next_major,
include_min=True,
always_include_max_prerelease=True,
)
if op == "!=": if op == "!=":
result = VersionRange().difference(result) result = VersionRange().difference(result)
......
...@@ -182,6 +182,10 @@ class Version(VersionRange): ...@@ -182,6 +182,10 @@ class Version(VersionRange):
return self return self
@property @property
def full_max(self):
return self
@property
def include_min(self): def include_min(self):
return True return True
...@@ -265,6 +269,13 @@ class Version(VersionRange): ...@@ -265,6 +269,13 @@ class Version(VersionRange):
return self return self
def equals_without_prerelease(self, other): # type: (Version) -> bool
return (
self.major == other.major
and self.minor == other.minor
and self.patch == other.patch
)
def _increment_major(self): # type: () -> Version def _increment_major(self): # type: () -> Version
return Version(self.major + 1, 0, 0, precision=self._precision) return Version(self.major + 1, 0, 0, precision=self._precision)
......
...@@ -12,8 +12,23 @@ class VersionRange(VersionConstraint): ...@@ -12,8 +12,23 @@ class VersionRange(VersionConstraint):
include_max=False, include_max=False,
always_include_max_prerelease=False, always_include_max_prerelease=False,
): ):
full_max = max
if (
always_include_max_prerelease
and not include_max
and not full_max.is_prerelease()
and not full_max.build
and (
min is None
or not min.is_prerelease()
or not min.equals_without_prerelease(full_max)
)
):
full_max = full_max.first_prerelease
self._min = min self._min = min
self._max = max self._max = max
self._full_max = full_max
self._include_min = include_min self._include_min = include_min
self._include_max = include_max self._include_max = include_max
...@@ -26,6 +41,10 @@ class VersionRange(VersionConstraint): ...@@ -26,6 +41,10 @@ class VersionRange(VersionConstraint):
return self._max return self._max
@property @property
def full_max(self):
return self._full_max
@property
def include_min(self): def include_min(self):
return self._include_min return self._include_min
...@@ -323,10 +342,10 @@ class VersionRange(VersionConstraint): ...@@ -323,10 +342,10 @@ class VersionRange(VersionConstraint):
if self.max is None or other.min is None: if self.max is None or other.min is None:
return False return False
if self.max < other.min: if self.full_max < other.min:
return True return True
if self.max > other.min: if self.full_max > other.min:
return False return False
return not self.include_max or not other.include_min return not self.include_max or not other.include_min
......
...@@ -1173,3 +1173,22 @@ def test_solver_returns_extras_if_requested_in_dependencies_and_not_in_root_pack ...@@ -1173,3 +1173,22 @@ def test_solver_returns_extras_if_requested_in_dependencies_and_not_in_root_pack
{"job": "install", "package": package_b}, {"job": "install", "package": package_b},
], ],
) )
def test_solver_should_not_resolve_prerelease_version_if_not_requested(
solver, repo, package
):
package.add_dependency("A", "~1.8.0")
package.add_dependency("B", "^0.5.0")
package_a185 = get_package("A", "1.8.5")
package_a19b1 = get_package("A", "1.9b1")
package_b = get_package("B", "0.5.0")
package_b.add_dependency("A", ">=1.9b1")
repo.add_package(package_a185)
repo.add_package(package_a19b1)
repo.add_package(package_b)
with pytest.raises(SolverProblemError):
solver.solve()
...@@ -170,6 +170,17 @@ def test_allows_any( ...@@ -170,6 +170,17 @@ def test_allows_any(
assert range.allows_any(VersionRange(v123, v234).union(v300)) assert range.allows_any(VersionRange(v123, v234).union(v300))
assert not range.allows_any(VersionRange(v234, v300).union(v010)) assert not range.allows_any(VersionRange(v234, v300).union(v010))
# pre-release min does not allow lesser than itself
range = VersionRange(Version.parse("1.9b1"), include_min=True)
assert not range.allows_any(
VersionRange(
Version.parse("1.8.0"),
Version.parse("1.9.0"),
include_min=True,
always_include_max_prerelease=True,
)
)
def test_intersect(v114, v123, v124, v200, v250, v300): def test_intersect(v114, v123, v124, v200, v250, v300):
# two overlapping ranges # two overlapping ranges
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment