Esempio n. 1
0
class Analyse(object):

    """A comparison engine for Rose."""

    def __init__(self, config, opts, args, method_paths, reporter=None,
                 popen=None):
        if reporter is None:
            self.reporter = Reporter(opts.verbosity - opts.quietness)
        else:
            self.reporter = reporter
        if popen is None:
            self.popen = RosePopener(event_handler=self.reporter)
        else:
            self.popen = popen
        self.opts = opts
        self.args = args
        self.config = config
        self.load_tasks()
        modules = []
        for path in method_paths:
            for filename in glob.glob(path + "/*.py"):
                modules.append(filename)
        self.load_user_comparison_modules(modules)

    def analyse(self):
        """Perform comparisons given a list of tasks."""
        rc = 0
        for task in self.tasks:

            if self.check_extract(task):
                # Internal AnalysisEngine extract+comparison test

                # Extract data from results and from kgoX
                task = self.do_extract(task, "result")

                for i in range(task.numkgofiles):
                    var = "kgo" + str(i + 1)
                    task = self.do_extract(task, var)

                task = self.do_comparison(task)
            else:
                # External program(s) doing test

                # Command to run
                command = task.extract

                result = re.search(r"\$file", command)
                # If the command contains $file, it is run separately for both
                # the KGO and the result files
                if result:
                    # Replace $file token with resultfile or kgofile
                    resultcommand = self._expand_tokens(command, task,
                                                        "result")
                    kgocommand = self._expand_tokens(command, task, "kgo1")

                    # Run the command on the resultfile
                    task.resultdata = self._run_command(resultcommand)

                    # Run the command on the KGO file
                    task.kgo1data = self._run_command(kgocommand)
                else:
                    # The command works on both files at the same time
                    # Replace tokens $kgofile and $resultfile for actual values
                    command = self._expand_tokens(command, task)

                    # Run the command
                    task.resultdata = self._run_command(command)

                # Run the comparison
                task = self.do_comparison(task)

            self.reporter(TaskCompletionEvent(task),
                          prefix="[%s]" % (task.userstatus))
            if task.numericstatus != PASS:
                rc += 1
        return rc, self.tasks

    def check_extract(self, task):
        """Check if an extract name is present in a user method."""
        for module_name, class_name, method, help in self.user_methods:
            if task.extract == class_name:
                return True
        return False

    def do_comparison(self, task):
        """Run the comparison."""
        for module_name, class_name, method, help in self.user_methods:
            comparison_name = ".".join([module_name, class_name])
            if task.comparison == class_name:
                for module in self.modules:
                    if module.__name__ == module_name:
                        comparison_inst = getattr(module, class_name)()
                        comparison_meth = getattr(comparison_inst, "run")(task)
        return task

    def do_extract(self, task, var):
        """Extract the specified data."""
        for module_name, class_name, method, help in self.user_methods:
            extract_name = ".".join([module_name, class_name])
            if task.extract == class_name:
                for module in self.modules:
                    if module.__name__ == module_name:
                        extract_inst = getattr(module, class_name)()
                        extract_meth = getattr(extract_inst, "run")(task, var)
        return task

    def _run_command(self, command):
        """Run an external command using rose.popen."""
        output, stderr = self.popen.run_ok(command, shell=True)
        output = "".join(output).splitlines()
        return output

    def _expand_tokens(self, inputstring, task, var=None):
        """Expands tokens $resultfile, $file and $kgoXfile."""
        filename = ''
        if var:
            filename = getattr(task, var + "file")
        expansions = {'resultfile': task.resultfile, 'file': filename}
        for i in range(1, task.numkgofiles + 1):
            key = "kgo" + str(i) + "file"
            value = getattr(task, key)
            expansions[key] = value
        inputstring = inputstring.format(**expansions)
        return inputstring

    def _find_file(self, var, task):
        """Finds a file given a variable name containing the filename.

        Given a variable name and task object, this returns the filename it
        points to, including expanding any * characters with glob.
        """

        filevar = var + "file"
        if hasattr(task, filevar):
            configvar = var + "fileconfig"
            setattr(task, configvar, getattr(task, filevar))
            filenames = glob.glob(env_var_process(getattr(task, filevar)))
            if len(filenames) > 0:
                setattr(task, filevar, os.path.abspath(filenames[0]))
        return task

    def load_tasks(self):
        """Loads AnalysisTasks from files.

        Given a list of files, return AnalysisTasks generated from those files.
        This also expands environment variables in filenames, but saves the
        original contents for use when writing out config files
        """

        tasks = []
        for task in self.config.value.keys():
            if task is "env":
                continue
            if task.startswith("file:"):
                continue
            newtask = AnalysisTask()
            newtask.name = task
            value = self.config.get_value([task, "resultfile"])

            # If the task is ignored, this will be None, so continue
            # on to the next task
            if value is None:
                continue

            if "{}" in value:
                newtask.resultfile = value.replace("{}", self.args[0])
            else:
                newtask.resultfile = value
            newtask = self._find_file("result", newtask)
            newtask.extract = self.config.get_value([task, "extract"])
            result = re.search(r":", newtask.extract)
            if result:
                newtask.subextract = ":".join(newtask.extract.split(":")[1:])
                newtask.extract = newtask.extract.split(":")[0]
            newtask.comparison = self.config.get_value([task, "comparison"])
            newtask.tolerance = env_var_process(self.config.get_value(
                                                [task, "tolerance"]))
            newtask.warnonfail = (
                self.config.get_value([task, "warnonfail"]) in ["yes", "true"])

            # Allow for multiple KGO, e.g. kgo1file, kgo2file, for
            # statistical comparisons of results
            newtask.numkgofiles = 0
            for i in range(1, MAX_KGO_FILES):
                kgovar = "kgo" + str(i)
                kgofilevar = kgovar + "file"
                if self.config.get([task, kgofilevar]):
                    value = self.config.get([task, kgofilevar])[:]
                    if "{}" in value:
                        setattr(
                            newtask, kgofilevar,
                            value.replace("{}", self.args[0]))
                    else:
                        setattr(newtask, kgofilevar, value)
                    newtask.numkgofiles += 1
                    newtask = self._find_file(kgovar, newtask)
                else:
                    break
            tasks.append(newtask)
        self.tasks = tasks
        return tasks

    def load_user_comparison_modules(self, files):
        """Import comparison modules and store them."""
        modules = []
        for filename in files:
            directory = os.path.dirname(filename)
            if (not directory.endswith(USRCOMPARISON_DIRNAME) or
                    not filename.endswith(USRCOMPARISON_EXT)):
                continue
            comparison_name = os.path.basename(filename).rpartition(
                USRCOMPARISON_EXT)[0]
            sys.path.insert(0, os.path.abspath(directory))
            try:
                modules.append(__import__(comparison_name))
            except ImportError as e:
                self.reporter(e)
            sys.path.pop(0)
        modules.sort()
        self.modules = modules

        user_methods = []
        for module in modules:
            comparison_name = module.__name__
            contents = inspect.getmembers(module, inspect.isclass)
            for obj_name, obj in contents:
                att_name = "run"
                if hasattr(obj, att_name) and callable(getattr(obj, att_name)):
                    doc_string = obj.__doc__
                    user_methods.append((comparison_name, obj_name, att_name,
                                        doc_string))
        self.user_methods = user_methods
        return user_methods

    def write_config(self, filename, tasks):
        """Write an analysis config file based on a list of tasks provided"""
        config = rose.config.ConfigNode()

        for task in tasks:
            sectionname = task.name
            if task.resultfileconfig:
                config.set([sectionname, "resultfile"], task.resultfileconfig)
            for i in range(1, task.numkgofiles + 1):
                origvar = "kgo" + str(i) + "fileconfig"
                valvar = "kgo" + str(i) + "file"
                if hasattr(task, origvar):
                    config.set([sectionname, valvar], getattr(task, origvar))
            if task.extract:
                config.set([sectionname, "extract"], task.extract)
            if task.subextract:
                config.set([sectionname, "extract"],
                           task.extract + ":" + task.subextract)
            if task.comparison:
                config.set([sectionname, "comparison"], task.comparison)
            if task.tolerance:
                config.set([sectionname, "tolerance"], task.tolerance)
            if task.warnonfail:
                config.set([sectionname, "warnonfail"], "true")
        rose.config.dump(config, filename)
