Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
14 changes: 12 additions & 2 deletions poetry/core/semver/version_range.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,20 @@ def allows(self, other: "Version") -> bool:
return False

if self.full_max is not None:
if other > self.full_max:
_this, _other = self.full_max, other

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

if not _this.is_postrelease() and _other.is_postrelease():
# allow weak equality to allow `3.0.0-1` for `<=3.0.0`
_other = _other.without_postrelease()

if _other > _this:
return False

if not self._include_max and other == self.full_max:
if not self._include_max and _other == _this:
return False

return True
Expand Down
24 changes: 24 additions & 0 deletions poetry/core/version/pep440/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ def __post_init__(self):
if self.local is not None and not isinstance(self.local, tuple):
object.__setattr__(self, "local", (self.local,))

if isinstance(self.release, tuple):
object.__setattr__(self, "release", Release(*self.release))

# we do this here to handle both None and tomlkit string values
object.__setattr__(
self, "text", self.to_string() if not self.text else str(self.text)
Expand Down Expand Up @@ -137,6 +140,9 @@ def is_postrelease(self) -> bool:
def is_devrelease(self) -> bool:
return self.dev is not None

def is_local(self) -> bool:
return self.local is not None

def is_no_suffix_release(self) -> bool:
return not (self.pre or self.post or self.dev)

Expand Down Expand Up @@ -200,3 +206,21 @@ def first_prerelease(self) -> "PEP440Version":
return self.__class__(
epoch=self.epoch, release=self.release, pre=ReleaseTag(RELEASE_PHASE_ALPHA)
)

def replace(self, **kwargs):
return self.__class__(
**{
**{
k: getattr(self, k)
for k in self.__dataclass_fields__.keys()
if k not in ("_compare_key", "text")
}, # setup defaults with current values, excluding compare keys and text
**kwargs, # keys to replace
}
)

def without_local(self) -> "PEP440Version":
return self.replace(local=None)

def without_postrelease(self) -> "PEP440Version":
return self.replace(post=None)
140 changes: 137 additions & 3 deletions tests/semver/test_version_range.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,135 @@ def v300b1():
return Version.parse("3.0.0b1")


def test_allows_all(v003, v010, v080, v114, v123, v124, v140, v200, v234, v250, v300):
@pytest.mark.parametrize(
"base,other",
[
pytest.param(Version.parse("3.0.0"), Version.parse("3.0.0-1"), id="post"),
pytest.param(
Version.parse("3.0.0"), Version.parse("3.0.0+local.1"), id="local"
),
],
)
def test_allows_post_releases_with_max(base, other):
range = VersionRange(max=base, include_max=True)
assert range.allows(other)


@pytest.mark.parametrize(
"base,other",
[
pytest.param(Version.parse("3.0.0"), Version.parse("3.0.0-1"), id="post"),
pytest.param(
Version.parse("3.0.0"), Version.parse("3.0.0+local.1"), id="local"
),
],
)
def test_allows_post_releases_with_min(base, other):
range = VersionRange(min=base, include_min=True)
assert range.allows(other)


def test_allows_post_releases_with_post_and_local_min():
one = Version.parse("3.0.0+local.1")
two = Version.parse("3.0.0-1")
three = Version.parse("3.0.0-1+local.1")
four = Version.parse("3.0.0+local.2")

assert VersionRange(min=one, include_min=True).allows(two)
assert VersionRange(min=one, include_min=True).allows(three)
assert VersionRange(min=one, include_min=True).allows(four)

assert not VersionRange(min=two, include_min=True).allows(one)
assert VersionRange(min=two, include_min=True).allows(three)
assert not VersionRange(min=two, include_min=True).allows(four)

assert not VersionRange(min=three, include_min=True).allows(one)
assert not VersionRange(min=three, include_min=True).allows(two)
assert not VersionRange(min=three, include_min=True).allows(four)

assert not VersionRange(min=four, include_min=True).allows(one)
assert VersionRange(min=four, include_min=True).allows(two)
assert VersionRange(min=four, include_min=True).allows(three)


def test_allows_post_releases_with_post_and_local_max():
one = Version.parse("3.0.0+local.1")
two = Version.parse("3.0.0-1")
three = Version.parse("3.0.0-1+local.1")
four = Version.parse("3.0.0+local.2")

assert VersionRange(max=one, include_max=True).allows(two)
assert VersionRange(max=one, include_max=True).allows(three)
assert not VersionRange(max=one, include_max=True).allows(four)

assert VersionRange(max=two, include_max=True).allows(one)
assert VersionRange(max=two, include_max=True).allows(three)
assert VersionRange(max=two, include_max=True).allows(four)

assert VersionRange(max=three, include_max=True).allows(one)
assert VersionRange(max=three, include_max=True).allows(two)
assert VersionRange(max=three, include_max=True).allows(four)

assert VersionRange(max=four, include_max=True).allows(one)
assert VersionRange(max=four, include_max=True).allows(two)
assert VersionRange(max=four, include_max=True).allows(three)


@pytest.mark.parametrize(
"base,one,two",
[
pytest.param(
Version.parse("3.0.0"),
Version.parse("3.0.0-1"),
Version.parse("3.0.0-2"),
id="post",
),
pytest.param(
Version.parse("3.0.0"),
Version.parse("3.0.0+local.1"),
Version.parse("3.0.0+local.2"),
id="local",
),
],
)
def test_allows_post_releases_explicit_with_max(base, one, two):
range = VersionRange(max=one, include_max=True)
assert range.allows(base)
assert not range.allows(two)

range = VersionRange(max=two, include_max=True)
assert range.allows(base)
assert range.allows(one)


@pytest.mark.parametrize(
"base,one,two",
[
pytest.param(
Version.parse("3.0.0"),
Version.parse("3.0.0-1"),
Version.parse("3.0.0-2"),
id="post",
),
pytest.param(
Version.parse("3.0.0"),
Version.parse("3.0.0+local.1"),
Version.parse("3.0.0+local.2"),
id="local",
),
],
)
def test_allows_post_releases_explicit_with_min(base, one, two):
range = VersionRange(min=one, include_min=True)
assert not range.allows(base)
assert range.allows(two)

range = VersionRange(min=two, include_min=True)
assert not range.allows(base)
assert not range.allows(one)


def test_allows_all(v123, v124, v140, v250, v300):
assert VersionRange(v123, v250).allows_all(EmptyConstraint())

range = VersionRange(v123, v250, include_max=True)
Expand All @@ -84,7 +212,8 @@ def test_allows_all(v003, v010, v080, v114, v123, v124, v140, v200, v234, v250,
assert range.allows_all(v250)
assert not range.allows_all(v300)

# with no min

def test_allows_all_with_no_min(v080, v140, v250, v300):
range = VersionRange(max=v250)
assert range.allows_all(VersionRange(v080, v140))
assert not range.allows_all(VersionRange(v080, v300))
Expand All @@ -93,7 +222,8 @@ def test_allows_all(v003, v010, v080, v114, v123, v124, v140, v200, v234, v250,
assert range.allows_all(range)
assert not range.allows_all(VersionRange())

# with no max

def test_allows_all_with_no_max(v003, v010, v080, v140):
range = VersionRange(min=v010)
assert range.allows_all(VersionRange(v080, v140))
assert not range.allows_all(VersionRange(v003, v140))
Expand All @@ -102,6 +232,8 @@ def test_allows_all(v003, v010, v080, v114, v123, v124, v140, v200, v234, v250,
assert range.allows_all(range)
assert not range.allows_all(VersionRange())


def test_allows_all_bordering_range_not_more_inclusive(v010, v250):
# Allows bordering range that is not more inclusive
exclusive = VersionRange(v010, v250)
inclusive = VersionRange(v010, v250, True, True)
Expand All @@ -110,6 +242,8 @@ def test_allows_all(v003, v010, v080, v114, v123, v124, v140, v200, v234, v250,
assert not exclusive.allows_all(inclusive)
assert exclusive.allows_all(exclusive)


def test_allows_all_contained_unions(v010, v114, v123, v124, v140, v200, v234):
# Allows unions that are completely contained
range = VersionRange(v114, v200)
assert range.allows_all(VersionRange(v123, v124).union(v140))
Expand Down
Loading