Package translate :: Package misc :: Module ini
[hide private]
[frames] | no frames]

Source Code for Module translate.misc.ini

  1  # Copyright (c) 2001, 2002, 2003 Python Software Foundation 
  2  # Copyright (c) 2004 Paramjit Oberoi <param.cs.wisc.edu> 
  3  # All Rights Reserved.  See LICENSE-PSF & LICENSE for details. 
  4   
  5  """Access and/or modify INI files 
  6   
  7  * Compatiable with ConfigParser 
  8  * Preserves order of sections & options 
  9  * Preserves comments/blank lines/etc 
 10  * More convenient access to data 
 11   
 12  Example: 
 13   
 14      >>> from StringIO import StringIO 
 15      >>> sio = StringIO('''# configure foo-application 
 16      ... [foo] 
 17      ... bar1 = qualia 
 18      ... bar2 = 1977 
 19      ... [foo-ext] 
 20      ... special = 1''') 
 21   
 22      >>> cfg = INIConfig(sio) 
 23      >>> print cfg.foo.bar1 
 24      qualia 
 25      >>> print cfg['foo-ext'].special 
 26      1 
 27      >>> cfg.foo.newopt = 'hi!' 
 28   
 29      >>> print cfg 
 30      # configure foo-application 
 31      [foo] 
 32      bar1 = qualia 
 33      bar2 = 1977 
 34      newopt = hi! 
 35      [foo-ext] 
 36      special = 1 
 37   
 38  """ 
 39   
 40  # An ini parser that supports ordered sections/options 
 41  # Also supports updates, while preserving structure 
 42  # Backward-compatiable with ConfigParser 
 43   
 44  import re 
 45  from iniparse import config 
 46  from sets import Set 
 47  from ConfigParser import DEFAULTSECT, ParsingError, MissingSectionHeaderError 
 48   
