Commit | Line | Data |
---|---|---|
920dae64 AT |
1 | """Configuration file parser. |
2 | ||
3 | A setup file consists of sections, lead by a "[section]" header, | |
4 | and followed by "name: value" entries, with continuations and such in | |
5 | the style of RFC 822. | |
6 | ||
7 | The option values can contain format strings which refer to other values in | |
8 | the same section, or values in a special [DEFAULT] section. | |
9 | ||
10 | For example: | |
11 | ||
12 | something: %(dir)s/whatever | |
13 | ||
14 | would resolve the "%(dir)s" to the value of dir. All reference | |
15 | expansions are done late, on demand. | |
16 | ||
17 | Intrinsic defaults can be specified by passing them into the | |
18 | ConfigParser constructor as a dictionary. | |
19 | ||
20 | class: | |
21 | ||
22 | ConfigParser -- responsible for parsing a list of | |
23 | configuration files, and managing the parsed database. | |
24 | ||
25 | methods: | |
26 | ||
27 | __init__(defaults=None) | |
28 | create the parser and specify a dictionary of intrinsic defaults. The | |
29 | keys must be strings, the values must be appropriate for %()s string | |
30 | interpolation. Note that `__name__' is always an intrinsic default; | |
31 | its value is the section's name. | |
32 | ||
33 | sections() | |
34 | return all the configuration section names, sans DEFAULT | |
35 | ||
36 | has_section(section) | |
37 | return whether the given section exists | |
38 | ||
39 | has_option(section, option) | |
40 | return whether the given option exists in the given section | |
41 | ||
42 | options(section) | |
43 | return list of configuration options for the named section | |
44 | ||
45 | read(filenames) | |
46 | read and parse the list of named configuration files, given by | |
47 | name. A single filename is also allowed. Non-existing files | |
48 | are ignored. Return list of successfully read files. | |
49 | ||
50 | readfp(fp, filename=None) | |
51 | read and parse one configuration file, given as a file object. | |
52 | The filename defaults to fp.name; it is only used in error | |
53 | messages (if fp has no `name' attribute, the string `<???>' is used). | |
54 | ||
55 | get(section, option, raw=False, vars=None) | |
56 | return a string value for the named option. All % interpolations are | |
57 | expanded in the return values, based on the defaults passed into the | |
58 | constructor and the DEFAULT section. Additional substitutions may be | |
59 | provided using the `vars' argument, which must be a dictionary whose | |
60 | contents override any pre-existing defaults. | |
61 | ||
62 | getint(section, options) | |
63 | like get(), but convert value to an integer | |
64 | ||
65 | getfloat(section, options) | |
66 | like get(), but convert value to a float | |
67 | ||
68 | getboolean(section, options) | |
69 | like get(), but convert value to a boolean (currently case | |
70 | insensitively defined as 0, false, no, off for False, and 1, true, | |
71 | yes, on for True). Returns False or True. | |
72 | ||
73 | items(section, raw=False, vars=None) | |
74 | return a list of tuples with (name, value) for each option | |
75 | in the section. | |
76 | ||
77 | remove_section(section) | |
78 | remove the given file section and all its options | |
79 | ||
80 | remove_option(section, option) | |
81 | remove the given option from the given section | |
82 | ||
83 | set(section, option, value) | |
84 | set the given option | |
85 | ||
86 | write(fp) | |
87 | write the configuration state in .ini format | |
88 | """ | |
89 | ||
90 | import re | |
91 | ||
92 | __all__ = ["NoSectionError", "DuplicateSectionError", "NoOptionError", | |
93 | "InterpolationError", "InterpolationDepthError", | |
94 | "InterpolationSyntaxError", "ParsingError", | |
95 | "MissingSectionHeaderError", | |
96 | "ConfigParser", "SafeConfigParser", "RawConfigParser", | |
97 | "DEFAULTSECT", "MAX_INTERPOLATION_DEPTH"] | |
98 | ||
99 | DEFAULTSECT = "DEFAULT" | |
100 | ||
101 | MAX_INTERPOLATION_DEPTH = 10 | |
102 | ||
103 | ||
104 | ||
105 | # exception classes | |
106 | class Error(Exception): | |
107 | """Base class for ConfigParser exceptions.""" | |
108 | ||
109 | def __init__(self, msg=''): | |
110 | self.message = msg | |
111 | Exception.__init__(self, msg) | |
112 | ||
113 | def __repr__(self): | |
114 | return self.message | |
115 | ||
116 | __str__ = __repr__ | |
117 | ||
118 | class NoSectionError(Error): | |
119 | """Raised when no section matches a requested option.""" | |
120 | ||
121 | def __init__(self, section): | |
122 | Error.__init__(self, 'No section: %r' % (section,)) | |
123 | self.section = section | |
124 | ||
125 | class DuplicateSectionError(Error): | |
126 | """Raised when a section is multiply-created.""" | |
127 | ||
128 | def __init__(self, section): | |
129 | Error.__init__(self, "Section %r already exists" % section) | |
130 | self.section = section | |
131 | ||
132 | class NoOptionError(Error): | |
133 | """A requested option was not found.""" | |
134 | ||
135 | def __init__(self, option, section): | |
136 | Error.__init__(self, "No option %r in section: %r" % | |
137 | (option, section)) | |
138 | self.option = option | |
139 | self.section = section | |
140 | ||
141 | class InterpolationError(Error): | |
142 | """Base class for interpolation-related exceptions.""" | |
143 | ||
144 | def __init__(self, option, section, msg): | |
145 | Error.__init__(self, msg) | |
146 | self.option = option | |
147 | self.section = section | |
148 | ||
149 | class InterpolationMissingOptionError(InterpolationError): | |
150 | """A string substitution required a setting which was not available.""" | |
151 | ||
152 | def __init__(self, option, section, rawval, reference): | |
153 | msg = ("Bad value substitution:\n" | |
154 | "\tsection: [%s]\n" | |
155 | "\toption : %s\n" | |
156 | "\tkey : %s\n" | |
157 | "\trawval : %s\n" | |
158 | % (section, option, reference, rawval)) | |
159 | InterpolationError.__init__(self, option, section, msg) | |
160 | self.reference = reference | |
161 | ||
162 | class InterpolationSyntaxError(InterpolationError): | |
163 | """Raised when the source text into which substitutions are made | |
164 | does not conform to the required syntax.""" | |
165 | ||
166 | class InterpolationDepthError(InterpolationError): | |
167 | """Raised when substitutions are nested too deeply.""" | |
168 | ||
169 | def __init__(self, option, section, rawval): | |
170 | msg = ("Value interpolation too deeply recursive:\n" | |
171 | "\tsection: [%s]\n" | |
172 | "\toption : %s\n" | |
173 | "\trawval : %s\n" | |
174 | % (section, option, rawval)) | |
175 | InterpolationError.__init__(self, option, section, msg) | |
176 | ||
177 | class ParsingError(Error): | |
178 | """Raised when a configuration file does not follow legal syntax.""" | |
179 | ||
180 | def __init__(self, filename): | |
181 | Error.__init__(self, 'File contains parsing errors: %s' % filename) | |
182 | self.filename = filename | |
183 | self.errors = [] | |
184 | ||
185 | def append(self, lineno, line): | |
186 | self.errors.append((lineno, line)) | |
187 | self.message += '\n\t[line %2d]: %s' % (lineno, line) | |
188 | ||
189 | class MissingSectionHeaderError(ParsingError): | |
190 | """Raised when a key-value pair is found before any section header.""" | |
191 | ||
192 | def __init__(self, filename, lineno, line): | |
193 | Error.__init__( | |
194 | self, | |
195 | 'File contains no section headers.\nfile: %s, line: %d\n%r' % | |
196 | (filename, lineno, line)) | |
197 | self.filename = filename | |
198 | self.lineno = lineno | |
199 | self.line = line | |
200 | ||
201 | ||
202 | ||
203 | class RawConfigParser: | |
204 | def __init__(self, defaults=None): | |
205 | self._sections = {} | |
206 | self._defaults = {} | |
207 | if defaults: | |
208 | for key, value in defaults.items(): | |
209 | self._defaults[self.optionxform(key)] = value | |
210 | ||
211 | def defaults(self): | |
212 | return self._defaults | |
213 | ||
214 | def sections(self): | |
215 | """Return a list of section names, excluding [DEFAULT]""" | |
216 | # self._sections will never have [DEFAULT] in it | |
217 | return self._sections.keys() | |
218 | ||
219 | def add_section(self, section): | |
220 | """Create a new section in the configuration. | |
221 | ||
222 | Raise DuplicateSectionError if a section by the specified name | |
223 | already exists. | |
224 | """ | |
225 | if section in self._sections: | |
226 | raise DuplicateSectionError(section) | |
227 | self._sections[section] = {} | |
228 | ||
229 | def has_section(self, section): | |
230 | """Indicate whether the named section is present in the configuration. | |
231 | ||
232 | The DEFAULT section is not acknowledged. | |
233 | """ | |
234 | return section in self._sections | |
235 | ||
236 | def options(self, section): | |
237 | """Return a list of option names for the given section name.""" | |
238 | try: | |
239 | opts = self._sections[section].copy() | |
240 | except KeyError: | |
241 | raise NoSectionError(section) | |
242 | opts.update(self._defaults) | |
243 | if '__name__' in opts: | |
244 | del opts['__name__'] | |
245 | return opts.keys() | |
246 | ||
247 | def read(self, filenames): | |
248 | """Read and parse a filename or a list of filenames. | |
249 | ||
250 | Files that cannot be opened are silently ignored; this is | |
251 | designed so that you can specify a list of potential | |
252 | configuration file locations (e.g. current directory, user's | |
253 | home directory, systemwide directory), and all existing | |
254 | configuration files in the list will be read. A single | |
255 | filename may also be given. | |
256 | ||
257 | Return list of successfully read files. | |
258 | """ | |
259 | if isinstance(filenames, basestring): | |
260 | filenames = [filenames] | |
261 | read_ok = [] | |
262 | for filename in filenames: | |
263 | try: | |
264 | fp = open(filename) | |
265 | except IOError: | |
266 | continue | |
267 | self._read(fp, filename) | |
268 | fp.close() | |
269 | read_ok.append(filename) | |
270 | return read_ok | |
271 | ||
272 | def readfp(self, fp, filename=None): | |
273 | """Like read() but the argument must be a file-like object. | |
274 | ||
275 | The `fp' argument must have a `readline' method. Optional | |
276 | second argument is the `filename', which if not given, is | |
277 | taken from fp.name. If fp has no `name' attribute, `<???>' is | |
278 | used. | |
279 | ||
280 | """ | |
281 | if filename is None: | |
282 | try: | |
283 | filename = fp.name | |
284 | except AttributeError: | |
285 | filename = '<???>' | |
286 | self._read(fp, filename) | |
287 | ||
288 | def get(self, section, option): | |
289 | opt = self.optionxform(option) | |
290 | if section not in self._sections: | |
291 | if section != DEFAULTSECT: | |
292 | raise NoSectionError(section) | |
293 | if opt in self._defaults: | |
294 | return self._defaults[opt] | |
295 | else: | |
296 | raise NoOptionError(option, section) | |
297 | elif opt in self._sections[section]: | |
298 | return self._sections[section][opt] | |
299 | elif opt in self._defaults: | |
300 | return self._defaults[opt] | |
301 | else: | |
302 | raise NoOptionError(option, section) | |
303 | ||
304 | def items(self, section): | |
305 | try: | |
306 | d2 = self._sections[section] | |
307 | except KeyError: | |
308 | if section != DEFAULTSECT: | |
309 | raise NoSectionError(section) | |
310 | d2 = {} | |
311 | d = self._defaults.copy() | |
312 | d.update(d2) | |
313 | if "__name__" in d: | |
314 | del d["__name__"] | |
315 | return d.items() | |
316 | ||
317 | def _get(self, section, conv, option): | |
318 | return conv(self.get(section, option)) | |
319 | ||
320 | def getint(self, section, option): | |
321 | return self._get(section, int, option) | |
322 | ||
323 | def getfloat(self, section, option): | |
324 | return self._get(section, float, option) | |
325 | ||
326 | _boolean_states = {'1': True, 'yes': True, 'true': True, 'on': True, | |
327 | '0': False, 'no': False, 'false': False, 'off': False} | |
328 | ||
329 | def getboolean(self, section, option): | |
330 | v = self.get(section, option) | |
331 | if v.lower() not in self._boolean_states: | |
332 | raise ValueError, 'Not a boolean: %s' % v | |
333 | return self._boolean_states[v.lower()] | |
334 | ||
335 | def optionxform(self, optionstr): | |
336 | return optionstr.lower() | |
337 | ||
338 | def has_option(self, section, option): | |
339 | """Check for the existence of a given option in a given section.""" | |
340 | if not section or section == DEFAULTSECT: | |
341 | option = self.optionxform(option) | |
342 | return option in self._defaults | |
343 | elif section not in self._sections: | |
344 | return False | |
345 | else: | |
346 | option = self.optionxform(option) | |
347 | return (option in self._sections[section] | |
348 | or option in self._defaults) | |
349 | ||
350 | def set(self, section, option, value): | |
351 | """Set an option.""" | |
352 | if not section or section == DEFAULTSECT: | |
353 | sectdict = self._defaults | |
354 | else: | |
355 | try: | |
356 | sectdict = self._sections[section] | |
357 | except KeyError: | |
358 | raise NoSectionError(section) | |
359 | sectdict[self.optionxform(option)] = value | |
360 | ||
361 | def write(self, fp): | |
362 | """Write an .ini-format representation of the configuration state.""" | |
363 | if self._defaults: | |
364 | fp.write("[%s]\n" % DEFAULTSECT) | |
365 | for (key, value) in self._defaults.items(): | |
366 | fp.write("%s = %s\n" % (key, str(value).replace('\n', '\n\t'))) | |
367 | fp.write("\n") | |
368 | for section in self._sections: | |
369 | fp.write("[%s]\n" % section) | |
370 | for (key, value) in self._sections[section].items(): | |
371 | if key != "__name__": | |
372 | fp.write("%s = %s\n" % | |
373 | (key, str(value).replace('\n', '\n\t'))) | |
374 | fp.write("\n") | |
375 | ||
376 | def remove_option(self, section, option): | |
377 | """Remove an option.""" | |
378 | if not section or section == DEFAULTSECT: | |
379 | sectdict = self._defaults | |
380 | else: | |
381 | try: | |
382 | sectdict = self._sections[section] | |
383 | except KeyError: | |
384 | raise NoSectionError(section) | |
385 | option = self.optionxform(option) | |
386 | existed = option in sectdict | |
387 | if existed: | |
388 | del sectdict[option] | |
389 | return existed | |
390 | ||
391 | def remove_section(self, section): | |
392 | """Remove a file section.""" | |
393 | existed = section in self._sections | |
394 | if existed: | |
395 | del self._sections[section] | |
396 | return existed | |
397 | ||
398 | # | |
399 | # Regular expressions for parsing section headers and options. | |
400 | # | |
401 | SECTCRE = re.compile( | |
402 | r'\[' # [ | |
403 | r'(?P<header>[^]]+)' # very permissive! | |
404 | r'\]' # ] | |
405 | ) | |
406 | OPTCRE = re.compile( | |
407 | r'(?P<option>[^:=\s][^:=]*)' # very permissive! | |
408 | r'\s*(?P<vi>[:=])\s*' # any number of space/tab, | |
409 | # followed by separator | |
410 | # (either : or =), followed | |
411 | # by any # space/tab | |
412 | r'(?P<value>.*)$' # everything up to eol | |
413 | ) | |
414 | ||
415 | def _read(self, fp, fpname): | |
416 | """Parse a sectioned setup file. | |
417 | ||
418 | The sections in setup file contains a title line at the top, | |
419 | indicated by a name in square brackets (`[]'), plus key/value | |
420 | options lines, indicated by `name: value' format lines. | |
421 | Continuations are represented by an embedded newline then | |
422 | leading whitespace. Blank lines, lines beginning with a '#', | |
423 | and just about everything else are ignored. | |
424 | """ | |
425 | cursect = None # None, or a dictionary | |
426 | optname = None | |
427 | lineno = 0 | |
428 | e = None # None, or an exception | |
429 | while True: | |
430 | line = fp.readline() | |
431 | if not line: | |
432 | break | |
433 | lineno = lineno + 1 | |
434 | # comment or blank line? | |
435 | if line.strip() == '' or line[0] in '#;': | |
436 | continue | |
437 | if line.split(None, 1)[0].lower() == 'rem' and line[0] in "rR": | |
438 | # no leading whitespace | |
439 | continue | |
440 | # continuation line? | |
441 | if line[0].isspace() and cursect is not None and optname: | |
442 | value = line.strip() | |
443 | if value: | |
444 | cursect[optname] = "%s\n%s" % (cursect[optname], value) | |
445 | # a section header or option header? | |
446 | else: | |
447 | # is it a section header? | |
448 | mo = self.SECTCRE.match(line) | |
449 | if mo: | |
450 | sectname = mo.group('header') | |
451 | if sectname in self._sections: | |
452 | cursect = self._sections[sectname] | |
453 | elif sectname == DEFAULTSECT: | |
454 | cursect = self._defaults | |
455 | else: | |
456 | cursect = {'__name__': sectname} | |
457 | self._sections[sectname] = cursect | |
458 | # So sections can't start with a continuation line | |
459 | optname = None | |
460 | # no section header in the file? | |
461 | elif cursect is None: | |
462 | raise MissingSectionHeaderError(fpname, lineno, line) | |
463 | # an option line? | |
464 | else: | |
465 | mo = self.OPTCRE.match(line) | |
466 | if mo: | |
467 | optname, vi, optval = mo.group('option', 'vi', 'value') | |
468 | if vi in ('=', ':') and ';' in optval: | |
469 | # ';' is a comment delimiter only if it follows | |
470 | # a spacing character | |
471 | pos = optval.find(';') | |
472 | if pos != -1 and optval[pos-1].isspace(): | |
473 | optval = optval[:pos] | |
474 | optval = optval.strip() | |
475 | # allow empty values | |
476 | if optval == '""': | |
477 | optval = '' | |
478 | optname = self.optionxform(optname.rstrip()) | |
479 | cursect[optname] = optval | |
480 | else: | |
481 | # a non-fatal parsing error occurred. set up the | |
482 | # exception but keep going. the exception will be | |
483 | # raised at the end of the file and will contain a | |
484 | # list of all bogus lines | |
485 | if not e: | |
486 | e = ParsingError(fpname) | |
487 | e.append(lineno, repr(line)) | |
488 | # if any parsing errors occurred, raise an exception | |
489 | if e: | |
490 | raise e | |
491 | ||
492 | ||
493 | class ConfigParser(RawConfigParser): | |
494 | ||
495 | def get(self, section, option, raw=False, vars=None): | |
496 | """Get an option value for a given section. | |
497 | ||
498 | All % interpolations are expanded in the return values, based on the | |
499 | defaults passed into the constructor, unless the optional argument | |
500 | `raw' is true. Additional substitutions may be provided using the | |
501 | `vars' argument, which must be a dictionary whose contents overrides | |
502 | any pre-existing defaults. | |
503 | ||
504 | The section DEFAULT is special. | |
505 | """ | |
506 | d = self._defaults.copy() | |
507 | try: | |
508 | d.update(self._sections[section]) | |
509 | except KeyError: | |
510 | if section != DEFAULTSECT: | |
511 | raise NoSectionError(section) | |
512 | # Update with the entry specific variables | |
513 | if vars: | |
514 | for key, value in vars.items(): | |
515 | d[self.optionxform(key)] = value | |
516 | option = self.optionxform(option) | |
517 | try: | |
518 | value = d[option] | |
519 | except KeyError: | |
520 | raise NoOptionError(option, section) | |
521 | ||
522 | if raw: | |
523 | return value | |
524 | else: | |
525 | return self._interpolate(section, option, value, d) | |
526 | ||
527 | def items(self, section, raw=False, vars=None): | |
528 | """Return a list of tuples with (name, value) for each option | |
529 | in the section. | |
530 | ||
531 | All % interpolations are expanded in the return values, based on the | |
532 | defaults passed into the constructor, unless the optional argument | |
533 | `raw' is true. Additional substitutions may be provided using the | |
534 | `vars' argument, which must be a dictionary whose contents overrides | |
535 | any pre-existing defaults. | |
536 | ||
537 | The section DEFAULT is special. | |
538 | """ | |
539 | d = self._defaults.copy() | |
540 | try: | |
541 | d.update(self._sections[section]) | |
542 | except KeyError: | |
543 | if section != DEFAULTSECT: | |
544 | raise NoSectionError(section) | |
545 | # Update with the entry specific variables | |
546 | if vars: | |
547 | for key, value in vars.items(): | |
548 | d[self.optionxform(key)] = value | |
549 | options = d.keys() | |
550 | if "__name__" in options: | |
551 | options.remove("__name__") | |
552 | if raw: | |
553 | return [(option, d[option]) | |
554 | for option in options] | |
555 | else: | |
556 | return [(option, self._interpolate(section, option, d[option], d)) | |
557 | for option in options] | |
558 | ||
559 | def _interpolate(self, section, option, rawval, vars): | |
560 | # do the string interpolation | |
561 | value = rawval | |
562 | depth = MAX_INTERPOLATION_DEPTH | |
563 | while depth: # Loop through this until it's done | |
564 | depth -= 1 | |
565 | if "%(" in value: | |
566 | value = self._KEYCRE.sub(self._interpolation_replace, value) | |
567 | try: | |
568 | value = value % vars | |
569 | except KeyError, e: | |
570 | raise InterpolationMissingOptionError( | |
571 | option, section, rawval, e[0]) | |
572 | else: | |
573 | break | |
574 | if "%(" in value: | |
575 | raise InterpolationDepthError(option, section, rawval) | |
576 | return value | |
577 | ||
578 | _KEYCRE = re.compile(r"%\(([^)]*)\)s|.") | |
579 | ||
580 | def _interpolation_replace(self, match): | |
581 | s = match.group(1) | |
582 | if s is None: | |
583 | return match.group() | |
584 | else: | |
585 | return "%%(%s)s" % self.optionxform(s) | |
586 | ||
587 | ||
588 | class SafeConfigParser(ConfigParser): | |
589 | ||
590 | def _interpolate(self, section, option, rawval, vars): | |
591 | # do the string interpolation | |
592 | L = [] | |
593 | self._interpolate_some(option, L, rawval, section, vars, 1) | |
594 | return ''.join(L) | |
595 | ||
596 | _interpvar_match = re.compile(r"%\(([^)]+)\)s").match | |
597 | ||
598 | def _interpolate_some(self, option, accum, rest, section, map, depth): | |
599 | if depth > MAX_INTERPOLATION_DEPTH: | |
600 | raise InterpolationDepthError(option, section, rest) | |
601 | while rest: | |
602 | p = rest.find("%") | |
603 | if p < 0: | |
604 | accum.append(rest) | |
605 | return | |
606 | if p > 0: | |
607 | accum.append(rest[:p]) | |
608 | rest = rest[p:] | |
609 | # p is no longer used | |
610 | c = rest[1:2] | |
611 | if c == "%": | |
612 | accum.append("%") | |
613 | rest = rest[2:] | |
614 | elif c == "(": | |
615 | m = self._interpvar_match(rest) | |
616 | if m is None: | |
617 | raise InterpolationSyntaxError(option, section, | |
618 | "bad interpolation variable reference %r" % rest) | |
619 | var = self.optionxform(m.group(1)) | |
620 | rest = rest[m.end():] | |
621 | try: | |
622 | v = map[var] | |
623 | except KeyError: | |
624 | raise InterpolationMissingOptionError( | |
625 | option, section, rest, var) | |
626 | if "%" in v: | |
627 | self._interpolate_some(option, accum, v, | |
628 | section, map, depth + 1) | |
629 | else: | |
630 | accum.append(v) | |
631 | else: | |
632 | raise InterpolationSyntaxError( | |
633 | option, section, | |
634 | "'%%' must be followed by '%%' or '(', found: %r" % (rest,)) | |
635 | ||
636 | def set(self, section, option, value): | |
637 | """Set an option. Extend ConfigParser.set: check for string values.""" | |
638 | if not isinstance(value, basestring): | |
639 | raise TypeError("option values must be strings") | |
640 | ConfigParser.set(self, section, option, value) |