| # 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 types import FunctionType |
| |
| from ...utils.formatting import safe_repr |
| from ...utils.type import full_type_name |
| from ..validation import Issue |
| from .null import NULL |
| |
| |
| def get_locator(*values): |
| """ |
| Gets the first available locator. |
| |
| :rtype: :class:`aria.parser.reading.Locator` |
| """ |
| |
| for v in values: |
| if hasattr(v, '_locator'): |
| locator = v._locator |
| if locator is not None: |
| return locator |
| return None |
| |
| |
| def parse_types_dict_names(types_dict_names): |
| """ |
| If the first element in the array is a function, extracts it out. |
| """ |
| |
| convert = None |
| if isinstance(types_dict_names[0], FunctionType): |
| convert = types_dict_names[0] |
| types_dict_names = types_dict_names[1:] |
| return types_dict_names, convert |
| |
| |
| def validate_primitive(value, cls, coerce=False): |
| """ |
| Checks if the value is of the primitive type, optionally attempting to coerce it |
| if it is not. |
| |
| :raises ValueError: if not a primitive type or if coercion failed. |
| """ |
| |
| if (cls is not None) and (value is not None) and (value is not NULL): |
| if (cls is unicode) or (cls is str): # These two types are interchangeable |
| valid = isinstance(value, basestring) |
| elif cls is int: |
| # In Python, a bool is an int |
| valid = isinstance(value, int) and not isinstance(value, bool) |
| else: |
| valid = isinstance(value, cls) |
| if not valid: |
| if coerce: |
| value = cls(value) |
| else: |
| raise ValueError('not a "%s": %s' % (full_type_name(cls), safe_repr(value))) |
| return value |
| |
| |
| def validate_no_short_form(context, presentation): |
| """ |
| Makes sure that we can use short form definitions only if we allowed it. |
| """ |
| |
| if not hasattr(presentation, 'SHORT_FORM_FIELD') and not isinstance(presentation._raw, dict): |
| context.validation.report('short form not allowed for field "%s"' % presentation._fullname, |
| locator=presentation._locator, |
| level=Issue.BETWEEN_FIELDS) |
| |
| |
| def validate_no_unknown_fields(context, presentation): |
| """ |
| Make sure that we can use unknown fields only if we allowed it. |
| """ |
| |
| if not getattr(presentation, 'ALLOW_UNKNOWN_FIELDS', False) \ |
| and not context.validation.allow_unknown_fields \ |
| and isinstance(presentation._raw, dict) \ |
| and hasattr(presentation, 'FIELDS'): |
| for k in presentation._raw: |
| if k not in presentation.FIELDS: |
| context.validation.report('field "%s" is not supported in "%s"' |
| % (k, presentation._fullname), |
| locator=presentation._get_child_locator(k), |
| level=Issue.BETWEEN_FIELDS) |
| |
| |
| def validate_known_fields(context, presentation): |
| """ |
| Validates all known fields. |
| """ |
| |
| if hasattr(presentation, '_iter_fields'): |
| for _, field in presentation._iter_fields(): |
| field.validate(presentation, context) |
| |
| |
| def get_parent_presentation(context, presentation, *types_dict_names): |
| """ |
| Returns the parent presentation according to the ``derived_from`` field, or ``None`` if invalid. |
| |
| Checks that we do not derive from ourselves and that we do not cause a circular hierarchy. |
| |
| The arguments from the third 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. |
| """ |
| |
| type_name = presentation.derived_from |
| |
| if type_name is None: |
| return None |
| |
| types_dict_names, convert = parse_types_dict_names(types_dict_names) |
| types_dict = context.presentation.get('service_template', *types_dict_names) or {} |
| |
| if convert: |
| type_name = convert(context, type_name, types_dict) |
| |
| # Make sure not derived from self |
| if type_name == presentation._name: |
| return None |
| # Make sure derived from type exists |
| elif type_name not in types_dict: |
| return None |
| else: |
| # Make sure derivation hierarchy is not circular |
| hierarchy = [presentation._name] |
| presentation_copy = presentation |
| while presentation_copy.derived_from is not None: |
| derived_from = presentation_copy.derived_from |
| if convert: |
| derived_from = convert(context, derived_from, types_dict) |
| |
| if derived_from == presentation_copy._name or derived_from not in types_dict: |
| return None |
| presentation_copy = types_dict[derived_from] |
| if presentation_copy._name in hierarchy: |
| return None |
| hierarchy.append(presentation_copy._name) |
| |
| return types_dict[type_name] |
| |
| |
| def report_issue_for_unknown_type(context, presentation, type_name, field_name, value=None): |
| if value is None: |
| value = getattr(presentation, field_name) |
| context.validation.report('"%s" refers to an unknown %s in "%s": %s' |
| % (field_name, type_name, presentation._fullname, safe_repr(value)), |
| locator=presentation._get_child_locator(field_name), |
| level=Issue.BETWEEN_TYPES) |
| |
| |
| def report_issue_for_parent_is_self(context, presentation, field_name): |
| context.validation.report('parent type of "%s" is self' % presentation._fullname, |
| locator=presentation._get_child_locator(field_name), |
| level=Issue.BETWEEN_TYPES) |
| |
| |
| def report_issue_for_unknown_parent_type(context, presentation, field_name): |
| context.validation.report('unknown parent type "%s" in "%s"' |
| % (getattr(presentation, field_name), presentation._fullname), |
| locator=presentation._get_child_locator(field_name), |
| level=Issue.BETWEEN_TYPES) |
| |
| |
| def report_issue_for_circular_type_hierarchy(context, presentation, field_name): |
| context.validation.report('"%s" of "%s" creates a circular type hierarchy' |
| % (getattr(presentation, field_name), presentation._fullname), |
| locator=presentation._get_child_locator(field_name), |
| level=Issue.BETWEEN_TYPES) |