Skip to content

Commit

Permalink
Fixes unstable version next breaking version
Browse files Browse the repository at this point in the history
  • Loading branch information
mazinesy committed Sep 20, 2022
1 parent 091e345 commit 87a7c65
Show file tree
Hide file tree
Showing 5 changed files with 277 additions and 15 deletions.
22 changes: 7 additions & 15 deletions src/poetry/core/semver/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,26 +27,18 @@ class Version(PEP440Version, VersionRangeConstraint):
def precision(self) -> int:
return self.release.precision

@property
def stable(self) -> Version:
if self.is_stable():
return self

return self.next_patch()

def next_breaking(self) -> Version:
if self.major == 0:
if self.minor is not None and self.minor != 0:
return self.next_minor()
if self.major > 0 or self.precision == 1:
return self.stable.next_major()

if self.precision == 1:
return self.next_major()
elif self.precision == 2:
return self.next_minor()
is_minor_greater_than_0 = self.minor is not None and self.minor > 0
if is_minor_greater_than_0 or self.precision == 2:
return self.stable.next_minor()

if self.precision > 3:
return self.next_patch()

return self.stable.next_major()
return self.stable.next_patch()

@property
def min(self) -> Version:
Expand Down
16 changes: 16 additions & 0 deletions src/poetry/core/version/pep440/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,22 @@ def non_semver_parts(self) -> tuple[int, ...]:
assert isinstance(self.release.extra, tuple)
return self.release.extra

@property
def stable(self: T) -> T:
if self.is_stable():
return self

if not self.is_prerelease() and self.is_postrelease():
return self.next_postrelease()

if self.release.precision == 1:
return self.next_major()

if self.release.precision == 2:
return self.next_minor()

return self.__class__(release=self.release, epoch=self.epoch)

