Skip to content

Commit 6f229a3

Browse files
committed
fix: remove VersionInfo entirely
keep graphql_server.version_info as a plain tuple add tests using the packaging package to make sure graphql_server.version and graphql_server.version_info are PEP 440–compliant and pointing to the same version
1 parent 246c977 commit 6f229a3

File tree

3 files changed

+60
-166
lines changed

3 files changed

+60
-166
lines changed

graphql_server/version.py

+11-42
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,15 @@
1-
import re
2-
from typing import NamedTuple
3-
41
__all__ = ["version", "version_info"]
52

63

74
version = "3.0.0b7"
8-
9-
_re_version = re.compile(r"(\d+)\.(\d+)\.(\d+)(\D*)(\d*)")
10-
11-
12-
class VersionInfo(NamedTuple):
13-
major: int
14-
minor: int
15-
micro: int
16-
releaselevel: str
17-
serial: int
18-
19-
@classmethod
20-
def from_str(cls, v: str) -> "VersionInfo":
21-
groups = _re_version.match(v).groups() # type: ignore
22-
major, minor, micro = map(int, groups[:3])
23-
level = (groups[3] or "")[:1]
24-
if level == "a":
25-
level = "alpha"
26-
elif level == "b":
27-
level = "beta"
28-
elif level in ("c", "r"):
29-
level = "candidate"
30-
else:
31-
level = "final"
32-
serial = groups[4]
33-
serial = int(serial) if serial else 0
34-
return cls(major, minor, micro, level, serial)
35-
36-
def __str__(self) -> str:
37-
v = f"{self.major}.{self.minor}.{self.micro}"
38-
level = self.releaselevel
39-
if level == "candidate":
40-
v = f"{v}rc{self.serial}"
41-
elif level and level != "final":
42-
v = f"{v}{level[:1]}{self.serial}"
43-
return v
44-
45-
46-
version_info = VersionInfo.from_str(version)
5+
version_info = (3, 0, 0, "beta", 7)
6+
# version_info has the same format as django.VERSION
7+
# https://github.com/django/django/blob/4a5048b036fd9e965515e31fdd70b0af72655cba/django/utils/version.py#L22
8+
#
9+
# examples
10+
# "3.0.0" -> (3, 0, 0, "final", 0)
11+
# "3.0.0rc1" -> (3, 0, 0, "rc", 1)
12+
# "3.0.0b7" -> (3, 0, 0, "beta", 7)
13+
# "3.0.0a2" -> (3, 0, 0, "alpha", 2)
14+
#
15+
# also see tests/test_version.py

setup.py

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"pytest-cov>=4,<5",
1414
"Jinja2>=3.1,<4",
1515
"sanic-testing>=22.3,<24",
16+
"packaging==23.2",
1617
]
1718

