# Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from ..validation import Issue from .utils import (parse_types_dict_names, report_issue_for_unknown_type, report_issue_for_parent_is_self, report_issue_for_unknown_parent_type, report_issue_for_circular_type_hierarchy) def type_validator(type_name, *types_dict_names): """ Makes sure that the field refers to an existing type defined in the root presenter. The arguments from the second onwards are used to locate a nested field under ``service_template`` under the root presenter. The first of these can optionally be a function, in which case it will be called to convert type names. This can be used to support shorthand type names, aliases, etc. Can be used with the :func:`field_validator` decorator. """ types_dict_names, convert = parse_types_dict_names(types_dict_names) def validator_fn(field, presentation, context): field.default_validate(presentation, context) # Make sure type exists value = getattr(presentation, field.name) if value is not None: types_dict = context.presentation.get('service_template', *types_dict_names) or {} if convert: value = convert(context, value, types_dict) if value not in types_dict: report_issue_for_unknown_type(context, presentation, type_name, field.name) return validator_fn def list_type_validator(type_name, *types_dict_names): """ Makes sure that the field's elements refer to existing types defined in the root presenter. Assumes that the field is a list. The arguments from the second onwards are used to locate a nested field under ``service_template`` under the root presenter. The first of these can optionally be a function, in which case it will be called to convert type names. This can be used to support shorthand type names, aliases, etc. Can be used with the :func:`field_validator` decorator. """ types_dict_names, convert = parse_types_dict_names(types_dict_names) def validator_fn(field, presentation, context): field.default_validate(presentation, context) # Make sure types exist values = getattr(presentation, field.name) if values is not None: types_dict = context.presentation.get('service_template', *types_dict_names) or {} for value in values: if convert: value = convert(context, value, types_dict) if value not in types_dict: report_issue_for_unknown_type(context, presentation, type_name, field.name) return validator_fn def list_length_validator(length): """ Makes sure the field has exactly a specific number of elements. Assumes that the field is a list. Can be used with the :func:`field_validator` decorator. """ def validator_fn(field, presentation, context): field.default_validate(presentation, context) # Make sure list has exactly the length values = getattr(presentation, field.name) if isinstance(values, list): if len(values) != length: context.validation.report('field "%s" does not have exactly %d elements in "%s"' % (field.name, length, presentation._fullname), locator=presentation._get_child_locator(field.name), level=Issue.FIELD) return validator_fn def derived_from_validator(*types_dict_names): """ Makes sure that the field refers to a valid parent type defined in the root presenter. Checks that we do not derive from ourselves and that we do not cause a circular hierarchy. The arguments are used to locate a nested field under ``service_template`` under the root presenter. The first of these can optionally be a function, in which case it will be called to convert type names. This can be used to support shorthand type names, aliases, etc. Can be used with the :func:`field_validator` decorator. """ types_dict_names, convert = parse_types_dict_names(types_dict_names) def validator_fn(field, presentation, context): field.default_validate(presentation, context) value = getattr(presentation, field.name) if value is not None: types_dict = context.presentation.get('service_template', *types_dict_names) or {} if convert: value = convert(context, value, types_dict) # Make sure not derived from self if value == presentation._name: report_issue_for_parent_is_self(context, presentation, field.name) # Make sure derived from type exists elif value not in types_dict: report_issue_for_unknown_parent_type(context, presentation, field.name) else: # Make sure derivation hierarchy is not circular hierarchy = [presentation._name] presentation_tmp = presentation while presentation_tmp.derived_from is not None: derived_from = presentation_tmp.derived_from if convert: derived_from = convert(context, derived_from, types_dict) if derived_from == presentation_tmp._name: # This should cause a validation issue at that type break elif derived_from not in types_dict: # This should cause a validation issue at that type break presentation_tmp = types_dict[derived_from] if presentation_tmp._name in hierarchy: report_issue_for_circular_type_hierarchy(context, presentation, field.name) break hierarchy.append(presentation_tmp._name) return validator_fn