eggs/py-1.4.0-py2.6.egg/py/_iniconfig.py
changeset 307 c6bca38c1cbf
equal deleted inserted replaced
306:5ff1fc726848 307:c6bca38c1cbf
       
     1 """ brain-dead simple parser for ini-style files.
       
     2 (C) Ronny Pfannschmidt, Holger Krekel -- MIT licensed
       
     3 """
       
     4 __version__ = "0.2.dev2"
       
     5 
       
     6 __all__ = ['IniConfig', 'ParseError']
       
     7 
       
     8 class ParseError(Exception):
       
     9     def __init__(self, path, lineno, msg):
       
    10         Exception.__init__(self, path, lineno, msg)
       
    11         self.path = path
       
    12         self.lineno = lineno
       
    13         self.msg = msg
       
    14 
       
    15     def __str__(self):
       
    16         return "%s:%s: %s" %(self.path, self.lineno+1, self.msg)
       
    17 
       
    18 class SectionWrapper(object):
       
    19     def __init__(self, config, name):
       
    20         self.config = config
       
    21         self.name = name
       
    22 
       
    23     def lineof(self, name):
       
    24         return self.config.lineof(self.name, name)
       
    25 
       
    26     def get(self, key, default=None, convert=str):
       
    27         return self.config.get(self.name, key, convert=convert, default=default)
       
    28 
       
    29     def __getitem__(self, key):
       
    30         return self.config.sections[self.name][key]
       
    31 
       
    32     def __iter__(self):
       
    33         section = self.config.sections.get(self.name, [])
       
    34         def lineof(key):
       
    35             return self.config.lineof(self.name, key)
       
    36         for name in sorted(section, key=lineof):
       
    37             yield name
       
    38 
       
    39     def items(self):
       
    40         for name in self:
       
    41             yield name, self[name]
       
    42 
       
    43 
       
    44 class IniConfig(object):
       
    45     def __init__(self, path, data=None):
       
    46         self.path = str(path) # convenience
       
    47         if data is None:
       
    48             f = open(self.path)
       
    49             try:
       
    50                 tokens = self._parse(iter(f))
       
    51             finally:
       
    52                 f.close()
       
    53         else:
       
    54             tokens = self._parse(data.splitlines(True))
       
    55 
       
    56         self._sources = {}
       
    57         self.sections = {}
       
    58 
       
    59         for lineno, section, name, value in tokens:
       
    60             if section is None:
       
    61                 self._raise(lineno, 'no section header defined')
       
    62             self._sources[section, name] = lineno
       
    63             if name is None:
       
    64                 if section in self.sections:
       
    65                     self._raise(lineno, 'duplicate section %r'%(section, ))
       
    66                 self.sections[section] = {}
       
    67             else:
       
    68                 if name in self.sections[section]:
       
    69                     self._raise(lineno, 'duplicate name %r'%(name, ))
       
    70                 self.sections[section][name] = value
       
    71 
       
    72     def _raise(self, lineno, msg):
       
    73         raise ParseError(self.path, lineno, msg)
       
    74 
       
    75     def _parse(self, line_iter):
       
    76         result = []
       
    77         section = None
       
    78         for lineno, line in enumerate(line_iter):
       
    79             name, data = self._parseline(line, lineno)
       
    80             # new value
       
    81             if name is not None and data is not None:
       
    82                 result.append((lineno, section, name, data))
       
    83             # new section
       
    84             elif name is not None and data is None:
       
    85                 if not name:
       
    86                     self._raise(lineno, 'empty section name')
       
    87                 section = name
       
    88                 result.append((lineno, section, None, None))
       
    89             # continuation
       
    90             elif name is None and data is not None:
       
    91                 if not result:
       
    92                     self._raise(lineno, 'unexpected value continuation')
       
    93                 last = result.pop()
       
    94                 last_name, last_data = last[-2:]
       
    95                 if last_name is None:
       
    96                     self._raise(lineno, 'unexpected value continuation')
       
    97 
       
    98                 if last_data:
       
    99                     data = '%s\n%s' % (last_data, data)
       
   100                 result.append(last[:-1] + (data,))
       
   101         return result
       
   102 
       
   103     def _parseline(self, line, lineno):
       
   104         # comments
       
   105         line = line.split('#')[0].rstrip()
       
   106         # blank lines
       
   107         if not line:
       
   108             return None, None
       
   109         # section
       
   110         if line[0] == '[' and line[-1] == ']':
       
   111             return line[1:-1], None
       
   112         # value
       
   113         elif not line[0].isspace():
       
   114             try:
       
   115                 name, value = line.split('=', 1)
       
   116                 if ": " in name:
       
   117                     raise ValueError()
       
   118             except ValueError:
       
   119                 try:
       
   120                     name, value = line.split(": ", 1)
       
   121                 except ValueError:
       
   122                     self._raise(lineno, 'unexpected line: %r' % line)
       
   123             return name.strip(), value.strip()
       
   124         # continuation
       
   125         else:
       
   126             return None, line.strip()
       
   127 
       
   128     def lineof(self, section, name=None):
       
   129         lineno = self._sources.get((section, name))
       
   130         if lineno is not None:
       
   131             return lineno + 1
       
   132 
       
   133     def get(self, section, name, default=None, convert=str):
       
   134         try:
       
   135             return convert(self.sections[section][name])
       
   136         except KeyError:
       
   137             return default
       
   138 
       
   139     def __getitem__(self, name):
       
   140         if name not in self.sections:
       
   141             raise KeyError(name)
       
   142         return SectionWrapper(self, name)
       
   143 
       
   144     def __iter__(self):
       
   145         for name in sorted(self.sections, key=self.lineof):
       
   146             yield SectionWrapper(self, name)
       
   147 
       
   148     def __contains__(self, arg):
       
   149         return arg in self.sections