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