Skip to content

Commit

Permalink
version: fix normalize_version() to support normalizations according …
Browse files Browse the repository at this point in the history
…to PEP440
  • Loading branch information
radoering authored and abn committed May 10, 2022
1 parent 259bbbe commit f8ca22e
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 1 deletion.
2 changes: 1 addition & 1 deletion src/poetry/core/utils/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def module_name(name: str) -> str:


def normalize_version(version: str) -> str:
return PEP440Version.parse(version).to_string(short=True)
return PEP440Version.parse(version).normalize()


@contextmanager
Expand Down
13 changes: 13 additions & 0 deletions src/poetry/core/version/pep440/segments.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,15 @@
RELEASE_PHASE_REV: "r",
RELEASE_PHASE_DEV: "dev",
}
RELEASE_PHASES_NORMALIZED = {
RELEASE_PHASE_ALPHA: "a",
RELEASE_PHASE_BETA: "b",
RELEASE_PHASE_RC: "rc",
RELEASE_PHASE_PREVIEW: "rc",
RELEASE_PHASE_POST: "post",
RELEASE_PHASE_REV: "post",
RELEASE_PHASE_DEV: "dev",
}
RELEASE_PHASES_SHORT = {v: k for k, v in RELEASE_PHASES.items() if k != "post"}


Expand Down Expand Up @@ -111,6 +120,10 @@ class ReleaseTag:
def __post_init__(self) -> None:
object.__setattr__(self, "phase", self.expand(self.phase))

def normalize(self) -> str:
normalized_phase = RELEASE_PHASES_NORMALIZED.get(self.phase, self.phase)
return f"{normalized_phase}{self.number}"

@classmethod
def shorten(cls, phase: str) -> str:
return RELEASE_PHASES.get(phase, phase)
Expand Down
22 changes: 22 additions & 0 deletions src/poetry/core/version/pep440/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,28 @@ def non_semver_parts(self) -> tuple[int, ...]:
assert isinstance(self.release.extra, tuple)
return self.release.extra

def normalize(self) -> str:
version_string = self.release.to_string()

if self.epoch:
# if epoch is non-zero we should include it
version_string = f"{self.epoch}!{version_string}"

if self.pre:
version_string += self.pre.normalize()

if self.post:
version_string = f"{version_string}.{self.post.normalize()}"

if self.dev:
version_string = f"{version_string}.{self.dev.normalize()}"

if self.local:
assert isinstance(self.local, tuple)
version_string += "+" + ".".join(map(str, self.local))

return version_string.lower()

def to_string(self, short: bool = False) -> str:
dash = "-" if not short else ""

Expand Down
71 changes: 71 additions & 0 deletions tests/utils/test_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,82 @@

from poetry.core.utils.helpers import canonicalize_name
from poetry.core.utils.helpers import combine_unicode
from poetry.core.utils.helpers import normalize_version
from poetry.core.utils.helpers import parse_requires
from poetry.core.utils.helpers import readme_content_type
from poetry.core.utils.helpers import temporary_directory


@pytest.mark.parametrize(
"version,normalized_version",
[
( # already normalized version
"1!2.3.4.5.6a7.post8.dev9+local1.123.abc",
"1!2.3.4.5.6a7.post8.dev9+local1.123.abc",
),
# PEP 440 Normalization
# Case sensitivity
("1.1RC1", "1.1rc1"),
# Integer Normalization
("00", "0"),
("09000", "9000"),
("1.0+foo0100", "1.0+foo0100"),
# Pre-release separators
("1.1.a1", "1.1a1"),
("1.1-a1", "1.1a1"),
("1.1_a1", "1.1a1"),
("1.1a.1", "1.1a1"),
("1.1a-1", "1.1a1"),
("1.1a_1", "1.1a1"),
# Pre-release spelling
("1.1alpha1", "1.1a1"),
("1.1beta2", "1.1b2"),
("1.1c3", "1.1rc3"),
("1.1pre4", "1.1rc4"),
("1.1preview5", "1.1rc5"),
# Implicit pre-release number
("1.2a", "1.2a0"),
# Post release separators
("1.2.post2", "1.2.post2"),
("1.2-post2", "1.2.post2"),
("1.2_post2", "1.2.post2"),
("1.2post.2", "1.2.post2"),
("1.2post-2", "1.2.post2"),
("1.2post_2", "1.2.post2"),
# Post release spelling
("1.0-r4", "1.0.post4"),
("1.0-rev4", "1.0.post4"),
# Implicit post release number
("1.2.post", "1.2.post0"),
# Implicit post releases
("1.0-1", "1.0.post1"),
# Development release separators
("1.2.dev2", "1.2.dev2"),
("1.2-dev2", "1.2.dev2"),
("1.2_dev2", "1.2.dev2"),
("1.2dev.2", "1.2.dev2"),
("1.2dev-2", "1.2.dev2"),
("1.2dev_2", "1.2.dev2"),
# Implicit development release number
("1.2.dev", "1.2.dev0"),
# Local version segments
("1.0+ubuntu-1", "1.0+ubuntu.1"),
("1.0+ubuntu_1", "1.0+ubuntu.1"),
# Preceding v character
("v1.0", "1.0"),
# Leading and Trailing Whitespace
(" 1.0 ", "1.0"),
("\t1.0\t", "1.0"),
("\n1.0\n", "1.0"),
("\r\n1.0\r\n", "1.0"),
("\f1.0\f", "1.0"),
("\v1.0\v", "1.0"),
],
)
def test_normalize_version(version: str, normalized_version: str) -> None:
assert normalize_version(version) == normalized_version


def test_parse_requires() -> None:
requires = """\
jsonschema>=2.6.0.0,<3.0.0.0
Expand Down

0 comments on commit f8ca22e

Please sign in to comment.