Package dics :: Module ConfigParserRT
[hide private]
[frames] | no frames]

Source Code for Module dics.ConfigParserRT

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