summaryrefslogtreecommitdiffstats
path: root/testing/mozbase/manifestparser/manifestparser/ini.py
diff options
context:
space:
mode:
Diffstat (limited to 'testing/mozbase/manifestparser/manifestparser/ini.py')
-rw-r--r--testing/mozbase/manifestparser/manifestparser/ini.py142
1 files changed, 142 insertions, 0 deletions
diff --git a/testing/mozbase/manifestparser/manifestparser/ini.py b/testing/mozbase/manifestparser/manifestparser/ini.py
new file mode 100644
index 000000000..5117dd1ae
--- /dev/null
+++ b/testing/mozbase/manifestparser/manifestparser/ini.py
@@ -0,0 +1,142 @@
+# 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/.
+
+import os
+
+__all__ = ['read_ini', 'combine_fields']
+
+
+def read_ini(fp, variables=None, default='DEFAULT', defaults_only=False,
+ comments=';#', separators=('=', ':'), strict=True,
+ handle_defaults=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
+ - handle_defaults : whether to incorporate defaults into each section
+ """
+
+ # 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)]
+
+ global_vars = variables if handle_defaults else {}
+ sections = [(i, combine_fields(global_vars, j)) for i, j in sections]
+ return sections
+
+
+def combine_fields(global_vars, local_vars):
+ """
+ Combine the given manifest entries according to the semantics of specific fields.
+ This is used to combine manifest level defaults with a per-test definition.
+ """
+ if not global_vars:
+ return local_vars
+ if not local_vars:
+ return global_vars
+ field_patterns = {
+ 'skip-if': '(%s) || (%s)',
+ 'support-files': '%s %s',
+ }
+ final_mapping = global_vars.copy()
+ for field_name, value in local_vars.items():
+ if field_name not in field_patterns or field_name not in global_vars:
+ final_mapping[field_name] = value
+ continue
+ global_value = global_vars[field_name]
+ pattern = field_patterns[field_name]
+ final_mapping[field_name] = pattern % (
+ global_value.split('#')[0], value.split('#')[0])
+ return final_mapping