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 functions and classes for namespaces XSD declarations/definitions. 12""" 13import warnings 14from collections import Counter 15from functools import lru_cache 16from typing import cast, Any, Callable, Dict, List, Iterable, Iterator, \ 17 MutableMapping, Optional, Set, Union, Tuple, Type 18 19from ..exceptions import XMLSchemaKeyError, XMLSchemaTypeError, XMLSchemaValueError, \ 20 XMLSchemaRuntimeError, XMLSchemaWarning 21from ..names import XSD_NAMESPACE, XSD_REDEFINE, XSD_OVERRIDE, XSD_NOTATION, \ 22 XSD_ANY_TYPE, XSD_SIMPLE_TYPE, XSD_COMPLEX_TYPE, XSD_GROUP, \ 23 XSD_ATTRIBUTE, XSD_ATTRIBUTE_GROUP, XSD_ELEMENT, XSI_TYPE 24from ..aliases import ComponentClassType, ElementType, SchemaType, BaseXsdType, \ 25 SchemaGlobalType 26from ..helpers import get_qname, local_name, get_extended_qname 27from ..namespaces import NamespaceResourcesMap 28from .exceptions import XMLSchemaNotBuiltError, XMLSchemaModelError, XMLSchemaModelDepthError, \ 29 XMLSchemaParseError 30from .xsdbase import XsdValidator, XsdComponent 31from .builtins import xsd_builtin_types_factory 32from . import XsdAttribute, XsdSimpleType, XsdComplexType, XsdElement, XsdAttributeGroup, \ 33 XsdGroup, XsdNotation, XsdIdentity, XsdAssert, XsdUnion, XsdAtomicRestriction 34 35 36# 37# Defines the load functions for XML Schema structures 38def create_load_function(tag: str) \ 39 -> Callable[[Dict[str, Any], Iterable[SchemaType]], None]: 40 41 def load_xsd_globals(xsd_globals: Dict[str, Any], 42 schemas: Iterable[SchemaType]) -> None: 43 redefinitions = [] 44 for schema in schemas: 45 target_namespace = schema.target_namespace 46 47 for elem in schema.root: 48 if elem.tag not in {XSD_REDEFINE, XSD_OVERRIDE}: 49 continue 50 51 location = elem.get('schemaLocation') 52 if location is None: 53 continue 54 for child in filter(lambda x: x.tag == tag and 'name' in x.attrib, elem): 55 qname = get_qname(target_namespace, child.attrib['name']) 56 redefinitions.append((qname, elem, child, schema, schema.includes[location])) 57 58 for elem in filter(lambda x: x.tag == tag and 'name' in x.attrib, schema.root): 59 qname = get_qname(target_namespace, elem.attrib['name']) 60 if qname not in xsd_globals: 61 xsd_globals[qname] = (elem, schema) 62 else: 63 try: 64 other_schema = xsd_globals[qname][1] 65 except (TypeError, IndexError): 66 pass 67 else: 68 # It's ignored or replaced in case of an override 69 if other_schema.override is schema: 70 continue 71 elif schema.override is other_schema: 72 xsd_globals[qname] = (elem, schema) 73 continue 74 75 msg = "global {} with name={!r} is already defined" 76 schema.parse_error(msg.format(local_name(tag), qname)) 77 78 redefined_names = Counter(x[0] for x in redefinitions) 79 for qname, elem, child, schema, redefined_schema in reversed(redefinitions): 80 81 # Checks multiple redefinitions 82 if redefined_names[qname] > 1: 83 redefined_names[qname] = 1 84 85 redefined_schemas: Any 86 redefined_schemas = [x[-1] for x in redefinitions if x[0] == qname] 87 if any(redefined_schemas.count(x) > 1 for x in redefined_schemas): 88 msg = "multiple redefinition for {} {!r}" 89 schema.parse_error(msg.format(local_name(child.tag), qname), child) 90 else: 91 redefined_schemas = {x[-1]: x[-2] for x in redefinitions if x[0] == qname} 92 for rs, s in redefined_schemas.items(): 93 while True: 94 try: 95 s = redefined_schemas[s] 96 except KeyError: 97 break 98 99 if s is rs: 100 msg = "circular redefinition for {} {!r}" 101 schema.parse_error(msg.format(local_name(child.tag), qname), child) 102 break 103 104 if elem.tag == XSD_OVERRIDE: 105 # Components which match nothing in the target schema are ignored. See the 106 # period starting with "Source declarations not present in the target set" 107 # of the paragraph https://www.w3.org/TR/xmlschema11-1/#override-schema. 108 if qname in xsd_globals: 109 xsd_globals[qname] = (child, schema) 110 else: 111 # Append to a list if it's a redefine 112 try: 113 xsd_globals[qname].append((child, schema)) 114 except KeyError: 115 schema.parse_error("not a redefinition!", child) 116 except AttributeError: 117 xsd_globals[qname] = [xsd_globals[qname], (child, schema)] 118 119 return load_xsd_globals 120 121 122load_xsd_simple_types = create_load_function(XSD_SIMPLE_TYPE) 123load_xsd_attributes = create_load_function(XSD_ATTRIBUTE) 124load_xsd_attribute_groups = create_load_function(XSD_ATTRIBUTE_GROUP) 125load_xsd_complex_types = create_load_function(XSD_COMPLEX_TYPE) 126load_xsd_elements = create_load_function(XSD_ELEMENT) 127load_xsd_groups = create_load_function(XSD_GROUP) 128load_xsd_notations = create_load_function(XSD_NOTATION) 129 130 131class XsdGlobals(XsdValidator): 132 """ 133 Mediator class for related XML schema instances. It stores the global 134 declarations defined in the registered schemas. Register a schema to 135 add its declarations to the global maps. 136 137 :param validator: the origin schema class/instance used for creating the global maps. 138 :param validation: the XSD validation mode to use, can be 'strict', 'lax' or 'skip'. 139 """ 140 types: Dict[str, Union[BaseXsdType, Tuple[ElementType, SchemaType]]] 141 attributes: Dict[str, Union[XsdAttribute, Tuple[ElementType, SchemaType]]] 142 attribute_groups: Dict[str, Union[XsdAttributeGroup, Tuple[ElementType, SchemaType]]] 143 groups: Dict[str, Union[XsdGroup, Tuple[ElementType, SchemaType]]] 144 notations: Dict[str, Union[XsdNotation, Tuple[ElementType, SchemaType]]] 145 elements: Dict[str, Union[XsdElement, Tuple[ElementType, SchemaType]]] 146 147 substitution_groups: Dict[str, Set[XsdElement]] 148 identities: Dict[str, XsdIdentity] 149 global_maps: Tuple[Dict[str, Any], ...] 150 151 missing_locations: List[str] 152 153 _lookup_function_resolver = { 154 XSD_SIMPLE_TYPE: 'lookup_type', 155 XSD_COMPLEX_TYPE: 'lookup_type', 156 XSD_ELEMENT: 'lookup_element', 157 XSD_GROUP: 'lookup_group', 158 XSD_ATTRIBUTE: 'lookup_attribute', 159 XSD_ATTRIBUTE_GROUP: 'lookup_attribute_group', 160 XSD_NOTATION: 'lookup_notation', 161 } 162 163 def __init__(self, validator: SchemaType, validation: str = 'strict') -> None: 164 super(XsdGlobals, self).__init__(validation) 165 166 self.validator = validator 167 self.namespaces = NamespaceResourcesMap() # Registered schemas by namespace URI 168 self.missing_locations = [] # Missing or failing resource locations 169 170 self.types = {} # Global types (both complex and simple) 171 self.attributes = {} # Global attributes 172 self.attribute_groups = {} # Attribute groups 173 self.groups = {} # Model groups 174 self.notations = {} # Notations 175 self.elements = {} # Global elements 176 self.substitution_groups = {} # Substitution groups 177 self.identities = {} # Identity constraints (uniqueness, keys, keyref) 178 179 self.global_maps = (self.notations, self.types, self.attributes, 180 self.attribute_groups, self.groups, self.elements) 181 182 self._builders: Dict[str, Callable[[ElementType, SchemaType], Any]] = { 183 XSD_NOTATION: validator.xsd_notation_class, 184 XSD_SIMPLE_TYPE: validator.simple_type_factory, 185 XSD_COMPLEX_TYPE: validator.xsd_complex_type_class, 186 XSD_ATTRIBUTE: validator.xsd_attribute_class, 187 XSD_ATTRIBUTE_GROUP: validator.xsd_attribute_group_class, 188 XSD_GROUP: validator.xsd_group_class, 189 XSD_ELEMENT: validator.xsd_element_class, 190 } 191 192 def __repr__(self) -> str: 193 return '%s(validator=%r, validation=%r)' % ( 194 self.__class__.__name__, self.validator, self.validation 195 ) 196 197 def copy(self, validator: Optional[SchemaType] = None, 198 validation: Optional[str] = None) -> 'XsdGlobals': 199 """Makes a copy of the object.""" 200 obj = self.__class__(self.validator if validator is None else validator, 201 validation or self.validation) 202 203 obj.namespaces.update(self.namespaces) 204 obj.types.update(self.types) 205 obj.attributes.update(self.attributes) 206 obj.attribute_groups.update(self.attribute_groups) 207 obj.groups.update(self.groups) 208 obj.notations.update(self.notations) 209 obj.elements.update(self.elements) 210 obj.substitution_groups.update(self.substitution_groups) 211 obj.identities.update(self.identities) 212 return obj 213 214 __copy__ = copy 215 216 def lookup(self, tag: str, qname: str) -> SchemaGlobalType: 217 """ 218 General lookup method for XSD global components. 219 220 :param tag: the expanded QName of the XSD the global declaration/definition \ 221 (eg. '{http://www.w3.org/2001/XMLSchema}element'), that is used to select \ 222 the global map for lookup. 223 :param qname: the expanded QName of the component to be looked-up. 224 :returns: an XSD global component. 225 :raises: an XMLSchemaValueError if the *tag* argument is not appropriate for a global \ 226 component, an XMLSchemaKeyError if the *qname* argument is not found in the global map. 227 """ 228 lookup_function: Callable[[str], SchemaGlobalType] 229 try: 230 lookup_function = getattr(self, self._lookup_function_resolver[tag]) 231 except KeyError: 232 msg = "wrong tag {!r} for an XSD global definition/declaration" 233 raise XMLSchemaValueError(msg.format(tag)) from None 234 else: 235 return lookup_function(qname) 236 237 def lookup_notation(self, qname: str) -> XsdNotation: 238 try: 239 obj = self.notations[qname] 240 except KeyError: 241 raise XMLSchemaKeyError(f'xs:notation {qname!r} not found') 242 else: 243 if isinstance(obj, XsdNotation): 244 return obj 245 return cast(XsdNotation, self._build_global(obj, qname, self.notations)) 246 247 def lookup_type(self, qname: str) -> BaseXsdType: 248 try: 249 obj = self.types[qname] 250 except KeyError: 251 raise XMLSchemaKeyError(f'global xs:simpleType/xs:complexType {qname!r} not found') 252 else: 253 if isinstance(obj, (XsdSimpleType, XsdComplexType)): 254 return obj 255 return cast(BaseXsdType, self._build_global(obj, qname, self.types)) 256 257 def lookup_attribute(self, qname: str) -> XsdAttribute: 258 try: 259 obj = self.attributes[qname] 260 except KeyError: 261 raise XMLSchemaKeyError(f'global xs:attribute {qname!r} not found') 262 else: 263 if isinstance(obj, XsdAttribute): 264 return obj 265 return cast(XsdAttribute, self._build_global(obj, qname, self.attributes)) 266 267 def lookup_attribute_group(self, qname: str) -> XsdAttributeGroup: 268 try: 269 obj = self.attribute_groups[qname] 270 except KeyError: 271 raise XMLSchemaKeyError(f'global xs:attributeGroup {qname!r} not found') 272 else: 273 if isinstance(obj, XsdAttributeGroup): 274 return obj 275 return cast(XsdAttributeGroup, self._build_global(obj, qname, self.attribute_groups)) 276 277 def lookup_group(self, qname: str) -> XsdGroup: 278 try: 279 obj = self.groups[qname] 280 except KeyError: 281 raise XMLSchemaKeyError(f'global xs:group {qname!r} not found') 282 else: 283 if isinstance(obj, XsdGroup): 284 return obj 285 return cast(XsdGroup, self._build_global(obj, qname, self.groups)) 286 287 def lookup_element(self, qname: str) -> XsdElement: 288 try: 289 obj = self.elements[qname] 290 except KeyError: 291 raise XMLSchemaKeyError(f'global xs:element {qname!r} not found') 292 else: 293 if isinstance(obj, XsdElement): 294 return obj 295 return cast(XsdElement, self._build_global(obj, qname, self.elements)) 296 297 def _build_global(self, obj: Any, qname: str, 298 global_map: Dict[str, Any]) -> Any: 299 factory_or_class: Callable[[ElementType, SchemaType], Any] 300 301 if isinstance(obj, tuple): 302 # Not built XSD global component without redefinitions 303 try: 304 elem, schema = obj 305 except ValueError: 306 return obj[0] # Circular build, simply return (elem, schema) couple 307 308 try: 309 factory_or_class = self._builders[elem.tag] 310 except KeyError: 311 raise XMLSchemaKeyError("wrong element %r for map %r." % (elem, global_map)) 312 313 global_map[qname] = obj, # Encapsulate into a tuple to catch circular builds 314 global_map[qname] = factory_or_class(elem, schema) 315 return global_map[qname] 316 317 elif isinstance(obj, list): 318 # Not built XSD global component with redefinitions 319 try: 320 elem, schema = obj[0] 321 except ValueError: 322 return obj[0][0] # Circular build, simply return (elem, schema) couple 323 324 try: 325 factory_or_class = self._builders[elem.tag] 326 except KeyError: 327 raise XMLSchemaKeyError("wrong element %r for map %r." % (elem, global_map)) 328 329 global_map[qname] = obj[0], # To catch circular builds 330 global_map[qname] = component = factory_or_class(elem, schema) 331 332 # Apply redefinitions (changing elem involve a re-parsing of the component) 333 for elem, schema in obj[1:]: 334 if component.schema.target_namespace != schema.target_namespace: 335 msg = "redefined schema {!r} has a different targetNamespace" 336 raise XMLSchemaValueError(msg.format(schema)) 337 338 component.redefine = component.copy() 339 component.redefine.parent = component 340 component.schema = schema 341 component.elem = elem 342 343 return global_map[qname] 344 345 else: 346 raise XMLSchemaTypeError(f"unexpected instance {obj} in global map") 347 348 def get_instance_type(self, type_name: str, base_type: BaseXsdType, 349 namespaces: MutableMapping[str, str]) -> BaseXsdType: 350 """ 351 Returns the instance XSI type from global maps, validating it with the reference base type. 352 353 :param type_name: the XSI type attribute value, a QName in prefixed format. 354 :param base_type: the XSD from which the instance type has to be derived. 355 :param namespaces: a mapping from prefixes to namespaces. 356 """ 357 if isinstance(base_type, XsdComplexType) and XSI_TYPE in base_type.attributes: 358 xsd_attribute = cast(XsdAttribute, base_type.attributes[XSI_TYPE]) 359 xsd_attribute.validate(type_name) 360 361 extended_name = get_extended_qname(type_name, namespaces) 362 xsi_type = self.lookup_type(extended_name) 363 if xsi_type.is_derived(base_type): 364 return xsi_type 365 elif isinstance(base_type, XsdSimpleType) and \ 366 base_type.is_union() and not base_type.facets: 367 # Can be valid only if the union doesn't have facets, see: 368 # https://www.w3.org/Bugs/Public/show_bug.cgi?id=4065 369 if isinstance(base_type, XsdAtomicRestriction) and \ 370 isinstance(base_type.primitive_type, XsdUnion): 371 if xsi_type in base_type.primitive_type.member_types: 372 return xsi_type 373 elif isinstance(base_type, XsdUnion): 374 if xsi_type in base_type.member_types: 375 return xsi_type 376 377 raise XMLSchemaTypeError("%r cannot substitute %r" % (xsi_type, base_type)) 378 379 @property 380 def built(self) -> bool: 381 return all(schema.built for schema in self.iter_schemas()) 382 383 @property 384 def unbuilt(self) -> List[Union[XsdComponent, SchemaType]]: 385 """Property that returns a list with unbuilt components.""" 386 return [c for s in self.iter_schemas() for c in s.iter_components() 387 if c is not s and not c.built] 388 389 @property 390 def validation_attempted(self) -> str: 391 if self.built: 392 return 'full' 393 elif any(schema.validation_attempted == 'partial' for schema in self.iter_schemas()): 394 return 'partial' 395 else: 396 return 'none' 397 398 @property 399 def validity(self) -> str: 400 if not self.namespaces: 401 return 'notKnown' 402 if all(schema.validity == 'valid' for schema in self.iter_schemas()): 403 return 'valid' 404 elif any(schema.validity == 'invalid' for schema in self.iter_schemas()): 405 return 'invalid' 406 else: 407 return 'notKnown' 408 409 @property 410 def xsd_version(self) -> str: 411 return self.validator.XSD_VERSION 412 413 @property 414 def all_errors(self) -> List[XMLSchemaParseError]: 415 errors = [] 416 for schema in self.iter_schemas(): 417 errors.extend(schema.all_errors) 418 return errors 419 420 def create_bindings(self, *bases: Type[Any], **attrs: Any) -> None: 421 """Creates data object bindings for the XSD elements of built schemas.""" 422 for xsd_element in self.iter_components(xsd_classes=XsdElement): 423 assert isinstance(xsd_element, XsdElement) 424 if xsd_element.target_namespace != XSD_NAMESPACE: 425 xsd_element.get_binding(*bases, replace_existing=True, **attrs) 426 427 def clear_bindings(self) -> None: 428 for xsd_element in self.iter_components(xsd_classes=XsdElement): 429 assert isinstance(xsd_element, XsdElement) 430 xsd_element.binding = None 431 432 def iter_components(self, xsd_classes: ComponentClassType = None) \ 433 -> Iterator[Union['XsdGlobals', XsdComponent]]: 434 """Creates an iterator for the XSD components of built schemas.""" 435 if xsd_classes is None or isinstance(self, xsd_classes): 436 yield self 437 for xsd_global in self.iter_globals(): 438 yield from xsd_global.iter_components(xsd_classes) 439 440 def iter_globals(self) -> Iterator[SchemaGlobalType]: 441 """Creates an iterator for the XSD global components of built schemas.""" 442 for global_map in self.global_maps: 443 yield from global_map.values() 444 445 def iter_schemas(self) -> Iterator[SchemaType]: 446 """Creates an iterator for the registered schemas.""" 447 for schemas in self.namespaces.values(): 448 yield from schemas 449 450 def register(self, schema: SchemaType) -> None: 451 """Registers an XMLSchema instance.""" 452 try: 453 ns_schemas = self.namespaces[schema.target_namespace] 454 except KeyError: 455 self.namespaces[schema.target_namespace] = [schema] 456 else: 457 if schema in ns_schemas: 458 return 459 elif schema.url is None: 460 # only by multi-source init or add_schema() by user initiative 461 ns_schemas.append(schema) 462 elif not any(schema.url == obj.url and schema.__class__ is obj.__class__ 463 for obj in ns_schemas): 464 ns_schemas.append(schema) 465 466 @lru_cache(maxsize=1000) 467 def load_namespace(self, namespace: str, build: bool = True) -> bool: 468 """ 469 Load namespace from available location hints. Returns `True` if the namespace 470 is already loaded or if the namespace can be loaded from one of the locations, 471 returns `False` otherwise. Failing locations are inserted into the missing 472 locations list. 473 474 :param namespace: the namespace to load. 475 :param build: if left with `True` value builds the maps after load. If the \ 476 build fails the resource URL is added to missing locations. 477 """ 478 namespace = namespace.strip() 479 if namespace in self.namespaces: 480 return True 481 elif self.validator.meta_schema is None: 482 return False # Do not load additional namespaces for meta-schema (XHTML) 483 484 # Try from schemas location hints: usually the namespaces related to these 485 # hints are already loaded during schema construction, but it's better to 486 # retry once if the initial load has failed. 487 for schema in self.iter_schemas(): 488 for url in schema.get_locations(namespace): 489 if url in self.missing_locations: 490 continue 491 492 try: 493 if schema.import_schema(namespace, url, schema.base_url) is not None: 494 if build: 495 self.build() 496 except (OSError, IOError): 497 pass 498 except XMLSchemaNotBuiltError: 499 self.clear(remove_schemas=True, only_unbuilt=True) 500 self.missing_locations.append(url) 501 else: 502 return True 503 504 # Try from library location hint, if there is any. 505 if namespace in self.validator.fallback_locations: 506 url = self.validator.fallback_locations[namespace] 507 if url not in self.missing_locations: 508 try: 509 if self.validator.import_schema(namespace, url) is not None: 510 if build: 511 self.build() 512 except (OSError, IOError): 513 return False 514 except XMLSchemaNotBuiltError: 515 self.clear(remove_schemas=True, only_unbuilt=True) 516 self.missing_locations.append(url) 517 else: 518 return True 519 520 return False 521 522 def clear(self, remove_schemas: bool = False, only_unbuilt: bool = False) -> None: 523 """ 524 Clears the instance maps and schemas. 525 526 :param remove_schemas: removes also the schema instances. 527 :param only_unbuilt: removes only not built objects/schemas. 528 """ 529 global_map: Dict[str, XsdComponent] 530 if only_unbuilt: 531 not_built_schemas = {s for s in self.iter_schemas() if not s.built} 532 if not not_built_schemas: 533 return 534 535 for global_map in self.global_maps: 536 for k in list(global_map.keys()): 537 obj = global_map[k] 538 if not isinstance(obj, XsdComponent) or obj.schema in not_built_schemas: 539 del global_map[k] 540 if k in self.substitution_groups: 541 del self.substitution_groups[k] 542 if k in self.identities: 543 del self.identities[k] 544 545 if remove_schemas: 546 namespaces = NamespaceResourcesMap() 547 for uri, value in self.namespaces.items(): 548 for schema in value: 549 if schema not in not_built_schemas: 550 namespaces[uri] = schema 551 self.namespaces = namespaces 552 553 else: 554 del self.missing_locations[:] 555 for global_map in self.global_maps: 556 global_map.clear() 557 self.substitution_groups.clear() 558 self.identities.clear() 559 560 if remove_schemas: 561 self.namespaces.clear() 562 563 def build(self) -> None: 564 """ 565 Build the maps of XSD global definitions/declarations. The global maps are 566 updated adding and building the globals of not built registered schemas. 567 """ 568 try: 569 meta_schema = self.namespaces[XSD_NAMESPACE][0] 570 except KeyError: 571 if self.validator.meta_schema is None: 572 msg = "missing XSD namespace in meta-schema instance {!r}" 573 raise XMLSchemaValueError(msg.format(self.validator)) 574 meta_schema = None 575 576 if meta_schema is None or meta_schema.meta_schema is not None: 577 # XSD namespace not imported or XSD namespace not managed by a meta-schema. 578 # Creates a new meta-schema instance from the XSD meta-schema source and 579 # replaces the default meta-schema instance in all registered schemas. 580 if self.validator.meta_schema is None: 581 msg = "missing default meta-schema instance {!r}" 582 raise XMLSchemaRuntimeError(msg.format(self.validator)) 583 584 url = self.validator.meta_schema.url 585 base_schemas = {k: v for k, v in self.validator.meta_schema.BASE_SCHEMAS.items() 586 if k not in self.namespaces} 587 meta_schema = self.validator.create_meta_schema(url, base_schemas, self) 588 589 for schema in self.iter_schemas(): 590 if schema.meta_schema is not None: 591 schema.meta_schema = meta_schema 592 else: 593 if not self.types and meta_schema.maps is not self: 594 for source_map, target_map in zip(meta_schema.maps.global_maps, self.global_maps): 595 target_map.update(source_map) 596 597 not_built_schemas = [schema for schema in self.iter_schemas() if not schema.built] 598 for schema in not_built_schemas: 599 schema._root_elements = None 600 601 # Load and build global declarations 602 load_xsd_simple_types(self.types, not_built_schemas) 603 load_xsd_complex_types(self.types, not_built_schemas) 604 load_xsd_notations(self.notations, not_built_schemas) 605 load_xsd_attributes(self.attributes, not_built_schemas) 606 load_xsd_attribute_groups(self.attribute_groups, not_built_schemas) 607 load_xsd_elements(self.elements, not_built_schemas) 608 load_xsd_groups(self.groups, not_built_schemas) 609 610 if not meta_schema.built: 611 xsd_builtin_types_factory(meta_schema, self.types) 612 613 if self is not meta_schema.maps: 614 # Rebuild xs:anyType for maps not owned by the meta-schema 615 # in order to do a correct namespace lookup for wildcards. 616 self.types[XSD_ANY_TYPE] = self.validator.create_any_type() 617 618 for qname in self.notations: 619 self.lookup_notation(qname) 620 for qname in self.attributes: 621 self.lookup_attribute(qname) 622 623 for qname in self.attribute_groups: 624 self.lookup_attribute_group(qname) 625 for schema in not_built_schemas: 626 if not isinstance(schema.default_attributes, str): 627 continue 628 629 try: 630 attributes = schema.maps.attribute_groups[schema.default_attributes] 631 except KeyError: 632 schema.default_attributes = None 633 msg = "defaultAttributes={!r} doesn't match an attribute group of {!r}" 634 schema.parse_error( 635 error=msg.format(schema.root.get('defaultAttributes'), schema), 636 elem=schema.root, 637 validation=schema.validation 638 ) 639 else: 640 schema.default_attributes = cast(XsdAttributeGroup, attributes) 641 642 for qname in self.types: 643 self.lookup_type(qname) 644 for qname in self.elements: 645 self.lookup_element(qname) 646 for qname in self.groups: 647 self.lookup_group(qname) 648 649 # Build element declarations inside model groups. 650 for schema in not_built_schemas: 651 for group in schema.iter_components(XsdGroup): 652 group.build() 653 654 # Build identity references and XSD 1.1 assertions 655 for schema in not_built_schemas: 656 for obj in schema.iter_components((XsdIdentity, XsdAssert)): 657 obj.build() 658 659 self.check(filter(lambda x: x.meta_schema is not None, not_built_schemas), self.validation) 660 661 def check(self, schemas: Optional[Iterable[SchemaType]] = None, 662 validation: str = 'strict') -> None: 663 """ 664 Checks the global maps. For default checks all schemas and raises an 665 exception at first error. 666 667 :param schemas: optional argument with the set of the schemas to check. 668 :param validation: overrides the default validation mode of the validator. 669 :raise: XMLSchemaParseError 670 """ 671 _schemas = set(schemas if schemas is not None else self.iter_schemas()) 672 673 # Checks substitution groups circularity 674 for qname in self.substitution_groups: 675 xsd_element = self.elements[qname] 676 assert isinstance(xsd_element, XsdElement), "global element not built!" 677 if any(e is xsd_element for e in xsd_element.iter_substitutes()): 678 msg = "circularity found for substitution group with head element %r" 679 xsd_element.parse_error(msg.format(xsd_element), validation=validation) 680 681 if validation == 'strict' and not self.built: 682 raise XMLSchemaNotBuiltError( 683 self, "global map has unbuilt components: %r" % self.unbuilt 684 ) 685 686 # Check redefined global groups restrictions 687 for group in self.groups.values(): 688 assert isinstance(group, XsdGroup), "global group not built!" 689 if group.schema not in _schemas or group.redefine is None: 690 continue 691 692 while group.redefine is not None: 693 if not any(isinstance(e, XsdGroup) and e.name == group.name for e in group) \ 694 and not group.is_restriction(group.redefine): 695 msg = "the redefined group is an illegal restriction of the original group" 696 group.parse_error(msg, validation=validation) 697 698 group = group.redefine 699 700 # Check complex content types models restrictions 701 for xsd_global in filter(lambda x: x.schema in _schemas, self.iter_globals()): 702 xsd_type: Any 703 for xsd_type in xsd_global.iter_components(XsdComplexType): 704 if not isinstance(xsd_type.content, XsdGroup): 705 continue 706 707 if xsd_type.derivation == 'restriction': 708 base_type = xsd_type.base_type 709 if base_type and base_type.name != XSD_ANY_TYPE and base_type.is_complex(): 710 if not xsd_type.content.is_restriction(base_type.content): 711 xsd_type.parse_error("the derived group is an illegal restriction " 712 "of the base type group", validation=validation) 713 714 if base_type.is_complex() and not base_type.open_content and \ 715 xsd_type.open_content and xsd_type.open_content.mode != 'none': 716 _group = xsd_type.schema.create_any_content_group( 717 parent=xsd_type, 718 any_element=xsd_type.open_content.any_element 719 ) 720 if not _group.is_restriction(base_type.content): 721 msg = "restriction has an open content but base type has not" 722 _group.parse_error(msg, validation=validation) 723 724 try: 725 xsd_type.content.check_model() 726 except XMLSchemaModelDepthError: 727 msg = "cannot verify the content model of {!r} " \ 728 "due to maximum recursion depth exceeded".format(xsd_type) 729 xsd_type.schema.warnings.append(msg) 730 warnings.warn(msg, XMLSchemaWarning, stacklevel=4) 731 except XMLSchemaModelError as err: 732 if validation == 'strict': 733 raise 734 xsd_type.errors.append(err) 735