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)
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)