Skip to content

Commit 88758e1

Browse files
bjoernricksy0urself
authored andcommitted
Change: Abstract Version and VersionCalculator
Change Version and VersionCalculator into abstract bases classes with a defined interface to allow implementing specific implementations for a versioning scheme like PEP 440. Also move VersionCommand into an own "private" module.
1 parent c305be0 commit 88758e1

File tree

3 files changed

+246
-200
lines changed

3 files changed

+246
-200
lines changed

pontos/version/_command.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# Copyright (C) 2023 Greenbone Networks GmbH
2+
#
3+
# SPDX-License-Identifier: GPL-3.0-or-later
4+
#
5+
# This program is free software: you can redistribute it and/or modify
6+
# it under the terms of the GNU General Public License as published by
7+
# the Free Software Foundation, either version 3 of the License, or
8+
# (at your option) any later version.
9+
#
10+
# This program is distributed in the hope that it will be useful,
11+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
# GNU General Public License for more details.
14+
#
15+
# You should have received a copy of the GNU General Public License
16+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
18+
from abc import ABC, abstractmethod
19+
from pathlib import Path
20+
from typing import Literal, Union
21+
22+
from .scheme import VersioningScheme
23+
from .version import Version, VersionUpdate
24+
25+
26+
class VersionCommand(ABC):
27+
"""Generic class usable to implement the
28+
version commands for several programming languages"""
29+
30+
project_file_name: str
31+
32+
def __init__(self, versioning_scheme: VersioningScheme) -> None:
33+
self.project_file_path = Path.cwd() / self.project_file_name
34+
self.versioning_scheme = versioning_scheme
35+
36+
@abstractmethod
37+
def get_current_version(self) -> Version:
38+
"""Get the current version of this project"""
39+
40+
@abstractmethod
41+
def verify_version(
42+
self, version: Union[Literal["current"], Version, None]
43+
) -> None:
44+
"""
45+
Verify the current version of this project
46+
47+
Args:
48+
version: Version to check against the current applied version of
49+
this project. If version is None or "current" the command should
50+
verify if all version information is consistent, for example if
51+
the version information in several files is the same.
52+
"""
53+
54+
@abstractmethod
55+
def update_version(
56+
self, new_version: Version, *, force: bool = False
57+
) -> VersionUpdate:
58+
"""
59+
Update the current version of this project
60+
61+
Args:
62+
new_version: Use this version in the update
63+
force: Force updating the version even if the current version is the
64+
same as the new version
65+
66+
Returns:
67+
The version update including the changed files
68+
"""
69+
70+
def project_found(self) -> bool:
71+
"""
72+
Returns True if a command has detected a corresponding project
73+
"""
74+
return self.project_file_path.exists()

pontos/version/calculator.py

Lines changed: 77 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -15,68 +15,71 @@
1515
# You should have received a copy of the GNU General Public License
1616
# along with this program. If not, see <http://www.gnu.org/licenses/>.
1717

18+
from abc import ABC, abstractmethod
1819
from datetime import datetime
20+
from typing import Type
1921

20-
from .errors import VersionError
21-
from .version import Version, parse_version
22+
from pontos.version.errors import VersionError
2223

24+
from .version import Version
2325