1819
dev_requires = [

tests/test_version.py

+48-124
Original file line numberDiff line numberDiff line change
@@ -1,126 +1,50 @@
1-
import re
2-
from typing import Tuple, Union
1+
import packaging
2+
from packaging.version import Version
33

4-
import pytest
5-
6-
import graphql_server
7-
from graphql_server.version import VersionInfo, version, version_info
8-
9-
_re_version = re.compile(r"(\d+)\.(\d+)\.(\d+)(?:([abc]|rc)(\d+))?$")
10-
11-
TEST_CASES = [
12-
(
13-
(1, 2, 3),
14-
"1.2.3",
15-
),
16-
(
17-
(1, 2, 3, "alpha", 4),
18-
"1.2.3a4",
19-
),
20-
(
21-
(1, 2, 3, "beta", 4),
22-
"1.2.3b4",
23-
),
24-
(
25-
(12, 34, 56, "candidate", 789),
26-
"12.34.56rc789",
27-
),
28-
]
29-
30-
31-
PARSED_VERSIONS = [parsed_version for parsed_version, _ in TEST_CASES]
32-
VERSION_STRINGS = [version_string for _, version_string in TEST_CASES]
33-
34-
35-
def extract_version_components(
36-
parsed_version: Union[Tuple[int, int, int], Tuple[int, int, int, str, int]],
37-
) -> Tuple[int, int, int, str, int]:
38-
if len(parsed_version) == 5:
39-
return parsed_version # type: ignore
40-
elif len(parsed_version) == 3:
41-
return tuple([*parsed_version, "final", 0]) # type: ignore
42-
43-
raise ValueError("parsed_version length should be either 3 or 5")
44-
45-
46-
@pytest.mark.parametrize("parsed_version", PARSED_VERSIONS)
47-
def test_create_version_info_from_fields(
48-
parsed_version: Union[Tuple[int, int, int], Tuple[int, int, int, str, int]],
49-
) -> None:
50-
version_components = extract_version_components(parsed_version)
51-
major, minor, micro, releaselevel, serial = version_components
52-
v = VersionInfo(*version_components)
53-
54-
assert v.major == major
55-
assert v.minor == minor
56-
assert v.micro == micro
57-
assert v.releaselevel == releaselevel
58-
assert v.serial == serial
59-
60-
61-
@pytest.mark.parametrize("parsed_version, version_string", TEST_CASES)
62-
def test_create_version_info_from_str(
63-
parsed_version: Union[Tuple[int, int, int], Tuple[int, int, int, str, int]],
64-
version_string: str,
65-
) -> None:
66-
v = VersionInfo.from_str(version_string)
67-
major, minor, micro, releaselevel, serial = extract_version_components(
68-
parsed_version
69-
)
70-
71-
assert v.major == major
72-
assert v.minor == minor
73-
assert v.micro == micro
74-
assert v.releaselevel == releaselevel
75-
assert v.serial == serial
76-
77-
78-
@pytest.mark.parametrize("parsed_version, version_string", TEST_CASES)
79-
def test_serialize_as_str(
80-
parsed_version: Union[Tuple[int, int, int], Tuple[int, int, int, str, int]],
81-
version_string: str,
82-
) -> None:
83-
v = VersionInfo(*extract_version_components(parsed_version))
84-
assert str(v) == version_string
85-
86-
87-
def test_base_package_has_correct_version() -> None:
88-
assert graphql_server.__version__ == version
89-
assert graphql_server.version == version
90-
91-
92-
def test_base_package_has_correct_version_info() -> None:
93-
assert graphql_server.__version_info__ is version_info
94-
assert graphql_server.version_info is version_info
95-
96-
97-
@pytest.mark.parametrize("version", VERSION_STRINGS + [version])
98-
def test_version_has_correct_format(version: str) -> None:
99-
assert isinstance(version, str)
100-
assert _re_version.match(version)
101-
102-
103-
@pytest.mark.parametrize(
104-
"version,version_info",
105-
zip(
106-
VERSION_STRINGS + [version],
107-
[VersionInfo.from_str(v) for v in VERSION_STRINGS] + [version_info],
108-
),
109-
)
110-
def test_version_info_has_correct_fields(
111-
version: str, version_info: VersionInfo
112-
) -> None:
4+
from graphql_server.version import version, version_info
5+
6+
RELEASE_LEVEL = {"alpha": "a", "beta": "b", "rc": "rc", "final": None}
7+
8+
9+
parsed_version = Version(version)
10+
11+
12+
def test_valid_version() -> None:
13+
packaging.version.parse(version)
14+
15+
16+
def test_valid_version_info() -> None:
17+
"""version_info has to be a tuple[int, int, int, str, int]"""
11318
assert isinstance(version_info, tuple)
114-
assert str(version_info) == version
115-
groups = _re_version.match(version).groups() # type: ignore
116-
assert version_info.major == int(groups[0])
117-
assert version_info.minor == int(groups[1])
118-
assert version_info.micro == int(groups[2])
119-
if groups[3] is None: # pragma: no cover
120-
assert groups[4] is None
121-
else: # pragma: no cover
122-
if version_info.releaselevel == "candidate":
123-
assert groups[3] == "rc"
124-
else:
125-
assert version_info.releaselevel[:1] == groups[3]
126-
assert version_info.serial == int(groups[4])
19+
assert len(version_info) == 5
20+
21+
major, minor, micro, release_level, serial = version_info
22+
assert isinstance(major, int)
23+
assert isinstance(minor, int)
24+
assert isinstance(micro, int)
25+
assert isinstance(release_level, str)
26+
assert isinstance(serial, int)
27+
28+
29+
def test_valid_version_release_level() -> None:
30+
if parsed_version.pre is not None:
31+
valid_release_levels = {v for v in RELEASE_LEVEL.values() if v is not None}
32+
assert parsed_version.pre[0] in valid_release_levels
33+
34+
35+
def test_valid_version_info_release_level() -> None:
36+
assert version_info[3] in RELEASE_LEVEL.keys()
37+
38+
39+
def test_version_same_as_version_info() -> None:
40+
assert (
41+
parsed_version.major,
42+
parsed_version.minor,
43+
parsed_version.micro,
44+
) == version_info[:3]
45+
46+
release_level, serial = version_info[-2:]
47+
if parsed_version.is_prerelease:
48+
assert (RELEASE_LEVEL[release_level], serial) == parsed_version.pre
49+
else:
50+
assert (release_level, serial) == ("final", 0)

0 commit comments

Comments
 (0)