Coverage for /Users/coordt/Documents/code/bump-my-version/bumpversion/config/__init__.py: 82%
43 statements
« prev ^ index » next coverage.py v7.4.4, created at 2024-06-12 09:26 -0500
« prev ^ index » next coverage.py v7.4.4, created at 2024-06-12 09:26 -0500
1"""Configuration management."""
3from __future__ import annotations
5from typing import TYPE_CHECKING, Any, Union
7from bumpversion.config.files import read_config_file
8from bumpversion.config.models import Config
9from bumpversion.exceptions import ConfigurationError
10from bumpversion.ui import get_indented_logger
12if TYPE_CHECKING: # pragma: no-coverage 12 ↛ 13line 12 didn't jump to line 13, because the condition on line 12 was never true
13 from pathlib import Path
15logger = get_indented_logger(__name__)
17DEFAULTS = {
18 "current_version": None,
19 "parse": r"(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)",
20 "serialize": ("{major}.{minor}.{patch}",),
21 "search": "{current_version}",
22 "replace": "{new_version}",
23 "regex": False,
24 "ignore_missing_version": False,
25 "ignore_missing_files": False,
26 "tag": False,
27 "sign_tags": False,
28 "tag_name": "v{new_version}",
29 "tag_message": "Bump version: {current_version} → {new_version}",
30 "allow_dirty": False,
31 "commit": False,
32 "message": "Bump version: {current_version} → {new_version}",
33 "commit_args": None,
34 "scm_info": None,
35 "parts": {},
36 "files": [],
37}
40def set_config_defaults(parsed_config: dict[str, Any], **overrides: Any) -> dict[str, Any]:
41 """Apply the defaults to the parsed config."""
42 config_dict = DEFAULTS.copy()
44 # We want to strip out unrecognized key-values to avoid inadvertent issues
45 config_dict.update({key: val for key, val in parsed_config.items() if key in DEFAULTS.keys()})
47 allowed_overrides = set(DEFAULTS.keys())
48 config_dict.update({key: val for key, val in overrides.items() if key in allowed_overrides})
50 return config_dict
53def get_configuration(config_file: Union[str, Path, None] = None, **overrides: Any) -> Config:
54 """
55 Return the configuration based on any configuration files and overrides.
57 Args:
58 config_file: An explicit configuration file to use, otherwise search for one
59 **overrides: Specific configuration key-values to override in the configuration
61 Returns:
62 The configuration
63 """
64 from bumpversion.config.utils import get_all_file_configs, get_all_part_configs
65 from bumpversion.scm import SCMInfo, SourceCodeManager, get_scm_info # noqa: F401
67 logger.info("Reading configuration")
68 logger.indent()
70 parsed_config = read_config_file(config_file) if config_file else {}
71 config_dict = set_config_defaults(parsed_config, **overrides)
73 # Set any missing version parts
74 config_dict["parts"] = get_all_part_configs(config_dict)
76 # Set any missing file configuration
77 config_dict["files"] = get_all_file_configs(config_dict)
79 # Resolve the SCMInfo class for Pydantic's BaseSettings
80 Config.model_rebuild()
81 config = Config(**config_dict) # type: ignore[arg-type]
83 # Get the information about the SCM
84 scm_info = get_scm_info(config.tag_name, config.parse)
85 config.scm_info = scm_info
87 # Update and verify the current_version
88 config.current_version = check_current_version(config)
90 logger.dedent()
92 return config
95def check_current_version(config: Config) -> str:
96 """
97 Returns the current version.
99 If the current version is not specified in the config file, command line or env variable,
100 it attempts to retrieve it via a tag.
102 Args:
103 config: The current configuration dictionary.
105 Returns:
106 The version number
108 Raises:
109 ConfigurationError: If it can't find the current version
110 """
111 current_version = config.current_version
112 scm_info = config.scm_info
114 if current_version is None and scm_info.current_version: 114 ↛ 115line 114 didn't jump to line 115, because the condition on line 114 was never true
115 return scm_info.current_version
116 elif current_version and scm_info.current_version and current_version != scm_info.current_version: 116 ↛ 117line 116 didn't jump to line 117, because the condition on line 116 was never true
117 logger.warning(
118 "Specified version (%s) does not match last tagged version (%s)",
119 current_version,
120 scm_info.current_version,
121 )
122 return current_version
123 elif current_version: 123 ↛ 126line 123 didn't jump to line 126, because the condition on line 123 was never false
124 return current_version
126 raise ConfigurationError("Unable to determine the current version.")