Package xmlschema_acue :: Package validators :: Module globals_

Source Code for Module xmlschema_acue.validators.globals_

  1  # -*- coding: utf-8 -*- 
  2  # 
  3  # Copyright (c), 2016-2019, SISSA (International School for Advanced Studies). 
  4  # All rights reserved. 
  5  # This file is distributed under the terms of the MIT License. 
  6  # See the file 'LICENSE' in the root directory of the present 
  7  # distribution, or http://opensource.org/licenses/MIT. 
  8  # 
  9  # @author Davide Brunato <brunato@sissa.it> 
 10  # 
 11  """ 
 12  This module contains functions and classes for managing namespaces's   
 13  XSD declarations/definitions. 
 14  """ 
 15  from __future__ import unicode_literals 
 16  import re 
 17  import warnings 
 18  from collections import Counter 
 19   
 20  from xmlschema_acue.exceptions import XMLSchemaKeyError, XMLSchemaTypeError, XMLSchemaValueError, XMLSchemaWarning 
 21  from xmlschema_acue.namespaces import XSD_NAMESPACE 
 22  from xmlschema_acue.qnames import XSD_INCLUDE, XSD_IMPORT, XSD_REDEFINE, XSD_OVERRIDE, XSD_NOTATION, XSD_ANY_TYPE, \ 
 23      XSD_SIMPLE_TYPE, XSD_COMPLEX_TYPE, XSD_GROUP, XSD_ATTRIBUTE, XSD_ATTRIBUTE_GROUP, XSD_ELEMENT 
 24  from xmlschema_acue.helpers import get_qname, local_name 
 25  from xmlschema_acue.namespaces import NamespaceResourcesMap 
 26   
 27  from xmlschema_acue.validators.assertions import XsdAssert 
 28  from xmlschema_acue.validators.attributes import XsdAttribute, XsdAttributeGroup 
 29  from xmlschema_acue.validators.complex_types import XsdComplexType 
 30  from xmlschema_acue.validators.elements import XsdElement 
 31  from xmlschema_acue.validators.exceptions import XMLSchemaNotBuiltError, XMLSchemaModelError, XMLSchemaModelDepthError 
 32  from xmlschema_acue.validators.groups import XsdGroup 
 33  from xmlschema_acue.validators.identities import XsdKeyref  
 34  from xmlschema_acue.validators.notations import XsdNotation 
 35  from xmlschema_acue.validators.simple_types import XsdSimpleType 
 36  from xmlschema_acue.validators.xsdbase import XsdValidator, XsdComponent 
 37   
 38   
 39  from xmlschema_acue.validators.builtins import xsd_builtin_types_factory 
