Skip to content

Commit

Permalink
Changed the management of file changes
Browse files Browse the repository at this point in the history
File changes are hashable to weed out duplication.
  • Loading branch information
coordt committed Dec 15, 2023
1 parent 84556f8 commit 909396d
Show file tree
Hide file tree
Showing 7 changed files with 53 additions and 38 deletions.
15 changes: 11 additions & 4 deletions bumpversion/config/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class FileChange(BaseModel):
"""A change to make to a file."""

parse: str
serialize: List[str]
serialize: tuple
search: str
replace: str
regex: bool
Expand All @@ -41,6 +41,10 @@ class FileChange(BaseModel):
glob: Optional[str] = None # Conflicts with filename. If both are specified, glob wins
key_path: Optional[str] = None # If specified, and has an appropriate extension, will be treated as a data file

def __hash__(self):
"""Return a hash of the model."""
return hash(tuple(sorted(self.model_dump().items())))

def get_search_pattern(self, context: MutableMapping) -> Tuple[re.Pattern, str]:
"""
Render the search pattern and return the compiled regex pattern and the raw pattern.
Expand Down Expand Up @@ -82,7 +86,7 @@ class Config(BaseSettings):

current_version: Optional[str]
parse: str
serialize: List[str] = Field(min_length=1)
serialize: tuple = Field(min_length=1)
search: str
replace: str
regex: bool
Expand All @@ -97,7 +101,7 @@ class Config(BaseSettings):
commit_args: Optional[str]
scm_info: Optional["SCMInfo"]
parts: Dict[str, VersionPartConfig]
files: List[FileChange]
files: List[FileChange] = Field(default_factory=list)
included_paths: List[str] = Field(default_factory=list)
excluded_paths: List[str] = Field(default_factory=list)
model_config = SettingsConfigDict(env_prefix="bumpversion_")
Expand All @@ -106,8 +110,9 @@ class Config(BaseSettings):
def add_files(self, filename: Union[str, List[str]]) -> None:
"""Add a filename to the list of files."""
filenames = [filename] if isinstance(filename, str) else filename
files = set(self.files)
for name in filenames:
self.files.append(
files.add(
FileChange(
filename=name,
glob=None,
Expand All @@ -120,6 +125,8 @@ def add_files(self, filename: Union[str, List[str]]) -> None:
ignore_missing_version=self.ignore_missing_version,
)
)
self.files = list(files)

self._resolved_filemap = None

@property
Expand Down
4 changes: 2 additions & 2 deletions bumpversion/version_part.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import re
import string
from copy import copy
from typing import Any, Dict, List, MutableMapping, Optional, Union
from typing import Any, Dict, List, MutableMapping, Optional, Tuple, Union

from click import UsageError

Expand Down Expand Up @@ -134,7 +134,7 @@ class VersionConfig:
def __init__(
self,
parse: str,
serialize: List[str],
serialize: Tuple[str],
search: str,
replace: str,
part_configs: Optional[Dict[str, VersionPartConfig]] = None,
Expand Down
11 changes: 6 additions & 5 deletions bumpversion/yaml_dump.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from collections import UserDict
from io import StringIO
from textwrap import indent
from typing import Any, Callable
from typing import Any, Callable, Union

DumperFunc = Callable[[Any], str]

Expand Down Expand Up @@ -89,7 +89,7 @@ def format_dict(val: dict) -> str:

for key, value in sorted(val.items()):
rendered_value = dump(value).strip()
if isinstance(value, (dict, list)):
if isinstance(value, (dict, list, tuple)):
rendered_value = f"\n{indent(rendered_value, INDENT)}"
else:
rendered_value = f" {rendered_value}"
Expand All @@ -101,7 +101,7 @@ def format_dict(val: dict) -> str:
YAML_DUMPERS.add_dumper(dict, format_dict)


def format_list(val: list) -> str:
def format_sequence(val: Union[list, tuple]) -> str:
"""Return a string representation of a value."""
buffer = StringIO()

Expand All @@ -110,7 +110,7 @@ def format_list(val: list) -> str:
if isinstance(item, dict):
rendered_value = indent(rendered_value, INDENT).strip()

if isinstance(item, list):
if isinstance(item, (list, tuple)):
rendered_value = f"\n{indent(rendered_value, INDENT)}"
else:
rendered_value = f" {rendered_value}"
Expand All @@ -119,7 +119,8 @@ def format_list(val: list) -> str:
return buffer.getvalue()


YAML_DUMPERS.add_dumper(list, format_list)
YAML_DUMPERS.add_dumper(list, format_sequence)
YAML_DUMPERS.add_dumper(tuple, format_sequence)


def format_none(_: None) -> str:
Expand Down
14 changes: 7 additions & 7 deletions tests/fixtures/basic_cfg_expected.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
'regex': False,
'replace': '{new_version}',
'search': '{current_version}',
'serialize': ['{major}.{minor}.{patch}-{release}',
'{major}.{minor}.{patch}']},
'serialize': ('{major}.{minor}.{patch}-{release}',
'{major}.{minor}.{patch}')},
{'filename': 'bumpversion/__init__.py',
'glob': None,
'ignore_missing_version': False,
Expand All @@ -21,8 +21,8 @@
'regex': False,
'replace': '{new_version}',
'search': '{current_version}',
'serialize': ['{major}.{minor}.{patch}-{release}',
'{major}.{minor}.{patch}']},
'serialize': ('{major}.{minor}.{patch}-{release}',
'{major}.{minor}.{patch}')},
{'filename': 'CHANGELOG.md',
'glob': None,
'ignore_missing_version': False,
Expand All @@ -31,8 +31,8 @@
'regex': False,
'replace': '**unreleased**\n**v{new_version}**',
'search': '**unreleased**',
'serialize': ['{major}.{minor}.{patch}-{release}',
'{major}.{minor}.{patch}']}],
'serialize': ('{major}.{minor}.{patch}-{release}',
'{major}.{minor}.{patch}')}],
'ignore_missing_version': False,
'included_paths': [],
'message': 'Bump version: {current_version} → {new_version}',
Expand Down Expand Up @@ -63,7 +63,7 @@
'short_branch_name': None,
'tool': None},
'search': '{current_version}',
'serialize': ['{major}.{minor}.{patch}-{release}', '{major}.{minor}.{patch}'],
'serialize': ('{major}.{minor}.{patch}-{release}', '{major}.{minor}.{patch}'),
'sign_tags': False,
'tag': True,
'tag_message': 'Bump version: {current_version} → {new_version}',
Expand Down
30 changes: 15 additions & 15 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ def test_cli_options_override_config(tmp_path: Path, fixtures_path: Path, mocker
assert the_config.current_version == "1.1.0"
assert the_config.allow_dirty
assert the_config.parse == r"XXX(?P<spam>\d+);(?P<blob>\d+);(?P<slurp>\d+)"
assert the_config.serialize == ["XXX{spam};{blob};{slurp}"]
assert the_config.serialize == ("XXX{spam};{blob};{slurp}",)
assert the_config.search == "my-search"
assert the_config.replace == "my-replace"
assert the_config.commit is False
Expand Down Expand Up @@ -203,7 +203,7 @@ def test_listing_with_version_part(tmp_path: Path, fixtures_path: Path):
"current_version=1.0.0",
"excluded_paths=[]",
"parse=(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)(\\-(?P<release>[a-z]+))?",
"serialize=['{major}.{minor}.{patch}-{release}', '{major}.{minor}.{patch}']",
"serialize=('{major}.{minor}.{patch}-{release}', '{major}.{minor}.{patch}')",
"search={current_version}",
"replace={new_version}",
"regex=False",
Expand All @@ -220,19 +220,19 @@ def test_listing_with_version_part(tmp_path: Path, fixtures_path: Path):
(
"files=[{'parse': "
"'(?P<major>\\\\d+)\\\\.(?P<minor>\\\\d+)\\\\.(?P<patch>\\\\d+)(\\\\-(?P<release>[a-z]+))?', "
"'serialize': ['{major}.{minor}.{patch}-{release}', "
"'{major}.{minor}.{patch}'], 'search': '{current_version}', 'replace': "
"'serialize': ('{major}.{minor}.{patch}-{release}', "
"'{major}.{minor}.{patch}'), 'search': '{current_version}', 'replace': "
"'{new_version}', 'regex': False, 'ignore_missing_version': False, "
"'filename': 'setup.py', 'glob': None, 'key_path': None}, {'parse': "
"'(?P<major>\\\\d+)\\\\.(?P<minor>\\\\d+)\\\\.(?P<patch>\\\\d+)(\\\\-(?P<release>[a-z]+))?', "
"'serialize': ['{major}.{minor}.{patch}-{release}', "
"'{major}.{minor}.{patch}'], 'search': '{current_version}', 'replace': "
"'serialize': ('{major}.{minor}.{patch}-{release}', "
"'{major}.{minor}.{patch}'), 'search': '{current_version}', 'replace': "
"'{new_version}', 'regex': False, 'ignore_missing_version': False, "
"'filename': 'bumpversion/__init__.py', 'glob': None, 'key_path': None}, "
"{'parse': "
"'(?P<major>\\\\d+)\\\\.(?P<minor>\\\\d+)\\\\.(?P<patch>\\\\d+)(\\\\-(?P<release>[a-z]+))?', "
"'serialize': ['{major}.{minor}.{patch}-{release}', "
"'{major}.{minor}.{patch}'], 'search': '**unreleased**', 'replace': "
"'serialize': ('{major}.{minor}.{patch}-{release}', "
"'{major}.{minor}.{patch}'), 'search': '**unreleased**', 'replace': "
"'**unreleased**\\n**v{new_version}**', 'regex': False, "
"'ignore_missing_version': False, 'filename': 'CHANGELOG.md', 'glob': None, "
"'key_path': None}]"
Expand Down Expand Up @@ -263,7 +263,7 @@ def test_listing_without_version_part(tmp_path: Path, fixtures_path: Path):
"current_version=1.0.0",
"excluded_paths=[]",
"parse=(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)(\\-(?P<release>[a-z]+))?",
"serialize=['{major}.{minor}.{patch}-{release}', '{major}.{minor}.{patch}']",
"serialize=('{major}.{minor}.{patch}-{release}', '{major}.{minor}.{patch}')",
"search={current_version}",
"replace={new_version}",
"regex=False",
Expand All @@ -280,19 +280,19 @@ def test_listing_without_version_part(tmp_path: Path, fixtures_path: Path):
(
"files=[{'parse': "
"'(?P<major>\\\\d+)\\\\.(?P<minor>\\\\d+)\\\\.(?P<patch>\\\\d+)(\\\\-(?P<release>[a-z]+))?', "
"'serialize': ['{major}.{minor}.{patch}-{release}', "
"'{major}.{minor}.{patch}'], 'search': '{current_version}', 'replace': "
"'serialize': ('{major}.{minor}.{patch}-{release}', "
"'{major}.{minor}.{patch}'), 'search': '{current_version}', 'replace': "
"'{new_version}', 'regex': False, 'ignore_missing_version': False, "
"'filename': 'setup.py', 'glob': None, 'key_path': None}, {'parse': "
"'(?P<major>\\\\d+)\\\\.(?P<minor>\\\\d+)\\\\.(?P<patch>\\\\d+)(\\\\-(?P<release>[a-z]+))?', "
"'serialize': ['{major}.{minor}.{patch}-{release}', "
"'{major}.{minor}.{patch}'], 'search': '{current_version}', 'replace': "
"'serialize': ('{major}.{minor}.{patch}-{release}', "
"'{major}.{minor}.{patch}'), 'search': '{current_version}', 'replace': "
"'{new_version}', 'regex': False, 'ignore_missing_version': False, "
"'filename': 'bumpversion/__init__.py', 'glob': None, 'key_path': None}, "
"{'parse': "
"'(?P<major>\\\\d+)\\\\.(?P<minor>\\\\d+)\\\\.(?P<patch>\\\\d+)(\\\\-(?P<release>[a-z]+))?', "
"'serialize': ['{major}.{minor}.{patch}-{release}', "
"'{major}.{minor}.{patch}'], 'search': '**unreleased**', 'replace': "
"'serialize': ('{major}.{minor}.{patch}-{release}', "
"'{major}.{minor}.{patch}'), 'search': '**unreleased**', 'replace': "
"'**unreleased**\\n**v{new_version}**', 'regex': False, "
"'ignore_missing_version': False, 'filename': 'CHANGELOG.md', 'glob': None, "
"'key_path': None}]"
Expand Down
4 changes: 2 additions & 2 deletions tests/test_config/test_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ def test_multiple_config_files(tmp_path: Path):

assert cfg.current_version == "0.10.5"
assert cfg.parse == "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)(\\-(?P<release>[a-z]+))?"
assert cfg.serialize == ["{major}.{minor}.{patch}-{release}", "{major}.{minor}.{patch}"]
assert cfg.serialize == ("{major}.{minor}.{patch}-{release}", "{major}.{minor}.{patch}")


TOML_EXPECTED_DIFF = (
Expand Down Expand Up @@ -283,7 +283,7 @@ def test_file_overrides_config(fixtures_path: Path):
assert file_map["should_override_parse.txt"].ignore_missing_version == conf.ignore_missing_version

assert file_map["should_override_serialize.txt"].parse == conf.parse
assert file_map["should_override_serialize.txt"].serialize == ["{major}"]
assert file_map["should_override_serialize.txt"].serialize == ("{major}",)
assert file_map["should_override_serialize.txt"].search == conf.search
assert file_map["should_override_serialize.txt"].replace == conf.replace
assert file_map["should_override_serialize.txt"].regex == conf.regex
Expand Down
13 changes: 10 additions & 3 deletions tests/test_yaml.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@


def test_dump_unknown():
assert yaml_dump.dump((1, 2)) == '"(1, 2)"'
assert yaml_dump.dump({1, 2}) == '"{1, 2}"'


def test_format_str():
Expand Down Expand Up @@ -42,10 +42,15 @@ def test_format_dict():
"key8": True,
"key9": False,
"key10": 1.43,
"key11": (1, 2, 3),
}
expected = (
'key: "strval"\n'
"key10: 1.43\n"
"key11:\n"
" - 1\n"
" - 2\n"
" - 3\n"
"key2: 30\n"
"key3: 2023-06-19 13:45:30\n"
"key4: 2023-06-19\n"
Expand All @@ -63,8 +68,10 @@ def test_format_dict():


def test_format_list():
assert yaml_dump.format_list(["item"]) == '- "item"\n'
assert yaml_dump.format_list(["item", ["item2"]]) == '- "item"\n-\n - "item2"\n'
assert yaml_dump.format_sequence(["item"]) == '- "item"\n'
assert yaml_dump.format_sequence(["item", ["item2"]]) == '- "item"\n-\n - "item2"\n'
assert yaml_dump.format_sequence(("item",)) == '- "item"\n'
assert yaml_dump.format_sequence(("item", ("item2",))) == '- "item"\n-\n - "item2"\n'


def test_dump_none_val():
Expand Down

0 comments on commit 909396d

Please sign in to comment.