24-
class VersionCalculator:
25-
def next_patch_version(self, current_version: Version) -> Version:
26-
"""
27-
Get the next patch version from a valid version
2826

29-
Examples:
30-
"1.2.3" will return "1.2.4"
31-
"1.2.3.dev1" will return "1.2.3"
27+
class VersionCalculator(ABC):
28+
"""
29+
An abstract base class for calculating a next version from a version
30+
"""
31+
32+
version_cls: Type[Version]
33+
34+
@classmethod
35+
def version_from_string(cls, version: str) -> Version:
3236
"""
37+
Create a version from a version string
3338
34-
if current_version.is_prerelease:
35-
next_version = parse_version(
36-
f"{current_version.major}."
37-
f"{current_version.minor}."
38-
f"{current_version.micro}"
39-
)
40-
else:
41-
next_version = parse_version(
42-
f"{current_version.major}."
43-
f"{current_version.minor}."
44-
f"{current_version.micro + 1}"
45-
)
39+
Args:
40+
version: Version string to parse
4641
47-
return next_version
42+
Raises:
43+
VersionError: If the version string is invalid.
44+
45+
Returns:
46+
A new version instance
47+
"""
48+
return cls.version_cls.from_string(version)
4849

49-
def next_calendar_version(self, current_version: Version) -> Version:
50+
@classmethod
51+
def next_calendar_version(cls, current_version: Version) -> Version:
5052
"""
5153
Find the correct next calendar version by checking latest version and
5254
the today's date
5355
5456
Raises:
55-
VersionError if version is invalid.
57+
VersionError: If version is invalid.
5658
"""
57-
5859
today = datetime.today()
5960
current_year_short = today.year % 100
6061

6162
if current_version.major < current_year_short or (
6263
current_version.major == current_year_short
6364
and current_version.minor < today.month
6465
):
65-
return parse_version(f"{current_year_short}.{today.month}.0")
66+
return cls.version_from_string(
67+
f"{current_year_short}.{today.month}.0"
68+
)
6669

6770
if (
6871
current_version.major == today.year % 100
6972
and current_version.minor == today.month
7073
):
7174
if current_version.dev is None:
72-
release_version = parse_version(
75+
release_version = cls.version_from_string(
7376
f"{current_year_short}.{today.month}."
74-
f"{current_version.micro + 1}"
77+
f"{current_version.patch + 1}"
7578
)
7679
else:
77-
release_version = parse_version(
80+
release_version = cls.version_from_string(
7881
f"{current_year_short}.{today.month}."
79-
f"{current_version.micro}"
82+
f"{current_version.patch}"
8083
)
8184
return release_version
8285
else:
@@ -85,147 +88,81 @@ def next_calendar_version(self, current_version: Version) -> Version:
8588
f"'{current_year_short}.{today.month}'."
8689
)
8790

88-
def next_minor_version(self, current_version: Version) -> Version:
89-
"""
90-
Get the next minor version from a valid version
91-
92-
Examples:
93-
"1.2.3" will return "1.3.0"
94-
"1.2.3.dev1" will return "1.3.0"
95-
"""
96-
return parse_version(
97-
f"{current_version.major}.{current_version.minor + 1}.0"
98-
)
99-
100-
def next_major_version(self, current_version: Version) -> Version:
91+
@classmethod
92+
def next_major_version(cls, current_version: Version) -> Version:
10193
"""
10294
Get the next major version from a valid version
10395
10496
Examples:
10597
"1.2.3" will return "2.0.0"
10698
"1.2.3.dev1" will return "2.0.0"
99+
"1.2.3-alpha1" will return "2.0.0"
107100
"""
108-
return parse_version(f"{current_version.major + 1}.0.0")
101+
return cls.version_from_string(f"{current_version.major + 1}.0.0")
109102

110-
def next_dev_version(self, current_version: Version) -> Version:
103+
@classmethod
104+
def next_minor_version(cls, current_version: Version) -> Version:
111105
"""
112-
Get the next development version from a valid version
106+
Get the next minor version from a valid version
113107
114108
Examples:
115-
"1.2.3" will return "1.2.4.dev1"
116-
"1.2.3.dev1" will return "1.2.3.dev2"
109+
"1.2.3" will return "1.3.0"
110+
"1.2.3.dev1" will return "1.3.0"
111+
"1.2.3-alpha1" will return "1.3.0"
117112
"""
118-
if current_version.is_devrelease:
119-
release_version = parse_version(
120-
f"{current_version.major}."
121-
f"{current_version.minor}."
122-
f"{current_version.micro }.dev{current_version.dev + 1}"
123-
)
124-
elif current_version.is_prerelease:
125-
release_version = parse_version(
126-
f"{current_version.major}."
127-
f"{current_version.minor}."
128-
f"{current_version.micro }{current_version.pre[0]}"
129-
f"{current_version.pre[1]}+dev1"
130-
)
131-
else:
132-
release_version = parse_version(
133-
f"{current_version.major}."
134-
f"{current_version.minor}."
135-
f"{current_version.micro + 1 }.dev1"
136-
)
137-
138-
return release_version
113+
return cls.version_from_string(
114+
f"{current_version.major}.{current_version.minor + 1}.0"
115+
)
139116

140-
def next_alpha_version(self, current_version: Version) -> Version:
117+
@classmethod
118+
def next_patch_version(cls, current_version: Version) -> Version:
141119
"""
142-
Get the next alpha version from a valid version
120+
Get the next patch version from a valid version
143121
144122
Examples:
145-
"1.2.3" will return "1.2.4a1"
146-
"1.2.3.dev1" will return "1.2.3a1"
123+
"1.2.3" will return "1.2.4"
124+
"1.2.3.dev1" will return "1.2.4"
125+
"1.2.3-alpha1" will return "1.2.4"
147126
"""
148-
if current_version.is_devrelease:
149-
release_version = parse_version(
127+
if current_version.is_pre_release:
128+
next_version = cls.version_from_string(
150129
f"{current_version.major}."
151130
f"{current_version.minor}."
152-
f"{current_version.micro }a1"
153-
)
154-
elif current_version.is_prerelease and current_version.pre[0] == "a":
155-
release_version = parse_version(
156-
f"{current_version.major}."
157-
f"{current_version.minor}."
158-
f"{current_version.micro }a{current_version.pre[1] + 1}"
131+
f"{current_version.patch}"
159132
)
160133
else:
161-
release_version = parse_version(
134+
next_version = cls.version_from_string(
162135
f"{current_version.major}."
163136
f"{current_version.minor}."
164-
f"{current_version.micro + 1 }a1"
137+
f"{current_version.patch + 1}"
165138
)
166139

167-
return release_version
140+
return next_version
168141

169-
def next_beta_version(self, current_version: Version) -> Version:
142+
@staticmethod
143+
@abstractmethod
144+
def next_dev_version(current_version: Version) -> Version:
170145
"""
171-
Get the next alpha version from a valid version
172-
173-
Examples:
174-
"1.2.3" will return "1.2.4b1"
175-
"1.2.3.dev1" will return "1.2.3b1"
146+
Get the next development version from a valid version
176147
"""
177-
if current_version.is_devrelease or (
178-
current_version.is_prerelease and current_version.pre[0] == "a"
179-
):
180-
release_version = parse_version(
181-
f"{current_version.major}."
182-
f"{current_version.minor}."
183-
f"{current_version.micro }b1"
184-
)
185-
elif current_version.is_prerelease and current_version.pre[0] == "b":
186-
release_version = parse_version(
187-
f"{current_version.major}."
188-
f"{current_version.minor}."
189-
f"{current_version.micro }b{current_version.pre[1] + 1}"
190-
)
191-
else:
192-
release_version = parse_version(
193-
f"{current_version.major}."
194-
f"{current_version.minor}."
195-
f"{current_version.micro + 1 }b1"
196-
)
197-
198-
return release_version
199148

200-
def next_release_candidate_version(
201-
self, current_version: Version
202-
) -> Version:
149+
@staticmethod
150+
@abstractmethod
151+
def next_alpha_version(current_version: Version) -> Version:
203152
"""
204153
Get the next alpha version from a valid version
154+
"""
205155

206-
Examples:
207-
"1.2.3" will return "1.2.4rc1"
208-
"1.2.3.dev1" will return "1.2.3rc1"
156+
@staticmethod
157+
@abstractmethod
158+
def next_beta_version(current_version: Version) -> Version:
159+
"""
160+
Get the next beta version from a valid version
209161
"""
210-
if current_version.is_devrelease or (
211-
current_version.is_prerelease and current_version.pre[0] != "rc"
212-
):
213-
release_version = parse_version(
214-
f"{current_version.major}."
215-
f"{current_version.minor}."
216-
f"{current_version.micro }rc1"
217-
)
218-
elif current_version.is_prerelease and current_version.pre[0] == "rc":
219-
release_version = parse_version(
220-
f"{current_version.major}."
221-
f"{current_version.minor}."
222-
f"{current_version.micro }rc{current_version.pre[1] + 1}"
223-
)
224-
else:
225-
release_version = parse_version(
226-
f"{current_version.major}."
227-
f"{current_version.minor}."
228-
f"{current_version.micro + 1 }rc1"
229-
)
230162

231-
return release_version
163+
@staticmethod
164+
@abstractmethod
165+
def next_release_candidate_version(current_version: Version) -> Version:
166+
"""
167+
Get the next release candidate version from a valid version
168+
"""

0 commit comments

Comments
 (0)