# An ini parser that supports ordered sections/options # Also supports updates, while preserving structure # Backward-compatiable with ConfigParser
import re from .configparser import DEFAULTSECT, ParsingError, MissingSectionHeaderError
import six
from . import config
class LineType(object):
line = None
def __init__(self, line=None): if line isnotNone:
self.line = line.strip('\n')
# Return the original line for unmodified objects # Otherwise construct using the current attribute values def __str__(self): if self.line isnotNone: return self.line else: return self.to_string()
# If an attribute is modified after initialization # set line to None since it is no longer accurate. def __setattr__(self, name, value): if hasattr(self,name):
self.__dict__['line'] = None
self.__dict__[name] = value
def to_string(self): raise Exception('This method must be overridden in derived classes')
class SectionLine(LineType):
regex = re.compile(r'^\['
r'(?P[^]]+)'
r'\]\s*'
r'((?P;|#)(?P.*))?$')
def to_string(self):
out = '[' + self.name + ']' if self.comment isnotNone: # try to preserve indentation of comments
out = (out+' ').ljust(self.comment_offset)
out = out + self.comment_separator + self.comment return out
def parse(cls, line):
m = cls.regex.match(line.rstrip()) if m isNone: returnNone return cls(m.group('name'), m.group('comment'),
m.group('csep'), m.start('csep'),
line)
parse = classmethod(parse)
def to_string(self):
out = '%s%s%s' % (self.name, self.separator, self.value) if self.comment isnotNone: # try to preserve indentation of comments
out = (out+' ').ljust(self.comment_offset)
out = out + self.comment_separator + self.comment return out
def parse(cls, line):
m = cls.regex.match(line.rstrip()) if m isNone: returnNone
name = m.group('name').rstrip()
value = m.group('value')
sep = m.group('name')[len(name):] + m.group('sep')
# comments are not detected in the regex because # ensuring total compatibility with ConfigParser # requires that: # option = value ;comment // value=='value' # option = value;1 ;comment // value=='value;1 ;comment' # # Doing this in a regex would be complicated. I # think this is a bug. The whole issue of how to # include ';' in the value needs to be addressed. # Also, '#' doesn't mark comments in options...
# If there is an existing ContinuationLine, use its offset
value_offset = None for v in self.contents: if isinstance(v, ContinuationLine):
value_offset = v.value_offset break
# Rebuild contents list, preserving initial OptionLine
self.contents = self.contents[0:1]
self.contents[0].value = lines[0] del lines[0] for line in lines: if line.strip():
self.add(ContinuationLine(line, value_offset)) else:
self.add(EmptyLine())
name = property(get_name, set_name)
value = property(get_value, set_value)
def __str__(self):
s = [x.__str__() for x in self.contents] return'\n'.join(s)
def finditer(self, key): for x in self.contents[::-1]: if hasattr(x, 'name') and x.name==key: yield x
def find(self, key): for x in self.finditer(key): return x raise KeyError(key)
def _compat_get(self, key): # identical to __getitem__ except that _compat_XXX # is checked for backward-compatible handling if key == '__name__': return self._lines[-1].name if self._optionxform: key = self._optionxform(key) try:
value = self._options[key].value
del_empty = key in self._compat_skip_empty_lines except KeyError: if self._defaults and key in self._defaults._options:
value = self._defaults._options[key].value
del_empty = key in self._defaults._compat_skip_empty_lines else: raise if del_empty:
value = re.sub('\n+', '\n', value) return value
def _getitem(self, key): if key == '__name__': return self._lines[-1].name if self._optionxform: key = self._optionxform(key) try: return self._options[key].value except KeyError: if self._defaults and key in self._defaults._options: return self._defaults._options[key].value else: raise
def __setitem__(self, key, value): if self._optionxform: xkey = self._optionxform(key) else: xkey = key if xkey in self._compat_skip_empty_lines:
self._compat_skip_empty_lines.remove(xkey) if xkey notin self._options: # create a dummy object - value may have multiple lines
obj = LineContainer(OptionLine(key, ''))
self._lines[-1].add(obj)
self._options[xkey] = obj # the set_value() function in LineContainer # automatically handles multi-line values
self._options[xkey].value = value
def __delitem__(self, key): if self._optionxform: key = self._optionxform(key) if key in self._compat_skip_empty_lines:
self._compat_skip_empty_lines.remove(key) for l in self._lines:
remaining = [] for o in l.contents: if isinstance(o, LineContainer):
n = o.name if self._optionxform: n = self._optionxform(n) if key != n: remaining.append(o) else:
remaining.append(o)
l.contents = remaining del self._options[key]
def __iter__(self):
d = set() for l in self._lines: for x in l.contents: if isinstance(x, LineContainer): if self._optionxform:
ans = self._optionxform(x.name) else:
ans = x.name if ans notin d: yield ans
d.add(ans) if self._defaults: for x in self._defaults: if x notin d: yield x
d.add(x)
def _getitem(self, key): if key == DEFAULTSECT: return self._defaults if self._sectionxform: key = self._sectionxform(key) return self._sections[key]
def __setitem__(self, key, value): raise Exception('Values must be inside sections', key, value)
def __delitem__(self, key): if self._sectionxform: key = self._sectionxform(key) for line in self._sections[key]._lines:
self._data.contents.remove(line) del self._sections[key]
def __iter__(self):
d = set()
d.add(DEFAULTSECT) for x in self._data.contents: if isinstance(x, LineContainer): if x.name notin d: yield x.name
d.add(x.name)
def _new_namespace(self, name): if self._data.contents:
self._data.add(EmptyLine())
obj = LineContainer(SectionLine(name))
self._data.add(obj) if self._sectionxform: name = self._sectionxform(name) if name in self._sections:
ns = self._sections[name]
ns._lines.append(obj) else:
ns = INISection(obj, defaults=self._defaults,
optionxformsource=self)
self._sections[name] = ns return ns
def _parse(self, line): for linetype in self._line_types:
lineobj = linetype.parse(line) if lineobj: return lineobj else: # can't parse line returnNone
for line in readline_iterator(fp): # Check for BOM on first line if line_count == 0 and isinstance(line, six.text_type): if line[0] == u'\ufeff':
line = line[1:]
self._bom = True
if line_obj isNone: if self._parse_exc: if exc isNone:
exc = ParsingError(fname)
exc.append(line_count, line)
line_obj = make_comment(line)
if isinstance(line_obj, ContinuationLine): if cur_option: if pending_lines:
cur_option.extend(pending_lines)
pending_lines = [] if pending_empty_lines:
optobj._compat_skip_empty_lines.add(cur_option_name)
pending_empty_lines = False
cur_option.add(line_obj) else: # illegal continuation line - convert to comment if self._parse_exc: if exc isNone:
exc = ParsingError(fname)
exc.append(line_count, line)
line_obj = make_comment(line)
if isinstance(line_obj, OptionLine): if pending_lines:
cur_section.extend(pending_lines)
pending_lines = []
pending_empty_lines = False
cur_option = LineContainer(line_obj)
cur_section.add(cur_option) if self._optionxform:
cur_option_name = self._optionxform(cur_option.name) else:
cur_option_name = cur_option.name if cur_section_name == DEFAULTSECT:
optobj = self._defaults else:
optobj = self._sections[cur_section_name]
optobj._options[cur_option_name] = cur_option
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.