Esempio n. 1
0
 def _set_formatter(self, filename):
     """Obtain instance of the formatter"""
     if self._formatter is None:
         if is_yeb_format(filename, self.rawcontent):
             self._formatter = FormatYeb()
         else:
             klass = self._get_format_version_class()
             self._formatter = klass()
     self._formatter.parse(self.rawcontent)
Esempio n. 2
0
class EasyConfigParser(object):
    """Read the easyconfig file, return a parsed config object
        Can contain references to multiple version and toolchain/toolchain versions
    """
    def __init__(self,
                 filename=None,
                 format_version=None,
                 rawcontent=None,
                 auto_convert_value_types=True):
        """
        Initialise the EasyConfigParser class
        :param filename: path to easyconfig file to parse (superseded by rawcontent, if specified)
        :param format_version: version of easyconfig file format, used to determine how to parse supplied easyconfig
        :param rawcontent: raw content of easyconfig file to parse (preferred over easyconfig supplied via filename)
        :param auto_convert_value_types: indicates whether types of easyconfig values should be automatically converted
                                         in case they are wrong
        """
        self.log = fancylogger.getLogger(self.__class__.__name__, fname=False)

        self.rawcontent = None  # the actual unparsed content

        self.auto_convert = auto_convert_value_types

        self.get_fn = None  # read method and args
        self.set_fn = None  # write method and args

        self.format_version = format_version
        self._formatter = None
        if rawcontent is not None:
            self.rawcontent = rawcontent
            self._set_formatter(filename)
        elif filename is not None:
            self._check_filename(filename)
            self.process()
        else:
            raise EasyBuildError(
                "Neither filename nor rawcontent provided to EasyConfigParser")

    def process(self, filename=None):
        """Create an instance"""
        self._read(filename=filename)
        self._set_formatter(filename)

    def check_values_types(self, cfg):
        """
        Check types of easyconfig parameter values.

        :param cfg: dictionary with easyconfig parameter values (result of get_config_dict())
        """
        wrong_type_msgs = []
        for key in cfg:
            type_ok, newval = check_type_of_param_value(
                key, cfg[key], self.auto_convert)
            if not type_ok:
                wrong_type_msgs.append(
                    "value for '%s' should be of type '%s'" %
                    (key, PARAMETER_TYPES[key].__name__))
            elif newval != cfg[key]:
                warning_msg = "Value for '%s' easyconfig parameter was converted from %s (type: %s) to %s (type: %s)"
                self.log.warning(warning_msg, key, cfg[key], type(cfg[key]),
                                 newval, type(newval))
                cfg[key] = newval

        if wrong_type_msgs:
            raise EasyBuildError(
                "Type checking of easyconfig parameter values failed: %s",
                ', '.join(wrong_type_msgs))
        else:
            self.log.info(
                "Type checking of easyconfig parameter values passed!")

    def _check_filename(self, fn):
        """Perform sanity check on the filename, and set mechanism to set the content of the file"""
        if os.path.isfile(fn):
            self.get_fn = (read_file, (fn, ))
            self.set_fn = (write_file, (fn, self.rawcontent))

        self.log.debug(
            "Process filename %s with get function %s, set function %s" %
            (fn, self.get_fn, self.set_fn))

        if self.get_fn is None:
            raise EasyBuildError(
                'Failed to determine get function for filename %s', fn)
        if self.set_fn is None:
            raise EasyBuildError(
                'Failed to determine set function for filename %s', fn)

    def _read(self, filename=None):
        """Read the easyconfig, dump content in self.rawcontent"""
        if filename is not None:
            self._check_filename(filename)

        try:
            self.rawcontent = self.get_fn[0](*self.get_fn[1])
        except IOError as err:
            raise EasyBuildError('Failed to obtain content with %s: %s',
                                 self.get_fn, err)

        if not isinstance(self.rawcontent, string_type):
            msg = 'rawcontent is not a string: type %s, content %s' % (type(
                self.rawcontent), self.rawcontent)
            raise EasyBuildError("Unexpected result for raw content: %s", msg)

    def _det_format_version(self):
        """Extract the format version from the raw content"""
        if self.format_version is None:
            self.format_version = get_format_version(self.rawcontent)
            if self.format_version is None:
                self.format_version = FORMAT_DEFAULT_VERSION
                self.log.debug('No version found, using default %s' %
                               self.format_version)

    def _get_format_version_class(self):
        """Locate the class matching the version"""
        if self.format_version is None:
            self._det_format_version()
        found_classes = get_format_version_classes(version=self.format_version)
        if len(found_classes) == 1:
            return found_classes[0]
        elif not found_classes:
            raise EasyBuildError('No format classes found matching version %s',
                                 self.format_version)
        else:
            raise EasyBuildError(
                "More than one format class found matching version %s in %s",
                self.format_version, found_classes)

    def _set_formatter(self, filename):
        """Obtain instance of the formatter"""
        if self._formatter is None:
            if is_yeb_format(filename, self.rawcontent):
                self._formatter = FormatYeb()
            else:
                klass = self._get_format_version_class()
                self._formatter = klass()
        self._formatter.parse(self.rawcontent)

    def set_format_text(self):
        """Create the text for the formatter instance"""
        # TODO create the data in self.rawcontent
        raise NotImplementedError

    def write(self, filename=None):
        """Write the easyconfig format instance, using content in self.rawcontent."""
        if filename is not None:
            self._check_filename(filename)

        try:
            self.set_fn[0](*self.set_fn[1])
        except IOError as err:
            raise EasyBuildError("Failed to process content with %s: %s",
                                 self.set_fn, err)

    def set_specifications(self, specs):
        """Set specifications."""
        self._formatter.set_specifications(specs)

    def get_config_dict(self, validate=True):
        """Return parsed easyconfig as a dict."""
        # allows to bypass the validation step, typically for testing
        if validate:
            self._formatter.validate()

        cfg = self._formatter.get_config_dict()
        self.check_values_types(cfg)

        return cfg

    def dump(self,
             ecfg,
             default_values,
             templ_const,
             templ_val,
             toolchain_hierarchy=None):
        """Dump easyconfig in format it was parsed from."""
        return self._formatter.dump(ecfg,
                                    default_values,
                                    templ_const,
                                    templ_val,
                                    toolchain_hierarchy=toolchain_hierarchy)