40 41 42 -def camel_case_split(s):
43 """ 44 Split words of a camel case string 45 """ 46 return re.findall(r'[A-Z]?[a-z]+|[A-Z]+(?=[A-Z]|$)', s)
47
48 49 -def iterchildren_by_tag(tag):
50 """ 51 Defines a generator that produce all child elements that have a specific tag. 52 """ 53 def iterfind_function(elem): 54 for e in elem: 55 if e.tag == tag: 56 yield e
57 iterfind_function.__name__ = str('iterfind_xsd_%ss' % '_'.join(camel_case_split(local_name(tag))).lower()) 58 return iterfind_function 59 60 61 iterchildren_xsd_import = iterchildren_by_tag(XSD_IMPORT) 62 iterchildren_xsd_include = iterchildren_by_tag(XSD_INCLUDE) 63 iterchildren_xsd_redefine = iterchildren_by_tag(XSD_REDEFINE) 64 iterchildren_xsd_override = iterchildren_by_tag(XSD_OVERRIDE)
65 66 67 # 68 # Defines the load functions for XML Schema structures 69 -def create_load_function(filter_function):
70 71 def load_xsd_globals(xsd_globals, schemas): 72 redefinitions = [] 73 for schema in schemas: 74 target_namespace = schema.target_namespace 75 for elem in iterchildren_xsd_redefine(schema.root): 76 location = elem.get('schemaLocation') 77 if location is None: 78 continue 79 for child in filter_function(elem): 80 qname = get_qname(target_namespace, child.attrib['name']) 81 redefinitions.append((qname, child, schema, schema.includes[location])) 82 83 for elem in filter_function(schema.root): 84 qname = get_qname(target_namespace, elem.attrib['name']) 85 try: 86 xsd_globals[qname].append((elem, schema)) 87 except KeyError: 88 xsd_globals[qname] = (elem, schema) 89 except AttributeError: 90 xsd_globals[qname] = [xsd_globals[qname], (elem, schema)] 91 92 tags = Counter([x[0] for x in redefinitions]) 93 for qname, elem, schema, redefined_schema in redefinitions: 94 95 # Checks multiple redefinitions 96 if tags[qname] > 1: 97 tags[qname] = 1 98 99 redefined_schemas = [x[3] for x in redefinitions if x[0] == qname] 100 if any(redefined_schemas.count(x) > 1 for x in redefined_schemas): 101 schema.parse_error( 102 "multiple redefinition for {} {!r}".format(local_name(elem.tag), qname), elem 103 ) 104 else: 105 redefined_schemas = {x[3]: x[2] for x in redefinitions if x[0] == qname} 106 for rs, s in redefined_schemas.items(): 107 while True: 108 try: 109 s = redefined_schemas[s] 110 except KeyError: 111 break 112 113 if s is rs: 114 schema.parse_error( 115 "circular redefinition for {} {!r}".format(local_name(elem.tag), qname), elem 116 ) 117 break 118 119 # Append redefinition 120 try: 121 xsd_globals[qname].append((elem, schema)) 122 except KeyError: 123 schema.parse_error("not a redefinition!", elem) 124 # xsd_globals[qname] = elem, schema 125 except AttributeError: 126 xsd_globals[qname] = [xsd_globals[qname], (elem, schema)]
127 128 return load_xsd_globals 129 130 131 load_xsd_simple_types = create_load_function(iterchildren_by_tag(XSD_SIMPLE_TYPE)) 132 load_xsd_attributes = create_load_function(iterchildren_by_tag(XSD_ATTRIBUTE)) 133 load_xsd_attribute_groups = create_load_function(iterchildren_by_tag(XSD_ATTRIBUTE_GROUP)) 134 load_xsd_complex_types = create_load_function(iterchildren_by_tag(XSD_COMPLEX_TYPE)) 135 load_xsd_elements = create_load_function(iterchildren_by_tag(XSD_ELEMENT)) 136 load_xsd_groups = create_load_function(iterchildren_by_tag(XSD_GROUP)) 137 load_xsd_notations = create_load_function(iterchildren_by_tag(XSD_NOTATION))
138 139 140 -def create_lookup_function(xsd_classes):
141 if isinstance(xsd_classes, tuple): 142 types_desc = ' or '.join([c.__name__ for c in xsd_classes]) 143 else: 144 types_desc = xsd_classes.__name__ 145 146 def lookup(global_map, qname, tag_map): 147 try: 148 obj = global_map[qname] 149 except KeyError: 150 if '{' in qname: 151 raise XMLSchemaKeyError("missing a %s component for %r!" % (types_desc, qname)) 152 raise XMLSchemaKeyError("missing a %s component for %r! As the name has no namespace " 153 "maybe a missing default namespace declaration." % (types_desc, qname)) 154 else: 155 if isinstance(obj, xsd_classes): 156 return obj 157 158 elif isinstance(obj, tuple): 159 # Not built XSD global component without redefinitions 160 try: 161 elem, schema = obj 162 except ValueError: 163 return obj[0] # Circular build, simply return (elem, schema) couple 164 165 try: 166 factory_or_class = tag_map[elem.tag] 167 except KeyError: 168 raise XMLSchemaKeyError("wrong element %r for map %r." % (elem, global_map)) 169 170 global_map[qname] = obj, # Encapsulate into a single-item tuple to catch circular builds 171 global_map[qname] = factory_or_class(elem, schema, parent=None) 172 return global_map[qname] 173 174 elif isinstance(obj, list): 175 # Not built XSD global component with redefinitions 176 try: 177 elem, schema = obj[0] 178 except ValueError: 179 return obj[0][0] # Circular build, simply return (elem, schema) couple 180 181 try: 182 factory_or_class = tag_map[elem.tag] 183 except KeyError: 184 raise XMLSchemaKeyError("wrong element %r for map %r." % (elem, global_map)) 185 186 global_map[qname] = obj[0], # To catch circular builds 187 global_map[qname] = component = factory_or_class(elem, schema, parent=None) 188 189 # Apply redefinitions (changing elem involve a re-parsing of the component) 190 for elem, schema in obj[1:]: 191 component.redefine = component.copy() 192 component.schema = schema 193 component.elem = elem 194 195 return global_map[qname] 196 197 else: 198 raise XMLSchemaTypeError( 199 "wrong instance %s for XSD global %r, a %s required." % (obj, qname, types_desc) 200 )
201 202 return lookup 203 204 205 lookup_notation = create_lookup_function(XsdNotation) 206 lookup_type = create_lookup_function((XsdSimpleType, XsdComplexType)) 207 lookup_attribute = create_lookup_function(XsdAttribute) 208 lookup_attribute_group = create_lookup_function(XsdAttributeGroup) 209 lookup_group = create_lookup_function(XsdGroup) 210 lookup_element = create_lookup_function(XsdElement)
211 212 213 -class XsdGlobals(XsdValidator):
214 """ 215 Mediator class for related XML schema instances. It stores the global 216 declarations defined in the registered schemas. Register a schema to 217 add it's declarations to the global maps. 218 219 :param validator: the origin schema class/instance used for creating the global maps. 220 :param validation: the XSD validation mode to use, can be 'strict', 'lax' or 'skip'. 221 """
222 - def __init__(self, validator, validation='strict'):
223 super(XsdGlobals, self).__init__(validation) 224 if not all(hasattr(validator, a) for a in ('meta_schema', 'BUILDERS_MAP')): 225 raise XMLSchemaValueError("The argument {!r} is not an XSD schema validator".format(validator)) 226 227 self.validator = validator 228 self.namespaces = NamespaceResourcesMap() # Registered schemas by namespace URI 229 230 self.types = {} # Global types (both complex and simple) 231 self.attributes = {} # Global attributes 232 self.attribute_groups = {} # Attribute groups 233 self.groups = {} # Model groups 234 self.notations = {} # Notations 235 self.elements = {} # Global elements 236 self.substitution_groups = {} # Substitution groups 237 self.constraints = {} # Constraints (uniqueness, keys, keyref) 238 239 self.global_maps = (self.notations, self.types, self.attributes, 240 self.attribute_groups, self.groups, self.elements)
241
242 - def __repr__(self):
243 return '%s(validator=%r, validation=%r)' % (self.__class__.__name__, self.validator, self.validation)
244
245 - def copy(self, validator=None, validation=None):
246 """Makes a copy of the object.""" 247 obj = XsdGlobals(self.validator if validator is None else validator, validation or self.validation) 248 obj.namespaces.update(self.namespaces) 249 obj.types.update(self.types) 250 obj.attributes.update(self.attributes) 251 obj.attribute_groups.update(self.attribute_groups) 252 obj.groups.update(self.groups) 253 obj.notations.update(self.notations) 254 obj.elements.update(self.elements) 255 obj.substitution_groups.update(self.substitution_groups) 256 obj.constraints.update(self.constraints) 257 return obj
258 259 __copy__ = copy 260
261 - def lookup_notation(self, qname):
262 return lookup_notation(self.notations, qname, self.validator.BUILDERS_MAP)
263
264 - def lookup_type(self, qname):
265 return lookup_type(self.types, qname, self.validator.BUILDERS_MAP)
266
267 - def lookup_attribute(self, qname):
268 return lookup_attribute(self.attributes, qname, self.validator.BUILDERS_MAP)
269
270 - def lookup_attribute_group(self, qname):
271 return lookup_attribute_group(self.attribute_groups, qname, self.validator.BUILDERS_MAP)
272
273 - def lookup_group(self, qname):
274 return lookup_group(self.groups, qname, self.validator.BUILDERS_MAP)
275
276 - def lookup_element(self, qname):
277 return lookup_element(self.elements, qname, self.validator.BUILDERS_MAP)
278
279 - def lookup(self, tag, qname):
280 if tag in (XSD_SIMPLE_TYPE, XSD_COMPLEX_TYPE): 281 return self.lookup_type(qname) 282 elif tag == XSD_ELEMENT: 283 return self.lookup_element(qname) 284 elif tag == XSD_GROUP: 285 return self.lookup_group(qname) 286 elif tag == XSD_ATTRIBUTE: 287 return self.lookup_attribute(qname) 288 elif tag == XSD_ATTRIBUTE_GROUP: 289 return self.lookup_attribute_group(qname) 290 elif tag == XSD_NOTATION: 291 return self.lookup_notation(qname) 292 else: 293 raise XMLSchemaValueError("wrong tag {!r} for an XSD global definition/declaration".format(tag))
294 295 @property
296 - def built(self):
297 for schema in self.iter_schemas(): 298 if not schema.built: 299 return False 300 return True
301 302 @property
303 - def validation_attempted(self):
304 if self.built: 305 return 'full' 306 elif any([schema.validation_attempted == 'partial' for schema in self.iter_schemas()]): 307 return 'partial' 308 else: 309 return 'none'
310 311 @property
312 - def validity(self):
313 if not self.namespaces: 314 return False 315 if all(schema.validity == 'valid' for schema in self.iter_schemas()): 316 return 'valid' 317 elif any(schema.validity == 'invalid' for schema in self.iter_schemas()): 318 return 'invalid' 319 else: 320 return 'notKnown'
321 322 @property
323 - def resources(self):
324 return [(schema.url, schema) for schemas in self.namespaces.values() for schema in schemas]
325 326 @property
327 - def all_errors(self):
328 errors = [] 329 for schema in self.iter_schemas(): 330 errors.extend(schema.all_errors) 331 return errors
332
333 - def iter_components(self, xsd_classes=None):
334 if xsd_classes is None or isinstance(self, xsd_classes): 335 yield self 336 for xsd_global in self.iter_globals(): 337 for obj in xsd_global.iter_components(xsd_classes): 338 yield obj
339
340 - def iter_schemas(self):
341 """Creates an iterator for the schemas registered in the instance.""" 342 for ns_schemas in self.namespaces.values(): 343 for schema in ns_schemas: 344 yield schema
345
346 - def iter_globals(self):
347 """ 348 Creates an iterator for XSD global definitions/declarations. 349 """ 350 for global_map in self.global_maps: 351 for obj in global_map.values(): 352 yield obj
353
354 - def register(self, schema):
355 """ 356 Registers an XMLSchema instance. 357 """ 358 try: 359 ns_schemas = self.namespaces[schema.target_namespace] 360 except KeyError: 361 self.namespaces[schema.target_namespace] = [schema] 362 else: 363 if schema in ns_schemas: 364 return 365 elif not any([schema.url == obj.url and schema.__class__ == obj.__class__ for obj in ns_schemas]): 366 ns_schemas.append(schema)
367
368 - def clear(self, remove_schemas=False, only_unbuilt=False):
369 """ 370 Clears the instance maps and schemas. 371 372 :param remove_schemas: removes also the schema instances. 373 :param only_unbuilt: removes only not built objects/schemas. 374 """ 375 if only_unbuilt: 376 not_built_schemas = {schema for schema in self.iter_schemas() if not schema.built} 377 if not not_built_schemas: 378 return 379 380 for global_map in self.global_maps: 381 for k in list(global_map.keys()): 382 obj = global_map[k] 383 if not isinstance(obj, XsdComponent) or obj.schema in not_built_schemas: 384 del global_map[k] 385 if k in self.substitution_groups: 386 del self.substitution_groups[k] 387 if k in self.constraints: 388 del self.constraints[k] 389 390 if remove_schemas: 391 namespaces = NamespaceResourcesMap() 392 for uri, value in self.namespaces.items(): 393 for schema in value: 394 if schema not in not_built_schemas: 395 namespaces[uri] = schema 396 self.namespaces = namespaces 397 398 else: 399 for global_map in self.global_maps: 400 global_map.clear() 401 self.substitution_groups.clear() 402 self.constraints.clear() 403 404 if remove_schemas: 405 self.namespaces.clear()
406
407 - def build(self):
408 """ 409 Build the maps of XSD global definitions/declarations. The global maps are 410 updated adding and building the globals of not built registered schemas. 411 """ 412 try: 413 meta_schema = self.namespaces[XSD_NAMESPACE][0] 414 except KeyError: 415 # Meta-schemas are not registered. If any of base namespaces is already registered 416 # create a new meta-schema, otherwise register the meta-schemas. 417 meta_schema = self.validator.meta_schema 418 if meta_schema is None: 419 raise XMLSchemaValueError("{!r} has not a meta-schema".format(self.validator)) 420 421 if any(ns in self.namespaces for ns in meta_schema.BASE_SCHEMAS): 422 base_schemas = {k: v for k, v in meta_schema.BASE_SCHEMAS.items() if k not in self.namespaces} 423 meta_schema = self.validator.create_meta_schema(meta_schema.url, base_schemas, self) 424 for schema in self.iter_schemas(): 425 if schema.meta_schema is not None: 426 schema.meta_schema = meta_schema 427 else: 428 for schema in meta_schema.maps.iter_schemas(): 429 self.register(schema) 430 431 self.types.update(meta_schema.maps.types) 432 self.attributes.update(meta_schema.maps.attributes) 433 self.attribute_groups.update(meta_schema.maps.attribute_groups) 434 self.groups.update(meta_schema.maps.groups) 435 self.notations.update(meta_schema.maps.notations) 436 self.elements.update(meta_schema.maps.elements) 437 self.substitution_groups.update(meta_schema.maps.substitution_groups) 438 self.constraints.update(meta_schema.maps.constraints) 439 440 not_built_schemas = [schema for schema in self.iter_schemas() if not schema.built] 441 for schema in not_built_schemas: 442 schema._root_elements = None 443 444 # Load and build global declarations 445 load_xsd_notations(self.notations, not_built_schemas) 446 load_xsd_simple_types(self.types, not_built_schemas) 447 load_xsd_attributes(self.attributes, not_built_schemas) 448 load_xsd_attribute_groups(self.attribute_groups, not_built_schemas) 449 load_xsd_complex_types(self.types, not_built_schemas) 450 load_xsd_elements(self.elements, not_built_schemas) 451 load_xsd_groups(self.groups, not_built_schemas) 452 453 if not meta_schema.built: 454 xsd_builtin_types_factory(meta_schema, self.types) 455 456 for qname in self.notations: 457 self.lookup_notation(qname) 458 for qname in self.attributes: 459 self.lookup_attribute(qname) 460 for qname in self.attribute_groups: 461 self.lookup_attribute_group(qname) 462 for qname in self.types: 463 self.lookup_type(qname) 464 for qname in self.elements: 465 self.lookup_element(qname) 466 for qname in self.groups: 467 self.lookup_group(qname) 468 469 # Builds element declarations inside model groups. 470 for schema in not_built_schemas: 471 for group in schema.iter_components(XsdGroup): 472 group.build() 473 474 for schema in filter(lambda x: x.meta_schema is not None, not_built_schemas): 475 # Build key references and assertions (XSD meta-schema doesn't have any of them) 476 for constraint in schema.iter_components(XsdKeyref): 477 constraint.parse_refer() 478 for assertion in schema.iter_components(XsdAssert): 479 assertion.parse() 480 self._check_schema(schema) 481 482 if self.validation == 'strict' and not self.built: 483 raise XMLSchemaNotBuiltError(self, "global map %r not built!" % self)
484
485 - def _check_schema(self, schema):
486 # Checks substitution groups circularities 487 for qname in self.substitution_groups: 488 xsd_element = self.elements[qname] 489 for e in xsd_element.iter_substitutes(): 490 if e is xsd_element: 491 schema.parse_error("circularity found for substitution group with head element %r" % xsd_element) 492 493 if schema.XSD_VERSION > '1.0' and schema.default_attributes is not None: 494 if not isinstance(schema.default_attributes, XsdAttributeGroup): 495 schema.default_attributes = None 496 schema.parse_error("defaultAttributes={!r} doesn't match an attribute group of {!r}" 497 .format(schema.root.get('defaultAttributes'), schema), schema.root) 498 499 if schema.validation == 'skip': 500 return 501 502 # Check redefined global groups 503 for group in filter(lambda x: x.schema is schema and x.redefine is not None, self.groups.values()): 504 if not any(isinstance(e, XsdGroup) and e.name == group.name for e in group) \ 505 and not group.is_restriction(group.redefine): 506 group.parse_error("The redefined group is an illegal restriction of the original group.") 507 508 # Check complex content types models 509 for xsd_type in schema.iter_components(XsdComplexType): 510 if not isinstance(xsd_type.content_type, XsdGroup): 511 continue 512 513 base_type = xsd_type.base_type 514 if xsd_type.derivation == 'restriction': 515 if base_type and base_type.name != XSD_ANY_TYPE and base_type.is_complex(): 516 if not xsd_type.content_type.is_restriction(base_type.content_type): 517 xsd_type.parse_error("The derived group is an illegal restriction of the base type group.") 518 519 try: 520 xsd_type.content_type.check_model() 521 except XMLSchemaModelDepthError: 522 msg = "cannot verify the content model of %r due to maximum recursion depth exceeded" % xsd_type 523 schema.warnings.append(msg) 524 warnings.warn(msg, XMLSchemaWarning, stacklevel=4) 525 except XMLSchemaModelError as err: 526 if self.validation == 'strict': 527 raise 528 xsd_type.errors.append(err)
529