Skip to content

Commit 5b90817

Browse files
committed
Refactor and rename version_part to versioning.version_config
Moved `version_part.py` to `versioning/version_config.py` and updated all import statements accordingly. Enhanced error handling in `VersionConfig` by adding `raise_error` flag and relevant exception raising for invalid version strings. Refined tests to reflect these changes.
1 parent ad46978 commit 5b90817

10 files changed

+126
-97
lines changed

bumpversion/config/models.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515

1616
if TYPE_CHECKING:
1717
from bumpversion.scm import SCMInfo
18-
from bumpversion.version_part import VersionConfig
1918
from bumpversion.versioning.models import VersionSpec
19+
from bumpversion.versioning.version_config import VersionConfig
2020

2121
logger = get_indented_logger(__name__)
2222

@@ -166,7 +166,7 @@ def files_to_modify(self) -> List[FileChange]:
166166
@property
167167
def version_config(self) -> "VersionConfig":
168168
"""Return the version configuration."""
169-
from bumpversion.version_part import VersionConfig
169+
from bumpversion.versioning.version_config import VersionConfig
170170

171171
return VersionConfig(self.parse, self.serialize, self.search, self.replace, self.parts)
172172

bumpversion/files.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
from bumpversion.exceptions import VersionNotFoundError
1212
from bumpversion.ui import get_indented_logger
1313
from bumpversion.utils import get_nested_value, set_nested_value
14-
from bumpversion.version_part import VersionConfig
1514
from bumpversion.versioning.models import Version, VersionComponentSpec
15+
from bumpversion.versioning.version_config import VersionConfig
1616

1717
logger = get_indented_logger(__name__)
1818

bumpversion/version_part.py bumpversion/versioning/version_config.py

+11-4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from click import UsageError
77

