From 76296db803e96a75abdae2e1cd3c723c7aadb0a8 Mon Sep 17 00:00:00 2001 From: douglas-raillard-arm Date: Tue, 2 Jun 2020 10:17:07 +0100 Subject: [PATCH] lisa.platforms.platinfo: Defer exceptions raising If an exception is raised when keys are populated from a target, log an error and wrap it in a DeferredExcep value. This way, the exception will actually be raised only when the key is actually used. --- lisa/conf.py | 47 +++++++++++++++++++++++++++++++++++++- lisa/platforms/platinfo.py | 24 ++++++++----------- 2 files changed, 56 insertions(+), 15 deletions(-) diff --git a/lisa/conf.py b/lisa/conf.py index 8d016197a..dc2698cf4 100644 --- a/lisa/conf.py +++ b/lisa/conf.py @@ -79,6 +79,25 @@ class DeferredValue: return ''.format(self.callback.__qualname__) +class DeferredExcep(DeferredValue): + """ + Specialization of :class:`DeferredValue` to lazily raise an exception. + + :param excep: Exception to raise when the value is used. + :type excep: BaseException + """ + + def __init__(self, excep): + self.excep = excep + + def callback(): + raise self.excep + super().__init__(callback=callback) + + def __str__(self): + return ''.format(self.excep.__class__.__qualname__) + + class KeyDescBase(abc.ABC): """ Base class for configuration files key descriptor. @@ -300,6 +319,16 @@ class MissingBaseKeyError(ConfigKeyError): pass +class DeferredValueComputationError(ConfigKeyError): + """ + Raised when computing the value of :class:`DeferredValue` lead to an + exception. + """ + def __init__(self, msg, excep): + super().__init__(msg) + self.excep = excep + + class KeyComputationRecursionError(ConfigKeyError, RecursionError): """ Raised when :meth:`DerivedKeyDesc.compute_val` is reentered while computing @@ -1303,7 +1332,23 @@ class MultiSrcConf(MultiSrcConfABC, Loggable, Mapping): key_desc = self._structure[key] val = self._key_map[key][src] if isinstance(val, DeferredValue): - val = val(key_desc=key_desc) + try: + val = val(key_desc=key_desc) + # Propagate ConfigKeyError as-is + except ConfigKeyError: + raise + # Wrap into a ConfigKeyError so that the user code can easily + # handle missing keys, and the original exception is still + # available as excep.__cause__ since it was chained with "from" + except Exception as e: + raise DeferredValueComputationError( + 'Could not compute "{key}" from source "{src}": {excep}'.format( + key=key, + src=src, + excep=e, + ), + excep=e, + ) from e key_desc.validate_val(val) self._key_map[key][src] = val return val diff --git a/lisa/platforms/platinfo.py b/lisa/platforms/platinfo.py index 7fc329635..1156ebbe1 100644 --- a/lisa/platforms/platinfo.py +++ b/lisa/platforms/platinfo.py @@ -22,8 +22,8 @@ from collections.abc import Mapping from lisa.utils import HideExekallID, group_by_value, memoized from lisa.conf import ( - DeferredValue, MultiSrcConf, KeyDesc, LevelKeyDesc, TopLevelKeyDesc, - DerivedKeyDesc, ConfigKeyError, + DeferredValue, DeferredExcep, MultiSrcConf, KeyDesc, LevelKeyDesc, + TopLevelKeyDesc, DerivedKeyDesc, ConfigKeyError, ) from lisa.generic import TypedDict, TypedList, SortedTypedList from lisa.energy_model import EnergyModel @@ -149,7 +149,7 @@ class PlatformInfo(MultiSrcConf, HideExekallID): :class:`lisa.conf.MultiSrcConf.add_src`. """ info = { - 'nrg-model': lambda: self._nrg_model_from_target(target), + 'nrg-model': lambda: EnergyModel.from_target(target), 'kernel': { 'version': lambda: target.kernel_version, 'config': lambda: target.config.typed_config, @@ -227,6 +227,8 @@ class PlatformInfo(MultiSrcConf, HideExekallID): return self._add_info(src, info, only_missing=only_missing, filter_none=True, **kwargs) def _add_info(self, src, new_info, only_missing, deferred=False, **kwargs): + logger = self.get_logger() + def dfs(existing_info, new_info): def evaluate(existing_info, key, val): if isinstance(val, Mapping): @@ -240,7 +242,11 @@ class PlatformInfo(MultiSrcConf, HideExekallID): elif deferred: return DeferredValue(val) else: - return val() + try: + return val() + except Exception as e: + logger.error('Cannot retrieve value of key {}: {}'.format(key, e)) + return DeferredExcep(excep=e) return { key: evaluate(existing_info, key, val) @@ -311,16 +317,6 @@ class PlatformInfo(MultiSrcConf, HideExekallID): # Internal methods used to compute some keys from a live devlib Target - @classmethod - def _nrg_model_from_target(cls, target): - logger = cls.get_logger() - logger.info('Attempting to read energy model from target') - try: - return EnergyModel.from_target(target) - except (TargetStableError, RuntimeError, ValueError) as err: - logger.error("Couldn't read target energy model: {}".format(err)) - return None - @classmethod def _read_kallsyms(cls, target): """ -- GitLab