| 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 iniparse import config
46 from sets import Set
47 from ConfigParser import DEFAULTSECT, ParsingError, MissingSectionHeaderError
48
50 line = None
51
55
56 # Return the original line for unmodified objects
57 # Otherwise construct using the current attribute values
63
64 # If an attribute is modified after initialization
65 # set line to None since it is no longer accurate.
70
73
74
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
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
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
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
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
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
166 regex = re.compile(r'^(?P<csep>[;#]|[rR][eE][mM])'
167 r'(?P<comment>.*)$')
168
170 super(CommentLine, self).__init__(line)
171 self.comment = comment
172 self.separator = separator
173
176
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
194
195
197 regex = re.compile(r'^\s+(?P<value>.*)$')
198
200 super(ContinuationLine, self).__init__(line)
201 self.value = value
202 self.value_offset = value_offset
203
205 return ' '*self.value_offset + self.value
206
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
217 self.contents = []
218 self.orgvalue = None
219 if d:
220 if isinstance(d, list): self.extend(d)
221 else: self.add(d)
222
224 self.contents.append(x)
225
228
230 return self.contents[0].name
231
234
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
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
262
267
272
273
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
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
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
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
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
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
370 raise Exception('No sub-sections allowed', name)
371
372
375
376
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
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
427 if key == DEFAULTSECT:
428 return self._defaults
429 if self._sectionxform: key = self._sectionxform(key)
430 return self._sections[key]
431
434
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
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
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
465 return str(self._data)
466
467 _line_types = [EmptyLine, CommentLine,
468 SectionLine, OptionLine,
469 ContinuationLine]
470
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
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
| Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Wed Mar 26 12:49:45 2008 | http://epydoc.sourceforge.net |