699 lines
30 KiB
Python
699 lines
30 KiB
Python
# -*- coding: utf-8 -*-
|
|
#
|
|
# Copyright (c), 2016-2019, SISSA (International School for Advanced Studies).
|
|
# All rights reserved.
|
|
# This file is distributed under the terms of the MIT License.
|
|
# See the file 'LICENSE' in the root directory of the present
|
|
# distribution, or http://opensource.org/licenses/MIT.
|
|
#
|
|
# @author Davide Brunato <brunato@sissa.it>
|
|
#
|
|
"""
|
|
This module contains classes for XML Schema attributes and attribute groups.
|
|
"""
|
|
from __future__ import unicode_literals
|
|
from decimal import Decimal
|
|
from elementpath.datatypes import AbstractDateTime, Duration
|
|
|
|
from ..compat import MutableMapping, ordered_dict_class
|
|
from ..exceptions import XMLSchemaAttributeError, XMLSchemaTypeError, XMLSchemaValueError
|
|
from ..qnames import XSD_ANNOTATION, XSD_ANY_SIMPLE_TYPE, XSD_SIMPLE_TYPE, \
|
|
XSD_ATTRIBUTE_GROUP, XSD_COMPLEX_TYPE, XSD_RESTRICTION, XSD_EXTENSION, \
|
|
XSD_SEQUENCE, XSD_ALL, XSD_CHOICE, XSD_ATTRIBUTE, XSD_ANY_ATTRIBUTE, \
|
|
get_namespace, get_qname
|
|
from ..helpers import get_xsd_form_attribute
|
|
from ..namespaces import XSI_NAMESPACE
|
|
|
|
from .exceptions import XMLSchemaValidationError
|
|
from .xsdbase import XsdComponent, ValidationMixin
|
|
from .simple_types import XsdSimpleType
|
|
from .wildcards import XsdAnyAttribute
|
|
|
|
|
|
class XsdAttribute(XsdComponent, ValidationMixin):
|
|
"""
|
|
Class for XSD 1.0 *attribute* declarations.
|
|
|
|
:ivar type: the XSD simpleType of the attribute.
|
|
|
|
.. <attribute
|
|
default = string
|
|
fixed = string
|
|
form = (qualified | unqualified)
|
|
id = ID
|
|
name = NCName
|
|
ref = QName
|
|
type = QName
|
|
use = (optional | prohibited | required) : optional
|
|
{any attributes with non-schema namespace ...}>
|
|
Content: (annotation?, simpleType?)
|
|
</attribute>
|
|
"""
|
|
_ADMITTED_TAGS = {XSD_ATTRIBUTE}
|
|
|
|
type = None
|
|
qualified = False
|
|
default = None
|
|
fixed = None
|
|
|
|
def __init__(self, elem, schema, parent):
|
|
super(XsdAttribute, self).__init__(elem, schema, parent)
|
|
if not hasattr(self, 'type'):
|
|
raise XMLSchemaAttributeError("undefined 'type' for %r." % self)
|
|
|
|
def __repr__(self):
|
|
if self.ref is None:
|
|
return '%s(name=%r)' % (self.__class__.__name__, self.prefixed_name)
|
|
else:
|
|
return '%s(ref=%r)' % (self.__class__.__name__, self.prefixed_name)
|
|
|
|
def __setattr__(self, name, value):
|
|
if name == "type":
|
|
if not isinstance(value, XsdSimpleType):
|
|
raise XMLSchemaTypeError("An XSD attribute's type must be a simpleType.")
|
|
super(XsdAttribute, self).__setattr__(name, value)
|
|
|
|
def _parse(self):
|
|
super(XsdAttribute, self)._parse()
|
|
attrib = self.elem.attrib
|
|
|
|
self.use = attrib.get('use')
|
|
if self.use is None:
|
|
self.use = 'optional'
|
|
elif self.parent is None:
|
|
self.parse_error("attribute 'use' not allowed in a global attribute.")
|
|
elif self.use not in {'optional', 'prohibited', 'required'}:
|
|
self.parse_error("wrong value %r for 'use' attribute." % self.use)
|
|
self.use = 'optional'
|
|
|
|
if 'default' in attrib:
|
|
self.default = attrib['default']
|
|
|
|
if 'fixed' in attrib:
|
|
self.fixed = attrib['fixed']
|
|
|
|
if self._parse_reference():
|
|
try:
|
|
xsd_attribute = self.maps.lookup_attribute(self.name)
|
|
except LookupError:
|
|
self.parse_error("unknown attribute %r" % self.name)
|
|
self.type = self.maps.lookup_type(XSD_ANY_SIMPLE_TYPE)
|
|
else:
|
|
self.ref = xsd_attribute
|
|
self.type = xsd_attribute.type
|
|
if xsd_attribute.qualified:
|
|
self.qualified = True
|
|
|
|
if self.default is None and xsd_attribute.default is not None:
|
|
self.default = xsd_attribute.default
|
|
|
|
if xsd_attribute.fixed is not None:
|
|
if self.fixed is None:
|
|
self.fixed = xsd_attribute.fixed
|
|
elif xsd_attribute.fixed != self.fixed:
|
|
msg = "referenced attribute has a different fixed value %r"
|
|
self.parse_error(msg % xsd_attribute.fixed)
|
|
|
|
for attribute in ('form', 'type'):
|
|
if attribute in self.elem.attrib:
|
|
self.parse_error("attribute %r is not allowed when attribute reference is used." % attribute)
|
|
|
|
child = self._parse_child_component(self.elem)
|
|
if child is not None and child.tag == XSD_SIMPLE_TYPE:
|
|
self.parse_error("not allowed type definition for XSD attribute reference")
|
|
return
|
|
|
|
try:
|
|
form = get_xsd_form_attribute(self.elem, 'form')
|
|
except ValueError as err:
|
|
self.parse_error(err)
|
|
else:
|
|
if form is None:
|
|
if self.schema.attribute_form_default == 'qualified':
|
|
self.qualified = True
|
|
elif self.parent is None:
|
|
self.parse_error("attribute 'form' not allowed in a global attribute.")
|
|
elif form == 'qualified':
|
|
self.qualified = True
|
|
|
|
name = attrib.get('name')
|
|
if name is not None:
|
|
if name == 'xmlns':
|
|
self.parse_error("an attribute name must be different from 'xmlns'")
|
|
|
|
if self.parent is None or self.qualified:
|
|
if self.target_namespace == XSI_NAMESPACE and \
|
|
name not in {'nil', 'type', 'schemaLocation', 'noNamespaceSchemaLocation'}:
|
|
self.parse_error("Cannot add attributes in %r namespace" % XSI_NAMESPACE)
|
|
self.name = get_qname(self.target_namespace, name)
|
|
else:
|
|
self.name = name
|
|
|
|
child = self._parse_child_component(self.elem)
|
|
if 'type' in attrib:
|
|
try:
|
|
type_qname = self.schema.resolve_qname(attrib['type'])
|
|
except (KeyError, ValueError, RuntimeError) as err:
|
|
self.parse_error(err)
|
|
xsd_type = self.maps.lookup_type(XSD_ANY_SIMPLE_TYPE)
|
|
else:
|
|
try:
|
|
xsd_type = self.maps.lookup_type(type_qname)
|
|
except LookupError as err:
|
|
self.parse_error(err)
|
|
xsd_type = self.maps.lookup_type(XSD_ANY_SIMPLE_TYPE)
|
|
|
|
if child is not None and child.tag == XSD_SIMPLE_TYPE:
|
|
self.parse_error("ambiguous type definition for XSD attribute")
|
|
elif child is not None:
|
|
self.parse_error("not allowed element in XSD attribute declaration: %r" % child[0])
|
|
elif child is not None:
|
|
# No 'type' attribute in declaration, parse for child local simpleType
|
|
xsd_type = self.schema.BUILDERS.simple_type_factory(child, self.schema, self)
|
|
else:
|
|
# Empty declaration means xsdAnySimpleType
|
|
xsd_type = self.maps.lookup_type(XSD_ANY_SIMPLE_TYPE)
|
|
|
|
try:
|
|
self.type = xsd_type
|
|
except TypeError as err:
|
|
self.parse_error(err)
|
|
|
|
# Check value constraints
|
|
if 'default' in attrib:
|
|
if 'fixed' in attrib:
|
|
self.parse_error("'default' and 'fixed' attributes are mutually exclusive")
|
|
if self.use != 'optional':
|
|
self.parse_error("the attribute 'use' must be 'optional' if the attribute 'default' is present")
|
|
if not self.type.is_valid(attrib['default']):
|
|
msg = "'default' value {!r} is not compatible with the type {!r}"
|
|
self.parse_error(msg.format(attrib['default'], self.type))
|
|
elif self.type.is_key() and self.xsd_version == '1.0':
|
|
self.parse_error("'xs:ID' or a type derived from 'xs:ID' cannot has a 'default'")
|
|
elif 'fixed' in attrib:
|
|
if not self.type.is_valid(attrib['fixed']):
|
|
msg = "'fixed' value {!r} is not compatible with the type {!r}"
|
|
self.parse_error(msg.format(attrib['fixed'], self.type))
|
|
elif self.type.is_key() and self.xsd_version == '1.0':
|
|
self.parse_error("'xs:ID' or a type derived from 'xs:ID' cannot has a 'default'")
|
|
|
|
@property
|
|
def built(self):
|
|
return True
|
|
|
|
@property
|
|
def validation_attempted(self):
|
|
return 'full'
|
|
|
|
@property
|
|
def form(self):
|
|
return get_xsd_form_attribute(self.elem, 'form')
|
|
|
|
def is_optional(self):
|
|
return self.use == 'optional'
|
|
|
|
def iter_components(self, xsd_classes=None):
|
|
if xsd_classes is None or isinstance(self, xsd_classes):
|
|
yield self
|
|
if self.ref is None and self.type.parent is not None:
|
|
for obj in self.type.iter_components(xsd_classes):
|
|
yield obj
|
|
|
|
def data_value(self, text):
|
|
"""Returns the decoded data value of the provided text as XPath fn:data()."""
|
|
for result in self.iter_decode(text, validation='skip'):
|
|
return result
|
|
return text
|
|
|
|
def iter_decode(self, text, validation='lax', **kwargs):
|
|
if not text and self.default is not None:
|
|
text = self.default
|
|
|
|
if self.fixed is not None:
|
|
if text is None:
|
|
text = self.fixed
|
|
elif text == self.fixed or validation == 'skip':
|
|
pass
|
|
elif self.type.text_decode(text) != self.type.text_decode(self.fixed):
|
|
yield self.validation_error(validation, "value differs from fixed value", text, **kwargs)
|
|
|
|
for result in self.type.iter_decode(text, validation, **kwargs):
|
|
if isinstance(result, XMLSchemaValidationError):
|
|
yield result
|
|
elif isinstance(result, Decimal):
|
|
try:
|
|
yield kwargs['decimal_type'](result)
|
|
except (KeyError, TypeError):
|
|
yield result
|
|
break
|
|
elif isinstance(result, (AbstractDateTime, Duration)):
|
|
try:
|
|
yield result if kwargs['datetime_types'] is True else text
|
|
except KeyError:
|
|
yield text
|
|
else:
|
|
yield result
|
|
break
|
|
|
|
def iter_encode(self, obj, validation='lax', **kwargs):
|
|
for result in self.type.iter_encode(obj, validation):
|
|
yield result
|
|
if not isinstance(result, XMLSchemaValidationError):
|
|
return
|
|
|
|
|
|
class Xsd11Attribute(XsdAttribute):
|
|
"""
|
|
Class for XSD 1.1 *attribute* declarations.
|
|
|
|
.. <attribute
|
|
default = string
|
|
fixed = string
|
|
form = (qualified | unqualified)
|
|
id = ID
|
|
name = NCName
|
|
ref = QName
|
|
targetNamespace = anyURI
|
|
type = QName
|
|
use = (optional | prohibited | required) : optional
|
|
inheritable = boolean
|
|
{any attributes with non-schema namespace . . .}>
|
|
Content: (annotation?, simpleType?)
|
|
</attribute>
|
|
"""
|
|
inheritable = False
|
|
_target_namespace = None
|
|
|
|
@property
|
|
def target_namespace(self):
|
|
if self._target_namespace is None:
|
|
return self.schema.target_namespace
|
|
return self._target_namespace
|
|
|
|
def _parse(self):
|
|
super(Xsd11Attribute, self)._parse()
|
|
if self.use == 'prohibited' and 'fixed' in self.elem.attrib:
|
|
self.parse_error("attribute 'fixed' with use=prohibited is not allowed in XSD 1.1")
|
|
if self._parse_boolean_attribute('inheritable'):
|
|
self.inheritable = True
|
|
self._parse_target_namespace()
|
|
|
|
|
|
class XsdAttributeGroup(MutableMapping, XsdComponent, ValidationMixin):
|
|
"""
|
|
Class for XSD *attributeGroup* definitions.
|
|
|
|
.. <attributeGroup
|
|
id = ID
|
|
name = NCName
|
|
ref = QName
|
|
{any attributes with non-schema namespace . . .}>
|
|
Content: (annotation?, ((attribute | attributeGroup)*, anyAttribute?))
|
|
</attributeGroup>
|
|
"""
|
|
redefine = None
|
|
_ADMITTED_TAGS = {
|
|
XSD_ATTRIBUTE_GROUP, XSD_COMPLEX_TYPE, XSD_RESTRICTION, XSD_EXTENSION,
|
|
XSD_SEQUENCE, XSD_ALL, XSD_CHOICE, XSD_ATTRIBUTE, XSD_ANY_ATTRIBUTE
|
|
}
|
|
|
|
def __init__(self, elem, schema, parent, derivation=None, base_attributes=None):
|
|
self.derivation = derivation
|
|
self._attribute_group = ordered_dict_class()
|
|
self.base_attributes = base_attributes
|
|
XsdComponent.__init__(self, elem, schema, parent)
|
|
|
|
def __repr__(self):
|
|
if self.ref is not None:
|
|
return '%s(ref=%r)' % (self.__class__.__name__, self.name)
|
|
elif self.name is not None:
|
|
return '%s(name=%r)' % (self.__class__.__name__, self.name)
|
|
elif self:
|
|
names = [a if a.name is None else a.name for a in self.values()]
|
|
return '%s(%r)' % (self.__class__.__name__, names)
|
|
else:
|
|
return '%s()' % self.__class__.__name__
|
|
|
|
# Implementation of abstract methods
|
|
def __getitem__(self, key):
|
|
return self._attribute_group[key]
|
|
|
|
def __setitem__(self, key, value):
|
|
if key is None:
|
|
assert isinstance(value, XsdAnyAttribute), 'An XsdAnyAttribute instance is required.'
|
|
self._attribute_group[key] = value
|
|
else:
|
|
assert isinstance(value, XsdAttribute), 'An XsdAttribute instance is required.'
|
|
if key[0] != '{':
|
|
if value.local_name != key:
|
|
raise XMLSchemaValueError("%r name and key %r mismatch." % (value.name, key))
|
|
if value.target_namespace != self.target_namespace:
|
|
# Qualify attributes of other namespaces
|
|
key = value.qualified_name
|
|
elif value.qualified_name != key:
|
|
raise XMLSchemaValueError("%r name and key %r mismatch." % (value.name, key))
|
|
|
|
self._attribute_group[key] = value
|
|
|
|
def __delitem__(self, key):
|
|
del self._attribute_group[key]
|
|
|
|
def __iter__(self):
|
|
if None in self._attribute_group:
|
|
# Put AnyAttribute ('None' key) at the end of iteration
|
|
return iter(sorted(self._attribute_group, key=lambda x: (x is None, x)))
|
|
else:
|
|
return iter(self._attribute_group)
|
|
|
|
def __len__(self):
|
|
return len(self._attribute_group)
|
|
|
|
# Other methods
|
|
def __setattr__(self, name, value):
|
|
super(XsdAttributeGroup, self).__setattr__(name, value)
|
|
if name == '_attribute_group':
|
|
assert isinstance(value, dict), 'A dictionary object is required.'
|
|
for k, v in value.items():
|
|
if k is None:
|
|
assert isinstance(value, XsdAnyAttribute), 'An XsdAnyAttribute instance is required.'
|
|
else:
|
|
assert isinstance(value, XsdAttribute), 'An XsdAttribute instance is required.'
|
|
|
|
def _parse(self):
|
|
super(XsdAttributeGroup, self)._parse()
|
|
elem = self.elem
|
|
any_attribute = None
|
|
attribute_group_refs = []
|
|
|
|
if elem.tag == XSD_ATTRIBUTE_GROUP:
|
|
if self.parent is not None:
|
|
return # Skip dummy definitions
|
|
try:
|
|
self.name = get_qname(self.target_namespace, elem.attrib['name'])
|
|
except KeyError:
|
|
self.parse_error("an attribute group declaration requires a 'name' attribute.")
|
|
return
|
|
else:
|
|
if self.schema.default_attributes == self.name and self.xsd_version > '1.0':
|
|
self.schema.default_attributes = self
|
|
|
|
attributes = ordered_dict_class()
|
|
for child in filter(lambda x: x.tag != XSD_ANNOTATION, elem):
|
|
if any_attribute is not None:
|
|
if child.tag == XSD_ANY_ATTRIBUTE:
|
|
self.parse_error("more anyAttribute declarations in the same attribute group")
|
|
else:
|
|
self.parse_error("another declaration after anyAttribute")
|
|
|
|
elif child.tag == XSD_ANY_ATTRIBUTE:
|
|
any_attribute = self.schema.BUILDERS.any_attribute_class(child, self.schema, self)
|
|
if None in attributes:
|
|
attributes[None] = attributes[None].copy()
|
|
attributes[None].intersection(any_attribute)
|
|
else:
|
|
attributes[None] = any_attribute
|
|
|
|
elif child.tag == XSD_ATTRIBUTE:
|
|
attribute = self.schema.BUILDERS.attribute_class(child, self.schema, self)
|
|
if attribute.name in attributes:
|
|
self.parse_error("multiple declaration for attribute {!r}".format(attribute.name))
|
|
else:
|
|
attributes[attribute.name] = attribute
|
|
|
|
elif child.tag == XSD_ATTRIBUTE_GROUP:
|
|
try:
|
|
ref = child.attrib['ref']
|
|
except KeyError:
|
|
self.parse_error("the attribute 'ref' is required in a local attributeGroup", elem)
|
|
continue
|
|
|
|
try:
|
|
attribute_group_qname = self.schema.resolve_qname(ref)
|
|
except (KeyError, ValueError, RuntimeError) as err:
|
|
self.parse_error(err, elem)
|
|
else:
|
|
if attribute_group_qname in attribute_group_refs:
|
|
self.parse_error("duplicated attributeGroup %r" % ref)
|
|
elif self.redefine is not None:
|
|
if attribute_group_qname == self.name:
|
|
if attribute_group_refs:
|
|
self.parse_error("in a redefinition the reference to itself must be the first")
|
|
attribute_group_refs.append(attribute_group_qname)
|
|
attributes.update(self._attribute_group.items())
|
|
continue
|
|
elif not attribute_group_refs:
|
|
# May be an attributeGroup restriction with a ref to another group
|
|
if not any(e.tag == XSD_ATTRIBUTE_GROUP and ref == e.get('ref')
|
|
for e in self.redefine.elem):
|
|
self.parse_error("attributeGroup ref=%r is not in the redefined group" % ref)
|
|
elif attribute_group_qname == self.name and self.xsd_version == '1.0':
|
|
self.parse_error("Circular attribute groups not allowed in XSD 1.0")
|
|
attribute_group_refs.append(attribute_group_qname)
|
|
|
|
try:
|
|
base_attributes = self.maps.lookup_attribute_group(attribute_group_qname)
|
|
except LookupError:
|
|
self.parse_error("unknown attribute group %r" % child.attrib['ref'], elem)
|
|
else:
|
|
if not isinstance(base_attributes, tuple):
|
|
for name, attr in base_attributes.items():
|
|
if name not in attributes:
|
|
attributes[name] = attr
|
|
elif name is not None:
|
|
self.parse_error("multiple declaration for attribute {!r}".format(name))
|
|
else:
|
|
attributes[None] = attributes[None].copy()
|
|
attributes[None].intersection(attr)
|
|
|
|
elif self.xsd_version == '1.0':
|
|
self.parse_error("Circular reference found between attribute groups "
|
|
"{!r} and {!r}".format(self.name, attribute_group_qname))
|
|
|
|
elif self.name is not None:
|
|
self.parse_error("(attribute | attributeGroup) expected, found %r." % child)
|
|
|
|
# Check and copy base attributes
|
|
if self.base_attributes is not None:
|
|
wildcard = self.base_attributes.get(None)
|
|
for name, attr in attributes.items():
|
|
if name not in self.base_attributes:
|
|
if self.derivation != 'restriction':
|
|
continue
|
|
elif wildcard is None or not wildcard.is_matching(name, self.default_namespace):
|
|
self.parse_error("Unexpected attribute %r in restriction" % name)
|
|
continue
|
|
|
|
base_attr = self.base_attributes[name]
|
|
|
|
if name is None:
|
|
if self.derivation == 'extension':
|
|
try:
|
|
attr.union(base_attr)
|
|
except ValueError as err:
|
|
self.parse_error(err)
|
|
elif not attr.is_restriction(base_attr):
|
|
self.parse_error("Attribute wildcard is not a restriction of the base wildcard")
|
|
continue
|
|
if self.derivation == 'restriction' and attr.type.name != XSD_ANY_SIMPLE_TYPE and \
|
|
not attr.type.is_derived(base_attr.type, 'restriction'):
|
|
self.parse_error("Attribute type is not a restriction of the base attribute type")
|
|
if base_attr.use != 'optional' and attr.use == 'optional' or \
|
|
base_attr.use == 'required' and attr.use != 'required':
|
|
self.parse_error("Attribute %r: unmatched attribute use in restriction" % name)
|
|
if base_attr.fixed is not None and \
|
|
attr.type.normalize(attr.fixed) != base_attr.type.normalize(base_attr.fixed):
|
|
self.parse_error("Attribute %r: derived attribute has a different fixed value" % name)
|
|
|
|
if self.redefine is not None:
|
|
pass # In case of redefinition do not copy base attributes
|
|
else:
|
|
self._attribute_group.update(self.base_attributes.items())
|
|
|
|
elif self.redefine is not None and not attribute_group_refs:
|
|
for name, attr in self._attribute_group.items():
|
|
if name is None:
|
|
continue
|
|
elif name not in attributes:
|
|
if attr.use == 'required':
|
|
self.parse_error("Missing required attribute %r in redefinition restriction" % name)
|
|
continue
|
|
if attr.use != 'optional' and attributes[name].use != attr.use:
|
|
self.parse_error("Attribute %r: unmatched attribute use in redefinition" % name)
|
|
if attr.fixed is not None and attributes[name].fixed is None:
|
|
self.parse_error("Attribute %r: redefinition remove fixed constraint" % name)
|
|
|
|
pos = 0
|
|
keys = list(self._attribute_group.keys())
|
|
for name in attributes:
|
|
try:
|
|
next_pos = keys.index(name)
|
|
except ValueError:
|
|
self.parse_error("Redefinition restriction contains additional attribute %r" % name)
|
|
else:
|
|
if next_pos < pos:
|
|
self.parse_error("Wrong attribute order in redefinition restriction")
|
|
break
|
|
pos = next_pos
|
|
self.clear()
|
|
|
|
self._attribute_group.update(attributes)
|
|
if None in self._attribute_group and None not in attributes and self.derivation == 'restriction':
|
|
wildcard = self._attribute_group[None].copy()
|
|
wildcard.namespace = wildcard.not_namespace = wildcard.not_qname = ()
|
|
self._attribute_group[None] = wildcard
|
|
|
|
if self.xsd_version == '1.0':
|
|
has_key = False
|
|
for attr in self._attribute_group.values():
|
|
if attr.name is not None and attr.type.is_key():
|
|
if has_key:
|
|
self.parse_error("multiple key attributes in a group not allowed in XSD 1.0")
|
|
has_key = True
|
|
|
|
elif self.parent is None and self.schema.default_attributes == self.name:
|
|
self.schema.default_attributes = self
|
|
|
|
@property
|
|
def built(self):
|
|
return True
|
|
|
|
def iter_required(self):
|
|
for k, v in self._attribute_group.items():
|
|
if k is not None and v.use == 'required':
|
|
yield k
|
|
|
|
def iter_predefined(self, use_defaults=True):
|
|
if use_defaults:
|
|
for k, v in self._attribute_group.items():
|
|
if k is None:
|
|
continue
|
|
elif v.fixed is not None:
|
|
yield k, v.fixed
|
|
elif v.default is not None:
|
|
yield k, v.default
|
|
else:
|
|
for k, v in self._attribute_group.items():
|
|
if k is not None and v.fixed is not None:
|
|
yield k, v.fixed
|
|
|
|
def iter_components(self, xsd_classes=None):
|
|
if xsd_classes is None or isinstance(self, xsd_classes):
|
|
yield self
|
|
if self.ref is None:
|
|
for attr in self.values():
|
|
if attr.parent is not None:
|
|
for obj in attr.iter_components(xsd_classes):
|
|
yield obj
|
|
|
|
def iter_decode(self, attrs, validation='lax', **kwargs):
|
|
if not attrs and not self:
|
|
return
|
|
|
|
if validation != 'skip':
|
|
for k in filter(lambda x: x not in attrs, self.iter_required()):
|
|
reason = "missing required attribute: %r" % k
|
|
yield self.validation_error(validation, reason, attrs, **kwargs)
|
|
|
|
use_defaults = kwargs.get('use_defaults', True)
|
|
additional_attrs = [(k, v) for k, v in self.iter_predefined(use_defaults) if k not in attrs]
|
|
if additional_attrs:
|
|
attrs = {k: v for k, v in attrs.items()}
|
|
attrs.update(additional_attrs)
|
|
|
|
filler = kwargs.get('filler')
|
|
result_list = []
|
|
for name, value in attrs.items():
|
|
try:
|
|
xsd_attribute = self[name]
|
|
except KeyError:
|
|
if get_namespace(name) == XSI_NAMESPACE:
|
|
try:
|
|
xsd_attribute = self.maps.lookup_attribute(name)
|
|
except LookupError:
|
|
if validation != 'skip':
|
|
reason = "%r is not an attribute of the XSI namespace." % name
|
|
yield self.validation_error(validation, reason, attrs, **kwargs)
|
|
continue
|
|
else:
|
|
try:
|
|
xsd_attribute = self[None] # None key ==> anyAttribute
|
|
value = (name, value)
|
|
except KeyError:
|
|
if validation != 'skip':
|
|
reason = "%r attribute not allowed for element." % name
|
|
yield self.validation_error(validation, reason, attrs, **kwargs)
|
|
continue
|
|
else:
|
|
if xsd_attribute.use == 'prohibited':
|
|
reason = "use of attribute %r is prohibited" % name
|
|
yield self.validation_error(validation, reason, attrs, **kwargs)
|
|
|
|
for result in xsd_attribute.iter_decode(value, validation, **kwargs):
|
|
if isinstance(result, XMLSchemaValidationError):
|
|
yield result
|
|
elif result is None and filler is not None:
|
|
result_list.append((name, filler(xsd_attribute)))
|
|
break
|
|
else:
|
|
result_list.append((name, result))
|
|
break
|
|
|
|
if kwargs.get('fill_missing') is True:
|
|
if filler is None:
|
|
result_list.extend((k, None) for k in self._attribute_group
|
|
if k is not None and k not in attrs)
|
|
else:
|
|
result_list.extend((k, filler(v)) for k, v in self._attribute_group.items()
|
|
if k is not None and k not in attrs)
|
|
|
|
yield result_list
|
|
|
|
def iter_encode(self, attrs, validation='lax', **kwargs):
|
|
if not attrs and not self:
|
|
return
|
|
|
|
if validation != 'skip':
|
|
for k in filter(lambda x: x not in attrs, self.iter_required()):
|
|
reason = "missing required attribute: %r" % k
|
|
yield self.validation_error(validation, reason, attrs, **kwargs)
|
|
|
|
use_defaults = kwargs.get('use_defaults', True)
|
|
additional_attrs = [(k, v) for k, v in self.iter_predefined(use_defaults) if k not in attrs]
|
|
if additional_attrs:
|
|
attrs = {k: v for k, v in attrs.items()}
|
|
attrs.update(additional_attrs)
|
|
|
|
result_list = []
|
|
for name, value in attrs.items():
|
|
try:
|
|
xsd_attribute = self[name]
|
|
except KeyError:
|
|
namespace = get_namespace(name) or self.target_namespace
|
|
if namespace == XSI_NAMESPACE:
|
|
try:
|
|
xsd_attribute = self.maps.lookup_attribute(name)
|
|
except LookupError:
|
|
if validation != 'skip':
|
|
reason = "%r is not an attribute of the XSI namespace." % name
|
|
yield self.validation_error(validation, reason, attrs, **kwargs)
|
|
continue
|
|
else:
|
|
try:
|
|
xsd_attribute = self[None] # None key ==> anyAttribute
|
|
value = (name, value)
|
|
except KeyError:
|
|
if validation != 'skip':
|
|
reason = "%r attribute not allowed for element." % name
|
|
yield self.validation_error(validation, reason, attrs, **kwargs)
|
|
continue
|
|
|
|
for result in xsd_attribute.iter_encode(value, validation, **kwargs):
|
|
if isinstance(result, XMLSchemaValidationError):
|
|
yield result
|
|
else:
|
|
if result is not None:
|
|
result_list.append((name, result))
|
|
break
|
|
|
|
yield result_list
|