Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1#!/usr/bin/env python 

2# cardinal_pythonlib/configfiles.py 

3 

4""" 

5=============================================================================== 

6 

7 Original code copyright (C) 2009-2021 Rudolf Cardinal (rudolf@pobox.com). 

8 

9 This file is part of cardinal_pythonlib. 

10 

11 Licensed under the Apache License, Version 2.0 (the "License"); 

12 you may not use this file except in compliance with the License. 

13 You may obtain a copy of the License at 

14 

15 https://www.apache.org/licenses/LICENSE-2.0 

16 

17 Unless required by applicable law or agreed to in writing, software 

18 distributed under the License is distributed on an "AS IS" BASIS, 

19 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 

20 See the License for the specific language governing permissions and 

21 limitations under the License. 

22 

23=============================================================================== 

24 

25**Support functions for config (.INI) file reading.** 

26 

27""" 

28 

29from configparser import ConfigParser, NoOptionError 

30import logging 

31from typing import Any, Callable, Iterable, List 

32 

33from cardinal_pythonlib.logs import get_brace_style_log_with_null_handler 

34 

35log = get_brace_style_log_with_null_handler(__name__) 

36 

37 

38# ============================================================================= 

39# Style 1 

40# ============================================================================= 

41 

42def get_config_string_option(parser: ConfigParser, 

43 section: str, 

44 option: str, 

45 default: str = None) -> str: 

46 """ 

47 Retrieves a string value from a parser. 

48 

49 Args: 

50 parser: instance of :class:`ConfigParser` 

51 section: section name within config file 

52 option: option (variable) name within that section 

53 default: value to return if option is absent 

54 

55 Returns: 

56 string value 

57 

58 Raises: 

59 ValueError: if the section is absent 

60 

61 """ 

62 if not parser.has_section(section): 

63 raise ValueError("config missing section: " + section) 

64 return parser.get(section, option, fallback=default) 

65 

66 

67def read_config_string_options(obj: Any, 

68 parser: ConfigParser, 

69 section: str, 

70 options: Iterable[str], 

71 default: str = None) -> None: 

72 """ 

73 Reads config options and writes them as attributes of ``obj``, with 

74 attribute names as per ``options``. 

75 

76 Args: 

77 obj: the object to modify 

78 parser: instance of :class:`ConfigParser` 

79 section: section name within config file 

80 options: option (variable) names within that section 

81 default: value to use for any missing options 

82 

83 Returns: 

84 

85 """ 

86 # enforce_str removed; ConfigParser always returns strings unless asked 

87 # specifically 

88 for o in options: 

89 setattr(obj, o, get_config_string_option(parser, section, o, 

90 default=default)) 

91 

92 

93def get_config_multiline_option(parser: ConfigParser, 

94 section: str, 

95 option: str, 

96 default: List[str] = None) -> List[str]: 

97 """ 

98 Retrieves a multi-line string value from a parser as a list of strings 

99 (one per line, ignoring blank lines). 

100 

101 Args: 

102 parser: instance of :class:`ConfigParser` 

103 section: section name within config file 

104 option: option (variable) name within that section 

105 default: value to return if option is absent (``None`` is mapped to 

106 ``[]``) 

107 

108 Returns: 

109 list of strings 

110 

111 Raises: 

112 ValueError: if the section is absent 

113 

114 """ 

115 default = default or [] 

116 if not parser.has_section(section): 

117 raise ValueError("config missing section: " + section) 

118 try: 

119 multiline = parser.get(section, option) 

120 values = [x.strip() for x in multiline.splitlines() if x.strip()] 

121 return values 

122 except NoOptionError: 

123 return default 

124 

125 

126def read_config_multiline_options(obj: Any, 

127 parser: ConfigParser, 

128 section: str, 

129 options: Iterable[str]) -> None: 

130 """ 

131 This is to :func:`read_config_string_options` as 

132 :func:`get_config_multiline_option` is to :func:`get_config_string_option`. 

133 """ 

134 for o in options: 

135 setattr(obj, o, get_config_multiline_option(parser, section, o)) 

136 

137 

138def get_config_bool_option(parser: ConfigParser, 

139 section: str, 

140 option: str, 

141 default: bool = None) -> bool: 

142 """ 

143 Retrieves a boolean value from a parser. 

144 

145 Args: 

146 parser: instance of :class:`ConfigParser` 

147 section: section name within config file 

148 option: option (variable) name within that section 

149 default: value to return if option is absent 

150 

151 Returns: 

152 Boolean value 

153 

154 Raises: 

155 ValueError: if the section is absent 

156 

157 """ 

158 if not parser.has_section(section): 

