Coverage for /Users/OORDCOR/Documents/code/bump-my-version/bumpversion/config/files_legacy.py: 11%
56 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-15 09:15 -0600
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-15 09:15 -0600
1"""This module handles the legacy config file format."""
2from __future__ import annotations
4import re
5from difflib import context_diff
6from pathlib import Path
7from typing import Any, Dict, Union
9from bumpversion.ui import get_indented_logger
11logger = get_indented_logger(__name__)
14def read_ini_file(file_path: Path) -> Dict[str, Any]: # noqa: C901
15 """
16 Parse an INI file and return a dictionary of sections and their options.
18 Args:
19 file_path: The path to the INI file.
21 Returns:
22 dict: A dictionary of sections and their options.
23 """
24 import configparser
26 from bumpversion import autocast
28 # Create a ConfigParser object and read the INI file
29 config_parser = configparser.RawConfigParser()
30 if file_path.name == "setup.cfg":
31 config_parser = configparser.ConfigParser()
33 config_parser.read(file_path)
35 # Create an empty dictionary to hold the parsed sections and options
36 bumpversion_options: Dict[str, Any] = {"files": [], "parts": {}}
38 # Loop through each section in the INI file
39 for section_name in config_parser.sections():
40 if not section_name.startswith("bumpversion"):
41 continue
43 section_parts = section_name.split(":")
44 num_parts = len(section_parts)
45 options = {key: autocast.autocast_value(val) for key, val in config_parser.items(section_name)}
47 if num_parts == 1: # bumpversion section
48 bumpversion_options.update(options)
49 serialize = bumpversion_options.get("serialize", [])
50 if "message" in bumpversion_options and isinstance(bumpversion_options["message"], list):
51 bumpversion_options["message"] = ",".join(bumpversion_options["message"])
52 if not isinstance(serialize, list):
53 bumpversion_options["serialize"] = [serialize]
54 elif num_parts > 1 and section_parts[1].startswith("file"):
55 file_options = {
56 "filename": section_parts[2],
57 }
58 file_options.update(options)
59 if "replace" in file_options and isinstance(file_options["replace"], list):
60 file_options["replace"] = "\n".join(file_options["replace"])
61 bumpversion_options["files"].append(file_options)
62 elif num_parts > 1 and section_parts[1].startswith("glob"):
63 file_options = {
64 "glob": section_parts[2],
65 }
66 file_options.update(options)
67 if "replace" in file_options and isinstance(file_options["replace"], list):
68 file_options["replace"] = "\n".join(file_options["replace"])
69 bumpversion_options["files"].append(file_options)
70 elif num_parts > 1 and section_parts[1].startswith("part"):
71 bumpversion_options["parts"][section_parts[2]] = options
73 # Return the dictionary of sections and options
74 return bumpversion_options
77def update_ini_config_file(
78 config_file: Union[str, Path], current_version: str, new_version: str, dry_run: bool = False
79) -> None:
80 """
81 Update the current_version key in the configuration file.
83 Instead of parsing and re-writing the config file with new information, it will use
84 a regular expression to just replace the current_version value. The idea is it will
85 avoid unintentional changes (like formatting) to the config file.
87 Args:
88 config_file: The configuration file to explicitly use.
89 current_version: The serialized current version.
90 new_version: The serialized new version.
91 dry_run: True if the update should be a dry run.
92 """
93 cfg_current_version_regex = re.compile(
94 f"(?P<section_prefix>\\[bumpversion]\n[^[]*current_version\\s*=\\s*)(?P<version>{current_version})",
95 re.MULTILINE,
96 )
98 config_path = Path(config_file)
99 existing_config = config_path.read_text()
100 if config_path.suffix == ".cfg" and cfg_current_version_regex.search(existing_config):
101 sub_str = f"\\g<section_prefix>{new_version}"
102 new_config = cfg_current_version_regex.sub(sub_str, existing_config)
103 else:
104 logger.info("Could not find the current version in the config file: %s.", config_path)
105 return
107 logger.info(
108 "%s to config file %s:",
109 "Would write" if dry_run else "Writing",
110 config_path,
111 )
113 logger.info(
114 "\n".join(
115 list(
116 context_diff(
117 existing_config.splitlines(),
118 new_config.splitlines(),
119 fromfile=f"before {config_path}",
120 tofile=f"after {config_path}",
121 lineterm="",
122 )
123 )
124 )
125 )
127 if not dry_run:
128 config_path.write_text(new_config)