def _add_found_extension(self, obj, ext): """ :obj: Found extension class :ext: matching extension item. """ self.logger.debug('\tAdding %s %s', ext.name, obj.name) key = identifier(obj.name.lower()) obj.kind = ext.name if key in self.extensions or key in self.aliases: raise LoaderError('{} {} already exists.'.format( ext.name, obj.name)) # Extensions are tracked both, in a common extensions # dict, and in per-extension kind dict (as retrieving # extensions by kind is a common use case. self.extensions[key] = obj store = self._get_store(ext) store[key] = obj for alias in obj.aliases: alias_id = identifier(alias.name) if alias_id in self.extensions or alias_id in self.aliases: raise LoaderError('{} {} already exists.'.format( ext.name, obj.name)) self.aliases[alias_id] = alias # Update global aliases list. If a global alias is already in the list, # then make sure this extension is in the same parent/child hierarchy # as the one already found. for param in obj.parameters: if param.global_alias: if param.global_alias not in self.global_param_aliases: ga = GlobalParameterAlias(param.global_alias) ga.update(obj) self.global_param_aliases[ga.name] = ga else: # global alias already exists. self.global_param_aliases[param.global_alias].update(obj)
def _add_found_extension(self, obj, ext): """ :obj: Found extension class :ext: matching extension item. """ self.logger.debug('\tAdding %s %s', ext.name, obj.name) key = identifier(obj.name.lower()) obj.kind = ext.name if key in self.extensions or key in self.aliases: raise LoaderError('{} {} already exists.'.format(ext.name, obj.name)) # Extensions are tracked both, in a common extensions # dict, and in per-extension kind dict (as retrieving # extensions by kind is a common use case. self.extensions[key] = obj store = self._get_store(ext) store[key] = obj for alias in obj.aliases: alias_id = identifier(alias.name) if alias_id in self.extensions or alias_id in self.aliases: raise LoaderError('{} {} already exists.'.format(ext.name, obj.name)) self.aliases[alias_id] = alias # Update global aliases list. If a global alias is already in the list, # then make sure this extension is in the same parent/child hierarchy # as the one already found. for param in obj.parameters: if param.global_alias: if param.global_alias not in self.global_param_aliases: ga = GlobalParameterAlias(param.global_alias) ga.update(obj) self.global_param_aliases[ga.name] = ga else: # global alias already exists. self.global_param_aliases[param.global_alias].update(obj)
def get_instrument(inst): if isinstance(inst, Instrument): return inst for installed_inst in installed: if identifier(installed_inst.name) == identifier(inst): return installed_inst raise ValueError('Instrument {} is not installed'.format(inst))
def get_instrument(inst): if isinstance(inst, Instrument): return inst for installed_inst in installed: if identifier(installed_inst.name) == identifier(inst): return installed_inst raise ValueError('Instrument {} is not installed'.format(inst))
def is_installed(instrument): if isinstance(instrument, Instrument): if instrument in installed: return True if instrument.name in [i.name for i in installed]: return True elif isinstance(instrument, type): if instrument in [i.__class__ for i in installed]: return True else: # assume string if identifier(instrument) in [identifier(i.name) for i in installed]: return True return False
def is_installed(instrument): if isinstance(instrument, Instrument): if instrument in installed: return True if instrument.name in [i.name for i in installed]: return True elif isinstance(instrument, type): if instrument in [i.__class__ for i in installed]: return True else: # assume string if identifier(instrument) in [identifier(i.name) for i in installed]: return True return False
def _merge_config(self, config): """ Merge the settings specified by the ``config`` dict-like object into current configuration. """ if not isinstance(config, dict): raise ValueError('config must be a dict; found {}'.format(config.__class__.__name__)) for k, v in config.iteritems(): k = identifier(k) if k in self.ext_loader.global_param_aliases: self._resolve_global_alias(k, v) elif k in self._general_config_map: self._set_run_config_item(k, v) elif self.ext_loader.has_extension(k): self._set_extension_config(k, v) elif k == 'device_config': self._set_raw_dict(k, v) elif k in ['instrumentation', 'result_processors']: # Instrumentation can be enabled and disabled by individual # workloads, so we need to track it in two places: a list of # all instruments for the run (as they will all need to be # initialized and installed, and a list of only the "global" # instruments which can then be merged into instrumentation # lists of individual workload specs. self._set_raw_list('_global_{}'.format(k), v) self._set_raw_list(k, v) elif k in self.ignore_names: pass else: raise ConfigError('Unknown configuration option: {}'.format(k))
def _merge_config(self, config): """ Merge the settings specified by the ``config`` dict-like object into current configuration. """ if not isinstance(config, dict): raise ValueError('config must be a dict; found {}'.format( config.__class__.__name__)) for k, v in config.iteritems(): k = identifier(k) if k in self.ext_loader.global_param_aliases: self._resolve_global_alias(k, v) elif k in self._general_config_map: self._set_run_config_item(k, v) elif self.ext_loader.has_extension(k): self._set_extension_config(k, v) elif k == 'device_config': self._set_raw_dict(k, v) elif k in ['instrumentation', 'result_processors']: # Instrumentation can be enabled and disabled by individual # workloads, so we need to track it in two places: a list of # all instruments for the run (as they will all need to be # initialized and installed, and a list of only the "global" # instruments which can then be merged into instrumentation # lists of individual workload specs. self._set_raw_list('_global_{}'.format(k), v) self._set_raw_list(k, v) elif k in self.ignore_names: pass else: raise ConfigError('Unknown configuration option: {}'.format(k))
def update_from_dict(self, source): normalized_source = dict( (identifier(k), v) for k, v in source.iteritems()) self._config = merge_dicts(self._config, normalized_source, list_duplicates='first', match_types=False, dict_type=OrderedDict) self._loaded = True
def __init__(self, name, kind=None, mandatory=None, default=None, override=False, allowed_values=None, description=None, constraint=None, global_alias=None, convert_types=True): """ Create a new Parameter object. :param name: The name of the parameter. This will become an instance member of the extension object to which the parameter is applied, so it must be a valid python identifier. This is the only mandatory parameter. :param kind: The type of parameter this is. This must be a callable that takes an arbitrary object and converts it to the expected type, or raised ``ValueError`` if such conversion is not possible. Most Python standard types -- ``str``, ``int``, ``bool``, etc. -- can be used here. This defaults to ``str`` if not specified. :param mandatory: If set to ``True``, then a non-``None`` value for this parameter *must* be provided on extension object construction, otherwise ``ConfigError`` will be raised. :param default: The default value for this parameter. If no value is specified on extension construction, this value will be used instead. (Note: if this is specified and is not ``None``, then ``mandatory`` parameter will be ignored). :param override: A ``bool`` that specifies whether a parameter of the same name further up the hierarchy should be overridden. If this is ``False`` (the default), an exception will be raised by the ``AttributeCollection`` instead. :param allowed_values: This should be the complete list of allowed values for this parameter. Note: ``None`` value will always be allowed, even if it is not in this list. If you want to disallow ``None``, set ``mandatory`` to ``True``. :param constraint: If specified, this must be a callable that takes the parameter value as an argument and return a boolean indicating whether the constraint has been satisfied. Alternatively, can be a two-tuple with said callable as the first element and a string describing the constraint as the second. :param global_alias: This is an alternative alias for this parameter, unlike the name, this alias will not be namespaced under the owning extension's name (hence the global part). This is introduced primarily for backward compatibility -- so that old extension settings names still work. This should not be used for new parameters. :param convert_types: If ``True`` (the default), will automatically convert ``kind`` values from native Python types to WA equivalents. This allows more ituitive interprestation of parameter values, e.g. the string ``"false"`` being interpreted as ``False`` when specifed as the value for a boolean Parameter. """ self.name = identifier(name) if kind is not None and not callable(kind): raise ValueError('Kind must be callable.') if convert_types and kind in self.kind_map: kind = self.kind_map[kind] self.kind = kind self.mandatory = mandatory self.default = default self.override = override self.allowed_values = allowed_values self.description = description if self.kind is None and not self.override: self.kind = str if constraint is not None and not callable(constraint) and not isinstance(constraint, tuple): raise ValueError('Constraint must be callable or a (callable, str) tuple.') self.constraint = constraint self.global_alias = global_alias
def __init__(self, name, kind=None, mandatory=None, default=None, override=False, allowed_values=None, description=None, constraint=None, global_alias=None, convert_types=True): """ Create a new Parameter object. :param name: The name of the parameter. This will become an instance member of the extension object to which the parameter is applied, so it must be a valid python identifier. This is the only mandatory parameter. :param kind: The type of parameter this is. This must be a callable that takes an arbitrary object and converts it to the expected type, or raised ``ValueError`` if such conversion is not possible. Most Python standard types -- ``str``, ``int``, ``bool``, etc. -- can be used here. This defaults to ``str`` if not specified. :param mandatory: If set to ``True``, then a non-``None`` value for this parameter *must* be provided on extension object construction, otherwise ``ConfigError`` will be raised. :param default: The default value for this parameter. If no value is specified on extension construction, this value will be used instead. (Note: if this is specified and is not ``None``, then ``mandatory`` parameter will be ignored). :param override: A ``bool`` that specifies whether a parameter of the same name further up the hierarchy should be overridden. If this is ``False`` (the default), an exception will be raised by the ``AttributeCollection`` instead. :param allowed_values: This should be the complete list of allowed values for this parameter. Note: ``None`` value will always be allowed, even if it is not in this list. If you want to disallow ``None``, set ``mandatory`` to ``True``. :param constraint: If specified, this must be a callable that takes the parameter value as an argument and return a boolean indicating whether the constraint has been satisfied. Alternatively, can be a two-tuple with said callable as the first element and a string describing the constraint as the second. :param global_alias: This is an alternative alias for this parameter, unlike the name, this alias will not be namespaced under the owning extension's name (hence the global part). This is introduced primarily for backward compatibility -- so that old extension settings names still work. This should not be used for new parameters. :param convert_types: If ``True`` (the default), will automatically convert ``kind`` values from native Python types to WA equivalents. This allows more ituitive interprestation of parameter values, e.g. the string ``"false"`` being interpreted as ``False`` when specifed as the value for a boolean Parameter. """ self.name = identifier(name) if kind is not None and not callable(kind): raise ValueError('Kind must be callable.') if convert_types and kind in self.kind_map: kind = self.kind_map[kind] self.kind = kind self.mandatory = mandatory self.default = default self.override = override self.allowed_values = allowed_values self.description = description if self.kind is None and not self.override: self.kind = str if constraint is not None and not callable(constraint) and not isinstance(constraint, tuple): raise ValueError('Constraint must be callable or a (callable, str) tuple.') self.constraint = constraint self.global_alias = global_alias
def _finalize_config_list(self, attr_name): """Note: the name is somewhat misleading. This finalizes a list form the specified configuration (e.g. "instrumentation"); internal representation is actually a dict, not a list...""" ext_config = {} raw_list = self._raw_config.get(attr_name, []) for extname in raw_list: default_config = self.ext_loader.get_default_config(extname) ext_config[extname] = self._raw_config.get(identifier(extname), default_config) list_name = '_global_{}'.format(attr_name) global_list = self._raw_config.get(list_name, []) setattr(self, list_name, global_list) setattr(self, attr_name, ext_config)
def _finalize_config_list(self, attr_name): """Note: the name is somewhat misleading. This finalizes a list form the specified configuration (e.g. "instrumentation"); internal representation is actually a dict, not a list...""" ext_config = {} raw_list = self._raw_config.get(attr_name, []) for extname in raw_list: default_config = self.ext_loader.get_default_config(extname) ext_config[extname] = self._raw_config.get(identifier(extname), default_config) list_name = '_global_{}'.format(attr_name) global_list = self._raw_config.get(list_name, []) setattr(self, list_name, global_list) setattr(self, attr_name, ext_config)
def resolve_alias(self, alias_name): """ Try to resolve the specified name as an extension alias. Returns a two-tuple, the first value of which is actual extension name, and the second is a dict of parameter values for this alias. If the name passed is already an extension name, then the result is ``(alias_name, {})``. """ alias_name = identifier(alias_name.lower()) if alias_name in self.extensions: return (alias_name, {}) if alias_name in self.aliases: alias = self.aliases[alias_name] return (alias.extension_name, alias.params) raise NotFoundError('Could not find extension or alias "{}"'.format(alias_name))
def resolve_alias(self, alias_name): """ Try to resolve the specified name as an extension alias. Returns a two-tuple, the first value of which is actual extension name, and the second is a dict of parameter values for this alias. If the name passed is already an extension name, then the result is ``(alias_name, {})``. """ alias_name = identifier(alias_name.lower()) if alias_name in self.extensions: return (alias_name, {}) if alias_name in self.aliases: alias = self.aliases[alias_name] return (alias.extension_name, alias.params) raise NotFoundError('Could not find extension or alias "{}"'.format(alias_name))
def update_result(self, context): # pylint: disable=too-many-locals if not self.raw_output: self.logger.warning('Did not get run_benchmark output.') return raw_outfile = os.path.join(context.output_directory, 'telemetry_raw.out') with open(raw_outfile, 'w') as wfh: wfh.write(self.raw_output) context.add_artifact('telemetry-raw', raw_outfile, kind='raw') results, artifacts = parse_telemetry_results(raw_outfile) csv_outfile = os.path.join(context.output_directory, 'telemetry.csv') with open(csv_outfile, 'wb') as wfh: writer = csv.writer(wfh) writer.writerow(['kind', 'url', 'iteration', 'value', 'units']) for result in results: name_template = identifier('{}_{}_{{}}'.format(result.url, result.kind)) writer.writerows(result.rows) for i, value in enumerate(result.values, 1): context.add_metric(result.kind, value, units=result.units, classifiers={'url': result.url, 'time': i}) context.add_artifact('telemetry', csv_outfile, kind='data') for idx, artifact in enumerate(artifacts): if is_zipfile(artifact): zf = ZipFile(artifact) for item in zf.infolist(): zf.extract(item, context.output_directory) zf.close() context.add_artifact('telemetry_trace_{}'.format(idx), path=item.filename, kind='data') else: # not a zip archive wa_path = os.path.join(context.output_directory, os.path.basename(artifact)) shutil.copy(artifact, wa_path) context.add_artifact('telemetry_artifact_{}'.format(idx), path=wa_path, kind='data') if self.extract_fps: self.logger.debug('Extracting FPS...') _extract_fps(context)
def _resolve_global_alias(self, name, value): ga = self.ext_loader.global_param_aliases[name] for param, ext in ga.iteritems(): for name in [ext.name] + [a.name for a in ext.aliases]: self._load_default_config_if_necessary(name) self._raw_config[identifier(name)][param.name] = value
def on_run_init(self, context): # pylint: disable=W0613 if self.measure_energy: self.sensors = discover_sensors(self.device, ['energy']) for sensor in self.sensors: sensor.label = identifier(sensor.label).upper()
def _load_default_config_if_necessary(self, name): name = identifier(name) if name not in self._raw_config: self._raw_config[name] = self.ext_loader.get_default_config(name)
def _set_raw_list(self, name, value): old_value = self._raw_config.get(name, []) new_value = merge_lists(old_value, value, duplicates='last') self._raw_config[identifier(name)] = new_value
def on_run_init(self, context): # pylint: disable=W0613 if self.measure_energy: self.sensors = discover_sensors(self.device, ['energy']) for sensor in self.sensors: sensor.label = identifier(sensor.label).upper()
def update_from_dict(self, source): normalized_source = dict((identifier(k), v) for k, v in source.iteritems()) self._config = merge_dicts(self._config, normalized_source, list_duplicates='first', match_types=False, dict_type=OrderedDict) self._loaded = True
def get_class_name(name, postfix=''): name = identifier(name) return ''.join(map(capitalize, name.split('_'))) + postfix
def get_class_name(name, postfix=''): name = identifier(name) return ''.join(map(capitalize, name.split('_'))) + postfix
def _set_raw_dict(self, name, value, default_config=None): existing_config = self._raw_config.get(name, default_config or {}) new_config = _merge_config_dicts(existing_config, value) self._raw_config[identifier(name)] = new_config
def _set_raw_list(self, name, value): old_value = self._raw_config.get(name, []) new_value = merge_lists(old_value, value, duplicates='last') self._raw_config[identifier(name)] = new_value
def _resolve_global_alias(self, name, value): ga = self.ext_loader.global_param_aliases[name] for param, ext in ga.iteritems(): for name in [ext.name] + [a.name for a in ext.aliases]: self._load_default_config_if_necessary(name) self._raw_config[identifier(name)][param.name] = value
def _load_default_config_if_necessary(self, name): name = identifier(name) if name not in self._raw_config: self._raw_config[name] = self.ext_loader.get_default_config(name)
def _set_raw_dict(self, name, value, default_config=None): existing_config = self._raw_config.get(name, default_config or {}) new_config = _merge_config_dicts(existing_config, value) self._raw_config[identifier(name)] = new_config