Skip to content

Commit 7d05414

Browse files
committed
Refactored versioning models and tests
1 parent 5ed546b commit 7d05414

15 files changed

+623
-182
lines changed

bumpversion/bump.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,11 @@ def get_next_version(
4242
elif version_part:
4343
logger.info("Attempting to increment part '%s'", version_part)
4444
logger.indent()
45-
next_version = current_version.bump(version_part, config.version_config.order)
45+
next_version = current_version.bump(version_part)
4646
else:
4747
raise ConfigurationError("Unable to get the next version.")
4848

49-
logger.info("Values are now: %s", key_val_string(next_version.values))
49+
logger.info("Values are now: %s", key_val_string(next_version.components))
5050
logger.dedent()
5151
return next_version
5252

@@ -113,7 +113,7 @@ def commit_and_tag(
113113
dry_run: bool = False,
114114
) -> None:
115115
"""
116-
Commit and tag the changes, if a tool is configured.
116+
Commit and tag the changes if a tool is configured.
117117
118118
Args:
119119
config: The configuration

bumpversion/config/models.py

+8-11
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,11 @@
1414
if TYPE_CHECKING:
1515
from bumpversion.scm import SCMInfo
1616
from bumpversion.version_part import VersionConfig
17+
from bumpversion.versioning.models import VersionComponentConfig, VersionSpec
1718

1819
logger = get_indented_logger(__name__)
1920

2021

21-
class VersionPartConfig(BaseModel):
22-
"""Configuration of a part of the version."""
23-
24-
values: Optional[list] = None # Optional. Numeric is used if missing or no items in list
25-
optional_value: Optional[str] = None # Optional.
26-
# Defaults to first value. 0 in the case of numeric. Empty string means nothing is optional.
27-
first_value: Union[str, int, None] = None # Optional. Defaults to first value in values
28-
independent: bool = False
29-
30-
3122
class FileChange(BaseModel):
3223
"""A change to make to a file."""
3324

@@ -100,7 +91,7 @@ class Config(BaseSettings):
10091
message: str
10192
commit_args: Optional[str]
10293
scm_info: Optional["SCMInfo"]
103-
parts: Dict[str, VersionPartConfig]
94+
parts: Dict[str, VersionComponentConfig]
10495
files: List[FileChange] = Field(default_factory=list)
10596
included_paths: List[str] = Field(default_factory=list)
10697
excluded_paths: List[str] = Field(default_factory=list)
@@ -169,3 +160,9 @@ def version_config(self) -> "VersionConfig":
169160
from bumpversion.version_part import VersionConfig
170161

171162
return VersionConfig(self.parse, self.serialize, self.search, self.replace, self.parts)
163+
164+
def version_spec(self, version: Optional[str] = None) -> "VersionSpec":
165+
"""Return the version specification."""
166+
from bumpversion.versioning.models import VersionSpec
167+
168+
return VersionSpec(self.parts)

bumpversion/config/utils.py

+21-10
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22
from __future__ import annotations
33

44
import glob
5-
import itertools
65
from typing import Dict, List
76

8-
from bumpversion.config.models import FileChange, VersionPartConfig
9-
from bumpversion.utils import labels_for_format
7+
from exceptions import BumpVersionError
8+
9+
from bumpversion.config.models import FileChange
10+
from bumpversion.versioning.models import VersionComponentConfig
1011

1112

1213
def get_all_file_configs(config_dict: dict) -> List[FileChange]:
@@ -25,15 +26,25 @@ def get_all_file_configs(config_dict: dict) -> List[FileChange]:
2526
return [FileChange(**f) for f in files]
2627

2728

28-
def get_all_part_configs(config_dict: dict) -> Dict[str, VersionPartConfig]:
29+
def get_all_part_configs(config_dict: dict) -> Dict[str, VersionComponentConfig]:
2930
"""Make sure all version parts are included."""
30-
serialize = config_dict["serialize"]
31+
import re
32+
33+
try:
34+
parsing_groups = list(re.compile(config_dict["parse"]).groupindex.keys())
35+
except re.error as e:
36+
raise BumpVersionError(f"Could not parse regex '{config_dict['parse']}': {e}") from e
3137
parts = config_dict["parts"]
32-
all_labels = set(itertools.chain.from_iterable([labels_for_format(fmt) for fmt in serialize]))
33-
return {
34-
label: VersionPartConfig(**parts[label]) if label in parts else VersionPartConfig() # type: ignore[call-arg]
35-
for label in all_labels
36-
}
38+
39+
part_configs = {}
40+
for label in parsing_groups:
41+
is_independent = label.startswith("$")
42+
part_configs[label] = (
43+
VersionComponentConfig(**parts[label])
44+
if label in parts
45+
else VersionComponentConfig(independent=is_independent)
46+
)
47+
return part_configs
3748

3849

3950
def resolve_glob_files(file_cfg: FileChange) -> List[FileChange]:

bumpversion/files.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
from pathlib import Path
77
from typing import Dict, List, MutableMapping, Optional
88

9-
from bumpversion.config.models import FileChange, VersionPartConfig
9+
from versioning.models import VersionComponentConfig
10+
11+
from bumpversion.config.models import FileChange
1012
from bumpversion.exceptions import VersionNotFoundError
1113
from bumpversion.ui import get_indented_logger
1214
from bumpversion.version_part import VersionConfig
@@ -297,7 +299,7 @@ class DataFileUpdater:
297299
def __init__(
298300
self,
299301
file_change: FileChange,
300-
version_part_configs: Dict[str, VersionPartConfig],
302+
version_part_configs: Dict[str, VersionComponentConfig],
301303
) -> None:
302304
self.file_change = file_change
303305
self.version_config = VersionConfig(

bumpversion/version_part.py

+15-10
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55
from typing import Any, Dict, List, MutableMapping, Optional, Tuple
66

77
from click import UsageError
8+
from versioning.models import VersionComponentConfig
89

9-
from bumpversion.config.models import VersionPartConfig
1010
from bumpversion.exceptions import FormattingError, MissingValueError
1111
from bumpversion.ui import get_indented_logger
1212
from bumpversion.utils import labels_for_format
13-
from bumpversion.versioning.models import Version, VersionPart
13+
from bumpversion.versioning.models import Version, VersionComponent, VersionSpec
1414
from bumpversion.versioning.serialization import parse_version
1515

1616
logger = get_indented_logger(__name__)
@@ -27,7 +27,7 @@ def __init__(
2727
serialize: Tuple[str],
2828
search: str,
2929
replace: str,
30-
part_configs: Optional[Dict[str, VersionPartConfig]] = None,
30+
part_configs: Optional[Dict[str, VersionComponentConfig]] = None,
3131
):
3232
try:
3333
self.parse_regex = re.compile(parse, re.VERBOSE)
@@ -36,6 +36,7 @@ def __init__(
3636

3737
self.serialize_formats = serialize
3838
self.part_configs = part_configs or {}
39+
self.version_spec = VersionSpec(self.part_configs)
3940
# TODO: I think these two should be removed from the config object
4041
self.search = search
4142
self.replace = replace
@@ -80,12 +81,16 @@ def parse(self, version_string: Optional[str] = None) -> Optional[Version]:
8081
if not parsed:
8182
return None
8283

83-
_parsed = {
84-
key: VersionPart(self.part_configs[key], value)
85-
for key, value in parsed.items()
86-
if key in self.part_configs
87-
}
88-
return Version(_parsed, version_string)
84+
version = self.version_spec.create_version(parsed)
85+
version.original = version_string
86+
return version
87+
88+
# _parsed = {
89+
# key: VersionComponent(self.part_configs[key], value)
90+
# for key, value in parsed.items()
91+
# if key in self.part_configs
92+
# }
93+
# return Version(_parsed, version_string)
8994

9095
def _serialize(
9196
self, version: Version, serialize_format: str, context: MutableMapping, raise_if_incomplete: bool = False
@@ -128,7 +133,7 @@ def _serialize(
128133
for i, k in enumerate(keys):
129134
v = values[k]
130135

131-
if not isinstance(v, VersionPart):
136+
if not isinstance(v, VersionComponent):
132137
# values coming from environment variables don't need
133138
# representation
134139
continue

bumpversion/versioning/functions.py

+19
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,23 @@ def bump(self, value: str) -> str:
1515
raise NotImplementedError
1616

1717

18+
class IndependentFunction(PartFunction):
19+
"""
20+
This is a class that provides an independent function for version parts.
21+
22+
It simply returns the optional value, which is equal to the first value.
23+
"""
24+
25+
def __init__(self, value: Union[str, int, None] = None):
26+
self.first_value = str(value)
27+
self.optional_value = str(value)
28+
self.independent = True
29+
30+
def bump(self, value: str) -> str:
31+
"""Return the optional value."""
32+
return self.optional_value
33+
34+
1835
class NumericFunction(PartFunction):
1936
"""
2037
This is a class that provides a numeric function for version parts.
@@ -37,6 +54,7 @@ def __init__(self, optional_value: Union[str, int, None] = None, first_value: Un
3754

3855
self.first_value = str(first_value or 0)
3956
self.optional_value = str(optional_value or self.first_value)
57+
self.independent = False
4058

4159
def bump(self, value: Union[str, int]) -> str:
4260
"""Increase the first numerical value by one."""
@@ -80,6 +98,7 @@ def __init__(
8098
raise ValueError("Version part values cannot be empty")
8199

82100
self._values = values
101+
self.independent = False
83102

84103
if optional_value is None:
85104
optional_value = values[0]

0 commit comments

Comments
 (0)