Skip to content

Commit

Permalink
Add utils for poetry/semver
Browse files Browse the repository at this point in the history
- boolean check for a sem-ver string
- filter and sort a sequence of sem-ver strings
  • Loading branch information
Darren Weber committed Sep 13, 2019
1 parent 0a5a70d commit 5bffc45
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 0 deletions.
44 changes: 44 additions & 0 deletions poetry/semver/semver_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"""
Strict SEM-VER utilities
The focus of this module is only on sem-ver support and not
full PEP-0440 support, which is not compatible with all the
SEM-VER specs.
https://legacy.python.org/dev/peps/pep-0440/#semantic-versioning
> Semantic versions containing a hyphen (pre-releases - clause 10) or
> a plus sign (builds - clause 11) are not compatible with this PEP
> and are not permitted in the public version field.
"""

from typing import List
from typing import Optional

import semver


def is_semver(value): # type: (str) -> bool
"""Check that a string is a single SEM-VER identifier
:param value: a single SEM-VER identifier
:return: True if value is a single SEM-VER identifier
"""
try:
semver.VersionInfo.parse(value)
return True
except (TypeError, ValueError):
return False


def sorted_semver(
values, reverse=False
): # type: (List[str], Optional[bool]) -> List[str]
"""Sort a list of single SEM-VER constraints (string);
discards any string that is not a SEM-VER identifier.
:param values: a list of single SEM-VER identifiers (strings)
:param reverse: sort descending (reverse=True) or ascending (reverse=False)
:return: sorted values based on SEM-VER sort criteria
"""
unsorted = [ver for ver in values if is_semver(ver)]
return sorted(unsorted, key=semver.VersionInfo.parse, reverse=reverse)
73 changes: 73 additions & 0 deletions tests/semver/test_semver_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import random

import pytest
import semver

from poetry.semver.semver_utils import is_semver
from poetry.semver.semver_utils import sorted_semver

# semver sorted values, from
# https://github.com/k-bx/python-semver/blob/master/test_semver.py
SORTED = [
"1.0.0-alpha",
"1.0.0-alpha.1",
"1.0.0-alpha.beta",
"1.0.0-beta",
"1.0.0-beta.2",
"1.0.0-beta.11",
"1.0.0-rc.1",
"1.0.0",
"1.0.0",
"2.0.0",
]


@pytest.mark.parametrize(
"version,result",
[
("1.2.3-alpha.1.2+build.11.e0f985a", True),
("2.0.0", True),
("1.0.0", True),
("1.0.0-alpha.1", True),
("1.0.0-alpha", True),
("1.0.0-alpha.beta", True),
("1.0.0-rc.1", True),
("1.0.0-beta.11", True),
("1.0.0-beta.2", True),
("1.0.0-beta", True),
("1.0.0.2", False),
("1.0.0.0rc2", False),
("n.a.n", False),
("hot-fix-666", False),
],
)
def test_is_semver(mocker, version, result):
# The semver library is responsible for testing all variants, so this
# test simply passes some examples and checks that semver.parse is called;
# See also https://github.com/k-bx/python-semver/blob/master/test_semver.py
parser = mocker.spy(semver.VersionInfo, name="parse")
assert is_semver(version) == result
assert parser.call_count == 1


@pytest.mark.parametrize(
"unsorted, sorted_",
[
(random.sample(SORTED, k=len(SORTED)), SORTED),
(["1.0.3", "1.0.2", "1.0.1"], ["1.0.1", "1.0.2", "1.0.3"]),
(
["1.0.3", "1.0.2", "1.0.1", "n.a.n", "hot-fix-666"],
["1.0.1", "1.0.2", "1.0.3"],
),
(["10.0.3", "1.0.3", "n.a.n", "hot-fix-666"], ["1.0.3", "10.0.3"]),
],
)
def test_sem_ver_sorted(mocker, unsorted, sorted_):
parser = mocker.spy(semver.VersionInfo, name="parse")
assert sorted_semver(unsorted) == sorted_
# parse is called to (a) check is_semver and (b) instantiate semver.VersionInfo for sort key
assert parser.call_count == (len(unsorted) + len(sorted_))
# test the reverse order
parser.reset_mock()
assert sorted_semver(unsorted, reverse=True) == list(reversed(sorted_))
assert parser.call_count == (len(unsorted) + len(sorted_))

0 comments on commit 5bffc45

Please sign in to comment.