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