Source code for spawn.specification.evaluators

# spawn
# Copyright (C) 2018-2019, Simmovation Ltd.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
"""Defines evaluators
"""
from abc import abstractmethod
import inspect

from spawn.errors import EvaluatorTypeError, ParameterNotFoundError, EvaluatorParameterNotFoundError

from .value_proxy import ValueProxy, evaluate

[docs]class Evaluator(ValueProxy): """Evaluator base class implementation of :class:`ValueProxy` Implements the :meth:`evaluate` method of the parent class to expand any arguments """ def __init__(self, *args, name=None): """Initialises the :class:`Evaluator` :param args: The arguments (may be evaluators) :type args: args :param name: The name of the function :type name: str """ self._args = args self._name = name #pylint: disable=arguments-differ
[docs] def evaluate(self, **kwargs): """Evaluates the value proxy. Expands any arguments that are evaluators, and calls the :meth:`_evaluate` implementation required by base class """ args = [self._evaluate_arg(a, **kwargs) for a in self._args] parameters = inspect.signature(self._evaluate).parameters try: if 'kwargs' in parameters: return self._evaluate(*args, **kwargs) return self._evaluate(*args) except TypeError: evaluator_name = self._name or type(self).__name__ named_parameters = [ k for k, v in parameters.items() if v.kind in (inspect.Parameter.POSITIONAL_ONLY, inspect.Parameter.POSITIONAL_OR_KEYWORD) ] raise EvaluatorTypeError(evaluator_name, named_parameters, args)
#pylint: disable=no-self-use def _evaluate_arg(self, arg, **kwargs): try: return evaluate(arg, **kwargs) if isinstance(arg, ValueProxy) else arg except ParameterNotFoundError as ex: evaluator_name = self._name or type(self).__name__ raise EvaluatorParameterNotFoundError(ex.parameter_name, evaluator_name) from ex @abstractmethod #pylint: disable=no-self-use def _evaluate(self, *args, **kwargs): raise NotImplementedError()
class RangeEvaluator(Evaluator): """Implementation of :class:`Evaluator` that returns a range from range_min up to and including range_max, in steps of range_step """ #pylint: disable=arguments-differ def _evaluate(self, start, end, step=1.0): if start != end and step * (end-start) <= 0: raise ValueError("step value '{}' invalid in range evaluator".format(step)) comp = (lambda v: end >= v) if step > 0 else (lambda v: end <= v) values = [] i = 0 while i < 100: val = start + i*step if comp(val): values.append(val) else: break i += 1 return values class MultiplyEvaluator(Evaluator): """Implementation of :class:`Evaluator` that multiplies two numbers """ #pylint: disable=arguments-differ def _evaluate(self, left, right): return left * right class DivideEvaluator(Evaluator): """Implementation of :class:`Evaluator` that divides the left by right """ #pylint: disable=arguments-differ def _evaluate(self, left, right): return left / right class AddEvaluator(Evaluator): """Implementation of :class:`Evaluator` that adds two numbers """ #pylint: disable=arguments-differ def _evaluate(self, left, right): return left + right class SubtractEvaluator(Evaluator): """Implementation of :class:`Evaluator` that subtracts right from left """ #pylint: disable=arguments-differ def _evaluate(self, left, right): return left - right class ParameterEvaluator(Evaluator): """Implementation of :class:`Evaluator` that returns the value of a parameter in the keyword arguments """ #pylint: disable=arguments-differ def _evaluate(self, parameter_name, **kwargs): if parameter_name not in kwargs: raise ParameterNotFoundError(parameter_name) return kwargs[parameter_name] class RepeatEvaluator(Evaluator): """Implementation of :class:`Evaluator` that repeats a value or evaluator ``count`` times """ #pylint: disable=arguments-differ def _evaluate(self, value, count, **kwargs): count = super()._evaluate_arg(count, **kwargs) values = [] for _ in range(count): values.append(super()._evaluate_arg(value, **kwargs)) return values def _evaluate_arg(self, arg, **kwargs): return arg def create_function_evaluator(function): """Create a function evaluator that will evaluate the provided function :param function: The function to wrap in a function evaluator :type function: function :returns: :class:`Evaluator` that wraps the function :rtype: :class:`Evaluator` """ class FunctionEvaluator(Evaluator): """Implementation of :class:`Evaluator` that evaluates a delegate function """ def _evaluate(self, *args, **kwargs): parameters = inspect.signature(function).parameters if 'kwargs' not in parameters: all_keys = list(parameters.keys()) kwargs = { k: v for k, v in kwargs.items() if k in parameters and all_keys.index(k) >= len(args) } return function(*args, **kwargs) return FunctionEvaluator