1 # Licensed to the Apache Software Foundation (ASF) under one or more
2 # contributor license agreements. See the NOTICE file distributed with
3 # this work for additional information regarding copyright ownership.
4 # The ASF licenses this file to You under the Apache License, Version 2.0
5 # (the "License"); you may not use this file except in compliance with
6 # the License. You may obtain a copy of the License at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
17 from ..validation import Issue
18 from .utils import (parse_types_dict_names, report_issue_for_unknown_type,
19 report_issue_for_parent_is_self, report_issue_for_unknown_parent_type,
20 report_issue_for_circular_type_hierarchy)
23 def type_validator(type_name, *types_dict_names):
25 Makes sure that the field refers to an existing type defined in the root presenter.
27 The arguments from the second onwards are used to locate a nested field under
28 ``service_template`` under the root presenter. The first of these can optionally be a function,
29 in which case it will be called to convert type names. This can be used to support shorthand
30 type names, aliases, etc.
32 Can be used with the :func:`field_validator` decorator.
35 types_dict_names, convert = parse_types_dict_names(types_dict_names)
37 def validator_fn(field, presentation, context):
38 field.default_validate(presentation, context)
40 # Make sure type exists
41 value = getattr(presentation, field.name)
43 types_dict = context.presentation.get('service_template', *types_dict_names) or {}
46 value = convert(context, value, types_dict)
48 if value not in types_dict:
49 report_issue_for_unknown_type(context, presentation, type_name, field.name)
54 def list_type_validator(type_name, *types_dict_names):
56 Makes sure that the field's elements refer to existing types defined in the root presenter.
58 Assumes that the field is a list.
60 The arguments from the second onwards are used to locate a nested field under
61 ``service_template`` under the root presenter. The first of these can optionally be a function,
62 in which case it will be called to convert type names. This can be used to support shorthand
63 type names, aliases, etc.
65 Can be used with the :func:`field_validator` decorator.
68 types_dict_names, convert = parse_types_dict_names(types_dict_names)
70 def validator_fn(field, presentation, context):
71 field.default_validate(presentation, context)
73 # Make sure types exist
74 values = getattr(presentation, field.name)
75 if values is not None:
76 types_dict = context.presentation.get('service_template', *types_dict_names) or {}
80 value = convert(context, value, types_dict)
82 if value not in types_dict:
83 report_issue_for_unknown_type(context, presentation, type_name, field.name)
88 def list_length_validator(length):
90 Makes sure the field has exactly a specific number of elements.
92 Assumes that the field is a list.
94 Can be used with the :func:`field_validator` decorator.
97 def validator_fn(field, presentation, context):
98 field.default_validate(presentation, context)
100 # Make sure list has exactly the length
101 values = getattr(presentation, field.name)
102 if isinstance(values, list):
103 if len(values) != length:
104 context.validation.report('field "%s" does not have exactly %d elements in "%s"'
105 % (field.name, length, presentation._fullname),
106 locator=presentation._get_child_locator(field.name),
112 def derived_from_validator(*types_dict_names):
114 Makes sure that the field refers to a valid parent type defined in the root presenter.
116 Checks that we do not derive from ourselves and that we do not cause a circular hierarchy.
118 The arguments are used to locate a nested field under ``service_template`` under the root
119 presenter. The first of these can optionally be a function, in which case it will be called to
120 convert type names. This can be used to support shorthand type names, aliases, etc.
122 Can be used with the :func:`field_validator` decorator.
125 types_dict_names, convert = parse_types_dict_names(types_dict_names)
127 def validator_fn(field, presentation, context):
128 field.default_validate(presentation, context)
130 value = getattr(presentation, field.name)
131 if value is not None:
132 types_dict = context.presentation.get('service_template', *types_dict_names) or {}
135 value = convert(context, value, types_dict)
137 # Make sure not derived from self
138 if value == presentation._name:
139 report_issue_for_parent_is_self(context, presentation, field.name)
140 # Make sure derived from type exists
141 elif value not in types_dict:
142 report_issue_for_unknown_parent_type(context, presentation, field.name)
144 # Make sure derivation hierarchy is not circular
145 hierarchy = [presentation._name]
146 presentation_tmp = presentation
147 while presentation_tmp.derived_from is not None:
148 derived_from = presentation_tmp.derived_from
150 derived_from = convert(context, derived_from, types_dict)
152 if derived_from == presentation_tmp._name:
153 # This should cause a validation issue at that type
155 elif derived_from not in types_dict:
156 # This should cause a validation issue at that type
158 presentation_tmp = types_dict[derived_from]
159 if presentation_tmp._name in hierarchy:
160 report_issue_for_circular_type_hierarchy(context, presentation, field.name)
162 hierarchy.append(presentation_tmp._name)