def to_string(self, short: bool = False) -> str:
if short:
import warnings
Expand Down
33 changes: 33 additions & 0 deletions tests/semver/test_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,30 @@ def test_parse_constraint_tilde(input: str, constraint: VersionRange) -> None:
Version.from_parts(0, 0, 3), Version.from_parts(0, 0, 4), True
),
),
(
"^0.0.3-alpha.21",
VersionRange(
Version.from_parts(0, 0, 3, pre=ReleaseTag("alpha", 21)),
Version.from_parts(0, 0, 4),
True,
),
),
(
"^0.1.3-alpha.21",
VersionRange(
Version.from_parts(0, 1, 3, pre=ReleaseTag("alpha", 21)),
Version.from_parts(0, 2, 0),
True,
),
),
(
"^0.0.0-alpha.21",
VersionRange(
Version.from_parts(0, 0, 0, pre=ReleaseTag("alpha", 21)),
Version.from_parts(0, 0, 1),
True,
),
),
],
)
def test_parse_constraint_caret(input: str, constraint: VersionRange) -> None:
Expand Down Expand Up @@ -392,6 +416,15 @@ def test_parse_constraints_with_trailing_comma(
("^1", ">=1,<2"),
("^1.0", ">=1.0,<2.0"),
("^1.0.0", ">=1.0.0,<2.0.0"),
("^1.0.0-alpha.1", ">=1.0.0-alpha.1,<2.0.0"),
("^0", ">=0,<1"),
("^0.1", ">=0.1,<0.2"),
("^0.0.2", ">=0.0.2,<0.0.3"),
("^0.1.2", ">=0.1.2,<0.2.0"),
("^0-alpha.1", ">=0-alpha.1,<1"),
("^0.1-alpha.1", ">=0.1-alpha.1,<0.2"),
("^0.0.2-alpha.1", ">=0.0.2-alpha.1,<0.0.3"),
("^0.1.2-alpha.1", ">=0.1.2-alpha.1,<0.2.0"),
("~1", ">=1,<2"),
("~1.0", ">=1.0,<1.1"),
("~1.0.0", ">=1.0.0,<1.1.0"),
Expand Down
100 changes: 100 additions & 0 deletions tests/semver/test_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,106 @@ def test_parse_invalid(value: str | None) -> None:
Version.parse(value) # type: ignore[arg-type]


@pytest.mark.parametrize(
"version, expected",
[
("1", "2"),
("1.2", "2.0"),
("1.2.3", "2.0.0"),
("2!1.2.3", "2!2.0.0"),
("1.2.3+local", "2.0.0"),
("1.2.3.4", "2.0.0.0"),
("1.dev0", "2"),
("1.2dev0", "2.0"),
("1.2.3dev0", "2.0.0"),
("1.2.3.4dev0", "2.0.0.0"),
("1.post1", "2"),
("1.2.post1", "2.0"),
("1.2.3.post1", "2.0.0"),
("1.post1.dev0", "2"),
("1.2.post1.dev0", "2.0"),
("1.2.3.post1.dev0", "2.0.0"),
("2.a1", "3"),
("2.2a1", "3.0"),
("2.2.3a1", "3.0.0"),
("2.2.3.4a1", "3.0.0.0"),
("2.a1.post2", "3"),
("2.2a1.post2", "3.0"),
("2.2.3a1.post2", "3.0.0"),
("2.2.3.4a1.post2", "3.0.0.0"),
("2.a1.post2.dev0", "3"),
("2.2a1.post2.dev0", "3.0"),
("2.2.3a1.post2.dev0", "3.0.0"),
("2.2.3.4a1.post2.dev0", "3.0.0.0"),
],
)
def test_next_breaking_for_major_over_0_results_into_next_major_and_preserves_precision(
version: str, expected: str
) -> None:
subject = Version.parse(version)

assert subject.next_breaking().text == expected


@pytest.mark.parametrize(
"version, expected",
[
("0", "1"),
("0.0", "0.1"),
("0.2", "0.3"),
("0.2.3", "0.3.0"),
("2!0.2.3", "2!0.3.0"),
("0.2.3+local", "0.3.0"),
("0.2.3.4", "0.3.0.0"),
("0.0.3.4", "0.0.4.0"),
("0.dev0", "1"),
("0.0dev0", "0.1"),
("0.2dev0", "0.3"),
("0.2.3dev0", "0.3.0"),
("0.0.3dev0", "0.0.4"),
("0.post1", "1"),
("0.0.post1", "0.1"),
("0.2.post1", "0.3"),
("0.2.3.post1", "0.3.0"),
("0.0.3.post1", "0.0.4"),
("0.post1.dev0", "1"),
("0.0.post1.dev0", "0.1"),
("0.2.post1.dev0", "0.3"),
("0.2.3.post1.dev0", "0.3.0"),
("0.0.3.post1.dev0", "0.0.4"),
("0.a1", "1"),
("0.0a1", "0.1"),
("0.2a1", "0.3"),
("0.2.3a1", "0.3.0"),
("0.2.3.4a1", "0.3.0.0"),
("0.0.3.4a1", "0.0.4.0"),
("0.a1.post2", "1"),
("0.0a1.post2", "0.1"),
("0.2a1.post2", "0.3"),
("0.2.3a1.post2", "0.3.0"),
("0.2.3.4a1.post2", "0.3.0.0"),
("0.0.3.4a1.post2", "0.0.4.0"),
("0.a1.post2.dev0", "1"),
("0.0a1.post2.dev0", "0.1"),
("0.2a1.post2.dev0", "0.3"),
("0.2.3a1.post2.dev0", "0.3.0"),
("0.2.3.4a1.post2.dev0", "0.3.0.0"),
("0.0.3.4a1.post2.dev0", "0.0.4.0"),
("0-alpha.1", "1"),
("0.0-alpha.1", "0.1"),
("0.2-alpha.1", "0.3"),
("0.0.1-alpha.2", "0.0.2"),
("0.1.2-alpha.1", "0.2.0"),
],
)
def test_next_breaking_for_major_0_version_is_treated_with_more_care_and_preserves_precision(
version: str, expected: str
) -> None:
subject = Version.parse(version)

assert subject.next_breaking().text == expected


@pytest.mark.parametrize(
"versions",
[
Expand Down
121 changes: 121 additions & 0 deletions tests/version/pep440/test_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,127 @@ def test_next_prerelease(version: str, expected: str) -> None:
assert v.next_prerelease().text == expected


@pytest.mark.parametrize(
"version, expected",
[
("1", "1"),
("1.2", "1.2"),
("1.2.3", "1.2.3"),
("2!1.2.3", "2!1.2.3"),
("1.2.3+local", "1.2.3+local"),
("1.2.3.4", "1.2.3.4"),
("1.dev0", "1"),
("1.2dev0", "1.2"),
("1.2.3dev0", "1.2.3"),
("1.2.3.4dev0", "1.2.3.4"),
("1.post1", "1.post1"),
("1.2.post1", "1.2.post1"),
("1.2.3.post1", "1.2.3.post1"),
("1.post1.dev0", "1.post1"),
("1.2.post1.dev0", "1.2.post1"),
("1.2.3.post1.dev0", "1.2.3.post1"),
("1.a1", "1"),
("1.2a1", "1.2"),
("1.2.3a1", "1.2.3"),
("1.2.3.4a1", "1.2.3.4"),
("1.a1.post2", "1"),
("1.2a1.post2", "1.2"),
("1.2.3a1.post2", "1.2.3"),
("1.2.3.4a1.post2", "1.2.3.4"),
("1.a1.post2.dev0", "1"),
("1.2a1.post2.dev0", "1.2"),
("1.2.3a1.post2.dev0", "1.2.3"),
("1.2.3.4a1.post2.dev0", "1.2.3.4"),
],
)
def test_stable(version: str, expected: str) -> None:
subject = PEP440Version.parse(version)

assert subject.stable.text == expected


@pytest.mark.parametrize(
"version, expected",
[
("1", True),
("1.2", True),
("1.2.3", True),
("2!1.2.3", True),
("1.2.3+local", True),
("1.2.3.4", True),
("1.dev0", False),
("1.2dev0", False),
("1.2.3dev0", False),
("1.2.3.4dev0", False),
("1.post1", True),
("1.2.post1", True),
("1.2.3.post1", True),
("1.post1.dev0", False),
("1.2.post1.dev0", False),
("1.2.3.post1.dev0", False),
("1.a1", False),
("1.2a1", False),
("1.2.3a1", False),
("1.2.3.4a1", False),
("1.a1.post2", False),
("1.2a1.post2", False),
("1.2.3a1.post2", False),
("1.2.3.4a1.post2", False),
("1.a1.post2.dev0", False),
("1.2a1.post2.dev0", False),
("1.2.3a1.post2.dev0", False),
("1.2.3.4a1.post2.dev0", False),
],
)
def test_is_stable(version: str, expected: bool) -> None:
subject = PEP440Version.parse(version)

assert subject.is_stable() == expected
assert subject.is_unstable() == (not expected)


@pytest.mark.parametrize(
"version, expected",
[
("0", True),
("0.2", True),
("0.2.3", True),
("2!0.2.3", True),
("0.2.3+local", True),
("0.2.3.4", True),
("0.dev0", False),
("0.2dev0", False),
("0.2.3dev0", False),
("0.2.3.4dev0", False),
("0.post1", True),
("0.2.post1", True),
("0.2.3.post1", True),
("0.post1.dev0", False),
("0.2.post1.dev0", False),
("0.2.3.post1.dev0", False),
("0.a1", False),
("0.2a1", False),
("0.2.3a1", False),
("0.2.3.4a1", False),
("0.a1.post2", False),
("0.2a1.post2", False),
("0.2.3a1.post2", False),
("0.2.3.4a1.post2", False),
("0.a1.post2.dev0", False),
("0.2a1.post2.dev0", False),
("0.2.3a1.post2.dev0", False),
("0.2.3.4a1.post2.dev0", False),
],
)
def test_is_stable_all_major_0_versions_are_treated_as_normal_versions(
version: str, expected: bool
) -> None:
subject = PEP440Version.parse(version)

assert subject.is_stable() == expected
assert subject.is_unstable() == (not expected)


@pytest.mark.parametrize(
"version, expected",
[
Expand Down

0 comments on commit 87a7c65

Please sign in to comment.