Source code for sciunit.validators

"""Cerberus validator classes for SciUnit."""

import inspect
from typing import Any

import quantities as pq
from cerberus import TypeDefinition, Validator


[docs]def register_type(cls, name: str) -> None: """Register `name` as a type to validate as an instance of class `cls`. Args: cls: a class name (str): the name to be registered. """ x = TypeDefinition(name, (cls,), ()) Validator.types_mapping[name] = x
[docs]def register_quantity(quantity: pq.Quantity, name: str) -> None: """Register `name` as a type to validate as an instance of the class of `quantity`. Args: quantity (pq.Quantity): a quantity. name (str): the name to be registered. """ x = TypeDefinition(name, (quantity.__class__,), ()) Validator.types_mapping[name] = x
[docs]class ObservationValidator(Validator): """Cerberus validator class for observations. Attributes: test (Test): A sciunit test instance to be validated. _error (str, str): The error that happens during the validating process. """
[docs] def __init__(self, *args, **kwargs): """Constructor of ObservationValidator. Must pass `test` as a keyword argument. Cannot be a positional argument without modifications to cerberus. Raises: Exception: "Observation validator constructor must have a `test` keyword argument." """ try: self.test = kwargs["test"] except KeyError: raise Exception( ( "Observation validator constructor must have " "a `test` keyword argument" ) ) super(ObservationValidator, self).__init__(*args, **kwargs) register_type(pq.quantity.Quantity, "quantity")
[docs] def _validate_iterable(self, is_iterable: bool, key: str, value: Any) -> None: """Validate fields with `iterable` key in schema set to True The rule's arguments are validated against this schema: {'type': 'boolean'} """ if is_iterable: try: iter(value) except TypeError: self._error(key, "Must be iterable (e.g. a list or array)")
[docs] def _validate_units(self, has_units: bool, key: str, value: Any) -> None: """Validate fields with `units` key in schema set to True. The rule's arguments are validated against this schema: {'type': 'boolean'} """ if has_units: if isinstance(self.test.units, dict): required_units = self.test.units[key] else: required_units = self.test.units if not isinstance(value, pq.quantity.Quantity): #self._error(key, "Must be a python quantity") value *= pq.dimensionless provided_units = value.simplified.units if not isinstance(required_units, pq.Dimensionless): required_units = required_units.simplified.units else: required_units = required_units.units if not required_units == provided_units: self._error(key, "Must have units of '%s'" % self.test.units.name)
[docs]class ParametersValidator(Validator): """Cerberus validator class for observations. Attributes: units_type ([type]): The type of Python quantity's unit. _error (str, str): value is not a Python quantity instance. """ # doc is needed here units_map = {"time": "s", "voltage": "V", "current": "A"}
[docs] def validate_quantity(self, value: pq.quantity.Quantity) -> None: """Validate that the value is of the `Quantity` type. Args: value (pq.quantity.Quantity): The Quantity instance to be validated. """ if not isinstance(value, pq.quantity.Quantity): self._error("%s" % value, "Must be a Python quantity.")
[docs] def validate_units(self, value: pq.quantity.Quantity) -> bool: """Validate units, assuming that it was called by _validate_type_*. Args: value (pq.quantity.Quantity): A python quantity instance. Returns: bool: Whether it is valid. """ self.validate_quantity(value) self.units_type = inspect.stack()[1][3].split("_")[-1] assert self.units_type, ( "`validate_units` should not be called " "directly. It should be called by a " "_validate_type_* methods that sets " "`units_type`" ) units = getattr(pq, self.units_map[self.units_type]) if not value.simplified.units == units: self._error("%s" % value, "Must have dimensions of %s." % self.units_type) return True
[docs] def _validate_type_time(self, value: pq.quantity.Quantity) -> bool: """Validate fields requiring `units` of seconds. Args: value (pq.quantity.Quantity): A python quantity instance. Returns: bool: Whether it is valid. """ return self.validate_units(value)
[docs] def _validate_type_voltage(self, value: pq.quantity.Quantity) -> bool: """Validate fields requiring `units` of volts. Args: value (pq.quantity.Quantity): A python quantity instance. Returns: bool: Whether it is valid. """ return self.validate_units(value)
[docs] def _validate_type_current(self, value: pq.quantity.Quantity) -> bool: """Validate fields requiring `units` of amps. Args: value (pq.quantity.Quantity): A python quantity instance. Returns: bool: Whether it is valid. """ return self.validate_units(value)