159 raise ValueError("config missing section: " + section) 

160 return parser.getboolean(section, option, fallback=default) 

161 

162 

163# ============================================================================= 

164# Style 2 

165# ============================================================================= 

166 

167# ============================================================================= 

168# Reading config files: style 2 

169# ============================================================================= 

170 

171def get_config_parameter(config: ConfigParser, 

172 section: str, 

173 param: str, 

174 fn: Callable[[Any], Any], 

175 default: Any = None, 

176 loglevel: int = logging.DEBUG) -> Any: 

177 """ 

178 Fetch parameter from ``configparser`` ``.INI`` file. 

179 

180 Args: 

181 config: :class:`ConfigParser` object 

182 section: section name within config file 

183 param: name of parameter within section 

184 fn: function to apply to string parameter (e.g. ``int``) 

185 default: default value 

186 loglevel: log level if default is needed 

187 

188 Returns: 

189 parameter value, or ``None`` if ``default is None``, or ``fn(default)`` 

190 """ 

191 try: 

192 value = fn(config.get(section, param)) 

193 except (TypeError, ValueError, NoOptionError): 

194 log.log( 

195 level=loglevel, 

196 msg=f"Configuration variable {param} not found or improper in " 

197 f"section [{section}]; using default of {default!r}") 

198 if default is None: 

199 value = default 

200 else: 

201 value = fn(default) 

202 return value 

203 

204 

205def get_config_parameter_boolean(config: ConfigParser, 

206 section: str, 

207 param: str, 

208 default: bool, 

209 loglevel: int = logging.DEBUG) -> bool: 

210 """ 

211 Get Boolean parameter from ``configparser`` ``.INI`` file. 

212 

213 Args: 

214 config: :class:`ConfigParser` object 

215 section: section name within config file 

216 param: name of parameter within section 

217 default: default value 

218 loglevel: log level if default is needed 

219 Returns: 

220 parameter value, or default 

221 """ 

222 try: 

223 value = config.getboolean(section, param) 

224 except (TypeError, ValueError, NoOptionError): 

225 log.log( 

226 level=loglevel, 

227 msg=f"Configuration variable {param} not found or improper in " 

228 f"section [{section}]; using default of {default!r}") 

229 value = default 

230 return value 

231 

232 

233def get_config_parameter_loglevel(config: ConfigParser, 

234 section: str, 

235 param: str, 

236 default: int, 

237 loglevel: int = logging.DEBUG) -> int: 

238 """ 

239 Get ``loglevel`` parameter from ``configparser`` ``.INI`` file, e.g. 

240 mapping ``'debug'`` to ``logging.DEBUG``. 

241 

242 Args: 

243 config: :class:`ConfigParser` object 

244 section: section name within config file 

245 param: name of parameter within section 

246 default: default value 

247 loglevel: log level if default is needed 

248 Returns: 

249 parameter value, or default 

250 """ 

251 try: 

252 value = config.get(section, param).lower() 

253 if value == "debug": 

254 return logging.DEBUG # 10 

255 elif value == "info": 

256 return logging.INFO 

257 elif value in ["warn", "warning"]: 

258 return logging.WARN 

259 elif value == "error": 

260 return logging.ERROR 

261 elif value in ["critical", "fatal"]: 

262 return logging.CRITICAL # 50 

263 else: 

264 raise ValueError 

265 except (TypeError, ValueError, NoOptionError, AttributeError): 

266 log.log( 

267 level=loglevel, 

268 msg=f"Configuration variable {param} not found or improper in " 

269 f"section [{section}]; using default of {default!r}") 

270 return default 

271 

272 

273def get_config_parameter_multiline(config: ConfigParser, 

274 section: str, 

275 param: str, 

276 default: List[str], 

277 loglevel: int = logging.DEBUG) -> List[str]: 

278 """ 

279 Get multi-line string parameter from ``configparser`` ``.INI`` file, 

280 as a list of strings (one per line, ignoring blank lines). 

281 

282 Args: 

283 config: :class:`ConfigParser` object 

284 section: section name within config file 

285 param: name of parameter within section 

286 default: default value 

287 loglevel: log level if default is needed 

288 Returns: 

289 parameter value, or default 

290 """ 

291 try: 

292 multiline = config.get(section, param) 

293 lines = [x.strip() for x in multiline.splitlines()] 

294 return [line for line in lines if line] 

295 except (TypeError, ValueError, NoOptionError): 

296 log.log( 

297 level=loglevel, 

298 msg=f"Configuration variable {param} not found or improper in " 

299 f"section [{section}]; using default of {default!r}") 

300 return default