Example #1
0
def _unit_validator(instance: Mapping, expected_dimensionality: str,
                    position: List[str]) -> Iterator[ValidationError]:
    """Validate the 'unit' key of the instance against the given string.

    Parameters
    ----------
    instance:
        Tree serialization with 'unit' key to validate.
    expected_dimensionality:
        String representation of the unit dimensionality to test against.
    position:
        Current position in nested structure for debugging

    Yields
    ------
    asdf.ValidationError

    """
    if not position:
        position = instance

    unit = instance["unit"]
    valid = Q_(unit).check(UREG.get_dimensionality(expected_dimensionality))
    if not valid:
        yield ValidationError(
            f"Error validating unit dimension for property '{position}'. "
            f"Expected unit of dimension '{expected_dimensionality}' "
            f"but got unit '{unit}'")
Example #2
0
def _validate_instance_shape(dict_test: dict,
                             shape_expected: list,
                             optional: bool = False) -> dict:
    """Validate a node instance against a shape definition.

    Parameters
    ----------
    dict_test
        The node to be validated
    shape_expected
        List that defines the expected shape constraints.
    optional
        flag if validation is optional

    Returns
    -------
    dict
        dictionary of shape keys (empty dictionary if no variables in shape_expected)

    Raises
    ------
    ValidationError
        If the shape does not match the requirements or no shape could be found but
        validation is not flagged as optional

    """
    shape = _get_instance_shape(dict_test)
    if shape is None:  # could not determine shape of node
        if optional:  # we are allowed to skip shape validation
            return {}
        raise ValidationError(
            f"Could not determine shape in instance {dict_test}.")

    list_test, list_expected = _prepare_list(shape, shape_expected)

    _validate_expected_list(list_expected)
    _dict_values = _compare_lists(list_test, list_expected)

    if _dict_values is False:
        raise ValidationError(
            f"Shape {list_test[::-1]} does not match requirement "
            f"{list_expected[::-1]}")

    return _dict_values
Example #3
0
    def _tag_validator(tagname, instance):
        """Validate against a tag string using ASDF uri match patterns."""
        if hasattr(instance, "_tag"):
            instance_tag = instance._tag
        else:
            # Try tags for known Python builtins
            instance_tag = _type_to_tag(type(instance))

        if instance_tag is not None:
            if not uri_match(tagname, instance_tag):
                yield ValidationError(
                    f"mismatched tags, wanted '{tagname}', got '{instance_tag}'"
                )
Example #4
0
def wx_shape_validator(validator, wx_shape, instance,
                       schema) -> Iterator[ValidationError]:
    """Custom validator for checking dimensions for objects with 'shape' property.

    ASDF documentation:
    https://asdf.readthedocs.io/en/2.6.0/asdf/extensions.html#adding-custom-validators

    Parameters
    ----------
    validator:
        A jsonschema.Validator instance.
    wx_shape:
        Enable shape validation for this schema.
    instance:
        Tree serialization (with default dtypes) of the instance
    schema:
        Dict representing the full ASDF schema.

    Yields
    ------
    asdf.ValidationError

    """

    dim_dict = None
    try:
        dim_dict = _custom_shape_validator(instance, wx_shape)
    except ValidationError:
        yield ValidationError(
            f"Error validating shape {wx_shape}.\nOn instance {instance}")

    if isinstance(dim_dict, dict):
        return None
    else:
        yield ValidationError(
            f"Error validating shape {wx_shape}.\nOn instance {instance}")
Example #5
0
def wx_tag_validator(validator, tagname, instance, schema):
    """Validate instance tag string with flexible version syntax.

    The following syntax is allowed to validate against:

    wx_tag: http://stsci.edu/schemas/asdf/core/software-* # allow every version
    wx_tag: http://stsci.edu/schemas/asdf/core/software-1 # fix major version
    wx_tag: http://stsci.edu/schemas/asdf/core/software-1.2 # fix minor version
    wx_tag: http://stsci.edu/schemas/asdf/core/software-1.2.3 # fix patch version

    Parameters
    ----------
    validator:
        A jsonschema.Validator instance.
    tagname:
        tag string with custom version syntax to validate against
    instance:
        Tree serialization (with default dtypes) of the instance
    schema:
        Dict representing the full ASDF schema.

    Returns
    -------
        bool

    """
    if hasattr(instance, "_tag"):
        instance_tag = instance._tag
    else:
        # Try tags for known Python builtins
        instance_tag = _type_to_tag(type(instance))

    if instance_tag is not None:
        if not _compare_tag_version(instance_tag, tagname):
            yield ValidationError(
                "mismatched tags, wanted '{0}', got '{1}'".format(
                    tagname, instance_tag))
Example #6
0
def _custom_shape_validator(dict_test: Dict[str, Any],
                            dict_expected: Dict[str, Any]):
    """Validate dimensions which are stored in two dictionaries dict_test and
    dict_expected.

    Syntax for the dict_expected:
    -----------------------------
    Items with arrays with each value having the following Syntax:
    1)  3 : an integer indicates a fix dimension for the same item in dict_test
    2)  "~", ":" or None : this string indicates a single dimension of arbitrary length.
    3)  "..." : this string indicates an arbitrary number of dimensions of arbitrary
            length. Can be optional.
    4)  "2~4" : this string indicates a single dimension of length 2, 3 or 4. This
            has to be ascending or you can give an unlimited interval limit like this
            "2~" which would indicate a single dimension of length greater then 1.
    5)  "n" : this indicates a single dimension fixed to a letter. Any letter or
            combination of letters should work The letter will be compared to the same
            letter in all the arrays in the dict_expected.
    6)  (x) : parenthesis indicates that the dict_test does not need to have this
            dimension. This can NOT be combined with 3) or the None from 2).

    Parameters
    ----------
    dict_test:
        dictionary to test against
    dict_expected:
        dictionary with the expected values

    raises
    ------
    ValueError:
        when dict_expected does violate against the Syntax rules

    returns
    -------
    False
        when any dimension mismatch occurs
    dict_values
        Dictionary - keys: variable names in the validation schemes. values: values of
        the validation schemes.
    """

    dict_values = {}

    # catch single shape definitions
    if isinstance(dict_expected, list):
        # get the shape of current
        shape = _get_instance_shape(dict_test)
        if not shape:
            raise ValidationError(
                f"Could not determine shape in instance {dict_test}.")

        list_test, list_expected = _prepare_list(shape, dict_expected)

        _validate_expected_list(list_expected)
        _dict_values = _compare_lists(list_test, list_expected)

        if _dict_values is False:
            raise ValidationError(
                f"Shape {list_test[::-1]} does not match requirement "
                f"{list_expected[::-1]}")

        return _dict_values

    elif isinstance(dict_expected, dict):
        for key, item in dict_expected.items():
            # allow optional syntax
            _optional = False
            if key.startswith("(") and key.endswith(")"):
                key = key[1:-1]
                _optional = True
                if len(key) == 0:
                    raise WxSyntaxError("wx_shape entry undefined")

            # test shapes
            if key in dict_test:
                # go one level deeper in the dictionary
                _dict_values = _custom_shape_validator(dict_test[key], item)
            elif _optional:
                _dict_values = {}
            else:
                raise ValidationError(
                    f"Could not access key '{key}'  in instance {dict_test}.")

            for key in _dict_values:
                if key not in dict_values:
                    dict_values[key] = _dict_values[key]
                elif dict_values[key] != _dict_values[key]:
                    return False
    else:
        raise WxSyntaxError(
            f"Found an incorrect object: {type(dict_expected)}. "
            "Should be a dict or list.")

    return dict_values