Esempio n. 2
0
class Analyse(object):
    """A comparison engine for Rose."""
    def __init__(self,
                 config,
                 opts,
                 args,
                 method_paths,
                 reporter=None,
                 popen=None):
        if reporter is None:
            self.reporter = Reporter(opts.verbosity - opts.quietness)
        else:
            self.reporter = reporter
        if popen is None:
            self.popen = RosePopener(event_handler=self.reporter)
        else:
            self.popen = popen
        self.opts = opts
        self.args = args
        self.config = config
        self.load_tasks()
        modules = []
        for path in method_paths:
            for filename in glob.glob(path + "/*.py"):
                modules.append(filename)
        self.load_user_comparison_modules(modules)

    def analyse(self):
        """Perform comparisons given a list of tasks."""
        rc = 0
        for task in self.tasks:

            if self.check_extract(task):
                # Internal AnalysisEngine extract+comparison test

                # Extract data from results and from kgoX
                task = self.do_extract(task, "result")

                for i in range(task.numkgofiles):
                    var = "kgo" + str(i + 1)
                    task = self.do_extract(task, var)

                task = self.do_comparison(task)
            else:
                # External program(s) doing test

                # Command to run
                command = task.extract

                result = re.search(r"\$file", command)
                # If the command contains $file, it is run separately for both
                # the KGO and the result files
                if result:
                    # Replace $file token with resultfile or kgofile
                    resultcommand = self._expand_tokens(
                        command, task, "result")
                    kgocommand = self._expand_tokens(command, task, "kgo1")

                    # Run the command on the resultfile
                    task.resultdata = self._run_command(resultcommand)

                    # Run the command on the KGO file
                    task.kgo1data = self._run_command(kgocommand)
                else:
                    # The command works on both files at the same time
                    # Replace tokens $kgofile and $resultfile for actual values
                    command = self._expand_tokens(command, task)

                    # Run the command
                    task.resultdata = self._run_command(command)

                # Run the comparison
                task = self.do_comparison(task)

            self.reporter(TaskCompletionEvent(task),
                          prefix="[%s]" % (task.userstatus))
            if task.numericstatus != PASS:
                rc += 1
        return rc, self.tasks

    def check_extract(self, task):
        """Check if an extract name is present in a user method."""
        for module_name, class_name, method, help in self.user_methods:
            if task.extract == class_name:
                return True
        return False

    def do_comparison(self, task):
        """Run the comparison."""
        for module_name, class_name, method, help in self.user_methods:
            comparison_name = ".".join([module_name, class_name])
            if task.comparison == class_name:
                for module in self.modules:
                    if module.__name__ == module_name:
                        comparison_inst = getattr(module, class_name)()
                        comparison_meth = getattr(comparison_inst, "run")(task)
        return task

    def do_extract(self, task, var):
        """Extract the specified data."""
        for module_name, class_name, method, help in self.user_methods:
            extract_name = ".".join([module_name, class_name])
            if task.extract == class_name:
                for module in self.modules:
                    if module.__name__ == module_name:
                        extract_inst = getattr(module, class_name)()
                        extract_meth = getattr(extract_inst, "run")(task, var)
        return task

    def _run_command(self, command):
        """Run an external command using rose.popen."""
        output, stderr = self.popen.run_ok(command, shell=True)
        output = "".join(output).splitlines()
        return output

    def _expand_tokens(self, inputstring, task, var=None):
        """Expands tokens $resultfile, $file and $kgoXfile."""
        filename = ''
        if var:
            filename = getattr(task, var + "file")
        expansions = {'resultfile': task.resultfile, 'file': filename}
        for i in range(1, task.numkgofiles + 1):
            key = "kgo" + str(i) + "file"
            value = getattr(task, key)
            expansions[key] = value
        inputstring = inputstring.format(**expansions)
        return inputstring

    def _find_file(self, var, task):
        """Finds a file given a variable name containing the filename.

        Given a variable name and task object, this returns the filename it
        points to, including expanding any * characters with glob.
        """

        filevar = var + "file"
        if hasattr(task, filevar):
            configvar = var + "fileconfig"
            setattr(task, configvar, getattr(task, filevar))
            filenames = glob.glob(env_var_process(getattr(task, filevar)))
            if len(filenames) > 0:
                setattr(task, filevar, os.path.abspath(filenames[0]))
        return task

    def load_tasks(self):
        """Loads AnalysisTasks from files.

        Given a list of files, return AnalysisTasks generated from those files.
        This also expands environment variables in filenames, but saves the
        original contents for use when writing out config files
        """

        tasks = []
        for task in self.config.value.keys():
            if task is "env":
                continue
            if task.startswith("file:"):
                continue
            newtask = AnalysisTask()
            newtask.name = task
            value = self.config.get_value([task, "resultfile"])

            # If the task is ignored, this will be None, so continue
            # on to the next task
            if value is None:
                continue

            if "{}" in value:
                newtask.resultfile = value.replace("{}", self.args[0])
            else:
                newtask.resultfile = value
            newtask = self._find_file("result", newtask)
            newtask.extract = self.config.get_value([task, "extract"])
            result = re.search(r":", newtask.extract)
            if result:
                newtask.subextract = ":".join(newtask.extract.split(":")[1:])
                newtask.extract = newtask.extract.split(":")[0]
            newtask.comparison = self.config.get_value([task, "comparison"])
            newtask.tolerance = env_var_process(
                self.config.get_value([task, "tolerance"]))
            newtask.warnonfail = (self.config.get_value([task, "warnonfail"])
                                  in ["yes", "true"])

            # Allow for multiple KGO, e.g. kgo1file, kgo2file, for
            # statistical comparisons of results
            newtask.numkgofiles = 0
            for i in range(1, MAX_KGO_FILES):
                kgovar = "kgo" + str(i)
                kgofilevar = kgovar + "file"
                if self.config.get([task, kgofilevar]):
                    value = self.config.get([task, kgofilevar])[:]
                    if "{}" in value:
                        setattr(newtask, kgofilevar,
                                value.replace("{}", self.args[0]))
                    else:
                        setattr(newtask, kgofilevar, value)
                    newtask.numkgofiles += 1
                    newtask = self._find_file(kgovar, newtask)
                else:
                    break
            tasks.append(newtask)
        self.tasks = tasks
        return tasks

    def load_user_comparison_modules(self, files):
        """Import comparison modules and store them."""
        modules = []
        for filename in files:
            directory = os.path.dirname(filename)
            if (not directory.endswith(USRCOMPARISON_DIRNAME)
                    or not filename.endswith(USRCOMPARISON_EXT)):
                continue
            comparison_name = os.path.basename(filename).rpartition(
                USRCOMPARISON_EXT)[0]
            sys.path.insert(0, os.path.abspath(directory))
            try:
                modules.append(__import__(comparison_name))
            except ImportError as e:
                self.reporter(e)
            sys.path.pop(0)
        modules.sort()
        self.modules = modules

        user_methods = []
        for module in modules:
            comparison_name = module.__name__
            contents = inspect.getmembers(module, inspect.isclass)
            for obj_name, obj in contents:
                att_name = "run"
                if hasattr(obj, att_name) and callable(getattr(obj, att_name)):
                    doc_string = obj.__doc__
                    user_methods.append(
                        (comparison_name, obj_name, att_name, doc_string))
        self.user_methods = user_methods
        return user_methods

    def write_config(self, filename, tasks):
        """Write an analysis config file based on a list of tasks provided"""
        config = rose.config.ConfigNode()

        for task in tasks:
            sectionname = task.name
            if task.resultfileconfig:
                config.set([sectionname, "resultfile"], task.resultfileconfig)
            for i in range(1, task.numkgofiles + 1):
                origvar = "kgo" + str(i) + "fileconfig"
                valvar = "kgo" + str(i) + "file"
                if hasattr(task, origvar):
                    config.set([sectionname, valvar], getattr(task, origvar))
            if task.extract:
                config.set([sectionname, "extract"], task.extract)
            if task.subextract:
                config.set([sectionname, "extract"],
                           task.extract + ":" + task.subextract)
            if task.comparison:
                config.set([sectionname, "comparison"], task.comparison)
            if task.tolerance:
                config.set([sectionname, "tolerance"], task.tolerance)
            if task.warnonfail:
                config.set([sectionname, "warnonfail"], "true")
        rose.config.dump(config, filename)