8+
from bumpversion.exceptions import BumpVersionError
89
from bumpversion.ui import get_indented_logger
910
from bumpversion.utils import labels_for_format
1011
from bumpversion.versioning.models import Version, VersionComponentSpec, VersionSpec
@@ -29,7 +30,7 @@ def __init__(
2930
try:
3031
self.parse_regex = re.compile(parse, re.VERBOSE)
3132
except re.error as e:
32-
raise UsageError(f"--parse '{parse}' is not a valid regex.") from e
33+
raise UsageError(f"'{parse}' is not a valid regex.") from e
3334

3435
self.serialize_formats = serialize
3536
self.part_configs = part_configs or {}
@@ -38,7 +39,7 @@ def __init__(
3839
self.search = search
3940
self.replace = replace
4041

41-
def __repr__(self) -> str:
42+
def __repr__(self) -> str: # pragma: no-coverage
4243
return f"<bumpversion.VersionConfig:{self.parse_regex.pattern}:{self.serialize_formats}>"
4344

4445
def __eq__(self, other: Any) -> bool:
@@ -63,19 +64,25 @@ def order(self) -> List[str]:
6364
"""
6465
return labels_for_format(self.serialize_formats[0])
6566

66-
def parse(self, version_string: Optional[str] = None) -> Optional[Version]:
67+
def parse(self, version_string: Optional[str] = None, raise_error: bool = False) -> Optional[Version]:
6768
"""
6869
Parse a version string into a Version object.
6970
7071
Args:
7172
version_string: Version string to parse
73+
raise_error: Raise an exception if a version string is invalid
7274
7375
Returns:
7476
A Version object representing the string.
77+
78+
Raises:
79+
BumpversionException: If a version string is invalid and raise_error is True.
7580
"""
7681
parsed = parse_version(version_string, self.parse_regex.pattern)
7782

78-
if not parsed:
83+
if not parsed and raise_error:
84+
raise BumpVersionError(f"Unable to parse version {version_string} using {self.parse_regex.pattern}")
85+
elif not parsed:
7986
return None
8087

8188
version = self.version_spec.create_version(parsed)

tests/conftest.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def inside_dir(dirpath: Path) -> Generator:
4242
def get_config_data(overrides: dict) -> tuple:
4343
"""Get the configuration, version_config and version."""
4444
from bumpversion import config
45-
from bumpversion.version_part import VersionConfig
45+
from bumpversion.versioning.version_config import VersionConfig
4646

4747
conf = config.get_configuration(config_file="missing", **overrides)
4848
version_config = VersionConfig(conf.parse, conf.serialize, conf.search, conf.replace, conf.parts)

tests/test_bump.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ def test_commit_and_tag_with_config_file(mock_context):
283283
def test_key_path_required_for_toml_change(tmp_path: Path, caplog):
284284
"""If the key_path is not provided, the toml file should use standard search and replace."""
285285
from bumpversion import config
286-
from bumpversion.version_part import VersionConfig
286+
from bumpversion.versioning.version_config import VersionConfig
287287

288288
# Arrange
289289
config_path = tmp_path / "pyproject.toml"

tests/test_configuredfile.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""Tests for the ConfiguredFile class."""
22

33
from bumpversion.files import ConfiguredFile, FileChange
4-
from bumpversion.version_part import VersionConfig
4+
from bumpversion.versioning.version_config import VersionConfig
55
from bumpversion.versioning.models import VersionComponentSpec
66

77

tests/test_files.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from bumpversion.context import get_context
1818
from bumpversion.exceptions import VersionNotFoundError
1919
from bumpversion.ui import setup_logging, get_indented_logger
20-
from bumpversion.version_part import VersionConfig
20+
from bumpversion.versioning.version_config import VersionConfig
2121
from tests.conftest import get_config_data, inside_dir
2222

2323

tests/test_version_part.py

-86
This file was deleted.

tests/test_versioning/test_models_version.py

+29
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from freezegun import freeze_time
44

5+
from bumpversion import exceptions
56
from bumpversion.versioning.models import VersionComponentSpec
67
from bumpversion.versioning.models import VersionSpec
78
import pytest
@@ -132,6 +133,34 @@ def test_changes_component_and_dependents(self, semver_version_spec: VersionSpec
132133
assert major_version_str == "2.0.0.4.6"
133134
assert build_version_str == "1.2.3.5.6"
134135

136+
def test_invalid_component_raises_error(self, semver_version_spec: VersionSpec):
137+
"""If you bump a version with an invalid component name raises an error."""
138+
version1 = semver_version_spec.create_version(
139+
{"major": "1", "minor": "2", "patch": "3", "build": "4", "auto": "5"}
140+
)
141+
with pytest.raises(exceptions.InvalidVersionPartError, match="No part named 'bugfix'"):
142+
version1.bump("bugfix")
143+
144+
def test_independent_component_is_independent(self, semver_version_spec: VersionSpec):
145+
"""An independent component can only get bumped independently."""
146+
# Arrange
147+
version1 = semver_version_spec.create_version(
148+
{"major": "1", "minor": "2", "patch": "3", "build": "4", "auto": "5"}
149+
)
150+
151+
# Act & Assert
152+
build_version_bump = version1.bump("build")
153+
assert build_version_bump["build"].value == "5"
154+
assert build_version_bump["major"].value == "1"
155+
156+
major_version_bump = build_version_bump.bump("major")
157+
assert major_version_bump["build"].value == "5"
158+
assert major_version_bump["major"].value == "2"
159+
160+
build_version_bump2 = major_version_bump.bump("build")
161+
assert build_version_bump2["build"].value == "6"
162+
assert build_version_bump2["major"].value == "2"
163+
135164
class TestRequiredComponents:
136165
"""Tests of the required_keys function."""
137166

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
from pathlib import Path
2+
3+
import pytest
4+
from click import UsageError
5+
6+
from bumpversion import exceptions
7+
from bumpversion.context import get_context
8+
from bumpversion.versioning.version_config import VersionConfig
9+
from tests.conftest import get_config_data
10+
11+
12+
def test_invalid_parse_raises_error():
13+
"""An invalid parse regex value raises an error."""
14+
with pytest.raises(exceptions.UsageError):
15+
VersionConfig(r"(.+", ("{major}.{minor}.{patch}",), search="", replace="")
16+
17+
18+
class TestParse:
19+
"""Tests for parsing a version."""
20+
21+
def test_cant_parse_returns_none(self):
22+
"""The default behavior when unable to parse a string is to return None."""
23+
# Assemble
24+
overrides = {
25+
"current_version": "19.6.0",
26+
"parse": r"(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+).*",
27+
"serialize": ["{major}.{minor}.{patch}"],
28+
}
29+
_, version_config, current_version = get_config_data(overrides)
30+
31+
# Act and Assert
32+
assert version_config.parse("A.B.C") is None
33+
34+
def test_cant_parse_raises_error_when_set(self):
35+
"""When `raise_error` is enabled, the inability to parse a string will raise an error."""
36+
# Assemble
37+
overrides = {
38+
"current_version": "19.6.0",
39+
"parse": r"(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+).*",
40+
"serialize": ["{major}.{minor}.{patch}"],
41+
}
42+
_, version_config, current_version = get_config_data(overrides)
43+
44+
# Act and Assert
45+
with pytest.raises(exceptions.UsageError):
46+
version_config.parse("A.B.C", raise_error=True)
47+
48+
49+
class TestSerialize:
50+
"""Tests for the VersionConfig.serialize() method."""
51+
52+
def test_distance_to_latest_tag_in_pattern(self):
53+
"""Using ``distance_to_latest_tag`` in the serialization string outputs correctly."""
54+
from bumpversion.scm import Git, SCMInfo
55+
56+
overrides = {
57+
"current_version": "19.6.0",
58+
"parse": r"(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+).*",
59+
"serialize": ["{major}.{minor}.{patch}-pre{distance_to_latest_tag}"],
60+
}
61+
conf, version_config, current_version = get_config_data(overrides)
62+
conf.scm_info = SCMInfo(
63+
tool=Git, commit_sha="1234123412341234", distance_to_latest_tag=3, current_version="19.6.0", dirty=False
64+
)
65+
assert version_config.serialize(current_version, get_context(conf)) == "19.6.0-pre3"
66+
67+
def test_environment_var_in_serialize_pattern(self):
68+
"""Environment variables are serialized correctly."""
69+
import os
70+
71+
os.environ["BUILD_NUMBER"] = "567"
72+
overrides = {
73+
"current_version": "2.3.4",
74+
"parse": r"(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+).*",
75+
"serialize": ["{major}.{minor}.{patch}-pre{$BUILD_NUMBER}"],
76+
}
77+
conf, version_config, current_version = get_config_data(overrides)
78+
assert version_config.serialize(current_version, get_context(conf)) == "2.3.4-pre567"
79+
del os.environ["BUILD_NUMBER"]

0 commit comments

Comments
 (0)