1# 2# Copyright (c), 2016-2020, SISSA (International School for Advanced Studies). 3# All rights reserved. 4# This file is distributed under the terms of the MIT License. 5# See the file 'LICENSE' in the root directory of the present 6# distribution, or http://opensource.org/licenses/MIT. 7# 8# @author Davide Brunato <brunato@sissa.it> 9# 10""" 11This module contains classes for XML Schema elements, complex types and model groups. 12""" 13import warnings 14from decimal import Decimal 15from types import GeneratorType 16from typing import TYPE_CHECKING, cast, Any, Dict, Iterator, List, Optional, Tuple, Type, Union 17from elementpath import XPath2Parser, ElementPathError, XPathContext, XPathToken 18from elementpath.datatypes import AbstractDateTime, Duration, AbstractBinary 19 20from ..exceptions import XMLSchemaTypeError, XMLSchemaValueError 21from ..names import XSD_COMPLEX_TYPE, XSD_SIMPLE_TYPE, XSD_ALTERNATIVE, \ 22 XSD_ELEMENT, XSD_ANY_TYPE, XSD_UNIQUE, XSD_KEY, XSD_KEYREF, XSI_NIL, \ 23 XSI_TYPE, XSD_ERROR, XSD_NOTATION_TYPE 24from ..etree import ElementData, etree_element 25from ..aliases import ElementType, SchemaType, BaseXsdType, SchemaElementType, \ 26 ModelParticleType, ComponentClassType, AtomicValueType, DecodeType, \ 27 IterDecodeType, IterEncodeType 28from ..helpers import get_qname, get_namespace, etree_iter_location_hints, \ 29 raw_xml_encode, strictly_equal 30from .. import dataobjects 31from ..converters import XMLSchemaConverter 32from ..xpath import XMLSchemaProtocol, ElementProtocol, XMLSchemaProxy, \ 33 ElementPathMixin, XPathElement 34 35from .exceptions import XMLSchemaValidationError, XMLSchemaTypeTableWarning 36from .helpers import get_xsd_derivation_attribute 37from .xsdbase import XSD_TYPE_DERIVATIONS, XSD_ELEMENT_DERIVATIONS, \ 38 XsdComponent, ValidationMixin 39from .particles import ParticleMixin, OccursCalculator 40from .identities import IdentityXPathContext, XsdIdentity, XsdKey, XsdUnique, \ 41 XsdKeyref, IdentityCounter, IdentityCounterType 42from .simple_types import XsdSimpleType 43from .attributes import XsdAttribute 44from .wildcards import XsdAnyElement 45 46if TYPE_CHECKING: 47 from .attributes import XsdAttributeGroup 48 from .groups import XsdGroup 49 50DataBindingType = Type['dataobjects.DataElement'] 51 52 53class XsdElement(XsdComponent, ParticleMixin, 54 ElementPathMixin[SchemaElementType], 55 ValidationMixin[ElementType, Any]): 56 """ 57 Class for XSD 1.0 *element* declarations. 58 59 :ivar type: the XSD simpleType or complexType of the element. 60 :ivar attributes: the group of the attributes associated with the element. 61 62 .. <element 63 abstract = boolean : false 64 block = (#all | List of (extension | restriction | substitution)) 65 default = string 66 final = (#all | List of (extension | restriction)) 67 fixed = string 68 form = (qualified | unqualified) 69 id = ID 70 maxOccurs = (nonNegativeInteger | unbounded) : 1 71 minOccurs = nonNegativeInteger : 1 72 name = NCName 73 nillable = boolean : false 74 ref = QName 75 substitutionGroup = QName 76 type = QName 77 {any attributes with non-schema namespace . . .}> 78 Content: (annotation?, ((simpleType | complexType)?, (unique | key | keyref)*)) 79 </element> 80 """ 81 name: str 82 local_name: str 83 qualified_name: str 84 prefixed_name: str 85 86 parent: Optional['XsdGroup'] 87 ref: Optional['XsdElement'] 88 attributes: 'XsdAttributeGroup' 89 90 type: BaseXsdType 91 abstract = False 92 nillable = False 93 qualified = False 94 form: Optional[str] = None 95 default: Optional[str] = None 96 fixed: Optional[str] = None 97 substitution_group: Optional[str] = None 98 99 identities: Dict[str, XsdIdentity] 100 alternatives = () # type: Union[Tuple[()], List[XsdAlternative]] 101 inheritable = () # type: Union[Tuple[()], Dict[str, XsdAttribute]] 102 103 _ADMITTED_TAGS = {XSD_ELEMENT} 104 _block: Optional[str] = None 105 _final: Optional[str] = None 106 _head_type = None 107 _build = True 108 109 binding: Optional[DataBindingType] = None 110 111 def __init__(self, elem: etree_element, 112 schema: SchemaType, 113 parent: Optional[XsdComponent] = None, 114 build: bool = True) -> None: 115 116 if not build: 117 self._build = False 118 super(XsdElement, self).__init__(elem, schema, parent) 119 120 def __repr__(self) -> str: 121 return '%s(%s=%r, occurs=%r)' % ( 122 self.__class__.__name__, 123 'name' if self.ref is None else 'ref', 124 self.prefixed_name, 125 list(self.occurs) 126 ) 127 128 def __setattr__(self, name: str, value: Any) -> None: 129 if name == "type": 130 if isinstance(value, XsdSimpleType): 131 self.attributes = self.schema.create_empty_attribute_group(self) 132 else: 133 self.attributes = value.attributes 134 super(XsdElement, self).__setattr__(name, value) 135 136 def __iter__(self) -> Iterator[SchemaElementType]: 137 if self.type.has_complex_content(): 138 yield from self.type.content.iter_elements() # type: ignore[union-attr] 139 140 def _parse(self) -> None: 141 if not self._build: 142 return 143 144 self._parse_particle(self.elem) 145 self._parse_attributes() 146 147 if self.ref is None: 148 self._parse_type() 149 self._parse_constraints() 150 151 if self.parent is None and 'substitutionGroup' in self.elem.attrib: 152 self._parse_substitution_group(self.elem.attrib['substitutionGroup']) 153 154 def _parse_attributes(self) -> None: 155 attrib = self.elem.attrib 156 if self._parse_reference(): 157 try: 158 xsd_element: XsdElement = self.maps.lookup_element(self.name) 159 except KeyError: 160 self.type = self.any_type 161 self.parse_error('unknown element %r' % self.name) 162 else: 163 self.ref = xsd_element 164 self.type = xsd_element.type 165 self.abstract = xsd_element.abstract 166 self.nillable = xsd_element.nillable 167 self.qualified = xsd_element.qualified 168 self.form = xsd_element.form 169 self.default = xsd_element.default 170 self.fixed = xsd_element.fixed 171 self.substitution_group = xsd_element.substitution_group 172 self.identities = xsd_element.identities 173 self.alternatives = xsd_element.alternatives 174 175 for attr_name in {'type', 'nillable', 'default', 'fixed', 'form', 176 'block', 'abstract', 'final', 'substitutionGroup'}: 177 if attr_name in attrib: 178 msg = "attribute {!r} is not allowed when element reference is used" 179 self.parse_error(msg.format(attr_name)) 180 return 181 182 if 'form' in attrib: 183 self.form = attrib['form'] 184 if self.form == 'qualified': 185 self.qualified = True 186 elif self.schema.element_form_default == 'qualified': 187 self.qualified = True 188 189 try: 190 if self.parent is None or self.qualified: 191 self.name = get_qname(self.target_namespace, attrib['name']) 192 else: 193 self.name = attrib['name'] 194 except KeyError: 195 pass 196 197 if 'abstract' in attrib: 198 if self.parent is not None: 199 self.parse_error("local scope elements cannot have abstract attribute") 200 if attrib['abstract'].strip() in {'true', '1'}: 201 self.abstract = True 202 203 if 'block' in attrib: 204 try: 205 self._block = get_xsd_derivation_attribute( 206 self.elem, 'block', XSD_ELEMENT_DERIVATIONS 207 ) 208 except ValueError as err: 209 self.parse_error(err) 210 211 if 'nillable' in attrib and attrib['nillable'].strip() in {'true', '1'}: 212 self.nillable = True 213 214 if self.parent is None: 215 if 'final' in attrib: 216 try: 217 self._final = get_xsd_derivation_attribute( 218 self.elem, 'final', XSD_TYPE_DERIVATIONS 219 ) 220 except ValueError as err: 221 self.parse_error(err) 222 223 for attr_name in {'ref', 'form', 'minOccurs', 'maxOccurs'}: 224 if attr_name in attrib: 225 msg = "attribute {!r} is not allowed in a global element declaration" 226 self.parse_error(msg.format(attr_name)) 227 else: 228 for attr_name in {'final', 'substitutionGroup'}: 229 if attr_name in attrib: 230 msg = "attribute {!r} not allowed in a local element declaration" 231 self.parse_error(msg.format(attr_name)) 232 233 def _parse_type(self) -> None: 234 type_name = self.elem.get('type') 235 if type_name is not None: 236 try: 237 extended_name = self.schema.resolve_qname(type_name) 238 except (KeyError, ValueError, RuntimeError) as err: 239 self.parse_error(err) 240 self.type = self.any_type 241 else: 242 if extended_name == XSD_ANY_TYPE: 243 self.type = self.any_type 244 else: 245 try: 246 self.type = self.maps.lookup_type(extended_name) 247 except KeyError: 248 self.parse_error('unknown type {!r}'.format(type_name)) 249 self.type = self.any_type 250 finally: 251 child = self._parse_child_component(self.elem, strict=False) 252 if child is not None and child.tag in (XSD_COMPLEX_TYPE, XSD_SIMPLE_TYPE): 253 self.parse_error("the attribute 'type' and a {} local declaration " 254 "are mutually exclusive".format(child.tag.split('}')[-1])) 255 else: 256 child = self._parse_child_component(self.elem, strict=False) 257 if child is None: 258 self.type = self.any_type 259 elif child.tag == XSD_COMPLEX_TYPE: 260 self.type = self.schema.xsd_complex_type_class(child, self.schema, self) 261 elif child.tag == XSD_SIMPLE_TYPE: 262 self.type = self.schema.simple_type_factory(child, self.schema, self) 263 else: 264 self.type = self.any_type 265 266 def _parse_constraints(self) -> None: 267 # Value constraints 268 if 'default' in self.elem.attrib: 269 self.default = self.elem.attrib['default'] 270 if 'fixed' in self.elem.attrib: 271 self.parse_error("'default' and 'fixed' attributes are mutually exclusive") 272 273 if not self.type.is_valid(self.default): 274 msg = "'default' value {!r} is not compatible with element's type" 275 self.parse_error(msg.format(self.default)) 276 self.default = None 277 elif self.xsd_version == '1.0' and self.type.is_key(): 278 self.parse_error("xs:ID or a type derived from xs:ID " 279 "cannot have a default value") 280 281 elif 'fixed' in self.elem.attrib: 282 self.fixed = self.elem.attrib['fixed'] 283 if not self.type.is_valid(self.fixed): 284 msg = "'fixed' value {!r} is not compatible with element's type" 285 self.parse_error(msg.format(self.fixed)) 286 self.fixed = None 287 elif self.xsd_version == '1.0' and self.type.is_key(): 288 self.parse_error("xs:ID or a type derived from xs:ID " 289 "cannot have a fixed value") 290 291 # Identity constraints 292 self.identities = {} 293 constraint: Union[XsdKey, XsdUnique, XsdKeyref] 294 for child in self.elem: 295 if child.tag == XSD_UNIQUE: 296 constraint = self.schema.xsd_unique_class(child, self.schema, self) 297 elif child.tag == XSD_KEY: 298 constraint = self.schema.xsd_key_class(child, self.schema, self) 299 elif child.tag == XSD_KEYREF: 300 constraint = self.schema.xsd_keyref_class(child, self.schema, self) 301 else: 302 # Invalid tags already caught by validation against the meta-schema 303 continue 304 305 if constraint.ref: 306 if constraint.name in self.identities: 307 self.parse_error("duplicated identity constraint %r:" % constraint.name, child) 308 self.identities[constraint.name] = constraint 309 continue 310 311 try: 312 if child != self.maps.identities[constraint.name].elem: 313 self.parse_error("duplicated identity constraint %r:" % constraint.name, child) 314 except KeyError: 315 self.maps.identities[constraint.name] = constraint 316 finally: 317 self.identities[constraint.name] = constraint 318 319 def _parse_substitution_group(self, substitution_group: str) -> None: 320 try: 321 substitution_group_qname = self.schema.resolve_qname(substitution_group) 322 except (KeyError, ValueError, RuntimeError) as err: 323 self.parse_error(err) 324 return 325 else: 326 if substitution_group_qname[0] != '{': 327 substitution_group_qname = get_qname( 328 self.target_namespace, substitution_group_qname 329 ) 330 331 try: 332 head_element = self.maps.lookup_element(substitution_group_qname) 333 except KeyError: 334 self.parse_error("unknown substitutionGroup %r" % substitution_group) 335 return 336 else: 337 if isinstance(head_element, tuple): 338 self.parse_error("circularity found for substitutionGroup %r" % substitution_group) 339 return 340 elif 'substitution' in head_element.block: 341 return 342 343 final = head_element.final 344 if self.type == head_element.type: 345 pass 346 elif self.type.name == XSD_ANY_TYPE: 347 if head_element.type.name != XSD_ANY_TYPE: 348 # Use head element's type for validate content 349 # ref: https://www.w3.org/TR/xmlschema-1/#cElement_Declarations 350 self._head_type = head_element.type 351 elif not self.type.is_derived(head_element.type): 352 self.parse_error("%r type is not of the same or a derivation " 353 "of the head element %r type." % (self, head_element)) 354 elif final == '#all' or 'extension' in final and 'restriction' in final: 355 self.parse_error("head element %r can't be substituted by an element " 356 "that has a derivation of its type" % head_element) 357 elif 'extension' in final and self.type.is_derived(head_element.type, 'extension'): 358 self.parse_error("head element %r can't be substituted by an element " 359 "that has an extension of its type" % head_element) 360 elif 'restriction' in final and self.type.is_derived(head_element.type, 'restriction'): 361 self.parse_error("head element %r can't be substituted by an element " 362 "that has a restriction of its type" % head_element) 363 364 try: 365 self.maps.substitution_groups[substitution_group_qname].add(self) 366 except KeyError: 367 self.maps.substitution_groups[substitution_group_qname] = {self} 368 finally: 369 self.substitution_group = substitution_group_qname 370 371 @property 372 def xpath_proxy(self) -> XMLSchemaProxy: 373 return XMLSchemaProxy( 374 schema=cast(XMLSchemaProtocol, self.schema), 375 base_element=cast(ElementProtocol, self) 376 ) 377 378 def build(self) -> None: 379 if self._build: 380 return 381 self._build = True 382 self._parse() 383 384 @property 385 def built(self) -> bool: 386 return hasattr(self, 'type') and \ 387 (self.type.parent is None or self.type.built) and \ 388 all(c.built for c in self.identities.values()) 389 390 @property 391 def validation_attempted(self) -> str: 392 if self.built: 393 return 'full' 394 elif self.type.validation_attempted == 'partial': 395 return 'partial' 396 elif any(c.validation_attempted == 'partial' for c in self.identities.values()): 397 return 'partial' 398 else: 399 return 'none' 400 401 @property 402 def scope(self) -> str: 403 """The scope of the element declaration that can be 'global' or 'local'.""" 404 return 'global' if self.parent is None else 'local' 405 406 @property 407 def value_constraint(self) -> Optional[str]: 408 """The fixed or the default value if either is defined, `None` otherwise.""" 409 return self.fixed if self.fixed is not None else self.default 410 411 @property 412 def final(self) -> str: 413 if self.ref is not None: 414 return self.ref.final 415 elif self._final is not None: 416 return self._final 417 return self.schema.final_default 418 419 @property 420 def block(self) -> str: 421 if self.ref is not None: 422 return self.ref.block 423 elif self._block is not None: 424 return self._block 425 return self.schema.block_default 426 427 def get_binding(self, *bases: Type[Any], replace_existing: bool = False, **attrs: Any) \ 428 -> DataBindingType: 429 """ 430 Gets data object binding for XSD element, creating a new one if it doesn't exist. 431 432 :param bases: base classes to use for creating the binding class. 433 :param replace_existing: provide `True` to replace an existing binding class. 434 :param attrs: attribute and method definitions for the binding class body. 435 """ 436 if self.binding is None or not replace_existing: 437 if not bases: 438 bases = (dataobjects.DataElement,) 439 attrs['xsd_element'] = self 440 class_name = '{}Binding'.format(self.local_name.title().replace('_', '')) 441 self.binding = cast(DataBindingType, 442 dataobjects.DataBindingMeta(class_name, bases, attrs)) 443 return self.binding 444 445 def get_attribute(self, name: str) -> Optional[XsdAttribute]: 446 if name[0] != '{': 447 name = get_qname(self.type.target_namespace, name) 448 if not isinstance(self.type, XsdSimpleType): 449 xsd_attribute = self.type.attributes[name] 450 assert isinstance(xsd_attribute, XsdAttribute) 451 return xsd_attribute 452 return None 453 454 def get_type(self, elem: Union[ElementType, ElementData], 455 inherited: Optional[Dict[str, Any]] = None) -> BaseXsdType: 456 return self._head_type or self.type 457 458 def get_attributes(self, xsd_type: BaseXsdType) -> 'XsdAttributeGroup': 459 if not isinstance(xsd_type, XsdSimpleType): 460 return xsd_type.attributes 461 elif xsd_type is self.type: 462 return self.attributes 463 else: 464 return self.schema.create_empty_attribute_group(self) 465 466 def get_path(self, ancestor: Optional[XsdComponent] = None, 467 reverse: bool = False) -> Optional[str]: 468 """ 469 Returns the XPath expression of the element. The path is relative to the schema instance 470 in which the element is contained or is relative to a specific ancestor passed as argument. 471 In the latter case returns `None` if the argument is not an ancestor. 472 473 :param ancestor: optional XSD component of the same schema, that maybe \ 474 an ancestor of the element. 475 :param reverse: if set to `True` returns the reverse path, from the element to ancestor. 476 """ 477 path: List[str] = [] 478 xsd_component: Optional[XsdComponent] = self 479 while xsd_component is not None: 480 if xsd_component is ancestor: 481 return '/'.join(reversed(path)) or '.' 482 elif isinstance(xsd_component, XsdElement): 483 path.append('..' if reverse else xsd_component.name) 484 xsd_component = xsd_component.parent 485 else: 486 if ancestor is None: 487 return '/'.join(reversed(path)) or '.' 488 return None 489 490 def iter_components(self, xsd_classes: Optional[ComponentClassType] = None) \ 491 -> Iterator[XsdComponent]: 492 493 if xsd_classes is None: 494 yield self 495 yield from self.identities.values() 496 else: 497 if isinstance(self, xsd_classes): 498 yield self 499 if issubclass(XsdIdentity, xsd_classes): 500 yield from self.identities.values() 501 502 if self.ref is None and self.type.parent is not None: 503 yield from self.type.iter_components(xsd_classes) 504 505 def iter_substitutes(self) -> Iterator['XsdElement']: 506 if self.parent is None or self.ref is not None: 507 for xsd_element in self.maps.substitution_groups.get(self.name, ()): 508 if not xsd_element.abstract: 509 yield xsd_element 510 for e in xsd_element.iter_substitutes(): 511 if not e.abstract: 512 yield e 513 514 def data_value(self, elem: ElementType) -> Optional[AtomicValueType]: 515 """Returns the decoded data value of the provided element as XPath fn:data().""" 516 text = elem.text 517 if text is None: 518 text = self.fixed if self.fixed is not None else self.default 519 if text is None: 520 return None 521 return self.type.text_decode(text) 522 523 def check_dynamic_context(self, elem: ElementType, **kwargs: Any) -> None: 524 try: 525 locations = kwargs['locations'] 526 except KeyError: 527 return 528 529 schema: Optional[SchemaType] 530 for ns, url in etree_iter_location_hints(elem): 531 if ns not in locations: 532 locations[ns] = url 533 elif locations[ns] is None: 534 reason = "schemaLocation declaration after namespace start" 535 raise XMLSchemaValidationError(self, elem, reason) 536 537 if ns == self.target_namespace: 538 schema = self.schema.include_schema(url, self.schema.base_url) 539 else: 540 schema = self.schema.import_schema(ns, url, self.schema.base_url) 541 542 if schema is None: 543 reason = f"missing dynamic loaded schema from {url}" 544 raise XMLSchemaValidationError(self, elem, reason) 545 elif not schema.built: 546 reason = "dynamic loaded schema change the assessment" 547 raise XMLSchemaValidationError(self, elem, reason) 548 549 if elem.attrib: 550 for name in elem.attrib: 551 if name[0] == '{': 552 ns = get_namespace(name) 553 if ns not in locations: 554 locations[ns] = None 555 556 if elem.tag[0] == '{': 557 ns = get_namespace(elem.tag) 558 if ns not in locations: 559 locations[ns] = None 560 561 def start_identities(self, identities: Dict[XsdIdentity, IdentityCounter]) -> None: 562 """ 563 Start tracking of XSD element's identities. 564 565 :param identities: a dictionary containing the identities counters. 566 """ 567 for constraint in self.identities.values(): 568 try: 569 identities[constraint].clear() 570 except KeyError: 571 identities[constraint] = constraint.get_counter() 572 573 def stop_identities(self, identities: Dict[XsdIdentity, IdentityCounter]) -> None: 574 """ 575 Stop tracking of XSD element's identities. 576 577 :param identities: a dictionary containing the identities counters. 578 """ 579 for identity in self.identities.values(): 580 try: 581 identities[identity].enabled = False 582 except KeyError: 583 identities[identity] = identity.get_counter(enabled=False) 584 585 def iter_decode(self, obj: ElementType, validation: str = 'lax', **kwargs: Any) \ 586 -> IterDecodeType[Any]: 587 """ 588 Creates an iterator for decoding an Element instance. 589 590 :param obj: the Element that has to be decoded. 591 :param validation: the validation mode, can be 'lax', 'strict' or 'skip'. 592 :param kwargs: keyword arguments for the decoding process. 593 :return: yields a decoded object, eventually preceded by a sequence of \ 594 validation or decoding errors. 595 """ 596 if self.abstract: 597 reason = "cannot use an abstract element for validation" 598 yield self.validation_error(validation, reason, obj, **kwargs) 599 600 try: 601 namespaces = kwargs['namespaces'] 602 except KeyError: 603 namespaces = None 604 605 try: 606 level = kwargs['level'] 607 except KeyError: 608 level = kwargs['level'] = 0 609 610 try: 611 identities = kwargs['identities'] 612 except KeyError: 613 identities = kwargs['identities'] = {} 614 615 self.start_identities(identities) 616 617 try: 618 converter = kwargs['converter'] 619 except KeyError: 620 converter = kwargs['converter'] = self.schema.get_converter(**kwargs) 621 else: 622 if converter is not None and not isinstance(converter, XMLSchemaConverter): 623 converter = kwargs['converter'] = self.schema.get_converter(**kwargs) 624 625 try: 626 pass # self.check_dynamic_context(elem, **kwargs) TODO: dynamic schema load 627 except XMLSchemaValidationError as err: 628 yield self.validation_error(validation, err, obj, **kwargs) 629 630 inherited = kwargs.get('inherited') 631 value = content = attributes = None 632 nilled = False 633 634 # Get the instance effective type 635 xsd_type = self.get_type(obj, inherited) 636 if XSI_TYPE in obj.attrib: 637 type_name = obj.attrib[XSI_TYPE].strip() 638 try: 639 xsd_type = self.maps.get_instance_type(type_name, xsd_type, namespaces) 640 except (KeyError, TypeError) as err: 641 yield self.validation_error(validation, err, obj, **kwargs) 642 else: 643 if self.identities: 644 xpath_element = XPathElement(self.name, xsd_type) 645 for identity in self.identities.values(): 646 if isinstance(identity.elements, tuple): 647 continue # Skip unbuilt identities 648 649 context = IdentityXPathContext( 650 self.schema, item=xpath_element # type: ignore[arg-type] 651 ) 652 for e in identity.selector.token.select_results(context): 653 if not isinstance(e, XsdElement): 654 reason = "selector xpath expression can only select elements" 655 yield self.validation_error(validation, reason, e, **kwargs) 656 elif e not in identity.elements: 657 identity.elements[e] = None 658 659 if xsd_type.is_blocked(self): 660 reason = "usage of %r is blocked" % xsd_type 661 yield self.validation_error(validation, reason, obj, **kwargs) 662 663 if xsd_type.abstract: 664 yield self.validation_error(validation, "%r is abstract", obj, **kwargs) 665 if xsd_type.is_complex() and self.xsd_version == '1.1': 666 kwargs['id_list'] = [] # Track XSD 1.1 multiple xs:ID attributes/children 667 668 content_decoder = xsd_type if isinstance(xsd_type, XsdSimpleType) else xsd_type.content 669 670 # Decode attributes 671 attribute_group = self.get_attributes(xsd_type) 672 result: Any 673 for result in attribute_group.iter_decode(obj.attrib, validation, **kwargs): 674 if isinstance(result, XMLSchemaValidationError): 675 yield self.validation_error(validation, result, obj, **kwargs) 676 else: 677 attributes = result 678 679 if self.inheritable and any(name in self.inheritable for name in obj.attrib): 680 if inherited: 681 inherited = inherited.copy() 682 inherited.update((k, v) for k, v in obj.attrib.items() if k in self.inheritable) 683 else: 684 inherited = {k: v for k, v in obj.attrib.items() if k in self.inheritable} 685 kwargs['inherited'] = inherited 686 687 # Checks the xsi:nil attribute of the instance 688 if XSI_NIL in obj.attrib: 689 xsi_nil = obj.attrib[XSI_NIL].strip() 690 if not self.nillable: 691 reason = "element is not nillable." 692 yield self.validation_error(validation, reason, obj, **kwargs) 693 elif xsi_nil not in {'0', '1', 'false', 'true'}: 694 reason = "xsi:nil attribute must have a boolean value." 695 yield self.validation_error(validation, reason, obj, **kwargs) 696 elif xsi_nil in ('0', 'false'): 697 pass 698 elif self.fixed is not None: 699 reason = "xsi:nil='true' but the element has a fixed value." 700 yield self.validation_error(validation, reason, obj, **kwargs) 701 elif obj.text is not None or len(obj): 702 reason = "xsi:nil='true' but the element is not empty." 703 yield self.validation_error(validation, reason, obj, **kwargs) 704 else: 705 nilled = True 706 707 if xsd_type.is_empty() and obj.text and xsd_type.normalize(obj.text): 708 reason = "character data is not allowed because content is empty" 709 yield self.validation_error(validation, reason, obj, **kwargs) 710 711 if nilled: 712 pass 713 elif not isinstance(content_decoder, XsdSimpleType): 714 if not isinstance(xsd_type, XsdSimpleType): 715 for assertion in xsd_type.assertions: 716 for error in assertion(obj, **kwargs): 717 yield self.validation_error(validation, error, **kwargs) 718 719 for result in content_decoder.iter_decode(obj, validation, **kwargs): 720 if isinstance(result, XMLSchemaValidationError): 721 yield self.validation_error(validation, result, obj, **kwargs) 722 else: 723 content = result 724 725 if content and len(content) == 1 and content[0][0] == 1: 726 value, content = content[0][1], None 727 728 if self.fixed is not None and \ 729 (len(obj) > 0 or value is not None and self.fixed != value): 730 reason = "must have the fixed value %r" % self.fixed 731 yield self.validation_error(validation, reason, obj, **kwargs) 732 733 else: 734 if len(obj): 735 reason = "a simple content element can't have child elements." 736 yield self.validation_error(validation, reason, obj, **kwargs) 737 738 text = obj.text 739 if self.fixed is not None: 740 if text is None: 741 text = self.fixed 742 elif text == self.fixed: 743 pass 744 elif not strictly_equal(xsd_type.text_decode(text), 745 xsd_type.text_decode(self.fixed)): 746 reason = "must have the fixed value %r" % self.fixed 747 yield self.validation_error(validation, reason, obj, **kwargs) 748 749 elif not text and self.default is not None and kwargs.get('use_defaults'): 750 text = self.default 751 752 if not isinstance(xsd_type, XsdSimpleType): 753 for assertion in xsd_type.assertions: 754 for error in assertion(obj, value=text, **kwargs): 755 yield self.validation_error(validation, error, **kwargs) 756 757 if text and content_decoder.is_list(): 758 value = text.split() 759 else: 760 value = text 761 762 elif xsd_type.is_notation(): 763 if xsd_type.name == XSD_NOTATION_TYPE: 764 msg = "cannot validate against xs:NOTATION directly, " \ 765 "only against a subtype with an enumeration facet" 766 yield self.validation_error(validation, msg, text, **kwargs) 767 elif not xsd_type.enumeration: 768 msg = "missing enumeration facet in xs:NOTATION subtype" 769 yield self.validation_error(validation, msg, text, **kwargs) 770 771 if text is None: 772 for result in content_decoder.iter_decode('', validation, **kwargs): 773 if isinstance(result, XMLSchemaValidationError): 774 yield self.validation_error(validation, result, obj, **kwargs) 775 if 'filler' in kwargs: 776 value = kwargs['filler'](self) 777 else: 778 for result in content_decoder.iter_decode(text, validation, **kwargs): 779 if isinstance(result, XMLSchemaValidationError): 780 yield self.validation_error(validation, result, obj, **kwargs) 781 elif result is None and 'filler' in kwargs: 782 value = kwargs['filler'](self) 783 else: 784 value = result 785 786 if 'value_hook' in kwargs: 787 value = kwargs['value_hook'](value, xsd_type) 788 elif isinstance(value, (int, float, list)) or value is None: 789 pass 790 elif isinstance(value, str): 791 if value.startswith('{') and xsd_type.is_qname(): 792 value = text 793 elif isinstance(value, Decimal): 794 try: 795 value = kwargs['decimal_type'](value) 796 except (KeyError, TypeError): 797 pass 798 elif isinstance(value, (AbstractDateTime, Duration)): 799 if not kwargs.get('datetime_types'): 800 value = str(value) if text is None else text.strip() 801 elif isinstance(value, AbstractBinary): 802 if not kwargs.get('binary_types'): 803 value = str(value) 804 805 if converter is not None: 806 element_data = ElementData(obj.tag, value, content, attributes) 807 yield converter.element_decode(element_data, self, xsd_type, level) 808 elif not level: 809 yield ElementData(obj.tag, value, None, attributes) 810 811 if content is not None: 812 del content 813 814 # Collects fields values for identities that refer to this element. 815 for identity, counter in identities.items(): 816 if not counter.enabled or not identity.elements: 817 continue 818 elif self in identity.elements: 819 xsd_element = self 820 elif self.ref in identity.elements: 821 xsd_element = self.ref 822 else: 823 continue 824 825 try: 826 xsd_fields: Optional[IdentityCounterType] 827 if xsd_type is self.type: 828 xsd_fields = identity.elements[xsd_element] 829 if xsd_fields is None: 830 xsd_fields = identity.get_fields(xsd_element) 831 identity.elements[xsd_element] = xsd_fields 832 else: 833 xsd_element = cast(XsdElement, self.copy()) 834 xsd_element.type = xsd_type 835 xsd_fields = identity.get_fields(xsd_element) 836 837 if all(x is None for x in xsd_fields): 838 continue 839 decoders = cast(Tuple[XsdAttribute, ...], xsd_fields) 840 fields = identity.get_fields(obj, namespaces, decoders=decoders) 841 except (XMLSchemaValueError, XMLSchemaTypeError) as err: 842 yield self.validation_error(validation, err, obj, **kwargs) 843 else: 844 if any(x is not None for x in fields) or nilled: 845 try: 846 counter.increase(fields) 847 except ValueError as err: 848 yield self.validation_error(validation, err, obj, **kwargs) 849 850 # Apply non XSD optional validations 851 if 'extra_validator' in kwargs: 852 try: 853 result = kwargs['extra_validator'](obj, self) 854 except XMLSchemaValidationError as err: 855 yield self.validation_error(validation, err, obj, **kwargs) 856 else: 857 if isinstance(result, GeneratorType): 858 for error in result: 859 yield self.validation_error(validation, error, obj, **kwargs) 860 861 # Disable collect for out of scope identities and check key references 862 if 'max_depth' not in kwargs: 863 for identity in self.identities.values(): 864 counter = identities[identity] 865 counter.enabled = False 866 if isinstance(identity, XsdKeyref): 867 for error in counter.iter_errors(identities): 868 yield self.validation_error(validation, error, obj, **kwargs) 869 elif level: 870 self.stop_identities(identities) 871 872 def to_objects(self, obj: ElementType, with_bindings: bool = False, **kwargs: Any) \ 873 -> DecodeType['dataobjects.DataElement']: 874 """ 875 Decodes XML data to Python data objects. 876 877 :param obj: the XML data source. 878 :param with_bindings: if `True` is provided the decoding is done using \ 879 :class:`DataBindingConverter` that used XML data binding classes. For \ 880 default the objects are instances of :class:`DataElement` and uses the \ 881 :class:`DataElementConverter`. 882 :param kwargs: other optional keyword arguments for the method \ 883 :func:`iter_decode`, except the argument *converter*. 884 """ 885 if with_bindings: 886 return self.decode(obj, converter=dataobjects.DataBindingConverter, **kwargs) 887 return self.decode(obj, converter=dataobjects.DataElementConverter, **kwargs) 888 889 def iter_encode(self, obj: Any, validation: str = 'lax', **kwargs: Any) \ 890 -> IterEncodeType[ElementType]: 891 """ 892 Creates an iterator for encoding data to an Element. 893 894 :param obj: the data that has to be encoded. 895 :param validation: the validation mode: can be 'lax', 'strict' or 'skip'. 896 :param kwargs: keyword arguments for the encoding process. 897 :return: yields an Element, eventually preceded by a sequence of \ 898 validation or encoding errors. 899 """ 900 errors: List[Union[str, Exception]] = [] 901 902 try: 903 converter = kwargs['converter'] 904 except KeyError: 905 converter = kwargs['converter'] = self.schema.get_converter(**kwargs) 906 else: 907 if not isinstance(converter, XMLSchemaConverter): 908 converter = kwargs['converter'] = self.schema.get_converter(**kwargs) 909 910 try: 911 level = kwargs['level'] 912 except KeyError: 913 level = 0 914 element_data = converter.element_encode(obj, self, level) 915 if not self.is_matching(element_data.tag, self.default_namespace): 916 errors.append("data tag does not match XSD element name") 917 918 if 'max_depth' in kwargs and kwargs['max_depth'] == 0: 919 for e in errors: 920 yield self.validation_error(validation, e, **kwargs) 921 return 922 else: 923 element_data = converter.element_encode(obj, self, level) 924 925 text = None 926 children = element_data.content 927 attributes = () 928 929 xsd_type = self.get_type(element_data) 930 if XSI_TYPE in element_data.attributes: 931 type_name = element_data.attributes[XSI_TYPE].strip() 932 try: 933 xsd_type = self.maps.get_instance_type(type_name, xsd_type, converter) 934 except (KeyError, TypeError) as err: 935 errors.append(err) 936 else: 937 default_namespace = converter.get('') 938 if default_namespace and not isinstance(xsd_type, XsdSimpleType): 939 # Adjust attributes mapped into default namespace 940 941 ns_part = '{%s}' % default_namespace 942 for k in list(element_data.attributes): 943 if not k.startswith(ns_part): 944 continue 945 elif k in xsd_type.attributes: 946 continue 947 948 local_name = k[len(ns_part):] 949 if local_name in xsd_type.attributes: 950 element_data.attributes[local_name] = element_data.attributes[k] 951 del element_data.attributes[k] 952 953 attribute_group = self.get_attributes(xsd_type) 954 result: Any 955 for result in attribute_group.iter_encode(element_data.attributes, validation, **kwargs): 956 if isinstance(result, XMLSchemaValidationError): 957 errors.append(result) 958 else: 959 attributes = result 960 961 if XSI_NIL in element_data.attributes: 962 xsi_nil = element_data.attributes[XSI_NIL].strip() 963 if not self.nillable: 964 errors.append("element is not nillable.") 965 elif xsi_nil not in {'0', '1', 'true', 'false'}: 966 errors.append("xsi:nil attribute must has a boolean value.") 967 elif xsi_nil in ('0', 'false'): 968 pass 969 elif self.fixed is not None: 970 errors.append("xsi:nil='true' but the element has a fixed value.") 971 elif element_data.text is not None or element_data.content: 972 errors.append("xsi:nil='true' but the element is not empty.") 973 else: 974 elem = converter.etree_element(element_data.tag, attrib=attributes, level=level) 975 for e in errors: 976 yield self.validation_error(validation, e, elem, **kwargs) 977 yield elem 978 return 979 980 if isinstance(xsd_type, XsdSimpleType): 981 if element_data.content: 982 errors.append("a simpleType element can't has child elements.") 983 984 if element_data.text is not None: 985 for result in xsd_type.iter_encode(element_data.text, validation, **kwargs): 986 if isinstance(result, XMLSchemaValidationError): 987 errors.append(result) 988 else: 989 text = result 990 991 elif self.fixed is not None: 992 text = self.fixed 993 elif self.default is not None and kwargs.get('use_defaults'): 994 text = self.default 995 996 elif xsd_type.has_simple_content(): 997 if element_data.text is not None: 998 for result in xsd_type.content.iter_encode(element_data.text, 999 validation, **kwargs): 1000 if isinstance(result, XMLSchemaValidationError): 1001 errors.append(result) 1002 else: 1003 text = result 1004 1005 elif self.fixed is not None: 1006 text = self.fixed 1007 elif self.default is not None and kwargs.get('use_defaults'): 1008 text = self.default 1009 1010 else: 1011 for result in xsd_type.content.iter_encode(element_data, validation, **kwargs): 1012 if isinstance(result, XMLSchemaValidationError): 1013 errors.append(result) 1014 elif result: 1015 text, children = result 1016 1017 elem = converter.etree_element(element_data.tag, text, children, attributes, level) 1018 1019 if errors: 1020 for e in errors: 1021 yield self.validation_error(validation, e, elem, **kwargs) 1022 yield elem 1023 del element_data 1024 1025 def is_matching(self, name: Optional[str], default_namespace: Optional[str] = None, 1026 group: Optional['XsdGroup'] = None, **kwargs: Any) -> bool: 1027 if not name: 1028 return False 1029 elif default_namespace and name[0] != '{': 1030 qname = '{%s}%s' % (default_namespace, name) 1031 if name == self.name or qname == self.name: 1032 return True 1033 return any(name == e.name or qname == e.name for e in self.iter_substitutes()) 1034 elif name == self.name: 1035 return True 1036 else: 1037 return any(name == e.name for e in self.iter_substitutes()) 1038 1039 def match(self, name: Optional[str], default_namespace: Optional[str] = None, 1040 **kwargs: Any) -> Optional['XsdElement']: 1041 if not name: 1042 return None 1043 elif default_namespace and name[0] != '{': 1044 qname = '{%s}%s' % (default_namespace, name) 1045 if name == self.name or qname == self.name: 1046 return self 1047 1048 for xsd_element in self.iter_substitutes(): 1049 if name == xsd_element.name or qname == xsd_element.name: 1050 return xsd_element 1051 1052 elif name == self.name: 1053 return self 1054 else: 1055 for xsd_element in self.iter_substitutes(): 1056 if name == xsd_element.name: 1057 return xsd_element 1058 return None 1059 1060 def is_restriction(self, other: ModelParticleType, check_occurs: bool = True) -> bool: 1061 e: ModelParticleType 1062 1063 if isinstance(other, XsdAnyElement): 1064 if self.min_occurs == self.max_occurs == 0: 1065 return True 1066 if check_occurs and not self.has_occurs_restriction(other): 1067 return False 1068 return other.is_matching(self.name, self.default_namespace) 1069 elif isinstance(other, XsdElement): 1070 if self.name != other.name: 1071 if other.name == self.substitution_group and \ 1072 other.min_occurs != other.max_occurs and \ 1073 self.max_occurs != 0 and not other.abstract \ 1074 and self.xsd_version == '1.0': 1075 # An UPA violation case. Base is the head element, it's not 1076 # abstract and has non deterministic occurs: this is less 1077 # restrictive than W3C test group (elemZ026), marked as 1078 # invalid despite it's based on an abstract declaration. 1079 # See also test case invalid_restrictions1.xsd. 1080 return False 1081 1082 for e in other.iter_substitutes(): 1083 if e.name == self.name: 1084 break 1085 else: 1086 return False 1087 1088 if check_occurs and not self.has_occurs_restriction(other): 1089 return False 1090 elif not self.is_consistent(other) and self.type.elem is not other.type.elem and \ 1091 not self.type.is_derived(other.type, 'restriction') and not other.type.abstract: 1092 return False 1093 elif other.fixed is not None and \ 1094 (self.fixed is None or self.type.normalize( 1095 self.fixed) != other.type.normalize(other.fixed)): 1096 return False 1097 elif other.nillable is False and self.nillable: 1098 return False 1099 elif any(value not in self.block for value in other.block.split()): 1100 return False 1101 elif not all(k in other.identities for k in self.identities): 1102 return False 1103 else: 1104 return True 1105 elif other.model == 'choice': 1106 if other.is_empty() and self.max_occurs != 0: 1107 return False 1108 1109 check_group_items_occurs = self.xsd_version == '1.0' 1110 total_occurs = OccursCalculator() 1111 for e in other.iter_model(): 1112 if not isinstance(e, (XsdElement, XsdAnyElement)): 1113 return False 1114 elif not self.is_restriction(e, check_group_items_occurs): 1115 continue 1116 total_occurs += e 1117 total_occurs *= other 1118 if self.has_occurs_restriction(total_occurs): 1119 return True 1120 total_occurs.reset() 1121 return False 1122 else: 1123 match_restriction = False 1124 for e in other.iter_model(): 1125 if match_restriction: 1126 if not e.is_emptiable(): 1127 return False 1128 elif self.is_restriction(e): 1129 match_restriction = True 1130 elif not e.is_emptiable(): 1131 return False 1132 return True 1133 1134 def is_overlap(self, other: SchemaElementType) -> bool: 1135 if isinstance(other, XsdElement): 1136 if self.name == other.name: 1137 return True 1138 elif other.substitution_group == self.name or other.name == self.substitution_group: 1139 return True 1140 elif isinstance(other, XsdAnyElement): 1141 if other.is_matching(self.name, self.default_namespace): 1142 return True 1143 for e in self.maps.substitution_groups.get(self.name, ()): 1144 if other.is_matching(e.name, self.default_namespace): 1145 return True 1146 return False 1147 1148 def is_consistent(self, other: SchemaElementType, strict: bool = True) -> bool: 1149 """ 1150 Element Declarations Consistent check between two element particles. 1151 1152 Ref: https://www.w3.org/TR/xmlschema-1/#cos-element-consistent 1153 1154 :returns: `True` if there is no inconsistency between the particles, `False` otherwise, 1155 """ 1156 return self.name != other.name or self.type is other.type 1157 1158 def is_single(self) -> bool: 1159 if self.parent is None: 1160 return True 1161 elif self.max_occurs != 1: 1162 return False 1163 elif self.parent.max_occurs == 1: 1164 return True 1165 else: 1166 return self.parent.model != 'choice' and len(self.parent) > 1 1167 1168 def is_empty(self) -> bool: 1169 return self.fixed == '' or self.type.is_empty() 1170 1171 1172class Xsd11Element(XsdElement): 1173 """ 1174 Class for XSD 1.1 *element* declarations. 1175 1176 .. <element 1177 abstract = boolean : false 1178 block = (#all | List of (extension | restriction | substitution)) 1179 default = string 1180 final = (#all | List of (extension | restriction)) 1181 fixed = string 1182 form = (qualified | unqualified) 1183 id = ID 1184 maxOccurs = (nonNegativeInteger | unbounded) : 1 1185 minOccurs = nonNegativeInteger : 1 1186 name = NCName 1187 nillable = boolean : false 1188 ref = QName 1189 substitutionGroup = List of QName 1190 targetNamespace = anyURI 1191 type = QName 1192 {any attributes with non-schema namespace . . .}> 1193 Content: (annotation?, ((simpleType | complexType)?, alternative*, 1194 (unique | key | keyref)*)) 1195 </element> 1196 """ 1197 _target_namespace: Optional[str] = None 1198 1199 def _parse(self) -> None: 1200 if not self._build: 1201 return 1202 1203 self._parse_particle(self.elem) 1204 self._parse_attributes() 1205 1206 if self.ref is None: 1207 self._parse_type() 1208 self._parse_alternatives() 1209 self._parse_constraints() 1210 1211 if self.parent is None and 'substitutionGroup' in self.elem.attrib: 1212 for substitution_group in self.elem.attrib['substitutionGroup'].split(): 1213 self._parse_substitution_group(substitution_group) 1214 1215 self._parse_target_namespace() 1216 1217 if any(v.inheritable for v in self.attributes.values()): 1218 self.inheritable = {} 1219 for k, v in self.attributes.items(): 1220 if k is not None and isinstance(v, XsdAttribute): 1221 if v.inheritable: 1222 self.inheritable[k] = v 1223 1224 def _parse_alternatives(self) -> None: 1225 alternatives = [] 1226 has_test = True 1227 for child in self.elem: 1228 if child.tag == XSD_ALTERNATIVE: 1229 alternatives.append(XsdAlternative(child, self.schema, self)) 1230 if not has_test: 1231 self.parse_error("test attribute missing on non-final alternative") 1232 has_test = 'test' in child.attrib 1233 1234 if alternatives: 1235 self.alternatives = alternatives 1236 1237 @property 1238 def built(self) -> bool: 1239 return (self.type.parent is None or self.type.built) and \ 1240 all(c.built for c in self.identities.values()) and \ 1241 all(a.built for a in self.alternatives) 1242 1243 @property 1244 def target_namespace(self) -> str: 1245 if self._target_namespace is not None: 1246 return self._target_namespace 1247 elif self.ref is not None: 1248 return self.ref.target_namespace 1249 else: 1250 return self.schema.target_namespace 1251 1252 def iter_components(self, xsd_classes: ComponentClassType = None) -> Iterator[XsdComponent]: 1253 if xsd_classes is None: 1254 yield self 1255 yield from self.identities.values() 1256 else: 1257 if isinstance(self, xsd_classes): 1258 yield self 1259 1260 for obj in self.identities.values(): 1261 if isinstance(obj, xsd_classes): 1262 yield obj 1263 1264 for alt in self.alternatives: 1265 yield from alt.iter_components(xsd_classes) 1266 1267 if self.ref is None and self.type.parent is not None: 1268 yield from self.type.iter_components(xsd_classes) 1269 1270 def iter_substitutes(self) -> Iterator[XsdElement]: 1271 if self.parent is None or self.ref is not None: 1272 for xsd_element in self.maps.substitution_groups.get(self.name, ()): 1273 yield xsd_element 1274 yield from xsd_element.iter_substitutes() 1275 1276 def get_type(self, elem: Union[ElementType, ElementData], 1277 inherited: Optional[Dict[str, Any]] = None) -> BaseXsdType: 1278 if not self.alternatives: 1279 return self._head_type or self.type 1280 1281 if isinstance(elem, ElementData): 1282 if elem.attributes: 1283 attrib: Dict[str, str] = {} 1284 for k, v in elem.attributes.items(): 1285 value = raw_xml_encode(v) 1286 if value is not None: 1287 attrib[k] = value 1288 1289 elem = etree_element(elem.tag, attrib=attrib) 1290 else: 1291 elem = etree_element(elem.tag) 1292 1293 if inherited: 1294 dummy = etree_element('_dummy_element', attrib=inherited) 1295 dummy.attrib.update(elem.attrib) 1296 1297 for alt in self.alternatives: 1298 if alt.type is not None: 1299 if alt.token is None or alt.test(elem) or alt.test(dummy): 1300 return alt.type 1301 else: 1302 for alt in self.alternatives: 1303 if alt.type is not None: 1304 if alt.token is None or alt.test(elem): 1305 return alt.type 1306 1307 return self._head_type or self.type 1308 1309 def is_overlap(self, other: SchemaElementType) -> bool: 1310 if isinstance(other, XsdElement): 1311 if self.name == other.name: 1312 return True 1313 elif any(self.name == x.name for x in other.iter_substitutes()): 1314 return True 1315 1316 for e in self.iter_substitutes(): 1317 if other.name == e.name or any(x is e for x in other.iter_substitutes()): 1318 return True 1319 1320 elif isinstance(other, XsdAnyElement): 1321 if other.is_matching(self.name, self.default_namespace): 1322 return True 1323 for e in self.maps.substitution_groups.get(self.name, ()): 1324 if other.is_matching(e.name, self.default_namespace): 1325 return True 1326 return False 1327 1328 def is_consistent(self, other: SchemaElementType, strict: bool = True) -> bool: 1329 if isinstance(other, XsdAnyElement): 1330 if other.process_contents == 'skip': 1331 return True 1332 xsd_element = other.match(self.name, self.default_namespace, resolve=True) 1333 return xsd_element is None or self.is_consistent(xsd_element, strict=False) 1334 1335 e1: XsdElement = self 1336 e2 = other 1337 if self.name != other.name: 1338 for e1 in self.iter_substitutes(): 1339 if e1.name == other.name: 1340 break 1341 else: 1342 for e2 in other.iter_substitutes(): 1343 if e2.name == self.name: 1344 break 1345 else: 1346 return True 1347 1348 if len(e1.alternatives) != len(e2.alternatives): 1349 return False 1350 elif e1.type is not e2.type and strict: 1351 return False 1352 elif e1.type is not e2.type or \ 1353 not all(any(a == x for x in e2.alternatives) for a in e1.alternatives) or \ 1354 not all(any(a == x for x in e1.alternatives) for a in e2.alternatives): 1355 msg = "Maybe a not equivalent type table between elements %r and %r." % (e1, e2) 1356 warnings.warn(msg, XMLSchemaTypeTableWarning, stacklevel=3) 1357 return True 1358 1359 1360class XsdAlternative(XsdComponent): 1361 """ 1362 XSD 1.1 type *alternative* definitions. 1363 1364 .. <alternative 1365 id = ID 1366 test = an XPath expression 1367 type = QName 1368 xpathDefaultNamespace = (anyURI | (##defaultNamespace | ##targetNamespace | ##local)) 1369 {any attributes with non-schema namespace . . .}> 1370 Content: (annotation?, (simpleType | complexType)?) 1371 </alternative> 1372 """ 1373 parent: XsdElement 1374 type: BaseXsdType 1375 path: Optional[str] = None 1376 token: Optional[XPathToken] = None 1377 _ADMITTED_TAGS = {XSD_ALTERNATIVE} 1378 1379 def __init__(self, elem: ElementType, schema: SchemaType, parent: XsdElement) -> None: 1380 super(XsdAlternative, self).__init__(elem, schema, parent) 1381 1382 def __repr__(self) -> str: 1383 return '%s(type=%r, test=%r)' % ( 1384 self.__class__.__name__, self.elem.get('type'), self.elem.get('test') 1385 ) 1386 1387 def __eq__(self, other: object) -> bool: 1388 return isinstance(other, XsdAlternative) and \ 1389 self.path == other.path and self.type is other.type and \ 1390 self.xpath_default_namespace == other.xpath_default_namespace 1391 1392 def __ne__(self, other: object) -> bool: 1393 return not isinstance(other, XsdAlternative) or \ 1394 self.path != other.path or self.type is not other.type or \ 1395 self.xpath_default_namespace != other.xpath_default_namespace 1396 1397 def _parse(self) -> None: 1398 attrib = self.elem.attrib 1399 1400 if 'xpathDefaultNamespace' in attrib: 1401 self.xpath_default_namespace = self._parse_xpath_default_namespace(self.elem) 1402 else: 1403 self.xpath_default_namespace = self.schema.xpath_default_namespace 1404 parser = XPath2Parser( 1405 namespaces=self.namespaces, 1406 strict=False, 1407 default_namespace=self.xpath_default_namespace 1408 ) 1409 1410 try: 1411 self.path = attrib['test'] 1412 except KeyError: 1413 pass # an absent test is not an error, it should be the default type 1414 else: 1415 try: 1416 self.token = parser.parse(self.path) 1417 except ElementPathError as err: 1418 self.parse_error(err) 1419 self.token = parser.parse('false()') 1420 self.path = 'false()' 1421 1422 try: 1423 type_qname = self.schema.resolve_qname(attrib['type']) 1424 except (KeyError, ValueError, RuntimeError) as err: 1425 if 'type' in attrib: 1426 self.parse_error(err) 1427 self.type = self.any_type 1428 else: 1429 child = self._parse_child_component(self.elem, strict=False) 1430 if child is None or child.tag not in (XSD_COMPLEX_TYPE, XSD_SIMPLE_TYPE): 1431 self.parse_error("missing 'type' attribute") 1432 self.type = self.any_type 1433 elif child.tag == XSD_COMPLEX_TYPE: 1434 self.type = self.schema.xsd_complex_type_class(child, self.schema, self) 1435 else: 1436 self.type = self.schema.simple_type_factory(child, self.schema, self) 1437 1438 if not self.type.is_derived(self.parent.type): 1439 msg = "declared type is not derived from {!r}" 1440 self.parse_error(msg.format(self.parent.type)) 1441 else: 1442 try: 1443 self.type = self.maps.lookup_type(type_qname) 1444 except KeyError: 1445 self.parse_error("unknown type %r" % attrib['type']) 1446 self.type = self.any_type 1447 else: 1448 if self.type.name != XSD_ERROR and not self.type.is_derived(self.parent.type): 1449 msg = "type {!r} is not derived from {!r}" 1450 self.parse_error(msg.format(attrib['type'], self.parent.type)) 1451 1452 child = self._parse_child_component(self.elem, strict=False) 1453 if child is not None and child.tag in (XSD_COMPLEX_TYPE, XSD_SIMPLE_TYPE): 1454 self.parse_error("the attribute 'type' and the <%s> local declaration " 1455 "are mutually exclusive" % child.tag.split('}')[-1]) 1456 1457 @property 1458 def built(self) -> bool: 1459 if not hasattr(self, 'type'): 1460 return False 1461 return self.type.parent is None or self.type.built 1462 1463 @property 1464 def validation_attempted(self) -> str: 1465 if self.built: 1466 return 'full' 1467 elif not hasattr(self, 'type'): 1468 return 'none' 1469 else: 1470 return self.type.validation_attempted 1471 1472 def iter_components(self, xsd_classes: ComponentClassType = None) -> Iterator[XsdComponent]: 1473 if xsd_classes is None or isinstance(self, xsd_classes): 1474 yield self 1475 if self.type is not None and self.type.parent is not None: 1476 yield from self.type.iter_components(xsd_classes) 1477 1478 def test(self, elem: ElementType) -> bool: 1479 if self.token is None: 1480 return False 1481 1482 try: 1483 result = list(self.token.select(context=XPathContext(elem))) 1484 return self.token.boolean_value(result) 1485 except (TypeError, ValueError): 1486 return False 1487