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}'")
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
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}'" )
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}")
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))
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