| Home | Trees | Indices | Help |
|
|---|
|
|
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
51 line = None
52
56
57 # Return the original line for unmodified objects
58 # Otherwise construct using the current attribute values
64
65 # If an attribute is modified after initialization
66 # set line to None since it is no longer accurate.
71
74
75
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
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
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
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
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
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
167 regex = re.compile(r'^(?P<csep>[;#]|[rR][eE][mM])'
168 r'(?P<comment>.*)$')
169
171 super(CommentLine, self).__init__(line)
172 self.comment = comment
173 self.separator = separator
174
177
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
195
196
198 regex = re.compile(r'^\s+(?P<value>.*)$')
199
201 super(ContinuationLine, self).__init__(line)
202 self.value = value
203 self.value_offset = value_offset
204
206 return ' '*self.value_offset + self.value
207
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
218 self.contents = []
219 self.orgvalue = None
220 if d:
221 if isinstance(d, list): self.extend(d)
222 else: self.add(d)
223
225 self.contents.append(x)
226
229
231 return self.contents[0].name
232
235
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
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
263
268
273
274
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
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
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
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
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
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
371 raise Exception('No sub-sections allowed', name)
372
373
376
377
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
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
428 if key == DEFAULTSECT:
429 return self._defaults
430 if self._sectionxform: key = self._sectionxform(key)
431 return self._sections[key]
432
435
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
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
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
466 return str(self._data)
467
468 _line_types = [EmptyLine, CommentLine,
469 SectionLine, OptionLine,
470 ContinuationLine]
471
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
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
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Tue Apr 12 18:12:01 2011 | http://epydoc.sourceforge.net |