|
3 | 3 | import re
|
4 | 4 | from copy import deepcopy
|
5 | 5 | from difflib import context_diff
|
6 |
| -from typing import List, MutableMapping, Optional, Tuple |
| 6 | +from pathlib import Path |
| 7 | +from typing import Dict, List, MutableMapping, Optional, Tuple |
7 | 8 |
|
8 | 9 | from bumpversion.config.models import FileConfig, VersionPartConfig
|
9 | 10 | from bumpversion.exceptions import VersionNotFoundError
|
@@ -248,3 +249,91 @@ def _check_files_contain_version(
|
248 | 249 | for f in files:
|
249 | 250 | context["current_version"] = f.version_config.serialize(current_version, context)
|
250 | 251 | f.contains_version(current_version, context)
|
| 252 | + |
| 253 | + |
| 254 | +class FileUpdater: |
| 255 | + """A class to handle updating files.""" |
| 256 | + |
| 257 | + def __init__( |
| 258 | + self, |
| 259 | + file_cfg: FileConfig, |
| 260 | + version_config: VersionConfig, |
| 261 | + search: Optional[str] = None, |
| 262 | + replace: Optional[str] = None, |
| 263 | + ) -> None: |
| 264 | + self.path = file_cfg.filename |
| 265 | + self.version_config = version_config |
| 266 | + self.parse = file_cfg.parse or version_config.parse_regex.pattern |
| 267 | + self.serialize = file_cfg.serialize or version_config.serialize_formats |
| 268 | + self.search = search or file_cfg.search or version_config.search |
| 269 | + self.replace = replace or file_cfg.replace or version_config.replace |
| 270 | + self.regex = file_cfg.regex or False |
| 271 | + self.ignore_missing_version = file_cfg.ignore_missing_version or False |
| 272 | + self.version_config = VersionConfig( |
| 273 | + self.parse, self.serialize, self.search, self.replace, version_config.part_configs |
| 274 | + ) |
| 275 | + self._newlines: Optional[str] = None |
| 276 | + |
| 277 | + def update_file( |
| 278 | + self, current_version: Version, new_version: Version, context: MutableMapping, dry_run: bool = False |
| 279 | + ) -> None: |
| 280 | + """Update the files.""" |
| 281 | + # TODO: Implement this |
| 282 | + pass |
| 283 | + |
| 284 | + |
| 285 | +class DataFileUpdater: |
| 286 | + """A class to handle updating files.""" |
| 287 | + |
| 288 | + def __init__( |
| 289 | + self, |
| 290 | + file_cfg: FileConfig, |
| 291 | + version_part_configs: Dict[str, VersionPartConfig], |
| 292 | + ) -> None: |
| 293 | + self.path = Path(file_cfg.filename) |
| 294 | + self.key_path = file_cfg.key_path |
| 295 | + self.search = file_cfg.search |
| 296 | + self.replace = file_cfg.replace |
| 297 | + self.regex = file_cfg.regex |
| 298 | + self.ignore_missing_version = file_cfg.ignore_missing_version |
| 299 | + self.version_config = VersionConfig( |
| 300 | + file_cfg.parse, file_cfg.serialize, file_cfg.search, file_cfg.replace, version_part_configs |
| 301 | + ) |
| 302 | + |
| 303 | + def update_file( |
| 304 | + self, current_version: Version, new_version: Version, context: MutableMapping, dry_run: bool = False |
| 305 | + ) -> None: |
| 306 | + """Update the files.""" |
| 307 | + new_context = deepcopy(context) |
| 308 | + new_context["current_version"] = self.version_config.serialize(current_version, context) |
| 309 | + new_context["new_version"] = self.version_config.serialize(new_version, context) |
| 310 | + search_for, raw_search_pattern = get_search_pattern(self.search, new_context, self.regex) |
| 311 | + replace_with = self.replace.format(**new_context) |
| 312 | + if self.path.suffix == ".toml": |
| 313 | + self._update_toml_file(search_for, raw_search_pattern, replace_with, dry_run) |
| 314 | + |
| 315 | + def _update_toml_file( |
| 316 | + self, search_for: re.Pattern, raw_search_pattern: str, replace_with: str, dry_run: bool = False |
| 317 | + ) -> None: |
| 318 | + """Update a TOML file.""" |
| 319 | + import dotted |
| 320 | + import tomlkit |
| 321 | + |
| 322 | + toml_data = tomlkit.parse(self.path.read_text()) |
| 323 | + value_before = dotted.get(toml_data, self.key_path) |
| 324 | + |
| 325 | + if value_before is None: |
| 326 | + raise KeyError(f"Key path '{self.key_path}' does not exist in {self.path}") |
| 327 | + elif not contains_pattern(search_for, value_before) and not self.ignore_missing_version: |
| 328 | + raise ValueError( |
| 329 | + f"Key '{self.key_path}' in {self.path} does not contain the correct contents: {raw_search_pattern}" |
| 330 | + ) |
| 331 | + |
| 332 | + new_value = search_for.sub(replace_with, value_before) |
| 333 | + log_changes(f"{self.path}:{self.key_path}", value_before, new_value, dry_run) |
| 334 | + |
| 335 | + if dry_run: |
| 336 | + return |
| 337 | + |
| 338 | + dotted.update(toml_data, self.key_path, new_value) |
| 339 | + self.path.write_text(tomlkit.dumps(toml_data)) |
0 commit comments