1
0
mirror of https://github.com/ARM-software/workload-automation.git synced 2024-10-06 02:41:11 +01:00

fw/output: add metadata attribute to Result

Metadata is a key-value mapping for arbitrary data, similar to
classifiers. Unlike classifiers, metadata does not directly relate to
the results of the execution, but to the execution itself, and typically
would not be processed by Output Processors in the same way as
classifiers. Metadata can also be a lot more free-form in it's value
structure; while classifier values are simple scalars, metadata values
can be arbitrary POD structures.
This commit is contained in:
Sergei Trofimov 2018-05-24 13:59:23 +01:00 committed by Marc Bonnici
parent 4cc5aec39a
commit 9257a787f9
2 changed files with 65 additions and 2 deletions

View File

@ -222,6 +222,12 @@ class ExecutionContext(object):
def add_event(self, message): def add_event(self, message):
self.output.add_event(message) self.output.add_event(message)
def add_metadata(self, key, *args, **kwargs):
self.output.add_metadata(key, *args, **kwargs)
def update_metadata(self, key, *args):
self.output.update_metadata(key, *args)
def take_screenshot(self, filename): def take_screenshot(self, filename):
filepath = self._get_unique_filepath(filename) filepath = self._get_unique_filepath(filename)
self.tm.target.capture_screen(filepath) self.tm.target.capture_screen(filepath)

View File

@ -2,7 +2,7 @@ import logging
import os import os
import shutil import shutil
from collections import OrderedDict from collections import OrderedDict
from copy import copy from copy import copy, deepcopy
from datetime import datetime from datetime import datetime
from wa.framework.configuration.core import JobSpec, Status from wa.framework.configuration.core import JobSpec, Status
@ -11,7 +11,7 @@ from wa.framework.exception import HostError
from wa.framework.run import RunState, RunInfo from wa.framework.run import RunState, RunInfo
from wa.framework.target.info import TargetInfo from wa.framework.target.info import TargetInfo
from wa.utils.misc import touch, ensure_directory_exists from wa.utils.misc import touch, ensure_directory_exists
from wa.utils.serializer import write_pod, read_pod from wa.utils.serializer import write_pod, read_pod, is_pod
from wa.utils.types import enum, numeric from wa.utils.types import enum, numeric
@ -129,6 +129,12 @@ class Output(object):
artifact = self.get_artifact(name) artifact = self.get_artifact(name)
return self.get_path(artifact.path) return self.get_path(artifact.path)
def add_metadata(self, key, *args, **kwargs):
self.result.add_metadata(key, *args, **kwargs)
def update_metadata(self, key, *args):
self.result.update_metadata(key, *args)
def __repr__(self): def __repr__(self):
return '<{} {}>'.format(self.__class__.__name__, return '<{} {}>'.format(self.__class__.__name__,
os.path.basename(self.basepath)) os.path.basename(self.basepath))
@ -313,6 +319,7 @@ class Result(object):
instance.artifacts = [Artifact.from_pod(a) for a in pod['artifacts']] instance.artifacts = [Artifact.from_pod(a) for a in pod['artifacts']]
instance.events = [Event.from_pod(e) for e in pod['events']] instance.events = [Event.from_pod(e) for e in pod['events']]
instance.classifiers = pod.get('classifiers', OrderedDict()) instance.classifiers = pod.get('classifiers', OrderedDict())
instance.metadata = pod.get('metadata', OrderedDict())
return instance return instance
def __init__(self): def __init__(self):
@ -322,6 +329,7 @@ class Result(object):
self.artifacts = [] self.artifacts = []
self.events = [] self.events = []
self.classifiers = OrderedDict() self.classifiers = OrderedDict()
self.metadata = OrderedDict()
def add_metric(self, name, value, units=None, lower_is_better=False, def add_metric(self, name, value, units=None, lower_is_better=False,
classifiers=None): classifiers=None):
@ -350,6 +358,54 @@ class Result(object):
return artifact return artifact
raise HostError('Artifact "{}" not found'.format(name)) raise HostError('Artifact "{}" not found'.format(name))
def add_metadata(self, key, *args, **kwargs):
force = kwargs.pop('force', False)
if kwargs:
msg = 'Unexpected keyword arguments: {}'
raise ValueError(msg.format(kwargs))
if key in self.metadata and not force:
msg = 'Metadata with key "{}" already exists.'
raise ValueError(msg.format(key))
if len(args) == 1:
value = args[0]
elif len(args) == 2:
value = {args[0]: args[1]}
elif not args:
value = None
else:
raise ValueError("Unexpected arguments: {}".format(args))
self.metadata[key] = value
def update_metadata(self, key, *args):
if not args:
del self.metadata[key]
return
if key not in self.metadata:
return self.add_metadata(key, *args)
if hasattr(self.metadata[key], 'iteritems'):
if len(args) == 2:
self.metadata[key][args[0]] = args[1]
elif len(args) > 2: # assume list of key-value pairs
for k, v in args:
self.metadata[key][k] = v
elif hasattr(args[0], 'iteritems'):
for k, v in args[0].iteritems():
self.metadata[key][k] = v
else:
raise ValueError('Invalid value for key "{}": {}'.format(key, args))
elif isiterable(self.metadata[key]):
self.metadata[key].extend(args)
else: # scalar
if len(args) > 1:
raise ValueError('Invalid value for key "{}": {}'.format(key, args))
self.metadata[key] = args[0]
def to_pod(self): def to_pod(self):
return dict( return dict(
status=str(self.status), status=str(self.status),
@ -357,6 +413,7 @@ class Result(object):
artifacts=[a.to_pod() for a in self.artifacts], artifacts=[a.to_pod() for a in self.artifacts],
events=[e.to_pod() for e in self.events], events=[e.to_pod() for e in self.events],
classifiers=copy(self.classifiers), classifiers=copy(self.classifiers),
metadata=deepcopy(self.metadata),
) )