1
2
3
4
5
6
7
8
9
10
11
12
13
14 __doc__ = """
15 Zenoss config parsers.
16
17 There are mutiple stages to config parsing. Parsing is split into stages so that
18 we can validate a whole config file and possibly rebuild it to correct errors.
19
20 The stages are:
21
22 * Parse - Split the config file in to ConfigLine types while maintaining line order, comments, and new lines
23 * Validate - Check that all lines are valid
24 * Report - Investigate why a line might be invalid (ex: invalid key format)
25 * Load - Get a config object back
26 * Write - An optional stage to write the config back to a file
27 """
28
29 import re
32 """
33 Error for problems parsing config files.
34 """
35 pass
36
38 """
39 Error for problems parsing config files with line context.
40 """
44
46 return '%s on line %d' % (self.message, self.lineno)
47
49 """
50 A group of errors while parsing config.
51 """
55
62
65
68
70 """
71 A bunch of configuration settings. Uses dictionary access,
72 or object attribute access.
73
74 Provides some Convenience functions for different types.
75 """
78
79 - def getbool(self, key, default=None):
80 """
81 Convenience function to convert the value to a bool.
82 Valid values are and case of true, yes, y, 1 anything
83 else is considered False.
84
85 If key doesn't exist, returns `default`.
86 """
87 try:
88 return self[key].lower() in ('true', 'yes', 'y', '1')
89 except KeyError:
90 return default
91
92 getboolean = getbool
93
94 - def getint(self, key, default=None):
95 """
96 Convenience function to convert the value to an int.
97 Valid values are anything `int(x)` will accept.
98
99 If key doesn't exist or can't be converted to int, returns `default`.
100 """
101 try:
102 return int(self[key])
103 except (KeyError, ValueError):
104 return default
105
107 """
108 Convenience function to convert the value to a float.
109 Valid values are anything `float(x)` will accept.
110
111 If key doesn't exist or can't be converted to float, returns `default`.
112 """
113 try:
114 return float(self[key])
115 except (KeyError, ValueError):
116 return default
117
119 """
120 Abstract class that represents a single line in the config.
121 """
124
127
128 @property
130 """
131 Return a key, value tuple if this line represents a setting.
132 Implemented in base classes.
133 """
134 return None
135
136 @classmethod
138 """
139 Returns an instance of cls if this class can parse this line. Otherwise returns None.
140 Implemented in base classes.
141 """
142 return None
143
144 @classmethod
146 """
147 Checks the string for possible matches, considers why it doesn't match exactly if it's close
148 and returns a ConfigLineError.
149 Implemented in base classes.
150 """
151 return None
152
154 """
155 Represents a config line with a `key = value` pair.
156 """
157 _regexp = re.compile(r'^(?P<key>[a-z][a-z\d_]*)\s*(?P<delim>(=|:|\s)+)\s*(?P<value>.+)$', re.I)
158
159 - def __init__(self, key, value=None, delim='='):
163
164 @property
167
169 return '{key} {delim} {value}'.format(**self.__dict__)
170
171 @classmethod
176
177
178 @classmethod
184
193
197
198 @classmethod
200 if line == '':
201 return cls()
202
205
207 """
208 Default line if no other ConfigLines matched. Assumed to be invalid
209 input.
210 """
211 pass
212
214 """
215 Parses Zenoss's config file format.
216
217 Example:
218
219 key value
220 key intvalue
221 key = value
222 key=value
223 key:value
224 key : value
225 """
226
227
228 _lineTypes = [
229 SettingLine,
230 CommentLine,
231 EmptyLine,
232 ]
233
234
235 _invalidLineType = InvalidLine
236
238 """
239 @param file file-like-object
240 """
241 self.file = file
242 self.filename = self.file.name if hasattr(self.file, 'name') else 'Unknown'
243 self._lines = None
244
253
254
261
263 """
264 Parse a config file which has key-value pairs.Returns a list of config
265 line information. This line information can be used to accuratly recreate
266 the config without losing comments or invalid data.
267 """
268 if self._lines is None:
269 self._lines = []
270 for line in self.file:
271 self._lines.append(self._parseLine(line))
272
273 return self._lines
274
276 """
277 Write the config out to a file. Takes a new file argument
278 because the input file object often doesn't have write access.
279
280 @param file file-like-object
281 """
282 for line in self:
283 file.write(str(line) + '\n')
284
286 """
287 Validate that there are no errors in the config file
288
289 @throws ConfigError
290 """
291 errors = []
292 for lineno, line in enumerate(self):
293 if isinstance(line, self._invalidLineType):
294
295 error = self._checkLine(line.line, lineno)
296 if error:
297 errors.append(error)
298 else:
299 errors.append(ConfigLineError('Unexpected config line "%s"' % line.line, lineno + 1))
300
301 if errors:
302 raise ConfigErrors('There were errors parsing the config "%s".' % self.filename, errors)
303
305 for line in self.parse():
306 yield line
307
312
318
321 """
322 Lazily load the config when requested.
323 """
325 """
326 @param config Config The config instance or class to load data into. Must support update which accepts an iterable of (key, value).
327 @param parser Parser The parser to use to parse the config files. Must be a callable and return an iterable of (key, value).
328 @param config_files list<string> A list of config file names to parse in order.
329 """
330 if not isinstance(config_files, list):
331 config_files = [config_files]
332
333 self.config_files = config_files
334 self.parser = parser
335 self.config = config
336 self._config = None
337
339 """
340 Load the config_files into an instance of config_class
341 """
342 if isinstance(self.config, type):
343 self._config = self.config()
344 else:
345 self._config = self.config
346
347 if not self.config_files:
348 raise ConfigError('Config loader has no config files to load.')
349
350 for file in self.config_files:
351 if not hasattr(file, 'read') and isinstance(file, basestring):
352
353 with open(file, 'r') as fp:
354 options = self.parser(fp)
355 else:
356 options = self.parser(file)
357
358 self._config.update(options)
359
361 """
362 Lazily load the config file.
363 """
364 if self._config is None:
365 self.load()
366
367 return self._config
368