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.
16 from types import FunctionType
18 from ...utils.formatting import safe_repr
19 from ...utils.type import full_type_name
20 from ..validation import Issue
21 from .null import NULL
24 def get_locator(*values):
26 Gets the first available locator.
28 :rtype: :class:`aria.parser.reading.Locator`
32 if hasattr(v, '_locator'):
34 if locator is not None:
39 def parse_types_dict_names(types_dict_names):
41 If the first element in the array is a function, extracts it out.
45 if isinstance(types_dict_names[0], FunctionType):
46 convert = types_dict_names[0]
47 types_dict_names = types_dict_names[1:]
48 return types_dict_names, convert
51 def validate_primitive(value, cls, coerce=False):
53 Checks if the value is of the primitive type, optionally attempting to coerce it
56 :raises ValueError: if not a primitive type or if coercion failed.
59 if (cls is not None) and (value is not None) and (value is not NULL):
60 if (cls is unicode) or (cls is str): # These two types are interchangeable
61 valid = isinstance(value, basestring)
63 # In Python, a bool is an int
64 valid = isinstance(value, int) and not isinstance(value, bool)
66 valid = isinstance(value, cls)
71 raise ValueError('not a "%s": %s' % (full_type_name(cls), safe_repr(value)))
75 def validate_no_short_form(context, presentation):
77 Makes sure that we can use short form definitions only if we allowed it.
80 if not hasattr(presentation, 'SHORT_FORM_FIELD') and not isinstance(presentation._raw, dict):
81 context.validation.report('short form not allowed for field "%s"' % presentation._fullname,
82 locator=presentation._locator,
83 level=Issue.BETWEEN_FIELDS)
86 def validate_no_unknown_fields(context, presentation):
88 Make sure that we can use unknown fields only if we allowed it.
91 if not getattr(presentation, 'ALLOW_UNKNOWN_FIELDS', False) \
92 and not context.validation.allow_unknown_fields \
93 and isinstance(presentation._raw, dict) \
94 and hasattr(presentation, 'FIELDS'):
95 for k in presentation._raw:
96 if k not in presentation.FIELDS:
97 context.validation.report('field "%s" is not supported in "%s"'
98 % (k, presentation._fullname),
99 locator=presentation._get_child_locator(k),
100 level=Issue.BETWEEN_FIELDS)
103 def validate_known_fields(context, presentation):
105 Validates all known fields.
108 if hasattr(presentation, '_iter_fields'):
109 for _, field in presentation._iter_fields():
110 field.validate(presentation, context)
113 def get_parent_presentation(context, presentation, *types_dict_names):
115 Returns the parent presentation according to the ``derived_from`` field, or ``None`` if invalid.
117 Checks that we do not derive from ourselves and that we do not cause a circular hierarchy.
119 The arguments from the third onwards are used to locate a nested field under
120 ``service_template`` under the root presenter. The first of these can optionally be a function,
121 in which case it will be called to convert type names. This can be used to support shorthand
122 type names, aliases, etc.
125 type_name = presentation.derived_from
127 if type_name is None:
130 types_dict_names, convert = parse_types_dict_names(types_dict_names)
131 types_dict = context.presentation.get('service_template', *types_dict_names) or {}
134 type_name = convert(context, type_name, types_dict)
136 # Make sure not derived from self
137 if type_name == presentation._name:
139 # Make sure derived from type exists
140 elif type_name not in types_dict:
143 # Make sure derivation hierarchy is not circular
144 hierarchy = [presentation._name]
145 presentation_copy = presentation
146 while presentation_copy.derived_from is not None:
147 derived_from = presentation_copy.derived_from
149 derived_from = convert(context, derived_from, types_dict)
151 if derived_from == presentation_copy._name or derived_from not in types_dict:
153 presentation_copy = types_dict[derived_from]
154 if presentation_copy._name in hierarchy:
156 hierarchy.append(presentation_copy._name)
158 return types_dict[type_name]
161 def report_issue_for_unknown_type(context, presentation, type_name, field_name, value=None):
163 value = getattr(presentation, field_name)
164 context.validation.report('"%s" refers to an unknown %s in "%s": %s'
165 % (field_name, type_name, presentation._fullname, safe_repr(value)),
166 locator=presentation._get_child_locator(field_name),
167 level=Issue.BETWEEN_TYPES)
170 def report_issue_for_parent_is_self(context, presentation, field_name):
171 context.validation.report('parent type of "%s" is self' % presentation._fullname,
172 locator=presentation._get_child_locator(field_name),
173 level=Issue.BETWEEN_TYPES)
176 def report_issue_for_unknown_parent_type(context, presentation, field_name):
177 context.validation.report('unknown parent type "%s" in "%s"'
178 % (getattr(presentation, field_name), presentation._fullname),
179 locator=presentation._get_child_locator(field_name),
180 level=Issue.BETWEEN_TYPES)
183 def report_issue_for_circular_type_hierarchy(context, presentation, field_name):
184 context.validation.report('"%s" of "%s" creates a circular type hierarchy'
185 % (getattr(presentation, field_name), presentation._fullname),
186 locator=presentation._get_child_locator(field_name),
187 level=Issue.BETWEEN_TYPES)