def test_dependency(self): """Test Dependency class""" versop_str = '>= 1.5' tc_versop_str = 'GCC >= 3.0' versop = VersionOperator(versop_str) tc_versop = ToolchainVersionOperator(tc_versop_str) txt = Dependency.SEPARATOR_DEP.join([versop_str]) dest = {'versop':versop} res = Dependency(txt) self.assertEqual(dest, res) self.assertEqual(str(res), txt) txt = Dependency.SEPARATOR_DEP.join([versop_str, tc_versop_str]) dest = {'versop':versop, 'tc_versop':tc_versop} res = Dependency(txt) self.assertEqual(dest, res) self.assertEqual(str(res), txt)
def parse_sections(self, toparse, current): """ Parse Section instance; convert all supported sections, keys and values to their respective representations Returns a dict of (nested) Sections :param toparse: a Section (or ConfigObj) instance, basically a dict of (unparsed) sections :param current: the current NestedDict """ # note: configobj already converts comma-separated strings in lists # # list of supported keywords, all else will fail special_keys = self.VERSION_OPERATOR_VALUE_TYPES.keys() self.log.debug('Processing current depth %s' % current.depth) for key, value in toparse.items(): if isinstance(value, Section): self.log.debug("Enter subsection key %s value %s" % (key, value)) # only supported types of section keys are: # * DEFAULT # * SUPPORTED # * dependencies # * VersionOperator or ToolchainVersionOperator (e.g. [> 2.0], [goolf > 1]) if key in [self.SECTION_MARKER_DEFAULT, self.SECTION_MARKER_SUPPORTED]: # parse value as a section, recursively new_value = self.parse_sections(value, current.get_nested_dict()) self.log.debug('Converted %s section to new value %s' % (key, new_value)) current[key] = new_value elif key == self.SECTION_MARKER_DEPENDENCIES: new_key = 'dependencies' new_value = [] for dep_name, dep_val in value.items(): if isinstance(dep_val, Section): raise EasyBuildError("Unsupported nested section '%s' in dependencies section", dep_name) else: # FIXME: parse the dependency specification for version, toolchain, suffix, etc. dep = Dependency(dep_val, name=dep_name) if dep.name() is None or dep.version() is None: raise EasyBuildError("Failed to find name/version in parsed dependency: %s (dict: %s)", dep, dict(dep)) new_value.append(dep) tmpl = 'Converted dependency section %s to %s, passed it to parent section (or default)' self.log.debug(tmpl % (key, new_value)) if isinstance(current, TopNestedDict): current[self.SECTION_MARKER_DEFAULT].update({new_key: new_value}) else: current.parent[new_key] = new_value else: # try parsing key as toolchain version operator first # try parsing as version operator if it's not a toolchain version operator for marker_type in self.KNOWN_VERSION_MARKER_TYPES: new_key = marker_type(key) if new_key: self.log.debug("'%s' was parsed as a %s section marker" % (key, marker_type.__name__)) break else: self.log.debug("Not a %s section marker" % marker_type.__name__) if not new_key: raise EasyBuildError("Unsupported section marker '%s'", key) # parse value as a section, recursively new_value = self.parse_sections(value, current.get_nested_dict()) self.log.debug('Converted section key %s value %s in new key %s new value %s' % (key, value, new_key, new_value)) current[new_key] = new_value else: # simply pass down any non-special key-value items if not key in special_keys: self.log.debug('Passing down key %s with value %s' % (key, value)) new_value = value # parse individual key-value assignments elif key in self.VERSION_OPERATOR_VALUE_TYPES: value_type = self.VERSION_OPERATOR_VALUE_TYPES[key] # list of supported toolchains/versions # first one is default if isinstance(value, basestring): # so the split should be unnecessary # (if it's not a list already, it's just one value) # TODO this is annoying. check if we can force this in configobj value = value.split(',') # remove possible surrounding whitespace (some people add space after comma) new_value = [value_type(x.strip()) for x in value] if False in [x.is_valid() for x in new_value]: raise EasyBuildError("Failed to parse '%s' as list of %s", value, value_type.__name__) else: raise EasyBuildError('Bug: supported but unknown key %s with non-string value: %s, type %s', key, value, type(value)) self.log.debug("Converted value '%s' for key '%s' into new value '%s'" % (value, key, new_value)) current[key] = new_value return current
def parse_sections(self, toparse, current): """ Parse Section instance; convert all supported sections, keys and values to their respective representations Returns a dict of (nested) Sections @param toparse: a Section (or ConfigObj) instance, basically a dict of (unparsed) sections @param current: the current NestedDict """ # note: configobj already converts comma-separated strings in lists # # list of supported keywords, all else will fail special_keys = self.VERSION_OPERATOR_VALUE_TYPES.keys() self.log.debug('Processing current depth %s' % current.depth) for key, value in toparse.items(): if isinstance(value, Section): self.log.debug("Enter subsection key %s value %s" % (key, value)) # only supported types of section keys are: # * DEFAULT # * SUPPORTED # * dependencies # * VersionOperator or ToolchainVersionOperator (e.g. [> 2.0], [goolf > 1]) if key in [ self.SECTION_MARKER_DEFAULT, self.SECTION_MARKER_SUPPORTED ]: # parse value as a section, recursively new_value = self.parse_sections(value, current.get_nested_dict()) self.log.debug('Converted %s section to new value %s' % (key, new_value)) current[key] = new_value elif key == self.SECTION_MARKER_DEPENDENCIES: new_key = 'dependencies' new_value = [] for dep_name, dep_val in value.items(): if isinstance(dep_val, Section): raise EasyBuildError( "Unsupported nested section '%s' in dependencies section", dep_name) else: # FIXME: parse the dependency specification for version, toolchain, suffix, etc. dep = Dependency(dep_val, name=dep_name) if dep.name() is None or dep.version() is None: raise EasyBuildError( "Failed to find name/version in parsed dependency: %s (dict: %s)", dep, dict(dep)) new_value.append(dep) tmpl = 'Converted dependency section %s to %s, passed it to parent section (or default)' self.log.debug(tmpl % (key, new_value)) if isinstance(current, TopNestedDict): current[self.SECTION_MARKER_DEFAULT].update( {new_key: new_value}) else: current.parent[new_key] = new_value else: # try parsing key as toolchain version operator first # try parsing as version operator if it's not a toolchain version operator for marker_type in self.KNOWN_VERSION_MARKER_TYPES: new_key = marker_type(key) if new_key: self.log.debug( "'%s' was parsed as a %s section marker" % (key, marker_type.__name__)) break else: self.log.debug("Not a %s section marker" % marker_type.__name__) if not new_key: raise EasyBuildError("Unsupported section marker '%s'", key) # parse value as a section, recursively new_value = self.parse_sections(value, current.get_nested_dict()) self.log.debug( 'Converted section key %s value %s in new key %s new value %s' % (key, value, new_key, new_value)) current[new_key] = new_value else: # simply pass down any non-special key-value items if not key in special_keys: self.log.debug('Passing down key %s with value %s' % (key, value)) new_value = value # parse individual key-value assignments elif key in self.VERSION_OPERATOR_VALUE_TYPES: value_type = self.VERSION_OPERATOR_VALUE_TYPES[key] # list of supported toolchains/versions # first one is default if isinstance(value, basestring): # so the split should be unnecessary # (if it's not a list already, it's just one value) # TODO this is annoying. check if we can force this in configobj value = value.split(',') # remove possible surrounding whitespace (some people add space after comma) new_value = [value_type(x.strip()) for x in value] if False in [x.is_valid() for x in new_value]: raise EasyBuildError( "Failed to parse '%s' as list of %s", value, value_type.__name__) else: raise EasyBuildError( 'Bug: supported but unknown key %s with non-string value: %s, type %s', key, value, type(value)) self.log.debug( "Converted value '%s' for key '%s' into new value '%s'" % (value, key, new_value)) current[key] = new_value return current