Coverage for /Users/coordt/Documents/code/bump-my-version/bumpversion/config/files_legacy.py: 10%

62 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-06-12 09:26 -0500

1"""This module handles the legacy config file format.""" 

2 

3from __future__ import annotations 

4 

5import re 

6from difflib import context_diff 

7from pathlib import Path 

8from typing import Any, Dict, Union 

9 

10from bumpversion.ui import get_indented_logger 

11 

12logger = get_indented_logger(__name__) 

13 

14 

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. 

18 

19 Args: 

20 file_path: The path to the INI file. 

21 

22 Returns: 

23 dict: A dictionary of sections and their options. 

24 """ 

25 import configparser 

26 

27 from bumpversion import autocast 

28 

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() 

33 

34 config_parser.read(file_path) 

35 

36 # Create an empty dictionary to hold the parsed sections and options 

37 bumpversion_options: Dict[str, Any] = {"files": [], "parts": {}} 

38 

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 

43 

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"]) 

49 

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 

79 

80 # Return the dictionary of sections and options 

81 return bumpversion_options 

82 

83 

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. 

89 

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. 

93 

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 ) 

104 

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 

113 

114 logger.info( 

115 "%s to config file %s:", 

116 "Would write" if dry_run else "Writing", 

117 config_path, 

118 ) 

119 

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 ) 

133 

134 if not dry_run: 

135 config_path.write_text(new_config, encoding="utf-8")