def test_get_qname_functions(self): self.assertEqual(get_qname(XSD_NAMESPACE, 'element'), XSD_ELEMENT) self.assertEqual(get_qname(XSI_NAMESPACE, 'type'), XSI_TYPE) self.assertEqual(get_qname(XSI_NAMESPACE, ''), '') self.assertEqual(get_qname(XSI_NAMESPACE, None), None) self.assertEqual(get_qname(XSI_NAMESPACE, 0), 0) self.assertEqual(get_qname(XSI_NAMESPACE, False), False) self.assertRaises(TypeError, get_qname, XSI_NAMESPACE, True) self.assertEqual(get_qname(None, True), True) self.assertEqual(get_qname(None, 'element'), 'element') self.assertEqual(get_qname(None, ''), '') self.assertEqual(get_qname('', 'element'), 'element')
def _parse_substitution_group(self): substitution_group = self.elem.get('substitutionGroup') if substitution_group is None: return try: substitution_group_qname = self.schema.resolve_qname(substitution_group) except ValueError as err: self.parse_error(err) return else: if substitution_group_qname[0] != '{': substitution_group_qname = get_qname(self.target_namespace, substitution_group_qname) try: head_element = self.maps.lookup_element(substitution_group_qname) except KeyError: self.parse_error("unknown substitutionGroup %r" % substitution_group) return else: if isinstance(head_element, tuple): self.parse_error("circularity found for substitutionGroup %r" % substitution_group) return elif 'substitution' in head_element.block: return final = head_element.final if self.type == head_element.type or self.type.name == XSD_ANY_TYPE: pass elif not self.type.is_derived(head_element.type): msg = "%r type is not of the same or a derivation of the head element %r type." self.parse_error(msg % (self, head_element)) elif final == '#all' or 'extension' in final and 'restriction' in final: msg = "head element %r can't be substituted by an element that has a derivation of its type" self.parse_error(msg % head_element) elif 'extension' in final and self.type.is_derived(head_element.type, 'extension'): msg = "head element %r can't be substituted by an element that has an extension of its type" self.parse_error(msg % head_element) elif 'restriction' in final and self.type.is_derived(head_element.type, 'restriction'): msg = "head element %r can't be substituted by an element that has a restriction of its type" self.parse_error(msg % head_element) if self.type.name == XSD_ANY_TYPE and 'type' not in self.elem.attrib: self.type = self.maps.elements[substitution_group_qname].type try: self.maps.substitution_groups[substitution_group_qname].add(self) except KeyError: self.maps.substitution_groups[substitution_group_qname] = {self} finally: self._substitution_group = substitution_group_qname
def _parse(self): super(XsdNotation, self)._parse() if not self.is_global: self.parse_error("a notation declaration must be global.", self.elem) try: self.name = get_qname(self.target_namespace, self.elem.attrib['name']) except KeyError: self.parse_error("a notation must have a 'name'.", self.elem) if 'public' not in self.elem.attrib and 'system' not in self.elem.attrib: self.parse_error( "a notation must has a 'public' or a 'system' attribute.", self.elem)
def _parse(self): super(XsdIdentity, self)._parse() elem = self.elem try: self.name = get_qname(self.target_namespace, elem.attrib['name']) except KeyError: self.parse_error("missing required attribute 'name'", elem) self.name = None child = self._parse_component(elem, required=False, strict=False) if child is None or child.tag != XSD_SELECTOR: self.parse_error("missing 'selector' declaration.", elem) self.selector = None else: self.selector = XsdSelector(child, self.schema, self) self.fields = [] for child in self._iterparse_components( elem, start=int(self.selector is not None)): if child.tag == XSD_FIELD: self.fields.append(XsdFieldSelector(child, self.schema, self)) else: self.parse_error("element %r not allowed here:" % child.tag, elem)
def _parse(self): super(XsdComplexType, self)._parse() elem = self.elem if elem.tag == XSD_RESTRICTION: return # a local restriction is already parsed by the caller if 'abstract' in elem.attrib: try: self.abstract = get_xml_bool_attribute(elem, 'abstract') except ValueError as err: self.parse_error(err, elem) if 'block' in elem.attrib: try: self._block = get_xsd_derivation_attribute( elem, 'block', ('extension', 'restriction')) except ValueError as err: self.parse_error(err, elem) if 'final' in elem.attrib: try: self._final = get_xsd_derivation_attribute( elem, 'final', ('extension', 'restriction')) except ValueError as err: self.parse_error(err, elem) if 'mixed' in elem.attrib: try: self.mixed = get_xml_bool_attribute(elem, 'mixed') except ValueError as err: self.parse_error(err, elem) try: self.name = get_qname(self.target_namespace, elem.attrib['name']) except KeyError: self.name = None else: if self.parent is not None: self.parse_error( "attribute 'name' not allowed for a local complexType", elem) content_elem = self._parse_component(elem, required=False, strict=False) if content_elem is None or content_elem.tag in \ {XSD_ATTRIBUTE, XSD_ATTRIBUTE_GROUP, XSD_ANY_ATTRIBUTE}: # # complexType with empty content self.content_type = self.schema.BUILDERS.group_class( SEQUENCE_ELEMENT, self.schema, self) self._parse_content_tail(elem) elif content_elem.tag in { XSD_GROUP, XSD_SEQUENCE, XSD_ALL, XSD_CHOICE }: # # complexType with child elements self.content_type = self.schema.BUILDERS.group_class( content_elem, self.schema, self) self._parse_content_tail(elem) elif content_elem.tag == XSD_SIMPLE_CONTENT: if 'mixed' in content_elem.attrib: self.parse_error( "'mixed' attribute not allowed with simpleContent", content_elem) derivation_elem = self._parse_derivation_elem(content_elem) if derivation_elem is None: return self.base_type = self._parse_base_type(derivation_elem) if derivation_elem.tag == XSD_RESTRICTION: self._parse_simple_content_restriction(derivation_elem, self.base_type) else: self._parse_simple_content_extension(derivation_elem, self.base_type) if content_elem is not elem[-1]: k = 2 if content_elem is not elem[0] else 1 self.parse_error( "unexpected tag %r after simpleContent declaration:" % elem[k].tag, elem) elif content_elem.tag == XSD_COMPLEX_CONTENT: # # complexType with complexContent restriction/extension if 'mixed' in content_elem.attrib: self.mixed = content_elem.attrib['mixed'] in ('true', '1') derivation_elem = self._parse_derivation_elem(content_elem) if derivation_elem is None: return base_type = self._parse_base_type(derivation_elem, complex_content=True) if derivation_elem.tag == XSD_RESTRICTION: self._parse_complex_content_restriction( derivation_elem, base_type) else: self._parse_complex_content_extension(derivation_elem, base_type) if content_elem is not elem[-1]: k = 2 if content_elem is not elem[0] else 1 self.parse_error( "unexpected tag %r after complexContent declaration:" % elem[k].tag, elem) if self.redefine or base_type is not self: self.base_type = base_type elif content_elem.tag == XSD_OPEN_CONTENT and self.schema.XSD_VERSION != '1.0': self.open_content = None if content_elem is elem[-1]: self.content_type = self.schema.BUILDERS.group_class( SEQUENCE_ELEMENT, self.schema, self) else: for child, index in enumerate(elem): if content_elem is not child: continue elif elem[index + 1].tag in { XSD_GROUP, XSD_SEQUENCE, XSD_ALL, XSD_CHOICE }: self.content_type = self.schema.BUILDERS.group_class( elem[index + 1], self.schema, self) else: self.content_type = self.schema.BUILDERS.group_class( SEQUENCE_ELEMENT, self.schema, self) break self._parse_content_tail(elem) else: if self.schema.validation == 'skip': # Also generated by meta-schema validation for 'lax' and 'strict' modes self.parse_error( "unexpected tag %r for complexType content:" % content_elem.tag, elem) self.content_type = self.schema.create_any_content_group(self) self.attributes = self.schema.create_any_attribute_group(self) if self.redefine is None: if self.base_type is not None and self.base_type.name == self.name: self.parse_error("wrong definition with self-reference", elem) elif self.base_type is None or self.base_type.name != self.name: self.parse_error("wrong redefinition without self-reference", elem)
def get_attribute(self, name): if name[0] != '{': return self.type.attributes[get_qname(self.type.target_namespace, name)] return self.type.attributes[name]
def _parse_attributes(self): elem = self.elem attrib = elem.attrib self._parse_particle(elem) try: self.qualified = (self.form or self.schema.element_form_default) == 'qualified' except ValueError as err: self.parse_error(err) name = elem.get('name') if name is not None: if self.parent is None or self.qualified: self.name = get_qname(self.target_namespace, attrib['name']) else: self.name = attrib['name'] elif self.parent is None: self.parse_error("missing 'name' in a global element declaration") self.name = elem.get('ref', 'nameless_%s' % str(id(self))) elif 'ref' not in attrib: self.parse_error("missing both 'name' and 'ref' attributes") self.name = elem.get('nameless_%s' % str(id(self))) else: try: element_name = self.schema.resolve_qname(attrib['ref']) except ValueError as err: self.parse_error(err) self.type = self.maps.types[XSD_ANY_TYPE] self.name = elem.get('nameless_%s' % str(id(self))) else: if not element_name: self.parse_error("empty 'ref' attribute") self.type = self.maps.types[XSD_ANY_TYPE] self.name = elem.get('nameless_%s' % str(id(self))) else: try: xsd_element = self.maps.lookup_element(element_name) except KeyError: self.parse_error('unknown element %r' % element_name) self.name = element_name self.type = self.maps.types[XSD_ANY_TYPE] else: self._ref = xsd_element self.name = xsd_element.name self.type = xsd_element.type self.qualified = xsd_element.qualified for attr_name in ('name', 'type', 'nillable', 'default', 'fixed', 'form', 'block', 'abstract', 'final', 'substitutionGroup'): if attr_name in attrib: self.parse_error("attribute %r is not allowed when element reference is used." % attr_name) return if 'default' in attrib and 'fixed' in attrib: self.parse_error("'default' and 'fixed' attributes are mutually exclusive.") if 'abstract' in elem.attrib: try: self._abstract = get_xml_bool_attribute(elem, 'abstract') except ValueError as err: self.parse_error(err, elem) else: if self.parent is not None: self.parse_error("local scope elements cannot have abstract attribute") if 'block' in elem.attrib: try: self._block = get_xsd_derivation_attribute( elem, 'block', ('extension', 'restriction', 'substitution') ) except ValueError as err: self.parse_error(err, elem) if self.parent is None: self._parse_properties('nillable') if 'final' in elem.attrib: try: self._final = get_xsd_derivation_attribute(elem, 'final', ('extension', 'restriction')) except ValueError as err: self.parse_error(err, elem) for attr_name in ('ref', 'form', 'minOccurs', 'maxOccurs'): if attr_name in attrib: self.parse_error("attribute %r not allowed in a global element declaration" % attr_name) else: self._parse_properties('form', 'nillable') for attr_name in ('final', 'substitutionGroup'): if attr_name in attrib: self.parse_error("attribute %r not allowed in a local element declaration" % attr_name)
def load_xsd_globals(xsd_globals, schemas): redefinitions = [] for schema in schemas: target_namespace = schema.target_namespace for elem in iterchildren_xsd_redefine(schema.root): location = elem.get('schemaLocation') if location is None: continue for child in filter_function(elem): qname = get_qname(target_namespace, child.attrib['name']) redefinitions.append( (qname, child, schema, schema.includes[location])) for elem in filter_function(schema.root): qname = get_qname(target_namespace, elem.attrib['name']) try: xsd_globals[qname].append((elem, schema)) except KeyError: xsd_globals[qname] = (elem, schema) except AttributeError: xsd_globals[qname] = [xsd_globals[qname], (elem, schema)] tags = Counter([x[0] for x in redefinitions]) for qname, elem, schema, redefined_schema in redefinitions: # Checks multiple redefinitions if tags[qname] > 1: tags[qname] = 1 redefined_schemas = [ x[3] for x in redefinitions if x[0] == qname ] if any( redefined_schemas.count(x) > 1 for x in redefined_schemas): schema.parse_error( "multiple redefinition for {} {!r}".format( local_name(elem.tag), qname), elem) else: redefined_schemas = { x[3]: x[2] for x in redefinitions if x[0] == qname } for rs, s in redefined_schemas.items(): while True: try: s = redefined_schemas[s] except KeyError: break if s is rs: schema.parse_error( "circular redefinition for {} {!r}".format( local_name(elem.tag), qname), elem) break # Append redefinition try: xsd_globals[qname].append((elem, schema)) except KeyError: schema.parse_error("not a redefinition!", elem) # xsd_globals[qname] = elem, schema except AttributeError: xsd_globals[qname] = [xsd_globals[qname], (elem, schema)]
def qualified_name(self): return get_qname(self.target_namespace, self.name)
def iter_encode(self, element_data, validation='lax', converter=None, **kwargs): """ Creates an iterator for encoding data to a list containing Element data. :param element_data: an ElementData instance with unencoded data. :param validation: the validation mode: can be 'lax', 'strict' or 'skip'. :param converter: an :class:`XMLSchemaConverter` subclass or instance. :param kwargs: Keyword arguments for the encoding process. :return: Yields a couple with the text of the Element and a list of 3-tuples \ (key, decoded data, decoder), eventually preceded by a sequence of validation \ or encoding errors. """ if not element_data.content: # <tag/> or <tag></tag> yield element_data.content return if not isinstance(converter, XMLSchemaConverter): converter = self.schema.get_converter(converter, **kwargs) errors = [] text = None children = [] level = kwargs.get('level', 0) indent = kwargs.get('indent', 4) padding = '\n' + ' ' * indent * level default_namespace = converter.get('') losslessly = converter.losslessly model = ModelVisitor(self) cdata_index = 0 for index, (name, value) in enumerate(element_data.content): if isinstance(name, int): if not children: text = padding + value if text is None else text + value + padding elif children[-1].tail is None: children[-1].tail = padding + value else: children[-1].tail += value + padding cdata_index += 1 continue if not default_namespace or name[0] == '{': tag = name else: tag = '{%s}%s' % (default_namespace, name) while model.element is not None: if tag in model.element.names or model.element.name is None \ and model.element.is_matching(tag, default_namespace): xsd_element = model.element else: for xsd_element in model.element.iter_substitutes(): if tag in xsd_element.names: break else: for particle, occurs, expected in model.advance(): errors.append((index - cdata_index, particle, occurs, expected)) continue if isinstance(xsd_element, XsdAnyElement): value = get_qname(default_namespace, name), value for result in xsd_element.iter_encode(value, validation, converter, **kwargs): if isinstance(result, XMLSchemaValidationError): yield result else: children.append(result) for particle, occurs, expected in model.advance(True): errors.append( (index - cdata_index, particle, occurs, expected)) break else: if losslessly: errors.append((index - cdata_index, self, 0, [])) for xsd_element in self.iter_elements(): if tag in xsd_element.names or xsd_element.name is None \ and xsd_element.is_matching(name, default_namespace): if isinstance(xsd_element, XsdAnyElement): value = get_qname(default_namespace, name), value for result in xsd_element.iter_encode( value, validation, converter, **kwargs): if isinstance(result, XMLSchemaValidationError): yield result else: children.append(result) break else: if validation != 'skip': reason = '%r does not match any declared element of the model group.' % name yield self.validation_error(validation, reason, value, **kwargs) if model.element is not None: index = len(element_data.content) - cdata_index for particle, occurs, expected in model.stop(): errors.append((index, particle, occurs, expected)) # If the validation is not strict tries to solve model errors with a reorder of the children if errors and validation != 'strict': children = self.sort_children(children, default_namespace) if children: if children[-1].tail is None: children[-1].tail = padding[:-indent] or '\n' else: children[-1].tail = children[-1].tail.strip() + ( padding[:-indent] or '\n') if validation != 'skip' and errors: attrib = { k: unicode_type(v) for k, v in element_data.attributes.items() } if validation == 'lax' and converter.etree_element_class is not etree_element: child_tags = [ converter.etree_element(e.tag, attrib=e.attrib) for e in children ] elem = converter.etree_element(element_data.tag, text, child_tags, attrib) else: elem = converter.etree_element(element_data.tag, text, children, attrib) for index, particle, occurs, expected in errors: yield self.children_validation_error(validation, elem, index, particle, occurs, expected, **kwargs) yield text, children
def _parse(self): super(XsdGroup, self)._parse() self.clear() elem = self.elem self._parse_particle(elem) if elem.tag == XSD_GROUP: # Global group (group) name = elem.get('name') ref = elem.get('ref') if name is None: if ref is not None: # Reference to a global group if self.parent is None: self.parse_error("a group reference cannot be global") try: self.name = self.schema.resolve_qname(ref) except ValueError as err: self.parse_error(err, elem) return try: xsd_group = self.schema.maps.lookup_group(self.name) except KeyError: self.parse_error("missing group %r" % self.prefixed_name) xsd_group = self.schema.create_any_content_group( self, self.name) if isinstance(xsd_group, tuple): # Disallowed circular definition, substitute with any content group. self.parse_error( "Circular definitions detected for group %r:" % self.ref, xsd_group[0]) self.model = 'sequence' self.mixed = True self.append( XsdAnyElement(ANY_ELEMENT, self.schema, self)) else: self.model = xsd_group.model if self.model == 'all': if self.max_occurs != 1: self.parse_error( "maxOccurs must be 1 for 'all' model groups" ) if self.min_occurs not in (0, 1): self.parse_error( "minOccurs must be (0 | 1) for 'all' model groups" ) if self.schema.XSD_VERSION == '1.0' and isinstance( self.parent, XsdGroup): self.parse_error( "in XSD 1.0 the 'all' model group cannot be nested" ) self.append(xsd_group) else: self.parse_error( "missing both attributes 'name' and 'ref'") return elif ref is None: # Global group self.name = get_qname(self.target_namespace, name) content_model = self._parse_component(elem) if self.parent is not None: self.parse_error( "attribute 'name' not allowed for a local group") else: if 'minOccurs' in elem.attrib: self.parse_error( "attribute 'minOccurs' not allowed for a global group" ) if 'maxOccurs' in elem.attrib: self.parse_error( "attribute 'maxOccurs' not allowed for a global group" ) if 'minOccurs' in content_model.attrib: self.parse_error( "attribute 'minOccurs' not allowed for the model of a global group", content_model) if 'maxOccurs' in content_model.attrib: self.parse_error( "attribute 'maxOccurs' not allowed for the model of a global group", content_model) if content_model.tag not in { XSD_SEQUENCE, XSD_ALL, XSD_CHOICE }: self.parse_error( 'unexpected tag %r' % content_model.tag, content_model) return else: self.parse_error("found both attributes 'name' and 'ref'") return elif elem.tag in {XSD_SEQUENCE, XSD_ALL, XSD_CHOICE}: # Local group (sequence|all|choice) if 'name' in elem.attrib: self.parse_error( "attribute 'name' not allowed for a local group") content_model = elem self.name = None elif elem.tag in {XSD_COMPLEX_TYPE, XSD_EXTENSION, XSD_RESTRICTION}: self.name = self.model = None return else: self.parse_error('unexpected tag %r' % elem.tag, elem) return self._parse_content_model(elem, content_model)
def _parse(self): super(XsdAttribute, self)._parse() elem = self.elem try: form = self.form except ValueError as err: self.parse_error(err) else: if form is None: self.qualified = self.schema.attribute_form_default == 'qualified' elif self.parent is None: self.parse_error( "attribute 'form' not allowed in a global attribute.") else: self.qualified = form == 'qualified' self.use = elem.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' name = elem.get('name') if name is not None: if 'ref' in elem.attrib: self.parse_error( "both 'name' and 'ref' in attribute declaration") elif 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 elif self.parent is None: self.parse_error("missing 'name' in global attribute declaration") else: try: attribute_qname = self.schema.resolve_qname(elem.attrib['ref']) except KeyError: self.parse_error( "missing both 'name' and 'ref' in attribute declaration") self.xsd_type = self.maps.lookup_type(XSD_ANY_SIMPLE_TYPE) return except ValueError as err: self.parse_error(err) self.xsd_type = self.maps.lookup_type(XSD_ANY_SIMPLE_TYPE) return else: try: xsd_attribute = self.maps.lookup_attribute(attribute_qname) except LookupError: self.parse_error("unknown attribute %r" % elem.attrib['ref']) self.type = self.maps.lookup_type(XSD_ANY_SIMPLE_TYPE) else: self.type = xsd_attribute.type self.qualified = xsd_attribute.qualified if xsd_attribute.fixed is not None and 'fixed' in elem.attrib and \ elem.get('fixed') != xsd_attribute.fixed: self.parse_error( "referenced attribute has a different fixed value %r" % xsd_attribute.fixed) self.name = attribute_qname 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) xsd_declaration = self._parse_component(elem, required=False) if xsd_declaration is not None and xsd_declaration.tag == XSD_SIMPLE_TYPE: self.parse_error( "not allowed type declaration for XSD attribute reference" ) return xsd_declaration = self._parse_component(elem, required=False) try: type_qname = self.schema.resolve_qname(elem.attrib['type']) except ValueError as err: self.parse_error(err, elem) xsd_type = self.maps.lookup_type(XSD_ANY_SIMPLE_TYPE) except KeyError: if xsd_declaration is not None: # No 'type' attribute in declaration, parse for child local simpleType xsd_type = self.schema.BUILDERS.simple_type_factory( xsd_declaration, self.schema, self) else: # Empty declaration means xsdAnySimpleType 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, elem) xsd_type = self.maps.lookup_type(XSD_ANY_SIMPLE_TYPE) if xsd_declaration is not None and xsd_declaration.tag == XSD_SIMPLE_TYPE: self.parse_error( "ambiguous type declaration for XSD attribute") elif xsd_declaration: self.parse_error( "not allowed element in XSD attribute declaration: %r" % xsd_declaration[0]) try: self.type = xsd_type except TypeError as err: self.parse_error(err) # Check value constraints if 'default' in elem.attrib: if 'fixed' in elem.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(elem.attrib['default']): msg = "'default' value {!r} is not compatible with the type {!r}" self.parse_error(msg.format(elem.attrib['default'], self.type)) elif self.type.is_key(): self.parse_error( "'xs:ID' or a type derived from 'xs:ID' cannot has a 'default'" ) elif 'fixed' in elem.attrib: if not self.type.is_valid(elem.attrib['fixed']): msg = "'fixed' value {!r} is not compatible with the type {!r}" self.parse_error(msg.format(elem.attrib['fixed'], self.type)) elif self.type.is_key(): self.parse_error( "'xs:ID' or a type derived from 'xs:ID' cannot has a 'default'" )
def _parse(self): super(XsdAttributeGroup, self)._parse() elem = self.elem any_attribute = False 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, self.elem.attrib['name']) except KeyError: self.parse_error( "an attribute group declaration requires a 'name' attribute." ) return attributes = ordered_dict_class() for child in self._iterparse_components(elem): if any_attribute: 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 = True attributes.update([(None, XsdAnyAttribute(child, self.schema, self))]) 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'] attribute_group_qname = self.schema.resolve_qname(ref) except ValueError as err: self.parse_error(err, elem) except KeyError: self.parse_error( "the attribute 'ref' is required in a local attributeGroup", 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.schema.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 isinstance(base_attributes, tuple): self.parse_error( "Circular reference found between attribute groups " "{!r} and {!r}".format(self.name, attribute_group_qname)) for name, attr in base_attributes.items(): if name is not None and name in attributes: self.parse_error( "multiple declaration for attribute {!r}". format(name)) else: attributes[name] = attr 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.extend_namespace(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) 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 self.schema.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