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  
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   
123      """Base class for ConfigParser exceptions.""" 
124   
126          self.message = msg 
127          Exception.__init__(self, msg) 
 128   
130          return self.message 
131   
132      __str__ = __repr__ 
 133   
135      """Raised when no section matches a requested option.""" 
136   
138          Error.__init__(self, 'No section: ' + `section`) 
139          self.section = section 
  140   
142      """Raised when a section is multiply-created.""" 
143   
145          Error.__init__(self, "Section %r already exists" % section) 
146          self.section = section 
  147   
149      """A requested option was not found.""" 
150   
152          Error.__init__(self, "No option %r in section: %r" % 
153                         (option, section)) 
154          self.option = option 
155          self.section = section 
  156   
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   
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   
179      """Raised when the source text into which substitutions are made 
180      does not conform to the required syntax.""" 
181   
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   
194      """Raised when a configuration file does not follow legal syntax.""" 
195   
200   
201 -    def append(self, lineno, line): 
 202          self.errors.append((lineno, line)) 
203          self.message += '\n\t[line %2d]: %s' % (lineno, line) 
  204   
206      """Raised when a key-value pair is found before any section header.""" 
207   
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   
228   
230          return self._defaults 
231   
233          """Return a list of section names, excluding [DEFAULT]""" 
234           
235          return self._sections.keys() 
 236   
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           
246          self._section[section] = opdict() 
 247   
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   
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               
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   
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   
349   
352   
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   
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   
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   
407          """Remove a file section.""" 
408          existed = section in self._sections 
409          if existed: 
410              del self._sections[section] 
411               
412              self._sect_list.remove(section) 
413          return existed 
414   
415       
416       
417       
418      SECTCRE = re.compile( 
419          r'\['                                  
420          r'(?P<header>[^]]+)'                   
421          r'\]'                                  
422          ) 
423      OPTCRE = re.compile( 
424          r'(?P<option>[^:=\s][^:=]*)'           
425          r'\s*(?P<vi>[:=])\s*'                  
426                                                 
427                                                 
428                                                 
429          r'(?P<value>.*)$'                      
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                             
443          optname = None 
444          lineno = 0 
445          e = None                                   
446          while True: 
447              line = fp.readline() 
448              if not line: 
449                  break 
450              lineno = lineno + 1 
451               
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                   
456                  continue 
457               
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               
463              else: 
464                   
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                           
474                          cursect = opdict({'__name__': sectname}) 
475                          self._sections[sectname] = cursect 
476                       
477                      optname = None 
478                   
479                  elif cursect is None: 
480                      raise MissingSectionHeaderError(fpname, lineno, `line`) 
481                   
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                               
488                               
489                              pos = optval.find(';') 
490                              if pos != -1 and optval[pos-1].isspace(): 
491                                  optval = optval[:pos] 
492                          optval = optval.strip() 
493                           
494                          if optval == '""': 
495                              optval = '' 
496                          optname = self.optionxform(optname.rstrip()) 
497                          cursect[optname] = optval 
498                      else: 
499                           
500                           
501                           
502                           
503                          if not e: 
504                              e = ParsingError(fpname) 
505                          e.append(lineno, `line`) 
506           
507          if e: 
508              raise e 
  509   
510   
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           
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           
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   
 592   
593   
595   
597           
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   
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               
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