Package xmlschema_acue ::
Package validators ::
Module schema
1
2
3
4
5
6
7
8
9
10
11 """
12 This module contains XMLSchema classes creator for xmlschema_acue package.
13
14 Two schema classes are created at the end of this module, XMLSchema10 for XSD 1.0 and
15 XMLSchema11 for XSD 1.1. The latter class parses also XSD 1.0 schemas, as prescribed by
16 the standard.
17
18 Those are the differences between XSD 1.0 and XSD 1.1 and their current development status:
19
20 * All model extended for content groups
21 * Assertions for simple types
22 * Default attributes for complex types
23 * Alternative type for elements
24 * Inheritable attributes
25 * targetNamespace for restricted element and attributes
26 * Assert for complex types
27 * TODO: OpenContent and XSD 1.1 wildcards for complex types
28 * schema overrides
29 """
30 import os
31 from collections import namedtuple, Counter
32 from abc import ABCMeta
33 import warnings
34 import elementpath
35
36 from xmlschema_acue.compat import add_metaclass
37 from xmlschema_acue.exceptions import XMLSchemaTypeError, XMLSchemaURLError, XMLSchemaValueError, XMLSchemaOSError
38 from xmlschema_acue.qnames import XSD_SCHEMA, XSD_ANNOTATION, XSD_NOTATION, XSD_ATTRIBUTE, XSD_ATTRIBUTE_GROUP, \
39 XSD_GROUP, XSD_SIMPLE_TYPE, XSD_COMPLEX_TYPE, XSD_ELEMENT, XSD_SEQUENCE, XSD_ANY, \
40 XSD_ANY_ATTRIBUTE, XSD_REDEFINE, XSD_OVERRIDE
41 from xmlschema_acue.helpers import has_xsd_components, get_xsd_derivation_attribute, get_xsd_form_attribute
42 from xmlschema_acue.namespaces import XSD_NAMESPACE, XML_NAMESPACE, XSI_NAMESPACE, XHTML_NAMESPACE, \
43 XLINK_NAMESPACE, NamespaceResourcesMap, NamespaceView
44 from xmlschema_acue.etree import etree_element, etree_tostring, ParseError
45 from xmlschema_acue.resources import is_remote_url, url_path_is_file, fetch_resource, XMLResource
46 from xmlschema_acue.converters import XMLSchemaConverter
47 from xmlschema_acue.xpath import ElementPathMixin
48
49 from xmlschema_acue.validators.exceptions import XMLSchemaParseError, XMLSchemaValidationError, XMLSchemaEncodeError, \
50 XMLSchemaNotBuiltError, XMLSchemaIncludeWarning, XMLSchemaImportWarning
51 from xmlschema_acue.validators.xsdbase import XsdValidator, ValidationMixin, XsdComponent
52 from xmlschema_acue.validators.notations import XsdNotation
53 from xmlschema_acue.validators.simple_types import xsd_simple_type_factory, XsdUnion, XsdAtomicRestriction, \
54 Xsd11AtomicRestriction, Xsd11Union
55 from xmlschema_acue.validators.attributes import XsdAttribute, XsdAttributeGroup, Xsd11Attribute
56 from xmlschema_acue.validators.complex_types import XsdComplexType, Xsd11ComplexType
57 from xmlschema_acue.validators.groups import XsdGroup, Xsd11Group
58 from xmlschema_acue.validators.elements import XsdElement, Xsd11Element
59 from xmlschema_acue.validators.wildcards import XsdAnyElement, XsdAnyAttribute, Xsd11AnyElement, Xsd11AnyAttribute
60 from xmlschema_acue.validators.globals_ import iterchildren_xsd_import, iterchildren_xsd_include, \
61 iterchildren_xsd_redefine, iterchildren_xsd_override, XsdGlobals
62
63
64
65 ATTRIBUTE_GROUP_ELEMENT = etree_element(XSD_ATTRIBUTE_GROUP)
66 ANY_ATTRIBUTE_ELEMENT = etree_element(
67 XSD_ANY_ATTRIBUTE, attrib={'namespace': '##any', 'processContents': 'lax'}
68 )
69 SEQUENCE_ELEMENT = etree_element(XSD_SEQUENCE)
70 ANY_ELEMENT = etree_element(
71 XSD_ANY,
72 attrib={
73 'namespace': '##any',
74 'processContents': 'lax',
75 'minOccurs': '0',
76 'maxOccurs': 'unbounded'
77 })
78
79 SCHEMAS_DIR = os.path.join(os.path.dirname(__file__), 'schemas/')
80 XML_SCHEMA_FILE = os.path.join(SCHEMAS_DIR, 'xml_minimal.xsd')
81 HFP_SCHEMA_FILE = os.path.join(SCHEMAS_DIR, 'XMLSchema-hasFacetAndProperty_minimal.xsd')
82 XSI_SCHEMA_FILE = os.path.join(SCHEMAS_DIR, 'XMLSchema-instance_minimal.xsd')
83 XLINK_SCHEMA_FILE = os.path.join(SCHEMAS_DIR, 'xlink.xsd')
135
138
142 """
143 Base class for an XML Schema instance.
144
145 :param source: an URI that reference to a resource or a file path or a file-like \
146 object or a string containing the schema or an Element or an ElementTree document.
147 :type source: Element or ElementTree or str or file-like object
148 :param namespace: is an optional argument that contains the URI of the namespace. \
149 When specified it must be equal to the *targetNamespace* declared in the schema.
150 :type namespace: str or None
151 :param validation: defines the XSD validation mode to use for build the schema, \
152 it's value can be 'strict', 'lax' or 'skip'.
153 :type validation: str
154 :param global_maps: is an optional argument containing an :class:`XsdGlobals` \
155 instance, a mediator object for sharing declaration data between dependents \
156 schema instances.
157 :type global_maps: XsdGlobals or None
158 :param converter: is an optional argument that can be an :class:`XMLSchemaConverter` \
159 subclass or instance, used for defining the default XML data converter for XML Schema instance.
160 :type converter: XMLSchemaConverter or None
161 :param locations: schema location hints for namespace imports. Can be a dictionary or \
162 a sequence of couples (namespace URI, resource URL).
163 :type locations: dict or list or None
164 :param base_url: is an optional base URL, used for the normalization of relative paths \
165 when the URL of the schema resource can't be obtained from the source argument.
166 :type base_url: str or None
167 :param defuse: defines when to defuse XML data. Can be 'always', 'remote' or 'never'. \
168 For default defuse only remote XML data.
169 :type defuse: str or None
170 :param timeout: the timeout in seconds for fetching resources. Default is `300`.
171 :type timeout: int
172 :param build: defines whether build the schema maps. Default is `True`.
173 :type build: bool
174 :param use_meta: if `True` the schema processor uses the package meta-schema, otherwise the \
175 meta-schema is added at the end. In the latter case the meta-schema is rebuilt if any base \
176 namespace has been overridden by an import. Ignored if the argument *global_maps* is provided.
177 :type use_meta: bool
178
179 :cvar XSD_VERSION: store the XSD version (1.0 or 1.1).
180 :vartype XSD_VERSION: str
181 :cvar BUILDERS: a namedtuple with attributes related to schema components classes. \
182 Used for build local components within parsing methods.
183 :vartype BUILDERS: namedtuple
184 :cvar BUILDERS_MAP: a dictionary that maps from tag to class for XSD global components. \
185 Used for build global components within lookup functions.
186 :vartype BUILDERS_MAP: dict
187 :cvar BASE_SCHEMAS: a dictionary from namespace to schema resource for meta-schema bases.
188 :vartype BASE_SCHEMAS: dict
189 :cvar meta_schema: the XSD meta-schema instance.
190 :vartype meta_schema: XMLSchema
191 :cvar attribute_form_default: the schema's *attributeFormDefault* attribute, defaults to 'unqualified'.
192 :vartype attribute_form_default: str
193 :cvar element_form_default: the schema's *elementFormDefault* attribute, defaults to 'unqualified'
194 :vartype element_form_default: str
195 :cvar block_default: the schema's *blockDefault* attribute, defaults to ''.
196 :vartype block_default: str
197 :cvar final_default: the schema's *finalDefault* attribute, defaults to ''.
198 :vartype final_default: str
199 :cvar default_attributes: the XSD 1.1 schema's *defaultAttributes* attribute, defaults to ``None``.
200 :vartype default_attributes: XsdAttributeGroup
201
202 :ivar target_namespace: is the *targetNamespace* of the schema, the namespace to which \
203 belong the declarations/definitions of the schema. If it's empty no namespace is associated \
204 with the schema. In this case the schema declarations can be reused from other namespaces as \
205 *chameleon* definitions.
206 :vartype target_namespace: str
207 :ivar validation: validation mode, can be 'strict', 'lax' or 'skip'.
208 :vartype validation: str
209 :ivar maps: XSD global declarations/definitions maps. This is an instance of :class:`XsdGlobal`, \
210 that store the global_maps argument or a new object when this argument is not provided.
211 :vartype maps: XsdGlobals
212 :ivar converter: the default converter used for XML data decoding/encoding.
213 :vartype converter: XMLSchemaConverter
214 :ivar locations: schema location hints.
215 :vartype locations: NamespaceResourcesMap
216 :ivar namespaces: a dictionary that maps from the prefixes used by the schema into namespace URI.
217 :vartype namespaces: dict
218 :ivar imports: a dictionary of namespace imports of the schema, that maps namespace URI to imported schema \
219 object, or `None` in case of unsuccessful import.
220 :vartype imports: dict
221 :ivar includes: a dictionary of included schemas, that maps a schema location to an included schema. \
222 It also comprehend schemas included by "xs:redefine" or "xs:override" statements.
223 :vartype warnings: dict
224 :ivar warnings: warning messages about failure of import and include elements.
225 :vartype warnings: list
226
227 :ivar notations: `xsd:notation` declarations.
228 :vartype notations: NamespaceView
229 :ivar types: `xsd:simpleType` and `xsd:complexType` global declarations.
230 :vartype types: NamespaceView
231 :ivar attributes: `xsd:attribute` global declarations.
232 :vartype attributes: NamespaceView
233 :ivar attribute_groups: `xsd:attributeGroup` definitions.
234 :vartype attribute_groups: NamespaceView
235 :ivar groups: `xsd:group` global definitions.
236 :vartype groups: NamespaceView
237 :ivar elements: `xsd:element` global declarations.
238 :vartype elements: NamespaceView
239 """
240 XSD_VERSION = None
241 BUILDERS = None
242 BUILDERS_MAP = None
243 BASE_SCHEMAS = None
244 meta_schema = None
245
246
247 target_namespace = ''
248 attribute_form_default = 'unqualified'
249 element_form_default = 'unqualified'
250 block_default = ''
251 final_default = ''
252 default_attributes = None
253
254 - def __init__(self, source, namespace=None, validation='strict', global_maps=None, converter=None,
255 locations=None, base_url=None, defuse='remote', timeout=300, build=True, use_meta=True):
256 super(XMLSchemaBase, self).__init__(validation)
257 self.source = XMLResource(source, base_url, defuse, timeout, lazy=False)
258 self.imports = {}
259 self.includes = {}
260 self.warnings = []
261 self._root_elements = None
262 root = self.source.root
263
264
265 self.namespaces = {'xml': XML_NAMESPACE}
266 self.namespaces.update(self.source.get_namespaces())
267
268 try:
269 self.target_namespace = root.attrib['targetNamespace']
270 except KeyError:
271 pass
272 else:
273 if self.target_namespace == '':
274
275 self.parse_error("The attribute 'targetNamespace' cannot be an empty string.", root)
276
277 if namespace is not None and self.target_namespace != namespace:
278 if self.target_namespace:
279 msg = u"wrong namespace (%r instead of %r) for XSD resource %r."
280 self.parse_error(msg % (self.target_namespace, namespace, self.url), root)
281
282
283 self.target_namespace = namespace
284 if '' not in self.namespaces:
285 self.namespaces[''] = namespace
286
287
288 if 'attributeFormDefault' in root.attrib:
289 try:
290 self.attribute_form_default = get_xsd_form_attribute(root, 'attributeFormDefault')
291 except ValueError as err:
292 self.parse_error(err, root)
293
294 if 'elementFormDefault' in root.attrib:
295 try:
296 self.element_form_default = get_xsd_form_attribute(root, 'elementFormDefault')
297 except ValueError as err:
298 self.parse_error(err, root)
299
300 if 'blockDefault' in root.attrib:
301 try:
302 self.block_default = get_xsd_derivation_attribute(
303 root, 'blockDefault', {'extension', 'restriction', 'substitution'}
304 )
305 except ValueError as err:
306 self.parse_error(err, root)
307
308 if 'finalDefault' in root.attrib:
309 try:
310 self.final_default = get_xsd_derivation_attribute(root, 'finalDefault')
311 except ValueError as err:
312 self.parse_error(err, root)
313
314 if self.XSD_VERSION > '1.0':
315
316 self.xpath_default_namespace = self._parse_xpath_default_namespace(root)
317 if 'defaultAttributes' in root.attrib:
318 try:
319 self.default_attributes = self.resolve_qname(root.attrib['defaultAttributes'])
320 except XMLSchemaValueError as error:
321 self.parse_error(str(error), root)
322
323
324 self.locations = NamespaceResourcesMap(self.source.get_locations(locations))
325 if self.meta_schema is not None:
326
327 self.locations[XHTML_NAMESPACE] = os.path.join(SCHEMAS_DIR, 'xhtml1-strict.xsd')
328 self.converter = self.get_converter(converter)
329
330
331 if self.meta_schema is None:
332 self.maps = global_maps or XsdGlobals(self)
333 return
334 elif global_maps is None:
335 if use_meta is False:
336 self.maps = XsdGlobals(self, validation)
337 self.locations.update(self.BASE_SCHEMAS)
338 elif self.target_namespace not in self.BASE_SCHEMAS:
339 self.maps = self.meta_schema.maps.copy(self, validation=validation)
340 else:
341 base_schemas = {k: v for k, v in self.BASE_SCHEMAS.items() if k != self.target_namespace}
342 meta_schema = self.create_meta_schema(base_schemas=base_schemas)
343 self.maps = meta_schema.maps
344 self.meta_schema = meta_schema
345
346 elif isinstance(global_maps, XsdGlobals):
347 self.maps = global_maps
348 else:
349 raise XMLSchemaTypeError("'global_maps' argument must be a %r instance." % XsdGlobals)
350
351
352 if validation == 'strict':
353 self.check_schema(root, self.namespaces)
354 elif validation == 'lax':
355 self.errors.extend([e for e in self.meta_schema.iter_errors(root, namespaces=self.namespaces)])
356
357
358 self._include_schemas()
359 self._import_namespaces()
360
361 if '' not in self.namespaces:
362 self.namespaces[''] = ''
363
364 if build:
365 self.maps.build()
366
368 if self.url:
369 basename = os.path.basename(self.url)
370 return u'%s(basename=%r, namespace=%r)' % (self.__class__.__name__, basename, self.target_namespace)
371 else:
372 return u'%s(namespace=%r)' % (self.__class__.__name__, self.target_namespace)
373
375 if name == 'root' and value.tag not in (XSD_SCHEMA, 'schema'):
376 raise XMLSchemaValueError("schema root element must has %r tag." % XSD_SCHEMA)
377 elif name == 'maps':
378 if self.meta_schema is None and hasattr(self, 'maps'):
379 raise XMLSchemaValueError("cannot change the global maps instance of a meta-schema")
380 super(XMLSchemaBase, self).__setattr__(name, value)
381 self.notations = NamespaceView(value.notations, self.target_namespace)
382 self.types = NamespaceView(value.types, self.target_namespace)
383 self.attributes = NamespaceView(value.attributes, self.target_namespace)
384 self.attribute_groups = NamespaceView(value.attribute_groups, self.target_namespace)
385 self.groups = NamespaceView(value.groups, self.target_namespace)
386 self.elements = NamespaceView(value.elements, self.target_namespace)
387 self.substitution_groups = NamespaceView(value.substitution_groups, self.target_namespace)
388 self.constraints = NamespaceView(value.constraints, self.target_namespace)
389 self.global_maps = (self.notations, self.types, self.attributes,
390 self.attribute_groups, self.groups, self.elements)
391 value.register(self)
392 elif name == 'validation' and value not in ('strict', 'lax', 'skip'):
393 raise XMLSchemaValueError("Wrong value %r for attribute 'validation'." % value)
394 else:
395 super(XMLSchemaBase, self).__setattr__(name, value)
396
398 for xsd_element in sorted(self.elements.values(), key=lambda x: x.name):
399 yield xsd_element
400
402 for xsd_element in sorted(self.elements.values(), key=lambda x: x.name, reverse=True):
403 yield xsd_element
404
407
408
409 @property
411 """Root element of the schema."""
412 return self.source.root
413
414 - def get_text(self):
415 """
416 Gets the XSD text of the schema. If the source text is not available creates
417 an encoded string representation of the XSD tree.
418 """
419 if self.source.text is None:
420 if self.source.url is None:
421 return etree_tostring(self.source.root, self.namespaces, xml_declaration=True)
422 else:
423 try:
424 self.source.load()
425 except XMLSchemaOSError:
426 return etree_tostring(self.source.root, self.namespaces, xml_declaration=True)
427 return self.source.text
428
429 @property
431 """Schema resource URL, is `None` if the schema is built from a string."""
432 return self.source.url
433
434 @property
436 """The base URL of the source of the schema."""
437 return self.source.base_url
438
439 @property
441 """Defines when to defuse XML data, can be 'always', 'remote' or 'never'."""
442 return self.source.defuse
443
444 @property
446 """Timeout in seconds for fetching resources."""
447 return self.source.timeout
448
449 @property
453
454
455 @property
457 """Schema root tag. For compatibility with the ElementTree API."""
458 return self.root.tag
459
460 @property
462 """The schema's *id* attribute, defaults to ``None``."""
463 return self.root.get('id')
464
465 @property
467 """The schema's *version* attribute, defaults to ``None``."""
468 return self.root.get('version')
469
470 @property
472 """A list of location hints extracted from the *xsi:schemaLocation* attribute of the schema."""
473 return [(k, v) for k, v in self.source.iter_location_hints() if k]
474
475 @property
477 """A location hint extracted from the *xsi:noNamespaceSchemaLocation* attribute of the schema."""
478 for k, v in self.source.iter_location_hints():
479 if not k:
480 return v
481
482 @property
484 """The namespace associated to the empty prefix ''."""
485 return self.namespaces.get('')
486
487 @property
494
495 @classmethod
504
505 @property
507 """
508 The list of global elements that are not used by reference in any model of the schema.
509 This is implemented as lazy property because it's computationally expensive to build
510 when the schema model is complex.
511 """
512 if not self.elements:
513 return []
514 elif len(self.elements) == 1:
515 return list(self.elements.values())
516 elif self._root_elements is None:
517 names = set(e.name for e in self.elements.values())
518 for xsd_element in self.elements.values():
519 for e in xsd_element.iter():
520 if e is xsd_element:
521 continue
522 elif e.ref or e.is_global:
523 if e.name in names:
524 names.discard(e.name)
525 if not names:
526 break
527 self._root_elements = list(names)
528
529 return [e for e in self.elements.values() if e.name in self._root_elements]
530
531 @classmethod
570
571 @classmethod
573 """Creates a new schema instance of the same class of the caller."""
574 return cls(*args, **kwargs)
575
576 - def create_any_content_group(self, parent, name=None):
577 """Creates a model group related to schema instance that accepts any content."""
578 group = self.BUILDERS.group_class(SEQUENCE_ELEMENT, self, parent, name)
579 group.append(XsdAnyElement(ANY_ELEMENT, self, group))
580 return group
581
587
601
602 __copy__ = copy
603
604 @classmethod
606 """
607 Validates the given schema against the XSD meta-schema (:attr:`meta_schema`).
608
609 :param schema: the schema instance that has to be validated.
610 :param namespaces: is an optional mapping from namespace prefix to URI.
611
612 :raises: :exc:`XMLSchemaValidationError` if the schema is invalid.
613 """
614 for error in cls.meta_schema.iter_errors(schema, namespaces=namespaces):
615 raise error
616
618 """Builds the schema XSD global maps."""
619 self.maps.build()
620
621 @property
653
654 @property
662
664 """
665 Creates an iterator for XSD global definitions/declarations related to schema namespace.
666
667 :param schema: Optional argument for filtering only globals related to a schema instance.
668 """
669 if schema is None:
670 for global_map in self.global_maps:
671 for obj in global_map.values():
672 yield obj
673 else:
674 for global_map in self.global_maps:
675 for obj in global_map.values():
676 if isinstance(obj, tuple):
677 if obj[1] == schema:
678 yield obj
679 elif obj.schema == schema:
680 yield obj
681
683 if xsd_classes is None or isinstance(self, xsd_classes):
684 yield self
685 for xsd_global in self.iter_globals(self):
686 for obj in xsd_global.iter_components(xsd_classes):
687 yield obj
688
690 """
691 Get a list of location hints for a namespace.
692 """
693 try:
694 return list(self.locations[namespace])
695 except KeyError:
696 return []
697
698 - def get_converter(self, converter=None, namespaces=None, **kwargs):
699 """
700 Returns a new converter instance.
701
702 :param converter: can be a converter class or instance. If it's an instance \
703 the new instance is copied from it and configured with the provided arguments.
704 :param namespaces: is an optional mapping from namespace prefix to URI.
705 :param kwargs: optional arguments for initialize the converter instance.
706 :return: a converter instance.
707 """
708 if converter is None:
709 converter = getattr(self, 'converter', XMLSchemaConverter)
710
711 if isinstance(converter, XMLSchemaConverter):
712 return converter.copy(namespaces=namespaces, **kwargs)
713 elif issubclass(converter, XMLSchemaConverter):
714 return converter(namespaces, **kwargs)
715 else:
716 msg = "'converter' argument must be a %r subclass or instance: %r"
717 raise XMLSchemaTypeError(msg % (XMLSchemaConverter, converter))
718
720 """Processes schema document inclusions and redefinitions."""
721 for child in iterchildren_xsd_include(self.root):
722 try:
723 self.include_schema(child.attrib['schemaLocation'], self.base_url)
724 except KeyError:
725 pass
726 except (OSError, IOError) as err:
727
728
729
730
731 self.warnings.append("Include schema failed: %s." % str(err))
732 warnings.warn(self.warnings[-1], XMLSchemaIncludeWarning, stacklevel=3)
733 except (XMLSchemaURLError, XMLSchemaParseError, XMLSchemaTypeError, ParseError) as err:
734 msg = 'cannot include schema %r: %s' % (child.attrib['schemaLocation'], err)
735 if isinstance(err, (XMLSchemaParseError, ParseError)):
736 self.parse_error(msg)
737 elif self.validation == 'strict':
738 raise type(err)(msg)
739 else:
740 self.errors.append(type(err)(msg))
741
742 for child in iterchildren_xsd_redefine(self.root):
743 try:
744 self.include_schema(child.attrib['schemaLocation'], self.base_url)
745 except KeyError:
746 pass
747 except (OSError, IOError) as err:
748
749
750 self.warnings.append("Redefine schema failed: %s." % str(err))
751 warnings.warn(self.warnings[-1], XMLSchemaIncludeWarning, stacklevel=3)
752 if has_xsd_components(child):
753 self.parse_error(str(err), child)
754 except (XMLSchemaURLError, XMLSchemaParseError, XMLSchemaTypeError, ParseError) as err:
755 msg = 'cannot redefine schema %r: %s' % (child.attrib['schemaLocation'], err)
756 if isinstance(err, (XMLSchemaParseError, ParseError)):
757 self.parse_error(msg)
758 elif self.validation == 'strict':
759 raise type(err)(msg)
760 else:
761 self.errors.append(type(err)(msg))
762
764 """
765 Includes a schema for the same namespace, from a specific URL.
766
767 :param location: is the URL of the schema.
768 :param base_url: is an optional base URL for fetching the schema resource.
769 :return: the included :class:`XMLSchema` instance.
770 """
771 schema_url = fetch_resource(location, base_url)
772 for schema in self.maps.namespaces[self.target_namespace]:
773 if schema_url == schema.url:
774 break
775 else:
776 schema = self.create_schema(
777 schema_url, self.target_namespace, self.validation, self.maps, self.converter,
778 self.locations, self.base_url, self.defuse, self.timeout, False
779 )
780
781 if location not in self.includes:
782 self.includes[location] = schema
783 elif self.includes[location] != schema:
784 self.includes[schema_url] = schema
785 return schema
786
788 """
789 Processes namespace imports. Imports are done on namespace basis not on resource: this
790 is the standard and also avoids import loops that sometimes are hard to detect.
791 """
792 namespace_imports = NamespaceResourcesMap(map(
793 lambda x: (x.get('namespace'), x.get('schemaLocation')),
794 iterchildren_xsd_import(self.root)
795 ))
796
797 for namespace, locations in namespace_imports.items():
798
799
800 if namespace is None:
801 namespace = ''
802 if namespace == self.target_namespace:
803 self.parse_error("if the 'namespace' attribute is not present on the import statement "
804 "then the importing schema must has a 'targetNamespace'")
805 continue
806 elif namespace == self.target_namespace:
807 self.parse_error("the attribute 'namespace' must be different from schema's 'targetNamespace'")
808 continue
809
810
811 if self.imports.get(namespace) is not None:
812 continue
813 elif namespace in self.maps.namespaces:
814 self.imports[namespace] = self.maps.namespaces[namespace][0]
815 continue
816
817 locations = [url for url in locations if url]
818 if not namespace:
819 pass
820 elif not locations:
821 locations = self.get_locations(namespace)
822 elif all(is_remote_url(url) for url in locations):
823
824
825
826
827
828 local_hints = [url for url in self.get_locations(namespace) if url and url_path_is_file(url)]
829 if local_hints:
830 locations = local_hints + locations
831
832 import_error = None
833 for url in locations:
834 try:
835 self.import_schema(namespace, url, self.base_url)
836 except (OSError, IOError) as err:
837
838
839 if import_error is None:
840 import_error = err
841 except (XMLSchemaURLError, XMLSchemaParseError, XMLSchemaTypeError, ParseError) as err:
842 if namespace:
843 msg = "cannot import namespace %r: %s." % (namespace, err)
844 else:
845 msg = "cannot import chameleon schema: %s." % err
846 if isinstance(err, (XMLSchemaParseError, ParseError)):
847 self.parse_error(msg)
848 elif self.validation == 'strict':
849 raise type(err)(msg)
850 else:
851 self.errors.append(type(err)(msg))
852 except XMLSchemaValueError as err:
853 self.parse_error(err)
854 else:
855 break
856 else:
857 if import_error is not None:
858 self.warnings.append("Namespace import failed: %s." % str(import_error))
859 warnings.warn(self.warnings[-1], XMLSchemaImportWarning, stacklevel=3)
860 self.imports[namespace] = None
861
862 - def import_schema(self, namespace, location, base_url=None, force=False):
863 """
864 Imports a schema for an external namespace, from a specific URL.
865
866 :param namespace: is the URI of the external namespace.
867 :param location: is the URL of the schema.
868 :param base_url: is an optional base URL for fetching the schema resource.
869 :param force: is set to `True` imports the schema also if the namespace is already imported.
870 :return: the imported :class:`XMLSchema` instance.
871 """
872 if not force:
873 if self.imports.get(namespace) is not None:
874 return self.imports[namespace]
875 elif namespace in self.maps.namespaces:
876 self.imports[namespace] = self.maps.namespaces[namespace][0]
877 return self.imports[namespace]
878
879 schema_url = fetch_resource(location, base_url)
880 if self.imports.get(namespace) is not None and self.imports[namespace].url == schema_url:
881 return self.imports[namespace]
882 elif namespace in self.maps.namespaces:
883 for schema in self.maps.namespaces[namespace]:
884 if schema_url == schema.url:
885 self.imports[namespace] = schema
886 return schema
887
888 schema = self.create_schema(
889 schema_url, None, self.validation, self.maps, self.converter,
890 self.locations, self.base_url, self.defuse, self.timeout, False
891 )
892 if schema.target_namespace != namespace:
893 raise XMLSchemaValueError('imported schema %r has an unmatched namespace %r' % (location, namespace))
894 self.imports[namespace] = schema
895 return schema
896
898 """
899 QName resolution for a schema instance.
900
901 :param qname: a string in xs:QName format.
902 :returns: an expanded QName in the format "{*namespace-URI*}*local-name*".
903 :raises: `XMLSchemaValueError` for an invalid xs:QName or if the namespace prefix is not \
904 declared in the schema instance or if the namespace is not the *targetNamespace* and \
905 the namespace is not imported by the schema.
906 """
907 qname = qname.strip()
908 if not qname or ' ' in qname or '\t' in qname or '\n' in qname:
909 raise XMLSchemaValueError("{!r} is not a valid value for xs:QName".format(qname))
910
911 if qname[0] == '{':
912 try:
913 namespace, local_name = qname[1:].split('}')
914 except ValueError:
915 raise XMLSchemaValueError("{!r} is not a valid value for xs:QName".format(qname))
916 elif ':' in qname:
917 try:
918 prefix, local_name = qname.split(':')
919 except ValueError:
920 raise XMLSchemaValueError("{!r} is not a valid value for xs:QName".format(qname))
921 else:
922 try:
923 namespace = self.namespaces[prefix]
924 except KeyError:
925 raise XMLSchemaValueError("prefix %r not found in namespace map" % prefix)
926 else:
927 namespace, local_name = self.namespaces.get('', ''), qname
928
929 if not namespace:
930 return local_name
931 elif self.meta_schema is not None and namespace != self.target_namespace and \
932 namespace not in {XSD_NAMESPACE, XSI_NAMESPACE} and namespace not in self.imports:
933 raise XMLSchemaValueError(
934 "the QName {!r} is mapped to the namespace {!r}, but this namespace has "
935 "not an xs:import statement in the schema.".format(qname, namespace)
936 )
937 return '{%s}%s' % (namespace, local_name)
938
939 - def iter_decode(self, source, path=None, validation='lax', process_namespaces=True,
940 namespaces=None, use_defaults=True, decimal_type=None, datetime_types=False,
941 converter=None, defuse=None, timeout=None, **kwargs):
942 """
943 Creates an iterator for decoding an XML source to a data structure.
944
945 :param source: the XML data source. Can be a path to a file or an URI of a resource or \
946 an opened file-like object or an Element Tree instance or a string containing XML data.
947 :param path: is an optional XPath expression that matches the parts of the document \
948 that have to be decoded. The XPath expression considers the schema as the root \
949 element with global elements as its children.
950 :param validation: defines the XSD validation mode to use for decode, can be 'strict', \
951 'lax' or 'skip'.
952 :param process_namespaces: indicates whether to use namespace information in the decoding \
953 process, using the map provided with the argument *namespaces* and the map extracted from \
954 the XML document.
955 :param namespaces: is an optional mapping from namespace prefix to URI.
956 :param use_defaults: indicates whether to use default values for filling missing data.
957 :param decimal_type: conversion type for `Decimal` objects (generated by XSD `decimal` \
958 built-in and derived types), useful if you want to generate a JSON-compatible data structure.
959 :param datetime_types: if set to `True` the datetime and duration XSD types are decoded, \
960 otherwise their origin XML string is returned.
961 :param converter: an :class:`XMLSchemaConverter` subclass or instance to use for the decoding.
962 :param defuse: Overrides when to defuse XML data. Can be 'always', 'remote' or 'never'.
963 :param timeout: Overrides the timeout setted for the schema.
964 :param kwargs: Keyword arguments containing options for converter and decoding.
965 :return: Yields a decoded data object, eventually preceded by a sequence of validation \
966 or decoding errors.
967 """
968 if not self.built:
969 raise XMLSchemaNotBuiltError(self, "schema %r is not built." % self)
970 elif not self.elements:
971 raise XMLSchemaValueError("decoding needs at least one XSD element declaration!")
972
973 if not isinstance(source, XMLResource):
974 defuse = defuse or self.defuse
975 timeout = timeout or self.timeout
976 source = XMLResource(source=source, defuse=defuse, timeout=timeout, lazy=False)
977 elif defuse and source.defuse != defuse or timeout and source.timeout != timeout:
978 source = source.copy(defuse=defuse, timeout=timeout, lazy=False)
979
980 if process_namespaces:
981 namespaces = {} if namespaces is None else namespaces.copy()
982 namespaces.update(source.get_namespaces())
983 else:
984 namespaces = {}
985
986 converter = self.get_converter(converter, namespaces, **kwargs)
987 id_map = Counter()
988
989 if path is None:
990 xsd_element = self.find(source.root.tag, namespaces=namespaces)
991 if not isinstance(xsd_element, XsdElement):
992 reason = "%r is not a global element of the schema!" % source.root.tag
993 yield XMLSchemaValidationError(self, source.root, reason, source, namespaces)
994 else:
995 for obj in xsd_element.iter_decode(
996 source.root, validation, converter, source=source, namespaces=namespaces,
997 use_defaults=use_defaults, decimal_type=decimal_type,
998 datetime_types=datetime_types, id_map=id_map, **kwargs):
999 yield obj
1000 else:
1001 xsd_element = self.find(path, namespaces=namespaces)
1002 if not isinstance(xsd_element, XsdElement):
1003 reason = "the path %r doesn't match any element of the schema!" % path
1004 obj = elementpath.select(source.root, path, namespaces=namespaces) or source.root
1005 yield XMLSchemaValidationError(self, obj, reason, source, namespaces)
1006 else:
1007 for elem in elementpath.select(source.root, path, namespaces=namespaces):
1008 for obj in xsd_element.iter_decode(
1009 elem, validation, converter, source=source, namespaces=namespaces,
1010 use_defaults=use_defaults, decimal_type=decimal_type,
1011 datetime_types=datetime_types, id_map=id_map, **kwargs):
1012 yield obj
1013
1014 for k, v in id_map.items():
1015 if v != 1:
1016 self.parse_error("Duplicated xsd:ID value {!r}".format(k), self.root)
1017
1018 - def iter_encode(self, obj, path=None, validation='lax', namespaces=None, converter=None, **kwargs):
1019 """
1020 Creates an iterator for encoding a data structure to an ElementTree's Element.
1021
1022 :param obj: the data that has to be encoded.
1023 :param path: is an optional XPath expression for selecting the element of the schema \
1024 that matches the data that has to be encoded. For default the first global element of \
1025 the schema is used.
1026 :param validation: the XSD validation mode. Can be 'strict', 'lax' or 'skip'.
1027 :param namespaces: is an optional mapping from namespace prefix to URI.
1028 :param converter: an :class:`XMLSchemaConverter` subclass or instance to use for the encoding.
1029 :param kwargs: Keyword arguments containing options for converter and encoding.
1030 :return: Yields an Element instance, eventually preceded by a sequence of validation \
1031 or encoding errors.
1032 """
1033 if not self.built:
1034 raise XMLSchemaNotBuiltError(self, "schema %r is not built." % self)
1035 elif not self.elements:
1036 yield XMLSchemaValueError("encoding needs at least one XSD element declaration!")
1037
1038 namespaces = {} if namespaces is None else namespaces.copy()
1039 converter = self.get_converter(converter, namespaces, **kwargs)
1040
1041 if path is not None:
1042 xsd_element = self.find(path, namespaces=namespaces)
1043 elif isinstance(obj, dict) and len(obj) == 1:
1044 xsd_element = self.elements.get(list(obj.keys())[0])
1045 elif len(self.elements) == 1:
1046 xsd_element = list(self.elements.values())[0]
1047 else:
1048 root_elements = self.root_elements
1049 xsd_element = root_elements[0] if len(root_elements) == 1 else None
1050
1051 if not isinstance(xsd_element, XsdElement):
1052 if path is not None:
1053 msg = "the path %r doesn't match any element of the schema!" % path
1054 else:
1055 msg = "unable to select an element for decoding data, provide a valid 'path' argument."
1056 yield XMLSchemaEncodeError(self, obj, self.elements, reason=msg)
1057 else:
1058 for result in xsd_element.iter_encode(obj, validation, converter, **kwargs):
1059 yield result
1060
1063 """
1064 XSD 1.0 schema class.
1065
1066 <schema
1067 attributeFormDefault = (qualified | unqualified) : unqualified
1068 blockDefault = (#all | List of (extension | restriction | substitution)) : ''
1069 elementFormDefault = (qualified | unqualified) : unqualified
1070 finalDefault = (#all | List of (extension | restriction | list | union)) : ''
1071 id = ID
1072 targetNamespace = anyURI
1073 version = token
1074 xml:lang = language
1075 {any attributes with non-schema namespace . . .}>
1076 Content: ((include | import | redefine | annotation)*, (((simpleType | complexType | group |
1077 attributeGroup) | element | attribute | notation), annotation*)*)
1078 </schema>
1079 """
1080 XSD_VERSION = '1.0'
1081 BUILDERS = {
1082 'notation_class': XsdNotation,
1083 'complex_type_class': XsdComplexType,
1084 'attribute_class': XsdAttribute,
1085 'any_attribute_class': XsdAnyAttribute,
1086 'attribute_group_class': XsdAttributeGroup,
1087 'group_class': XsdGroup,
1088 'element_class': XsdElement,
1089 'any_element_class': XsdAnyElement,
1090 'restriction_class': XsdAtomicRestriction,
1091 'union_class': XsdUnion,
1092 'simple_type_factory': xsd_simple_type_factory
1093 }
1094 meta_schema = os.path.join(SCHEMAS_DIR, 'XSD_1.0/XMLSchema.xsd')
1095 BASE_SCHEMAS = {
1096 XML_NAMESPACE: XML_SCHEMA_FILE,
1097
1098 XSI_NAMESPACE: XSI_SCHEMA_FILE,
1099 XLINK_NAMESPACE: XLINK_SCHEMA_FILE,
1100 }
1101
1105 """
1106 XSD 1.1 schema class.
1107
1108 <schema
1109 attributeFormDefault = (qualified | unqualified) : unqualified
1110 blockDefault = (#all | List of (extension | restriction | substitution)) : ''
1111 defaultAttributes = QName
1112 xpathDefaultNamespace = (anyURI | (##defaultNamespace | ##targetNamespace | ##local)) : ##local
1113 elementFormDefault = (qualified | unqualified) : unqualified
1114 finalDefault = (#all | List of (extension | restriction | list | union)) : ''
1115 id = ID
1116 targetNamespace = anyURI
1117 version = token
1118 xml:lang = language
1119 {any attributes with non-schema namespace . . .}>
1120 Content: ((include | import | redefine | override | annotation)*, (defaultOpenContent, annotation*)?,
1121 ((simpleType | complexType | group | attributeGroup | element | attribute | notation), annotation*)*)
1122 </schema>
1123
1124 <schema
1125 attributeFormDefault = (qualified | unqualified) : unqualified
1126 blockDefault = (#all | List of (extension | restriction | substitution)) : ''
1127 elementFormDefault = (qualified | unqualified) : unqualified
1128 finalDefault = (#all | List of (extension | restriction | list | union)) : ''
1129 id = ID
1130 targetNamespace = anyURI
1131 version = token
1132 xml:lang = language
1133 {any attributes with non-schema namespace . . .}>
1134 Content: ((include | import | redefine | annotation)*, (((simpleType | complexType | group |
1135 attributeGroup) | element | attribute | notation), annotation*)*)
1136 </schema>
1137 """
1138 XSD_VERSION = '1.1'
1139 BUILDERS = {
1140 'notation_class': XsdNotation,
1141 'complex_type_class': Xsd11ComplexType,
1142 'attribute_class': Xsd11Attribute,
1143 'any_attribute_class': Xsd11AnyAttribute,
1144 'attribute_group_class': XsdAttributeGroup,
1145 'group_class': Xsd11Group,
1146 'element_class': Xsd11Element,
1147 'any_element_class': Xsd11AnyElement,
1148 'restriction_class': Xsd11AtomicRestriction,
1149 'union_class': Xsd11Union,
1150 'simple_type_factory': xsd_simple_type_factory
1151 }
1152 meta_schema = os.path.join(SCHEMAS_DIR, 'XSD_1.1/XMLSchema.xsd')
1153 BASE_SCHEMAS = {
1154 XSD_NAMESPACE: os.path.join(SCHEMAS_DIR, 'XSD_1.1/list_builtins.xsd'),
1155 XML_NAMESPACE: XML_SCHEMA_FILE,
1156
1157 XSI_NAMESPACE: XSI_SCHEMA_FILE,
1158 XLINK_NAMESPACE: XLINK_SCHEMA_FILE,
1159 }
1160
1175
1176
1177 XMLSchema = XMLSchema10
1178 """The default class for schema instances."""
1179