Skip to content

Commit e160b40

Browse files
committed
Added key_path to FileConfig
- Also made all attributes required except `filename`, `glob`, and `key_path`
1 parent ff5aaaa commit e160b40

8 files changed

+69
-38
lines changed

bumpversion/config.py

+8-6
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,15 @@ class VersionPartConfig(BaseModel):
3737
class FileConfig(BaseModel):
3838
"""Search and replace file config."""
3939

40+
parse: str
41+
serialize: List[str]
42+
search: str
43+
replace: str
44+
regex: bool
45+
ignore_missing_version: bool
4046
filename: Optional[str] = None
4147
glob: Optional[str] = None # Conflicts with filename. If both are specified, glob wins
42-
parse: Optional[str] = None # If different from outer scope
43-
serialize: Optional[List[str]] = None # If different from outer scope
44-
search: Optional[str] = None # If different from outer scope
45-
replace: Optional[str] = None # If different from outer scope
46-
regex: Optional[bool] = None # If different from outer scope
47-
ignore_missing_version: Optional[bool] = None
48+
key_path: Optional[str] = None # If specified, and has an appropriate extension, will be treated as a data file
4849

4950

5051
class Config(BaseSettings):
@@ -82,6 +83,7 @@ def add_files(self, filename: Union[str, List[str]]) -> None:
8283
FileConfig(
8384
filename=name,
8485
glob=None,
86+
key_path=None,
8587
parse=self.parse,
8688
serialize=self.serialize,
8789
search=self.search,

bumpversion/show.py

+6-5
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from bumpversion.config import Config
99
from bumpversion.exceptions import BadInputError
1010
from bumpversion.ui import print_error, print_info
11-
from bumpversion.utils import get_context
11+
from bumpversion.utils import get_context, recursive_sort_dict
1212

1313

1414
def output_default(value: dict) -> None:
@@ -17,15 +17,15 @@ def output_default(value: dict) -> None:
1717
print_info(next(iter(value.values())))
1818
else:
1919
buffer = StringIO()
20-
pprint(value, stream=buffer) # noqa: T203
20+
pprint(value, stream=buffer, sort_dicts=True) # noqa: T203
2121
print_info(buffer.getvalue())
2222

2323

2424
def output_yaml(value: dict) -> None:
2525
"""Output the value as yaml."""
2626
from bumpversion.yaml_dump import dump
2727

28-
print_info(dump(value))
28+
print_info(dump(recursive_sort_dict(value)))
2929

3030

3131
def output_json(value: dict) -> None:
@@ -117,13 +117,14 @@ def log_list(config: Config, version_part: Optional[str], new_version: Optional[
117117

118118
print_info(f"new_version={next_version_str}")
119119

120-
for key, value in config.dict(exclude={"scm_info", "parts"}).items():
120+
config_dict = recursive_sort_dict(config.model_dump(exclude={"scm_info", "parts"}))
121+
for key, value in config_dict.items():
121122
print_info(f"{key}={value}")
122123

123124

124125
def do_show(*args, config: Config, format_: str = "default", increment: Optional[str] = None) -> None:
125126
"""Show current version or configuration information."""
126-
config_dict = config.dict()
127+
config_dict = config.model_dump()
127128
ctx = get_context(config)
128129

129130
if increment:

bumpversion/utils.py

+9-1
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,21 @@
22
import string
33
from collections import ChainMap
44
from dataclasses import asdict
5-
from typing import TYPE_CHECKING, List, Optional
5+
from typing import TYPE_CHECKING, Any, List, Optional
66

77
if TYPE_CHECKING: # pragma: no-coverage
88
from bumpversion.config import Config
99
from bumpversion.version_part import Version
1010

1111

12+
def recursive_sort_dict(input_value: Any) -> Any:
13+
"""Sort a dictionary recursively."""
14+
if not isinstance(input_value, dict):
15+
return input_value
16+
17+
return {key: recursive_sort_dict(input_value[key]) for key in sorted(input_value.keys())}
18+
19+
1220
def key_val_string(d: dict) -> str:
1321
"""Render the dictionary as a comma-delimited key=value string."""
1422
return ", ".join(f"{k}={v}" for k, v in sorted(d.items()))

bumpversion/version_part.py

+1
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ def __init__(
146146

147147
self.serialize_formats = serialize
148148
self.part_configs = part_configs or {}
149+
# TODO: I think these two should be removed from the config object
149150
self.search = search
150151
self.replace = replace
151152

tests/fixtures/basic_cfg_expected.txt

+3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
'files': [{'filename': 'setup.py',
77
'glob': None,
88
'ignore_missing_version': False,
9+
'key_path': None,
910
'parse': '(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)(\\-(?P<release>[a-z]+))?',
1011
'regex': False,
1112
'replace': '{new_version}',
@@ -15,6 +16,7 @@
1516
{'filename': 'bumpversion/__init__.py',
1617
'glob': None,
1718
'ignore_missing_version': False,
19+
'key_path': None,
1820
'parse': '(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)(\\-(?P<release>[a-z]+))?',
1921
'regex': False,
2022
'replace': '{new_version}',
@@ -24,6 +26,7 @@
2426
{'filename': 'CHANGELOG.md',
2527
'glob': None,
2628
'ignore_missing_version': False,
29+
'key_path': None,
2730
'parse': '(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)(\\-(?P<release>[a-z]+))?',
2831
'regex': False,
2932
'replace': '**unreleased**\n**v{new_version}**',

tests/fixtures/basic_cfg_expected.yaml

+3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ files:
88
- filename: "setup.py"
99
glob: null
1010
ignore_missing_version: false
11+
key_path: null
1112
parse: "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)(\\-(?P<release>[a-z]+))?"
1213
regex: false
1314
replace: "{new_version}"
@@ -18,6 +19,7 @@ files:
1819
- filename: "bumpversion/__init__.py"
1920
glob: null
2021
ignore_missing_version: false
22+
key_path: null
2123
parse: "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)(\\-(?P<release>[a-z]+))?"
2224
regex: false
2325
replace: "{new_version}"
@@ -28,6 +30,7 @@ files:
2830
- filename: "CHANGELOG.md"
2931
glob: null
3032
ignore_missing_version: false
33+
key_path: null
3134
parse: "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)(\\-(?P<release>[a-z]+))?"
3235
regex: false
3336
replace: "**unreleased**\n**v{new_version}**"

tests/fixtures/basic_cfg_expected_full.json

+3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"filename": "setup.py",
1010
"glob": null,
1111
"ignore_missing_version": false,
12+
"key_path": null,
1213
"parse": "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)(\\-(?P<release>[a-z]+))?",
1314
"regex": false,
1415
"replace": "{new_version}",
@@ -22,6 +23,7 @@
2223
"filename": "bumpversion/__init__.py",
2324
"glob": null,
2425
"ignore_missing_version": false,
26+
"key_path": null,
2527
"parse": "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)(\\-(?P<release>[a-z]+))?",
2628
"regex": false,
2729
"replace": "{new_version}",
@@ -35,6 +37,7 @@
3537
"filename": "CHANGELOG.md",
3638
"glob": null,
3739
"ignore_missing_version": false,
40+
"key_path": null,
3841
"parse": "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)(\\-(?P<release>[a-z]+))?",
3942
"regex": false,
4043
"replace": "**unreleased**\n**v{new_version}**",

tests/test_cli.py

+36-26
Original file line numberDiff line numberDiff line change
@@ -218,19 +218,24 @@ def test_listing_with_version_part(tmp_path: Path, fixtures_path: Path):
218218
"message=Bump version: {current_version} → {new_version}",
219219
"commit_args=None",
220220
(
221-
"files=["
222-
"{'filename': 'setup.py', 'glob': None, 'parse': "
223-
"'(?P<major>\\\\d+)\\\\.(?P<minor>\\\\d+)\\\\.(?P<patch>\\\\d+)(\\\\-(?P<release>[a-z]+))?', 'serialize': "
224-
"['{major}.{minor}.{patch}-{release}', '{major}.{minor}.{patch}'], 'search': '{current_version}', "
225-
"'replace': '{new_version}', 'regex': False, 'ignore_missing_version': False}, "
226-
"{'filename': 'bumpversion/__init__.py', 'glob': None, 'parse': "
227-
"'(?P<major>\\\\d+)\\\\.(?P<minor>\\\\d+)\\\\.(?P<patch>\\\\d+)(\\\\-(?P<release>[a-z]+))?', 'serialize': "
228-
"['{major}.{minor}.{patch}-{release}', '{major}.{minor}.{patch}'], 'search': '{current_version}', "
229-
"'replace': '{new_version}', 'regex': False, 'ignore_missing_version': False}, "
230-
"{'filename': 'CHANGELOG.md', 'glob': None, 'parse': "
231-
"'(?P<major>\\\\d+)\\\\.(?P<minor>\\\\d+)\\\\.(?P<patch>\\\\d+)(\\\\-(?P<release>[a-z]+))?', 'serialize': "
232-
"['{major}.{minor}.{patch}-{release}', '{major}.{minor}.{patch}'], 'search': '**unreleased**', "
233-
"'replace': '**unreleased**\\n**v{new_version}**', 'regex': False, 'ignore_missing_version': False}]"
221+
"files=[{'parse': "
222+
"'(?P<major>\\\\d+)\\\\.(?P<minor>\\\\d+)\\\\.(?P<patch>\\\\d+)(\\\\-(?P<release>[a-z]+))?', "
223+
"'serialize': ['{major}.{minor}.{patch}-{release}', "
224+
"'{major}.{minor}.{patch}'], 'search': '{current_version}', 'replace': "
225+
"'{new_version}', 'regex': False, 'ignore_missing_version': False, "
226+
"'filename': 'setup.py', 'glob': None, 'key_path': None}, {'parse': "
227+
"'(?P<major>\\\\d+)\\\\.(?P<minor>\\\\d+)\\\\.(?P<patch>\\\\d+)(\\\\-(?P<release>[a-z]+))?', "
228+
"'serialize': ['{major}.{minor}.{patch}-{release}', "
229+
"'{major}.{minor}.{patch}'], 'search': '{current_version}', 'replace': "
230+
"'{new_version}', 'regex': False, 'ignore_missing_version': False, "
231+
"'filename': 'bumpversion/__init__.py', 'glob': None, 'key_path': None}, "
232+
"{'parse': "
233+
"'(?P<major>\\\\d+)\\\\.(?P<minor>\\\\d+)\\\\.(?P<patch>\\\\d+)(\\\\-(?P<release>[a-z]+))?', "
234+
"'serialize': ['{major}.{minor}.{patch}-{release}', "
235+
"'{major}.{minor}.{patch}'], 'search': '**unreleased**', 'replace': "
236+
"'**unreleased**\\n**v{new_version}**', 'regex': False, "
237+
"'ignore_missing_version': False, 'filename': 'CHANGELOG.md', 'glob': None, "
238+
"'key_path': None}]"
234239
),
235240
}
236241

@@ -273,19 +278,24 @@ def test_listing_without_version_part(tmp_path: Path, fixtures_path: Path):
273278
"message=Bump version: {current_version} → {new_version}",
274279
"commit_args=None",
275280
(
276-
"files=["
277-
"{'filename': 'setup.py', 'glob': None, 'parse': "
278-
"'(?P<major>\\\\d+)\\\\.(?P<minor>\\\\d+)\\\\.(?P<patch>\\\\d+)(\\\\-(?P<release>[a-z]+))?', 'serialize': "
279-
"['{major}.{minor}.{patch}-{release}', '{major}.{minor}.{patch}'], 'search': '{current_version}', "
280-
"'replace': '{new_version}', 'regex': False, 'ignore_missing_version': False}, "
281-
"{'filename': 'bumpversion/__init__.py', 'glob': None, 'parse': "
282-
"'(?P<major>\\\\d+)\\\\.(?P<minor>\\\\d+)\\\\.(?P<patch>\\\\d+)(\\\\-(?P<release>[a-z]+))?', 'serialize': "
283-
"['{major}.{minor}.{patch}-{release}', '{major}.{minor}.{patch}'], 'search': '{current_version}', "
284-
"'replace': '{new_version}', 'regex': False, 'ignore_missing_version': False}, "
285-
"{'filename': 'CHANGELOG.md', 'glob': None, 'parse': "
286-
"'(?P<major>\\\\d+)\\\\.(?P<minor>\\\\d+)\\\\.(?P<patch>\\\\d+)(\\\\-(?P<release>[a-z]+))?', 'serialize': "
287-
"['{major}.{minor}.{patch}-{release}', '{major}.{minor}.{patch}'], 'search': '**unreleased**', "
288-
"'replace': '**unreleased**\\n**v{new_version}**', 'regex': False, 'ignore_missing_version': False}]"
281+
"files=[{'parse': "
282+
"'(?P<major>\\\\d+)\\\\.(?P<minor>\\\\d+)\\\\.(?P<patch>\\\\d+)(\\\\-(?P<release>[a-z]+))?', "
283+
"'serialize': ['{major}.{minor}.{patch}-{release}', "
284+
"'{major}.{minor}.{patch}'], 'search': '{current_version}', 'replace': "
285+
"'{new_version}', 'regex': False, 'ignore_missing_version': False, "
286+
"'filename': 'setup.py', 'glob': None, 'key_path': None}, {'parse': "
287+
"'(?P<major>\\\\d+)\\\\.(?P<minor>\\\\d+)\\\\.(?P<patch>\\\\d+)(\\\\-(?P<release>[a-z]+))?', "
288+
"'serialize': ['{major}.{minor}.{patch}-{release}', "
289+
"'{major}.{minor}.{patch}'], 'search': '{current_version}', 'replace': "
290+
"'{new_version}', 'regex': False, 'ignore_missing_version': False, "
291+
"'filename': 'bumpversion/__init__.py', 'glob': None, 'key_path': None}, "
292+
"{'parse': "
293+
"'(?P<major>\\\\d+)\\\\.(?P<minor>\\\\d+)\\\\.(?P<patch>\\\\d+)(\\\\-(?P<release>[a-z]+))?', "
294+
"'serialize': ['{major}.{minor}.{patch}-{release}', "
295+
"'{major}.{minor}.{patch}'], 'search': '**unreleased**', 'replace': "
296+
"'**unreleased**\\n**v{new_version}**', 'regex': False, "
297+
"'ignore_missing_version': False, 'filename': 'CHANGELOG.md', 'glob': None, "
298+
"'key_path': None}]"
289299
),
290300
}
291301

0 commit comments

Comments
 (0)