49 -class LineType(object):
50 line = None 51
52 - def __init__(self, line=None):
53 if line is not None: 54 self.line = line.strip('\n')
55 56 # Return the original line for unmodified objects 57 # Otherwise construct using the current attribute values
58 - def __str__(self):
59 if self.line is not None: 60 return self.line 61 else: 62 return self.to_string()
63 64 # If an attribute is modified after initialization 65 # set line to None since it is no longer accurate.
66 - def __setattr__(self, name, value):
67 if hasattr(self,name): 68 self.__dict__['line'] = None 69 self.__dict__[name] = value
70
71 - def to_string(self):
72 raise Exception('This method must be overridden in derived classes')
73 74
75 -class SectionLine(LineType):
76 regex = re.compile(r'^\[' 77 r'(?P<name>[^]]+)' 78 r'\]\s*' 79 r'((?P<csep>;|#)(?P<comment>.*))?$') 80
81 - def __init__(self, name, comment=None, comment_separator=None, 82 comment_offset=-1, line=None):
83 super(SectionLine, self).__init__(line) 84 self.name = name 85 self.comment = comment 86 self.comment_separator = comment_separator 87 self.comment_offset = comment_offset
88
89 - def to_string(self):
90 out = '[' + self.name + ']' 91 if self.comment is not None: 92 # try to preserve indentation of comments 93 out = (out+' ').ljust(self.comment_offset) 94 out = out + self.comment_separator + self.comment 95 return out
96
97 - def parse(cls, line):
98 m = cls.regex.match(line.rstrip()) 99 if m is None: 100 return None 101 return cls(m.group('name'), m.group('comment'), 102 m.group('csep'), m.start('csep'), 103 line)
104 parse = classmethod(parse)
105 106
107 -class OptionLine(LineType):
108 - def __init__(self, name, value, separator=' = ', comment=None, 109 comment_separator=None, comment_offset=-1, line=None):
110 super(OptionLine, self).__init__(line) 111 self.name = name 112 self.value = value 113 self.separator = separator 114 self.comment = comment 115 self.comment_separator = comment_separator 116 self.comment_offset = comment_offset
117
118 - def to_string(self):
119 out = '%s%s%s' % (self.name, self.separator, self.value) 120 if self.comment is not None: 121 # try to preserve indentation of comments 122 out = (out+' ').ljust(self.comment_offset) 123 out = out + self.comment_separator + self.comment 124 return out
125 126 regex = re.compile(r'^(?P<name>[^:=\s[][^:=\s]*)' 127 r'(?P<sep>\s*[:=]\s*)' 128 r'(?P<value>.*)$') 129
130 - def parse(cls, line):
131 m = cls.regex.match(line.rstrip()) 132 if m is None: 133 return None 134 135 name = m.group('name').rstrip() 136 value = m.group('value') 137 sep = m.group('sep') 138 139 # comments are not detected in the regex because 140 # ensuring total compatibility with ConfigParser 141 # requires that: 142 # option = value ;comment // value=='value' 143 # option = value;1 ;comment // value=='value;1 ;comment' 144 # 145 # Doing this in a regex would be complicated. I 146 # think this is a bug. The whole issue of how to 147 # include ';' in the value needs to be addressed. 148 # Also, '#' doesn't mark comments in options... 149 150 coff = value.find(';') 151 if coff != -1 and value[coff-1].isspace(): 152 comment = value[coff+1:] 153 csep = value[coff] 154 value = value[:coff].rstrip() 155 coff = m.start('value') + coff 156 else: 157 comment = None 158 csep = None 159 coff = -1 160 161 return cls(name, value, sep, comment, csep, coff, line)
162 parse = classmethod(parse)
163 164
165 -class CommentLine(LineType):
166 regex = re.compile(r'^(?P<csep>[;#]|[rR][eE][mM])' 167 r'(?P<comment>.*)$') 168
169 - def __init__(self, comment='', separator='#', line=None):
170 super(CommentLine, self).__init__(line) 171 self.comment = comment 172 self.separator = separator
173
174 - def to_string(self):
175 return self.separator + self.comment
176
177 - def parse(cls, line):
178 m = cls.regex.match(line.rstrip()) 179 if m is None: 180 return None 181 return cls(m.group('comment'), m.group('csep'), line)
182 parse = classmethod(parse)
183 184
185 -class EmptyLine(LineType):
186 # could make this a singleton
187 - def to_string(self):
188 return ''
189
190 - def parse(cls, line):
191 if line.strip(): return None 192 return cls(line)
193 parse = classmethod(parse)
194 195
196 -class ContinuationLine(LineType):
197 regex = re.compile(r'^\s+(?P<value>.*)$') 198
199 - def __init__(self, value, value_offset=8, line=None):
200 super(ContinuationLine, self).__init__(line) 201 self.value = value 202 self.value_offset = value_offset
203
204 - def to_string(self):
205 return ' '*self.value_offset + self.value
206
207 - def parse(cls, line):
208 m = cls.regex.match(line.rstrip()) 209 if m is None: 210 return None 211 return cls(m.group('value'), m.start('value'), line)
212 parse = classmethod(parse)
213 214
215 -class LineContainer(object):
216 - def __init__(self, d=None):
217 self.contents = [] 218 self.orgvalue = None 219 if d: 220 if isinstance(d, list): self.extend(d) 221 else: self.add(d)
222
223 - def add(self, x):
224 self.contents.append(x)
225
226 - def extend(self, x):
227 for i in x: self.add(i)
228
229 - def get_name(self):
230 return self.contents[0].name
231
232 - def set_name(self, data):
233 self.contents[0].name = data
234
235 - def get_value(self):
236 if self.orgvalue is not None: 237 return self.orgvalue 238 elif len(self.contents) == 1: 239 return self.contents[0].value 240 else: 241 return '\n'.join([str(x.value) for x in self.contents 242 if not isinstance(x, (CommentLine, EmptyLine))])
243
244 - def set_value(self, data):
245 self.orgvalue = data 246 lines = str(data).split('\n') 247 linediff = len(lines) - len(self.contents) 248 if linediff > 0: 249 for _ in range(linediff): 250 self.add(ContinuationLine('')) 251 elif linediff < 0: 252 self.contents = self.contents[:linediff] 253 for i,v in enumerate(lines): 254 self.contents[i].value = v
255 256 name = property(get_name, set_name) 257 value = property(get_value, set_value) 258
259 - def __str__(self):
260 s = [str(x) for x in self.contents] 261 return '\n'.join(s)
262
263 - def finditer(self, key):
264 for x in self.contents[::-1]: 265 if hasattr(x, 'name') and x.name==key: 266 yield x
267
268 - def find(self, key):
269 for x in self.finditer(key): 270 return x 271 raise KeyError(key)
272 273
274 -def _make_xform_property(myattrname, srcattrname=None):
275 private_attrname = myattrname + 'value' 276 private_srcname = myattrname + 'source' 277 if srcattrname is None: 278 srcattrname = myattrname 279 280 def getfn(self): 281 srcobj = getattr(self, private_srcname) 282 if srcobj is not None: 283 return getattr(srcobj, srcattrname) 284 else: 285 return getattr(self, private_attrname)
286 287 def setfn(self, value): 288 srcobj = getattr(self, private_srcname) 289 if srcobj is not None: 290 setattr(srcobj, srcattrname, value) 291 else: 292 setattr(self, private_attrname, value) 293 294 return property(getfn, setfn) 295 296
297 -class INISection(config.ConfigNamespace):
298 _lines = None 299 _options = None 300 _defaults = None 301 _optionxformvalue = None 302 _optionxformsource = None
303 - def __init__(self, lineobj, defaults = None, 304 optionxformvalue=None, optionxformsource=None):
305 self._lines = [lineobj] 306 self._defaults = defaults 307 self._optionxformvalue = optionxformvalue 308 self._optionxformsource = optionxformsource 309 self._options = {}
310 311 _optionxform = _make_xform_property('_optionxform') 312
313 - def __getitem__(self, key):
314 if key == '__name__': 315 return self._lines[-1].name 316 if self._optionxform: key = self._optionxform(key) 317 try: 318 return self._options[key].value 319 except KeyError: 320 if self._defaults and key in self._defaults._options: 321 return self._defaults._options[key].value 322 else: 323 raise
324
325 - def __setitem__(self, key, value):
326 if self._optionxform: xkey = self._optionxform(key) 327 else: xkey = key 328 if xkey not in self._options: 329 # create a dummy object - value may have multiple lines 330 obj = LineContainer(OptionLine(key, '')) 331 self._lines[-1].add(obj) 332 self._options[xkey] = obj 333 # the set_value() function in LineContainer 334 # automatically handles multi-line values 335 self._options[xkey].value = value
336
337 - def __delitem__(self, key):
338 if self._optionxform: key = self._optionxform(key) 339 for l in self._lines: 340 remaining = [] 341 for o in l.contents: 342 if isinstance(o, LineContainer): 343 n = o.name 344 if self._optionxform: n = self._optionxform(n) 345 if key != n: remaining.append(o) 346 else: 347 remaining.append(o) 348 l.contents = remaining 349 del self._options[key]
350
351 - def __iter__(self):
352 d = Set() 353 for l in self._lines: 354 for x in l.contents: 355 if isinstance(x, LineContainer): 356 if self._optionxform: 357 ans = self._optionxform(x.name) 358 else: 359 ans = x.name 360 if ans not in d: 361 yield ans 362 d.add(ans) 363 if self._defaults: 364 for x in self._defaults: 365 if x not in d: 366 yield x 367 d.add(x)
368
369 - def new_namespace(self, name):
370 raise Exception('No sub-sections allowed', name)
371 372
373 -def make_comment(line):
374 return CommentLine(line.rstrip())
375 376
377 -def readline_iterator(f):
378 """iterate over a file by only using the file object's readline method""" 379 380 have_newline = False 381 while True: 382 line = f.readline() 383 384 if not line: 385 if have_newline: 386 yield "" 387 return 388 389 if line.endswith('\n'): 390 have_newline = True 391 else: 392 have_newline = False 393 394 yield line
395 396
397 -class INIConfig(config.ConfigNamespace):
398 _data = None 399 _sections = None 400 _defaults = None 401 _optionxformvalue = None 402 _optionxformsource = None 403 _sectionxformvalue = None 404 _sectionxformsource = None 405 _parse_exc = None
406 - def __init__(self, fp=None, defaults = None, parse_exc=True, 407 optionxformvalue=str.lower, optionxformsource=None, 408 sectionxformvalue=None, sectionxformsource=None):
409 self._data = LineContainer() 410 self._parse_exc = parse_exc 411 self._optionxformvalue = optionxformvalue 412 self._optionxformsource = optionxformsource 413 self._sectionxformvalue = sectionxformvalue 414 self._sectionxformsource = sectionxformsource 415 self._sections = {} 416 if defaults is None: defaults = {} 417 self._defaults = INISection(LineContainer(), optionxformsource=self) 418 for name, value in defaults.iteritems(): 419 self._defaults[name] = value 420 if fp is not None: 421 self.readfp(fp)
422 423 _optionxform = _make_xform_property('_optionxform', 'optionxform') 424 _sectionxform = _make_xform_property('_sectionxform', 'optionxform') 425
426 - def __getitem__(self, key):
427 if key == DEFAULTSECT: 428 return self._defaults 429 if self._sectionxform: key = self._sectionxform(key) 430 return self._sections[key]
431
432 - def __setitem__(self, key, value):
433 raise Exception('Values must be inside sections', key, value)
434
435 - def __delitem__(self, key):
436 if self._sectionxform: key = self._sectionxform(key) 437 for line in self._sections[key]._lines: 438 self._data.contents.remove(line) 439 del self._sections[key]
440
441 - def __iter__(self):
442 d = Set() 443 for x in self._data.contents: 444 if isinstance(x, LineContainer): 445 if x.name not in d: 446 yield x.name 447 d.add(x.name)
448
449 - def new_namespace(self, name):
450 if self._data.contents: 451 self._data.add(EmptyLine()) 452 obj = LineContainer(SectionLine(name)) 453 self._data.add(obj) 454 if self._sectionxform: name = self._sectionxform(name) 455 if name in self._sections: 456 ns = self._sections[name] 457 ns._lines.append(obj) 458 else: 459 ns = INISection(obj, defaults=self._defaults, 460 optionxformsource=self) 461 self._sections[name] = ns 462 return ns
463
464 - def __str__(self):
465 return str(self._data)
466 467 _line_types = [EmptyLine, CommentLine, 468 SectionLine, OptionLine, 469 ContinuationLine] 470
471 - def _parse(self, line):
472 for linetype in self._line_types: 473 lineobj = linetype.parse(line) 474 if lineobj: 475 return lineobj 476 else: 477 # can't parse line 478 return None
479
480 - def readfp(self, fp):
481 cur_section = None 482 cur_option = None 483 cur_section_name = None 484 cur_option_name = None 485 pending_lines = [] 486 try: 487 fname = fp.name 488 except AttributeError: 489 fname = '<???>' 490 linecount = 0 491 exc = None 492 line = None 493 494 for line in readline_iterator(fp): 495 lineobj = self._parse(line) 496 linecount += 1 497 498 if not cur_section and not isinstance(lineobj, 499 (CommentLine, EmptyLine, SectionLine)): 500 if self._parse_exc: 501 raise MissingSectionHeaderError(fname, linecount, line) 502 else: 503 lineobj = make_comment(line) 504 505 if lineobj is None: 506 if self._parse_exc: 507 if exc is None: exc = ParsingError(fname) 508 exc.append(linecount, line) 509 lineobj = make_comment(line) 510 511 if isinstance(lineobj, ContinuationLine): 512 if cur_option: 513 cur_option.extend(pending_lines) 514 pending_lines = [] 515 cur_option.add(lineobj) 516 else: 517 # illegal continuation line - convert to comment 518 if self._parse_exc: 519 if exc is None: exc = ParsingError(fname) 520 exc.append(linecount, line) 521 lineobj = make_comment(line) 522 523 if isinstance(lineobj, OptionLine): 524 cur_section.extend(pending_lines) 525 pending_lines = [] 526 cur_option = LineContainer(lineobj) 527 cur_section.add(cur_option) 528 if self._optionxform: 529 cur_option_name = self._optionxform(cur_option.name) 530 else: 531 cur_option_name = cur_option.name 532 if cur_section_name == DEFAULTSECT: 533 optobj = self._defaults 534 else: 535 optobj = self._sections[cur_section_name] 536 optobj._options[cur_option_name] = cur_option 537 538 if isinstance(lineobj, SectionLine): 539 self._data.extend(pending_lines) 540 pending_lines = [] 541 cur_section = LineContainer(lineobj) 542 self._data.add(cur_section) 543 cur_option = None 544 cur_option_name = None 545 if cur_section.name == DEFAULTSECT: 546 self._defaults._lines.append(cur_section) 547 cur_section_name = DEFAULTSECT 548 else: 549 if self._sectionxform: 550 cur_section_name = self._sectionxform(cur_section.name) 551 else: 552 cur_section_name = cur_section.name 553 if not self._sections.has_key(cur_section_name): 554 self._sections[cur_section_name] = \ 555 INISection(cur_section, defaults=self._defaults, 556 optionxformsource=self) 557 else: 558 self._sections[cur_section_name]._lines.append(cur_section) 559 560 if isinstance(lineobj, (CommentLine, EmptyLine)): 561 pending_lines.append(lineobj) 562 563 self._data.extend(pending_lines) 564 if line and line[-1]=='\n': 565 self._data.add(EmptyLine()) 566 567 if exc: 568 raise exc
569