blob: 30b348ebaddfbdc2da891c286162c59e2db2e76b [file] [log] [blame]
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
__all__ = ['read_ini']
import os
def read_ini(fp, variables=None, default='DEFAULT', defaults_only=False,
comments=';#', separators=('=', ':'),
strict=True):
"""
read an .ini file and return a list of [(section, values)]
- fp : file pointer or path to read
- variables : default set of variables
- default : name of the section for the default section
- defaults_only : if True, return the default section only
- comments : characters that if they start a line denote a comment
- separators : strings that denote key, value separation in order
- strict : whether to be strict about parsing
"""
# variables
variables = variables or {}
sections = []
key = value = None
section_names = set()
if isinstance(fp, basestring):
fp = file(fp)
# read the lines
for (linenum, line) in enumerate(fp.read().splitlines(), start=1):
stripped = line.strip()
# ignore blank lines
if not stripped:
# reset key and value to avoid continuation lines
key = value = None
continue
# ignore comment lines
if stripped[0] in comments:
continue
# check for a new section
if len(stripped) > 2 and stripped[0] == '[' and stripped[-1] == ']':
section = stripped[1:-1].strip()
key = value = None
# deal with DEFAULT section
if section.lower() == default.lower():
if strict:
assert default not in section_names
section_names.add(default)
current_section = variables
continue
if strict:
# make sure this section doesn't already exist
assert section not in section_names, "Section '%s' already found in '%s'" % (section, section_names)
section_names.add(section)
current_section = {}
sections.append((section, current_section))
continue
# if there aren't any sections yet, something bad happen
if not section_names:
raise Exception('No sections found')
# (key, value) pair
for separator in separators:
if separator in stripped:
key, value = stripped.split(separator, 1)
key = key.strip()
value = value.strip()
if strict:
# make sure this key isn't already in the section or empty
assert key
if current_section is not variables:
assert key not in current_section
current_section[key] = value
break
else:
# continuation line ?
if line[0].isspace() and key:
value = '%s%s%s' % (value, os.linesep, stripped)
current_section[key] = value
else:
# something bad happened!
if hasattr(fp, 'name'):
filename = fp.name
else:
filename = 'unknown'
raise Exception("Error parsing manifest file '%s', line %s" %
(filename, linenum))
# server-root is a special os path declared relative to the manifest file.
# inheritance demands we expand it as absolute
if 'server-root' in variables:
root = os.path.join(os.path.dirname(fp.name),
variables['server-root'])
variables['server-root'] = os.path.abspath(root)
# return the default section only if requested
if defaults_only:
return [(default, variables)]
# interpret the variables
def interpret_variables(global_dict, local_dict):
variables = global_dict.copy()
if 'skip-if' in local_dict and 'skip-if' in variables:
local_dict['skip-if'] = "(%s) || (%s)" % (variables['skip-if'].split('#')[0], local_dict['skip-if'].split('#')[0])
variables.update(local_dict)
return variables
sections = [(i, interpret_variables(variables, j)) for i, j in sections]
return sections