Coverage for src/rhiza_tools/config.py: 100%

30 statements  

« prev     ^ index     » next       coverage.py v7.14.1, created at 2026-06-30 13:37 +0000

1"""Configuration management for rhiza-tools. 

2 

3This module provides configuration loading and access for rhiza-tools. 

4It reads configuration from a TOML file and provides convenient access 

5to tool-specific settings. 

6 

7Example: 

8 Load configuration from default path:: 

9 

10 from rhiza_tools.config import load_config 

11 

12 config = load_config() 

13 bumpversion_config = config.bumpversion 

14 

15 Load configuration from custom path:: 

16 

17 from pathlib import Path 

18 from rhiza_tools.config import load_config 

19 

20 config = load_config(Path("custom/.cfg.toml")) 

21 value = config.get("custom_key", "default_value") 

22""" 

23 

24from pathlib import Path 

25from typing import Any 

26 

27import tomlkit 

28import tomlkit.exceptions 

29from loguru import logger 

30 

31from rhiza_tools import console 

32 

33CONFIG_FILENAME = ".rhiza/.cfg.toml" 

34 

35 

36class RhizaConfig: 

37 """Rhiza tools configuration. 

38 

39 Manages loading and accessing configuration from a TOML file. Provides 

40 convenient access to tool-specific configuration sections. 

41 

42 Attributes: 

43 config_path: Path to the configuration TOML file. 

44 

45 Example: 

46 Basic usage:: 

47 

48 config = RhizaConfig() 

49 bumpversion = config.bumpversion 

50 custom_value = config.get("my_key", "default") 

51 

52 With custom path:: 

53 

54 config = RhizaConfig(Path("custom/.cfg.toml")) 

55 config.load() 

56 """ 

57 

58 def __init__(self, config_path: Path | None = None) -> None: 

59 """Initialize RhizaConfig. 

60 

61 Args: 

62 config_path: Path to the configuration file. If None, uses the 

63 default path defined in CONFIG_FILENAME. 

64 """ 

65 self.config_path = config_path or Path(CONFIG_FILENAME) 

66 self._data: tomlkit.TOMLDocument = tomlkit.TOMLDocument() 

67 self.load() 

68 

69 def load(self) -> None: 

70 """Load configuration from file. 

71 

72 Reads and parses the TOML configuration file. If the file doesn't exist, 

73 the configuration will be empty. Logs errors if parsing fails. 

74 

75 Raises: 

76 tomlkit.exceptions.ParseError: If the configuration file exists but contains invalid TOML. 

77 OSError: If the configuration file cannot be read. 

78 

79 Example: 

80 config = RhizaConfig(Path("custom/.cfg.toml")) 

81 config.load() 

82 """ 

83 if not self.config_path.exists(): 

84 logger.debug(f"Configuration file {self.config_path} not found.") 

85 return 

86 

87 try: 

88 with open(self.config_path) as f: 

89 self._data = tomlkit.parse(f.read()) 

90 except (tomlkit.exceptions.ParseError, OSError) as e: 

91 console.error(f"Failed to parse configuration file {self.config_path}: {e}") 

92 raise 

93 

94 @property 

95 # Returns the raw ``[tool.bumpversion]`` TOML sub-table. Its keys and value 

96 # types are user-defined and open-ended (strings, bools, lists, nested 

97 # tables), so ``dict[str, Any]`` is the honest type for this passthrough — 

98 # the strongly-typed bump path uses bump-my-version's own ``Config`` model. 

99 def bumpversion(self) -> dict[str, Any]: 

100 """Get bumpversion configuration. 

101 

102 Returns: 

103 Dictionary containing bumpversion-specific configuration from the 

104 [tool.bumpversion] section of the configuration file. 

105 

106 Example: 

107 config = RhizaConfig() 

108 bv_config = config.bumpversion 

109 print(bv_config.get("current_version")) 

110 """ 

111 result: dict[str, Any] = self._data.get("tool", {}).get("bumpversion", {}) 

112 return result 

113 

114 # Generic accessor over arbitrary top-level TOML keys; the value type is 

115 # only known to the caller, so ``Any`` is intentional here (TOML passthrough). 

116 def get(self, key: str, default: Any = None) -> Any: 

117 """Get configuration value. 

118 

119 Args: 

120 key: Configuration key to retrieve. 

121 default: Default value to return if key is not found. 

122 

123 Returns: 

124 The configuration value for the given key, or default if not found. 

125 

126 Example: 

127 config = RhizaConfig() 

128 value = config.get("custom_setting", "default_value") 

129 """ 

130 return self._data.get(key, default) 

131 

132 

133def load_config(path: Path | None = None) -> RhizaConfig: 

134 """Load configuration. 

135 

136 Convenience function to create and load a RhizaConfig instance. 

137 

138 Args: 

139 path: Path to the configuration file. If None, uses the default path. 

140 

141 Returns: 

142 A RhizaConfig instance with the configuration loaded. 

143 

144 Example: 

145 Load default configuration:: 

146 

147 config = load_config() 

148 

149 Load from custom path:: 

150 

151 from pathlib import Path 

152 config = load_config(Path("custom/.cfg.toml")) 

153 """ 

154 return RhizaConfig(path)