Esempio n. 1
0
def _validate_expected_list(list_expected):
    """Validate an expected List and raises exceptions.

    The syntax of the expected list is validated.
    Examples that will raise errors:

    Variable length should be at beginning or end.
    [1, 2, "...", 4, 5]
    [1, 2, "(3)", 4, 5]

    Additional arguments are not accepted
    [1, 2, 3, 4, "5..."]
    [1, 2, 3, 4, "5(5)"]

    params
    ------
    list_expected:
        Expected List to validate against

    raises
    ------
    ValueError:
        ValueError will be raised if an rule violation is found
    """
    validator = 0
    for exp in list_expected:
        if validator == 1 and not ("(" in str(exp) or "..." in str(exp)):
            raise WxSyntaxError(
                "Optional dimensions in the expected "
                "shape should only stand at the end/beginning.")
        if validator == 2:
            raise WxSyntaxError('After "..." should not be another dimension.')
        if "..." in str(exp):
            if "..." != exp:
                raise WxSyntaxError(
                    f'"..." should not have additional properties:'
                    f" {exp} was found.")
            validator = 2
        elif "(" in str(exp):
            val = re.search(r"\((.*)\)", exp)
            if (val is None or len(val.group(1)) + 2 != len(exp)
                    or not _is_range_format_valid(val.group(1))):
                raise WxSyntaxError(
                    f'Invalid optional dimension format. Correct format is "(_)", but '
                    f" {exp} was found.")

            validator = 1
        elif not _is_range_format_valid(str(exp)):
            raise WxSyntaxError(
                f"{exp} is an invalid range format."
                f"Consult the documentation for a list of all valid options")
Esempio n. 2
0
def _compare_tag_version(instance_tag: str, tagname: str):
    """Compare ASDF tag-strings with flexible version syntax.

    Parameters
    ----------
    instance_tag:
        the full ASDF tag to validate
    tagname:
        tag string with custom version syntax to validate against

    Returns
    -------
        bool
    """
    if instance_tag is None:
        return True

    if instance_tag.startswith("tag:yaml.org"):  # test for python builtins
        return instance_tag == tagname
    instance_tag_version = [
        int(v) for v in instance_tag.rpartition("-")[-1].split(".")
    ]

    tag_parts = tagname.rpartition("-")
    tag_uri = tag_parts[0]
    tag_version = [v for v in tag_parts[-1].split(".")]

    if tag_version == ["*"]:
        version_compatible = True
    elif all([vstr.isdigit() for vstr in tag_version]):
        vnum = [int(vstr) for vstr in tag_version]
        version_compatible = all(
            [v[0] == v[1] for v in zip(vnum, instance_tag_version)])
    else:
        raise WxSyntaxError(f"Unknown wx_tag syntax {tagname}")

    if (not instance_tag.startswith(tag_uri)) or (not version_compatible):
        return False
    return True
Esempio n. 3
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
Esempio n. 4
0
def _compare(_int, exp_string):
    """Compare helper of two strings for _custom_shape_validator.

    An integer and an expected string are compared so that the string either contains
    a ":" and thus describes an interval or a string consisting of numbers. So if our
    integer is within the interval or equal to the described number, True is returned.
    The interval can be open, in that there is no number left or right of the ":"
    symbol.

    Examples:
    ---------

    _int = 5
    exp_string = "5"
    -> True

    _int = 5
    exp_string = ":"
    -> True

    Open interval:
    _int = 5
    exp_string = "3:"
    -> True

    _int = 5
    exp_string = "4"
    -> False

    _int = 5
    exp_string = "6:8"
    -> False

    Parameters
    ----------
    _int:
        Integer
    exp_string:
        String with the expected dimension

    Returns
    -------
    bool
        True or False
    """
    if _int < 0:
        raise WxSyntaxError("Negative dimension found")

    if ":" in exp_string:
        ranges = exp_string.split(":")

        if ranges[0] == "":
            ranges[0] = 0
        elif ranges[0].isnumeric():
            ranges[0] = int(ranges[0])
        else:
            raise WxSyntaxError(f"Non numeric character in range {exp_string}")
        if ranges[1] == "":
            ranges[1] = _int
        elif ranges[1].isnumeric():
            ranges[1] = int(ranges[1])
        else:
            raise WxSyntaxError(f"Non numeric character in range {exp_string}")

        if ranges[0] > ranges[1]:
            raise WxSyntaxError(
                f"The range should not be descending in {exp_string}")
        return int(ranges[0]) <= _int <= int(ranges[1])

    else:
        return _int == int(exp_string)