mirror of
https://github.com/ARM-software/workload-automation.git
synced 2025-02-20 20:09:11 +00:00
Adding intialize and finalize methods to workloads that will only be invoked once per run
- added initialze and finalize methods to workloads, which were the only major extension types that did not have them - Semanatics for initialize/finalize for *all* Extensions are changed so that now they will always run at most once per run. They will not be executed twice even if invoke via istances of different subclasses (if those subclasses defined their own verions, then their versions will be invoked once each, but the base version will only get invoked once).
This commit is contained in:
parent
557b792c77
commit
b3a0933221
@ -527,6 +527,10 @@ class Runner(object):
|
||||
self.logger.info('Initializing device')
|
||||
self.device.initialize(self.context)
|
||||
|
||||
self.logger.info('Initializing workloads')
|
||||
for workload_spec in self.context.config.workload_specs:
|
||||
workload_spec.workload.initialize(self.context)
|
||||
|
||||
props = self.device.get_properties(self.context)
|
||||
self.context.run_info.device_properties = props
|
||||
self.result_manager.initialize(self.context)
|
||||
|
@ -392,7 +392,8 @@ class ExtensionMeta(type):
|
||||
('core_modules', str, ListCollection),
|
||||
]
|
||||
|
||||
virtual_methods = ['validate']
|
||||
virtual_methods = ['validate', 'initialize', 'finalize']
|
||||
global_virtuals = ['initialize', 'finalize']
|
||||
|
||||
def __new__(mcs, clsname, bases, attrs):
|
||||
mcs._propagate_attributes(bases, attrs)
|
||||
@ -441,13 +442,13 @@ class ExtensionMeta(type):
|
||||
|
||||
super(cls, self).vmname()
|
||||
|
||||
.. note:: current implementation imposes a restriction in that
|
||||
parameters into the function *must* be passed as keyword
|
||||
arguments. There *must not* be positional arguments on
|
||||
virutal method invocation.
|
||||
This also ensures that the methods that have beend identified as
|
||||
"globally virtual" are executed exactly once per WA execution, even if
|
||||
invoked through instances of different subclasses
|
||||
|
||||
"""
|
||||
methods = {}
|
||||
called_globals = set()
|
||||
for vmname in mcs.virtual_methods:
|
||||
clsmethod = getattr(cls, vmname, None)
|
||||
if clsmethod:
|
||||
@ -455,11 +456,24 @@ class ExtensionMeta(type):
|
||||
methods[vmname] = [bm for bm in basemethods if bm != clsmethod]
|
||||
methods[vmname].append(clsmethod)
|
||||
|
||||
def wrapper(self, __name=vmname, **kwargs):
|
||||
for dm in methods[__name]:
|
||||
dm(self, **kwargs)
|
||||
def generate_method_wrapper(vname): # pylint: disable=unused-argument
|
||||
# this creates a closure with the method name so that it
|
||||
# does not need to be passed to the wrapper as an argument,
|
||||
# leaving the wrapper to accept exactly the same set of
|
||||
# arguments as the method it is wrapping.
|
||||
name__ = vmname # pylint: disable=cell-var-from-loop
|
||||
|
||||
setattr(cls, vmname, wrapper)
|
||||
def wrapper(self, *args, **kwargs):
|
||||
for dm in methods[name__]:
|
||||
if name__ in mcs.global_virtuals:
|
||||
if dm not in called_globals:
|
||||
dm(self, *args, **kwargs)
|
||||
called_globals.add(dm)
|
||||
else:
|
||||
dm(self, *args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
setattr(cls, vmname, generate_method_wrapper(vmname))
|
||||
|
||||
|
||||
class Extension(object):
|
||||
@ -539,6 +553,12 @@ class Extension(object):
|
||||
for param in self.parameters:
|
||||
param.validate(self)
|
||||
|
||||
def initialize(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def finalize(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def check_artifacts(self, context, level):
|
||||
"""
|
||||
Make sure that all mandatory artifacts have been generated.
|
||||
|
@ -288,9 +288,15 @@ def install(instrument):
|
||||
attr = getattr(instrument, attr_name)
|
||||
if not callable(attr):
|
||||
raise ValueError('Attribute {} not callable in {}.'.format(attr_name, instrument))
|
||||
arg_num = len(inspect.getargspec(attr).args)
|
||||
if not arg_num == 2:
|
||||
raise ValueError('{} must take exactly 2 arguments; {} given.'.format(attr_name, arg_num))
|
||||
argspec = inspect.getargspec(attr)
|
||||
arg_num = len(argspec.args)
|
||||
# Instrument callbacks will be passed exactly two arguments: self
|
||||
# (the instrument instance to which the callback is bound) and
|
||||
# context. However, we also allow callbacks to capture the context
|
||||
# in variable arguments (declared as "*args" in the definition).
|
||||
if arg_num > 2 or (arg_num < 2 and argspec.varargs is None):
|
||||
message = '{} must take exactly positional arguments; {} given.'
|
||||
raise ValueError(message.format(attr_name, arg_num))
|
||||
|
||||
logger.debug('\tConnecting %s to %s', attr.__name__, SIGNAL_MAP[stripped_attr_name])
|
||||
mc = ManagedCallback(instrument, attr)
|
||||
@ -379,6 +385,12 @@ class Instrument(Extension):
|
||||
self.is_enabled = True
|
||||
self.is_broken = False
|
||||
|
||||
def initialize(self, context): # pylint: disable=arguments-differ
|
||||
pass
|
||||
|
||||
def finalize(self, context): # pylint: disable=arguments-differ
|
||||
pass
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
@ -140,7 +140,7 @@ class ResultProcessor(Extension):
|
||||
|
||||
"""
|
||||
|
||||
def initialize(self, context):
|
||||
def initialize(self, context): # pylint: disable=arguments-differ
|
||||
pass
|
||||
|
||||
def process_iteration_result(self, result, context):
|
||||
@ -155,7 +155,7 @@ class ResultProcessor(Extension):
|
||||
def export_run_result(self, result, context):
|
||||
pass
|
||||
|
||||
def finalize(self, context):
|
||||
def finalize(self, context): # pylint: disable=arguments-differ
|
||||
pass
|
||||
|
||||
|
||||
|
@ -53,18 +53,25 @@ class Workload(Extension):
|
||||
|
||||
def init_resources(self, context):
|
||||
"""
|
||||
May be optionally overridden by concrete instances in order to discover and initialise
|
||||
necessary resources. This method will be invoked at most once during the execution:
|
||||
before running any workloads, and before invocation of ``validate()``, but after it is
|
||||
clear that this workload will run (i.e. this method will not be invoked for workloads
|
||||
that have been discovered but have not been scheduled run in the agenda).
|
||||
This method may be used to perform early resource discovery and initialization. This is invoked
|
||||
during the initial loading stage and before the device is ready, so cannot be used for any
|
||||
device-dependent initialization. This method is invoked before the workload instance is
|
||||
validated.
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
def initialize(self, context): # pylint: disable=arguments-differ
|
||||
"""
|
||||
This method should be used to perform once-per-run initialization of a workload instance, i.e.,
|
||||
unlike ``setup()`` it will not be invoked on each iteration.
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
def setup(self, context):
|
||||
"""
|
||||
Perform the setup necessary to run the workload, such as copying the necessry files
|
||||
Perform the setup necessary to run the workload, such as copying the necessary files
|
||||
to the device, configuring the environments, etc.
|
||||
|
||||
This is also the place to perform any on-device checks prior to attempting to execute
|
||||
@ -89,6 +96,9 @@ class Workload(Extension):
|
||||
""" Perform any final clean up for the Workload. """
|
||||
pass
|
||||
|
||||
def finalize(self, context): # pylint: disable=arguments-differ
|
||||
pass
|
||||
|
||||
def __str__(self):
|
||||
return '<Workload {}>'.format(self.name)
|
||||
|
||||
|
@ -27,7 +27,7 @@ from wlauto.core.execution import BySpecRunner, ByIterationRunner
|
||||
from wlauto.exceptions import DeviceError
|
||||
from wlauto.core.configuration import WorkloadRunSpec, RebootPolicy
|
||||
from wlauto.core.instrumentation import Instrument
|
||||
from wlauto.core.device import Device
|
||||
from wlauto.core.device import Device, DeviceMeta
|
||||
from wlauto.core import instrumentation, signal
|
||||
from wlauto.core.workload import Workload
|
||||
from wlauto.core.result import IterationResult
|
||||
@ -61,8 +61,37 @@ class Mock(object):
|
||||
pass
|
||||
|
||||
|
||||
class BadDeviceMeta(DeviceMeta):
|
||||
|
||||
@classmethod
|
||||
def _implement_virtual(mcs, cls, bases):
|
||||
"""
|
||||
This version of _implement_virtual does not inforce "call global virutals only once"
|
||||
policy, so that intialize() and finalize() my be invoked multiple times to test that
|
||||
the errors they generated are handled correctly.
|
||||
|
||||
"""
|
||||
# pylint: disable=cell-var-from-loop,unused-argument
|
||||
methods = {}
|
||||
for vmname in mcs.virtual_methods:
|
||||
clsmethod = getattr(cls, vmname, None)
|
||||
if clsmethod:
|
||||
basemethods = [getattr(b, vmname) for b in bases if hasattr(b, vmname)]
|
||||
methods[vmname] = [bm for bm in basemethods if bm != clsmethod]
|
||||
methods[vmname].append(clsmethod)
|
||||
def generate_method_wrapper(vname):
|
||||
name__ = vmname
|
||||
def wrapper(self, *args, **kwargs):
|
||||
for dm in methods[name__]:
|
||||
dm(self, *args, **kwargs)
|
||||
return wrapper
|
||||
setattr(cls, vmname, generate_method_wrapper(vmname))
|
||||
|
||||
|
||||
class BadDevice(Device):
|
||||
|
||||
__metaclass__ = BadDeviceMeta
|
||||
|
||||
def __init__(self, when_to_fail, exception=DeviceError):
|
||||
#pylint: disable=super-init-not-called
|
||||
self.when_to_fail = when_to_fail
|
||||
|
@ -220,6 +220,63 @@ class ExtensionMetaTest(TestCase):
|
||||
assert_equal(acid.v2, 2)
|
||||
assert_equal(acid.vv2, 2)
|
||||
|
||||
def test_initialization(self):
|
||||
class MyExt(Extension):
|
||||
name = 'myext'
|
||||
values = {'a': 0}
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(MyExt, self).__init__(*args, **kwargs)
|
||||
self.instance_init = 0
|
||||
def initialize(self):
|
||||
self.values['a'] += 1
|
||||
|
||||
class MyChildExt(MyExt):
|
||||
name = 'mychildext'
|
||||
def initialize(self):
|
||||
self.instance_init += 1
|
||||
|
||||
ext = _instantiate(MyChildExt)
|
||||
ext.initialize()
|
||||
|
||||
assert_equal(MyExt.values['a'], 1)
|
||||
assert_equal(ext.instance_init, 1)
|
||||
|
||||
def test_initialization_happens_once(self):
|
||||
class MyExt(Extension):
|
||||
name = 'myext'
|
||||
values = {'a': 0}
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(MyExt, self).__init__(*args, **kwargs)
|
||||
self.instance_init = 0
|
||||
self.instance_validate = 0
|
||||
def initialize(self):
|
||||
self.values['a'] += 1
|
||||
def validate(self):
|
||||
self.instance_validate += 1
|
||||
|
||||
class MyChildExt(MyExt):
|
||||
name = 'mychildext'
|
||||
def initialize(self):
|
||||
self.instance_init += 1
|
||||
def validate(self):
|
||||
self.instance_validate += 1
|
||||
|
||||
ext1 = _instantiate(MyExt)
|
||||
ext2 = _instantiate(MyExt)
|
||||
ext3 = _instantiate(MyChildExt)
|
||||
ext1.initialize()
|
||||
ext2.initialize()
|
||||
ext3.initialize()
|
||||
ext1.validate()
|
||||
ext2.validate()
|
||||
ext3.validate()
|
||||
|
||||
assert_equal(MyExt.values['a'], 1)
|
||||
assert_equal(ext1.instance_init, 0)
|
||||
assert_equal(ext3.instance_init, 1)
|
||||
assert_equal(ext1.instance_validate, 1)
|
||||
assert_equal(ext3.instance_validate, 2)
|
||||
|
||||
|
||||
class ParametersTest(TestCase):
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user