1# -*- coding: utf-8 -*-
2# Copyright 2009-2013, Peter A. Bigot
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain a
6# copy of the License at:
7#
8#            http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
15
16"""Extensions of standard exceptions for PyXB events.
17
18Yeah, I'd love this module to be named exceptions.py, but it can't
19because the standard library has one of those, and we need to
20reference it below.
21"""
22
23import pyxb
24from pyxb.utils import six
25
26class PyXBException (Exception):
27    """Base class for exceptions that indicate a problem that the user should fix."""
28
29    """The arguments passed to the exception constructor."""
30    _args = None
31
32    """The keywords passed to the exception constructor.
33
34    @note: Do not pop values from the keywords array in subclass
35    constructors that recognize and extract values from them.  They
36    should be kept around so they're accessible generically."""
37    _kw = None
38
39    def __init__ (self, *args, **kw):
40        """Create an exception indicating a PyXB-related problem.
41
42        If no args are present, a default argument is taken from the
43        C{message} keyword.
44
45        @keyword message : Text to provide the user with information about the problem.
46        """
47        if 0 == len(args) and 'message' in kw:
48            args = (kw.pop('message'),)
49        self._args = args
50        self._kw = kw
51        super(PyXBException, self).__init__(*args)
52
53    if six.PY2:
54        def _str_from_unicode (self):
55            return unicode(self).encode(pyxb._OutputEncoding)
56
57class PyXBVersionError (PyXBException):
58    """Raised on import of a binding generated with a different version of PYXB"""
59    pass
60
61class DOMGenerationError (PyXBException):
62    """A non-validation error encountered converting bindings to DOM."""
63    pass
64
65@six.python_2_unicode_compatible
66class UnboundElementError (DOMGenerationError):
67    """An instance converting to DOM had no bound element."""
68
69    instance = None
70    """The binding instance.  This is missing an element binding (via
71    L{pyxb.binding.basis._TypeBinding_mixin._element}) and no
72    C{element_name} was passed."""
73
74    def __init__ (self, instance):
75        super(UnboundElementError, self).__init__(instance)
76        self.instance = instance
77
78    def __str__ (self):
79        return six.u('Instance of type %s has no bound element for start tag') % (self.instance._diagnosticName(),)
80
81class SchemaValidationError (PyXBException):
82    """Raised when the XML hierarchy does not appear to be valid for an XML schema."""
83    pass
84
85class NamespaceError (PyXBException):
86    """Violation of some rule relevant to XML Namespaces"""
87    def __init__ (self, namespace, *args, **kw):
88        PyXBException.__init__(self, *args, **kw)
89        self.__namespace = namespace
90
91    def namespace (self): return self.__namespace
92
93class NamespaceArchiveError (PyXBException):
94    """Problem related to namespace archives"""
95    pass
96
97class SchemaUniquenessError (PyXBException):
98    """Raised when somebody tries to create a schema component using a
99    schema that has already been used in that namespace.  Import and
100    include processing would have avoided this, so somebody asked for
101    it specifically."""
102    def __init__ (self, namespace, schema_location, existing_schema, *args, **kw):
103        super(SchemaUniquenessError, self).__init__(*args, **kw)
104        self.__namespace = namespace
105        self.__schemaLocation = schema_location
106        self.__existingSchema = existing_schema
107
108    def namespace (self): return self.__namespace
109    def schemaLocation (self): return self.__schemaLocation
110    def existingSchema (self): return self.__existingSchema
111
112class BindingGenerationError (PyXBException):
113    """Raised when something goes wrong generating the binding classes"""
114    pass
115
116class NamespaceUniquenessError (NamespaceError):
117    """Raised when an attempt is made to record multiple objects of the same name in the same namespace category."""
118    pass
119
120class NotInNamespaceError (PyXBException):
121    '''Raised when a name is referenced that is not defined in the appropriate namespace.'''
122    __namespace = None
123    __ncName = None
124
125class QNameResolutionError (NamespaceError):
126    '''Raised when a QName cannot be associated with a namespace.'''
127    namespaceContext = None
128    qname = None
129
130    def __init__ (self, message, qname, xmlns_context):
131        self.qname = qname
132        self.namespaceContext = xmlns_context
133        super(QNameResolutionError, self).__init__(message, qname, xmlns_context)
134
135class BadDocumentError (PyXBException):
136    """Raised when processing document content and an error is encountered."""
137    pass
138
139class StructuralBadDocumentError (BadDocumentError):
140    """Raised when processing document and the content model is not satisfied."""
141    @property
142    def element_use (self):
143        """The L{pyxb.binding.content.ElementDeclaration} instance to which the content should conform, if available."""
144        return self.__elementUse
145
146    @property
147    def container (self):
148        """The L{pyxb.binding.basis.complexTypeDefinition} instance to which the content would belong, if available."""
149        return self.__container
150
151    @property
152    def content (self):
153        """The value which could not be reconciled with the content model."""
154        return self.__content
155
156    def __init__ (self, *args, **kw):
157        """Raised when processing document and the content model is not satisfied.
158
159        @keyword content : The value that could not be reconciled with the content model
160        @keyword container : Optional binding instance into which the content was to be assigned
161        @keyword element_use : Optional reference to an element use identifying the element to which the value was to be reconciled
162        """
163        self.__content = kw.pop('content', None)
164        if args:
165            self.__content = args[0]
166        self.__container = kw.pop('container', None)
167        self.__elementUse = kw.pop('element_use', None)
168        if self.__content is not None:
169            if self.__container is not None:
170                kw.setdefault('message', '%s cannot accept wildcard content %s' % (self.__container._Name(), self.__content))
171            elif self.__elementUse is not None:
172                kw.setdefault('message', '%s not consistent with content model for %s' % (self.__content, self.__elementUse))
173            else:
174                kw.setdefault('message', six.text_type(self.__content))
175        BadDocumentError.__init__(self, **kw)
176
177class UnrecognizedDOMRootNodeError (StructuralBadDocumentError):
178    """A root DOM node could not be resolved to a schema element"""
179
180    node = None
181    """The L{xml.dom.Element} instance that could not be recognized"""
182
183    def __get_node_name (self):
184        """The QName of the L{node} as a L{pyxb.namespace.ExpandedName}"""
185        import pyxb.namespace
186        return  pyxb.namespace.ExpandedName(self.node.namespaceURI, self.node.localName)
187    node_name = property(__get_node_name)
188
189    def __init__ (self, node):
190        """@param node: the value for the L{node} attribute."""
191        self.node = node
192        super(UnrecognizedDOMRootNodeError, self).__init__(node)
193
194class ValidationError (PyXBException):
195    """Raised when something in the infoset fails to satisfy a content model or attribute requirement.
196
197    All validation errors include a L{location} attribute which shows
198    where in the original XML the problem occurred.  The attribute may
199    be C{None} if the content did not come from an XML document, or
200    the underlying XML infrastructure did not provide a location.
201
202    More refined validation error exception classes add more attributes."""
203
204    location = None
205    """Where the error occurred in the document being parsed, if
206    available.  This will be C{None}, or an instance of
207    L{pyxb.utils.utility.Location}."""
208
209    def details (self):
210        """Provide information describing why validation failed.
211
212        In many cases, this is simply the informal string content that
213        would be obtained through the C{str} built-in function.  For
214        certain errors this method gives more details on what would be
215        acceptable and where the descriptions can be found in the
216        original schema.
217
218        @return: a string description of validation failure"""
219        return six.text_type(self)
220
221@six.python_2_unicode_compatible
222class NonElementValidationError (ValidationError):
223    """Raised when an element (or a value bound to an element) appears
224    in context that does not permit an element."""
225
226    element = None
227    """The content that is not permitted.  This may be an element, or
228    a DOM representation that would have been made into an element had
229    matters progressed further."""
230
231    def __init__ (self, element, location=None):
232        """@param element: the value for the L{element} attribute.
233        @param location: the value for the L{location} attribute.
234        """
235        self.element = element
236        if (location is None) and isinstance(element, pyxb.utils.utility.Locatable_mixin):
237            location = element._location()
238        self.location = location
239        super(NonElementValidationError, self).__init__(element, location)
240
241    def __str__ (self):
242        import pyxb.binding.basis
243        import xml.dom
244        value = ''
245        boundto = ''
246        location = ''
247        if isinstance(self.element, pyxb.binding.basis._TypeBinding_mixin):
248            eb = self.element._element()
249            boundto = ''
250            if eb is not None:
251                boundto = ' bound to %s' % (eb.name(),)
252            if isinstance(self.element, pyxb.binding.basis.simpleTypeDefinition):
253                value = self.element.xsdLiteral()
254            elif self.element._IsSimpleTypeContent():
255                value = six.text_type(self.element.value())
256            else:
257                value = 'Complex value'
258        elif isinstance(self.element, xml.dom.Node):
259            value = 'DOM node %s' % (self.element.nodeName,)
260        else:
261            value = '%s type %s' % (six.text_type(self.element), type(self.element))
262        if self.location is not None:
263            location = ' at %s' % (self.location,)
264        return six.u('%s%s not permitted%s') % (value, boundto, location)
265
266class ElementValidationError (ValidationError):
267    """Raised when a validation requirement for an element is not satisfied."""
268    pass
269
270@six.python_2_unicode_compatible
271class AbstractElementError (ElementValidationError):
272    """Attempt to create an instance of an abstract element.
273
274    Raised when an element is created and the identified binding is
275    abstract.  Such elements cannot be created directly; instead the
276    creation must derive from an instance of the abstract element's
277    substitution group.
278
279    Since members of the substitution group self-identify using the
280    C{substitutionGroup} attribute, there is no general way to find
281    the set of elements which would be acceptable in place of the
282    abstract element."""
283
284    element = None
285    """The abstract L{pyxb.binding.basis.element} in question"""
286
287    value = None
288    """The value proposed for the L{element}.  This is usually going
289    to be a C{xml.dom.Node} used in the attempt to create the element,
290    C{None} if the abstract element was invoked without a node, or
291    another type if
292    L{pyxb.binding.content.ElementDeclaration.toDOM} is
293    mis-used."""
294
295    def __init__ (self, element, location, value=None):
296        """@param element: the value for the L{element} attribute.
297        @param location: the value for the L{location} attribute.
298        @param value: the value for the L{value} attribute."""
299        self.element = element
300        self.location = location
301        self.value = value
302        super(AbstractElementError, self).__init__(element, location, value)
303
304    def __str__ (self):
305        return six.u('Cannot instantiate abstract element %s directly') % (self.element.name(),)
306
307@six.python_2_unicode_compatible
308class ContentInNilInstanceError (ElementValidationError):
309    """Raised when an element that is marked to be nil is assigned content."""
310
311    instance = None
312    """The binding instance which is xsi:nil"""
313
314    content = None
315    """The content that was to be assigned to the instance."""
316
317    def __init__ (self, instance, content, location=None):
318        """@param instance: the value for the L{instance} attribute.
319        @param content: the value for the L{content} attribute.
320        @param location: the value for the L{location} attribute.  Default taken from C{instance} if possible."""
321
322        self.instance = instance
323        self.content = content
324        if location is None:
325            location = self.instance._location()
326        self.location = location
327        super(ContentInNilInstanceError, self).__init__(instance, content, location)
328
329    def __str__ (self):
330        from pyxb.namespace.builtin import XMLSchema_instance as XSI
331        return six.u('%s with %s=true cannot have content') % (self.instance._diagnosticName(), XSI.nil)
332
333class NoNillableSupportError (ElementValidationError):
334    """Raised when invoking L{_setIsNil<pyxb.binding.basis._TypeBinding_mixin._setIsNil>} on a type that does not support nillable."""
335
336    instance = None
337    """The binding instance on which an inappropriate operation was invoked."""
338
339    def __init__ (self, instance, location=None):
340        """@param instance: the value for the L{instance} attribute.
341        @param location: the value for the L{location} attribute.  Default taken from C{instance} if possible."""
342        self.instance = instance
343        if location is None:
344            location = self.instance._location()
345        self.location = location
346        super(NoNillableSupportError, self).__init__(instance, location)
347
348@six.python_2_unicode_compatible
349class ElementChangeError (ElementValidationError):
350    """Attempt to change an element that has a fixed value constraint."""
351
352    element = None
353    """The L{pyxb.binding.basis.element} that has a fixed value."""
354
355    value = None
356    """The value that was to be assigned to the element."""
357
358    def __init__ (self, element, value, location=None):
359        """@param element: the value for the L{element} attribute.
360        @param value: the value for the L{value} attribute.
361        @param location: the value for the L{location} attribute.  Default taken from C{value} if possible."""
362
363        import pyxb.utils.utility
364        self.element = element
365        self.value = value
366        if (location is None) and isinstance(value, pyxb.utils.utility.Locatable_mixin):
367            location = value._location()
368        self.location = location
369        super(ElementChangeError, self).__init__(element, value, location)
370
371    def __str__ (self):
372        return six.u('Value %s for element %s incompatible with fixed content') % (self.value, self.element.name())
373
374class ComplexTypeValidationError (ValidationError):
375    """Raised when a validation requirement for a complex type is not satisfied."""
376    pass
377
378@six.python_2_unicode_compatible
379class AbstractInstantiationError (ComplexTypeValidationError):
380    """Attempt to create an instance of an abstract complex type.
381
382    These types are analogous to abstract base classes, and cannot be
383    created directly.  A type should be used that extends the abstract
384    class.
385
386    When an incoming document is missing the xsi:type attribute which
387    redirects an element with an abstract type to the correct type,
388    the L{node} attribute is provided so the user can get a clue as to
389    where the problem occured.  When this exception is a result of
390    constructor mis-use in Python code, the traceback will tell you
391    where the problem lies.
392    """
393
394    type = None
395    """The abstract L{pyxb.binding.basis.complexTypeDefinition} subclass used."""
396
397    node = None
398    """The L{xml.dom.Element} from which instantiation was attempted, if available."""
399
400    def __init__ (self, type, location, node):
401        """@param type: the value for the L{type} attribute.
402        @param location: the value for the L{location} attribute.
403        @param node: the value for the L{node} attribute."""
404        self.type = type
405        self.location = location
406        self.node = node
407        super(AbstractInstantiationError, self).__init__(type, location, node)
408
409    def __str__ (self):
410        # If the type is abstract, it has to have a name.
411        return six.u('Cannot instantiate abstract type %s directly') % (self.type._ExpandedName,)
412
413@six.python_2_unicode_compatible
414class AttributeOnSimpleTypeError (ComplexTypeValidationError):
415    """Attempt made to set an attribute on an element with simple type.
416
417    Note that elements with complex type and simple content may have
418    attributes; elements with simple type must not."""
419
420    instance = None
421    """The simple type binding instance on which no attributes exist."""
422
423    tag = None
424    """The name of the proposed attribute."""
425
426    value = None
427    """The value proposed to be assigned to the non-existent attribute."""
428
429    def __init__ (self, instance, tag, value, location=None):
430        """@param instance: the value for the L{instance} attribute.
431        @param tag: the value for the L{tag} attribute.
432        @param value: the value for the L{value} attribute.
433        @param location: the value for the L{location} attribute.  Default taken from C{instance} if possible."""
434
435        self.instance = instance
436        self.tag = tag
437        self.value = value
438        if location is None:
439            location = self.instance._location()
440        self.location = location
441        super(AttributeOnSimpleTypeError, self).__init__(instance, tag, value, location)
442
443    def __str__ (self):
444        return six.u('Simple type %s cannot support attribute %s') % (self.instance._Name(), self.tag)
445
446class ContentValidationError (ComplexTypeValidationError):
447    """Violation of a complex type content model."""
448    pass
449
450@six.python_2_unicode_compatible
451class ContentNondeterminismExceededError (ContentValidationError):
452    """Content validation exceeded the allowed limits of nondeterminism."""
453
454    instance = None
455    """The binding instance being validated."""
456
457    def __init__ (self, instance):
458        """@param instance: the value for the L{instance} attribute."""
459        self.instance = instance
460        super(ContentNondeterminismExceededError, self).__init__(instance)
461
462    def __str__ (self):
463        return six.u('Nondeterminism exceeded validating %s') % (self.instance._Name(),)
464
465@six.python_2_unicode_compatible
466class SimpleContentAbsentError (ContentValidationError):
467    """An instance with simple content was not provided with a value."""
468
469    instance = None
470    """The binding instance for which simple content is missing."""
471
472    def __init__ (self, instance, location):
473        """@param instance: the value for the L{instance} attribute.
474        @param location: the value for the L{location} attribute."""
475        self.instance = instance
476        self.location = location
477        super(SimpleContentAbsentError, self).__init__(instance, location)
478
479    def __str__ (self):
480        return six.u('Type %s requires content') % (self.instance._Name(),)
481
482@six.python_2_unicode_compatible
483class ExtraSimpleContentError (ContentValidationError):
484    """A complex type with simple content was provided too much content."""
485
486    instance = None
487    """The binding instance that already has simple content assigned."""
488
489    value = None
490    """The proposed addition to that simple content."""
491
492    def __init__ (self, instance, value, location=None):
493        """@param instance: the value for the L{instance} attribute.
494        @param value: the value for the L{value} attribute.
495        @param location: the value for the L{location} attribute."""
496        self.instance = instance
497        self.value = value
498        self.location = location
499        super(ExtraSimpleContentError, self).__init__(instance, value, location)
500
501    def __str__ (self):
502        return six.u('Instance of %s already has simple content value assigned') % (self.instance._Name(),)
503
504@six.python_2_unicode_compatible
505class NonPluralAppendError (ContentValidationError):
506    """Attempt to append to an element which does not accept multiple instances."""
507
508    instance = None
509    """The binding instance containing the element"""
510
511    element_declaration = None
512    """The L{pyxb.binding.content.ElementDeclaration} contained in C{instance} that does not accept multiple instances"""
513
514    value = None
515    """The proposed addition to the element in the instance"""
516
517    def __init__ (self, instance, element_declaration, value):
518        """@param instance: the value for the L{instance} attribute.
519        @param element_declaration: the value for the L{element_declaration} attribute.
520        @param value: the value for the L{value} attribute."""
521        self.instance = instance
522        self.element_declaration = element_declaration
523        self.value = value
524        super(NonPluralAppendError, self).__init__(instance, element_declaration, value)
525
526    def __str__ (self):
527        return six.u('Instance of %s cannot append to element %s') % (self.instance._Name(), self.element_declaration.name())
528
529@six.python_2_unicode_compatible
530class MixedContentError (ContentValidationError):
531    """Non-element content added to a complex type instance that does not support mixed content."""
532
533    instance = None
534    """The binding instance."""
535
536    value = None
537    """The non-element content."""
538
539    def __init__ (self, instance, value, location=None):
540        """@param instance: the value for the L{instance} attribute.
541        @param value: the value for the L{value} attribute.
542        @param location: the value for the L{location} attribute."""
543        self.instance = instance
544        self.value = value
545        self.location = location
546        super(MixedContentError, self).__init__(instance, value, location)
547
548    def __str__ (self):
549        if self.location is not None:
550            return six.u('Invalid non-element content at %s') % (self.location,)
551        return six.u('Invalid non-element content')
552
553@six.python_2_unicode_compatible
554class UnprocessedKeywordContentError (ContentValidationError):
555    """A complex type constructor was provided with keywords that could not be recognized."""
556
557    instance = None
558    """The binding instance being constructed."""
559
560    keywords = None
561    """The keywords that could not be recognized.  These may have been
562    intended to be attributes or elements, but cannot be identified as
563    either."""
564
565    def __init__ (self, instance, keywords, location=None):
566        """@param instance: the value for the L{instance} attribute.
567        @param keywords: the value for the L{keywords} attribute.
568        @param location: the value for the L{location} attribute."""
569        self.instance = instance
570        self.keywords = keywords
571        self.location = location
572        super(UnprocessedKeywordContentError, self).__init__(instance, keywords, location)
573
574    def __str__ (self):
575        return six.u('Unprocessed keywords instantiating %s: %s') % (self.instance._Name(), ' '.join(six.iterkeys(self.keywords)))
576
577class IncrementalElementContentError (ContentValidationError):
578    """Element or element-like content could not be validly associated with an sub-element in the content model.
579
580    This exception occurs when content is added to an element during
581    incremental validation, such as when positional arguments are used
582    in a constructor or material is appended either explicitly or
583    through parsing a DOM instance."""
584
585    instance = None
586    """The binding for which the L{value} could not be associated with an element."""
587
588    automaton_configuration = None
589    """The L{pyxb.binding.content.AutomatonConfiguration} representing the current state of the L{instance} content."""
590
591    value = None
592    """The value that could not be associated with allowable content."""
593
594    def __init__ (self, instance, automaton_configuration, value, location=None):
595        """@param instance: the value for the L{instance} attribute.
596        @param automaton_configuration: the value for the L{automaton_configuration} attribute.
597        @param value: the value for the L{value} attribute.
598        @param location: the value for the L{location} attribute."""
599        self.instance = instance
600        self.automaton_configuration = automaton_configuration
601        self.value = value
602        self.location = location
603        super(IncrementalElementContentError, self).__init__(instance, automaton_configuration, value, location)
604
605    def _valueDescription (self):
606        import xml.dom
607        if isinstance(self.value, pyxb.binding.basis._TypeBinding_mixin):
608            return self.value._diagnosticName()
609        if isinstance(self.value, xml.dom.Node):
610            return self.value.nodeName
611        return six.text_type(self.value)
612
613@six.python_2_unicode_compatible
614class UnrecognizedContentError (IncrementalElementContentError):
615    """Element or element-like content could not be validly associated with an sub-element in the content model.
616
617    This exception occurs when content is added to an element during incremental validation."""
618
619    def __str__ (self):
620        value = self._valueDescription()
621        acceptable = self.automaton_configuration.acceptableContent()
622        if 0 == acceptable:
623            expect = 'no more content'
624        else:
625            import pyxb.binding.content
626            seen = set()
627            names = []
628            for u in acceptable:
629                if isinstance(u, pyxb.binding.content.ElementUse):
630                    n = six.text_type(u.elementBinding().name())
631                else:
632                    assert isinstance(u, pyxb.binding.content.WildcardUse)
633                    n = 'xs:any'
634                if not (n in seen):
635                    names.append(n)
636                    seen.add(n)
637            expect = ' or '.join(names)
638        location = ''
639        if self.location is not None:
640            location = ' at %s' % (self.location,)
641        return six.u('Invalid content %s%s (expect %s)') % (value, location, expect)
642
643    def details (self):
644        import pyxb.binding.basis
645        import pyxb.binding.content
646        i = self.instance
647        rv = [ ]
648        if i._element() is not None:
649            rv.append('The containing element %s is defined at %s.' % (i._element().name(), i._element().xsdLocation()))
650        rv.append('The containing element type %s is defined at %s' % (self.instance._Name(), six.text_type(self.instance._XSDLocation)))
651        if self.location is not None:
652            rv.append('The unrecognized content %s begins at %s' % (self._valueDescription(), self.location))
653        else:
654            rv.append('The unrecognized content is %s' % (self._valueDescription(),))
655        rv.append('The %s automaton %s in an accepting state.' % (self.instance._Name(), self.automaton_configuration.isAccepting() and "is" or "is not"))
656        if isinstance(self.instance, pyxb.binding.basis.complexTypeDefinition) and self.instance._IsMixed():
657            rv.append('Character information content would be permitted.')
658        acceptable = self.automaton_configuration.acceptableContent()
659        if 0 == len(acceptable):
660            rv.append('No elements or wildcards would be accepted at this point.')
661        else:
662            rv.append('The following element and wildcard content would be accepted:')
663            rv2 = []
664            for u in acceptable:
665                if isinstance(u, pyxb.binding.content.ElementUse):
666                    rv2.append('An element %s per %s' % (u.elementBinding().name(), u.xsdLocation()))
667                else:
668                    assert isinstance(u, pyxb.binding.content.WildcardUse)
669                    rv2.append('A wildcard per %s' % (u.xsdLocation(),))
670            rv.append('\t' + '\n\t'.join(rv2))
671        return '\n'.join(rv)
672
673class BatchElementContentError (ContentValidationError):
674    """Element/wildcard content cannot be reconciled with the required content model.
675
676    This exception occurs in post-construction validation using a
677    fresh validating automaton."""
678
679    instance = None
680    """The binding instance being constructed."""
681
682    fac_configuration = None
683    """The L{pyxb.utils.fac.Configuration} representing the current state of the L{instance} automaton."""
684
685    symbols = None
686    """The sequence of symbols that were accepted as content prior to the error."""
687
688    symbol_set = None
689    """The leftovers from L{pyxb.binding.basis.complexTypeDefinition._symbolSet} that could not be reconciled with the content model."""
690
691    def __init__ (self, instance, fac_configuration, symbols, symbol_set):
692        """@param instance: the value for the L{instance} attribute.
693        @param fac_configuration: the value for the L{fac_configuration} attribute.
694        @param symbols: the value for the L{symbols} attribute.
695        @param symbol_set: the value for the L{symbol_set} attribute."""
696        self.instance = instance
697        self.fac_configuration = fac_configuration
698        self.symbols = symbols
699        self.symbol_set = symbol_set
700        super(BatchElementContentError, self).__init__(instance, fac_configuration, symbols, symbol_set)
701
702    def details (self):
703        import pyxb.binding.basis
704        import pyxb.binding.content
705        i = self.instance
706        rv = [ ]
707        if i._element() is not None:
708            rv.append('The containing element %s is defined at %s.' % (i._element().name(), i._element().xsdLocation()))
709        rv.append('The containing element type %s is defined at %s' % (self.instance._Name(), six.text_type(self.instance._XSDLocation)))
710        rv.append('The %s automaton %s in an accepting state.' % (self.instance._Name(), self.fac_configuration.isAccepting() and "is" or "is not"))
711        if self.symbols is None:
712            rv.append('Any accepted content has been stored in instance')
713        elif 0 == len(self.symbols):
714            rv.append('No content has been accepted')
715        else:
716            rv.append('The last accepted content was %s' % (self.symbols[-1].value._diagnosticName(),))
717        if isinstance(self.instance, pyxb.binding.basis.complexTypeDefinition) and self.instance._IsMixed():
718            rv.append('Character information content would be permitted.')
719        acceptable = self.fac_configuration.acceptableSymbols()
720        if 0 == len(acceptable):
721            rv.append('No elements or wildcards would be accepted at this point.')
722        else:
723            rv.append('The following element and wildcard content would be accepted:')
724            rv2 = []
725            for u in acceptable:
726                if isinstance(u, pyxb.binding.content.ElementUse):
727                    rv2.append('An element %s per %s' % (u.elementBinding().name(), u.xsdLocation()))
728                else:
729                    assert isinstance(u, pyxb.binding.content.WildcardUse)
730                    rv2.append('A wildcard per %s' % (u.xsdLocation(),))
731            rv.append('\t' + '\n\t'.join(rv2))
732        if (self.symbol_set is None) or (0 == len(self.symbol_set)):
733            rv.append('No content remains unconsumed')
734        else:
735            rv.append('The following content was not processed by the automaton:')
736            rv2 = []
737            for (ed, syms) in six.iteritems(self.symbol_set):
738                if ed is None:
739                    rv2.append('xs:any (%u instances)' % (len(syms),))
740                else:
741                    rv2.append('%s (%u instances)' % (ed.name(), len(syms)))
742            rv.append('\t' + '\n\t'.join(rv2))
743        return '\n'.join(rv)
744
745class IncompleteElementContentError (BatchElementContentError):
746    """Validation of an instance failed to produce an accepting state.
747
748    This exception occurs in batch-mode validation."""
749    pass
750
751class UnprocessedElementContentError (BatchElementContentError):
752    """Validation of an instance produced an accepting state but left element material unconsumed.
753
754    This exception occurs in batch-mode validation."""
755    pass
756
757class InvalidPreferredElementContentError (BatchElementContentError):
758    """Use of a preferred element led to inability to generate a valid document"""
759
760    preferred_symbol = None
761    """The element symbol which was not accepted."""
762
763    def __init__ (self, instance, fac_configuration, symbols, symbol_set, preferred_symbol):
764        """@param instance: the value for the L{instance} attribute.
765        @param fac_configuration: the value for the L{fac_configuration} attribute.
766        @param symbols: the value for the L{symbols} attribute.
767        @param symbol_set: the value for the L{symbol_set} attribute.
768        @param preferred_symbol: the value for the L{preferred_symbol} attribute.
769        """
770        self.instance = instance
771        self.fac_configuration = fac_configuration
772        self.symbols = symbols
773        self.symbol_set = symbol_set
774        self.preferred_symbol = preferred_symbol
775        # Bypass immediate parent so we preserve the last argument
776        super(BatchElementContentError, self).__init__(instance, fac_configuration, symbols, symbol_set, preferred_symbol)
777
778@six.python_2_unicode_compatible
779class OrphanElementContentError (ContentValidationError):
780    """An element expected to be used in content is not present in the instance.
781
782    This exception occurs in batch-mode validation when
783    L{pyxb.ValidationConfig.contentInfluencesGeneration} applies,
784    L{pyxb.ValidationConfig.orphanElementInContent} is set to
785    L{pyxb.ValidationConfig.RAISE_EXCEPTION}, and the content list
786    includes an element that is not in the binding instance
787    content.
788    """
789
790    instance = None
791    """The binding instance."""
792
793    preferred = None
794    """An element value from the L{instance} L{content<pyxb.binding.basis.complexTypeDefinition.content>} list which was not found in the L{instance}."""
795
796    def __init__ (self, instance, preferred):
797        """@param instance: the value for the L{instance} attribute.
798        @param preferred: the value for the L{preferred} attribute.
799        """
800        self.instance = instance
801        self.preferred = preferred
802        super(OrphanElementContentError, self).__init__(instance, preferred)
803
804    def __str__ (self):
805        return six.u('Preferred content element not found in instance')
806
807@six.python_2_unicode_compatible
808class SimpleTypeValueError (ValidationError):
809    """Raised when a simple type value does not satisfy its constraints."""
810    type = None
811    """The L{pyxb.binding.basis.simpleTypeDefinition} that constrains values."""
812
813    value = None
814    """The value that violates the constraints of L{type}.  In some
815    cases this is a tuple of arguments passed to a constructor that
816    failed with a built-in exception likeC{ValueError} or
817    C{OverflowError}."""
818
819    def __init__ (self, type, value, location=None):
820        """@param type: the value for the L{type} attribute.
821        @param value: the value for the L{value} attribute.
822        @param location: the value for the L{location} attribute.  Default taken from C{value} if possible."""
823        import pyxb.utils.utility
824        self.type = type
825        self.value = value
826        if (location is None) and isinstance(value, pyxb.utils.utility.Locatable_mixin):
827            location = value._location()
828        self.location = location
829        super(SimpleTypeValueError, self).__init__(type, value, location)
830
831    def __str__ (self):
832        import pyxb.binding.basis
833        if isinstance(self.value, pyxb.binding.basis._TypeBinding_mixin):
834            return six.u('Type %s cannot be created from %s: %s') % (self.type._Name(), self.value._Name(), self.value)
835        return six.u('Type %s cannot be created from: %s') % (self.type._Name(), self.value)
836
837@six.python_2_unicode_compatible
838class SimpleListValueError (SimpleTypeValueError):
839    """Raised when a list simple type contains a member that does not satisfy its constraints.
840
841    In this case, L{type} is the type of the list, and value
842    C{type._ItemType} is the type for which the L{value} is
843    unacceptable."""
844
845    def __str__ (self):
846        return six.u('Member type %s of list type %s cannot accept %s') % (self.type._ItemType._Name(), self.type._Name(), self.value)
847
848@six.python_2_unicode_compatible
849class SimpleUnionValueError (SimpleTypeValueError):
850    """Raised when a union simple type contains a member that does not satisfy its constraints.
851
852    In this case, L{type} is the type of the union, and the value
853    C{type._MemberTypes} is the set of types for which the value is
854    unacceptable.
855
856    The L{value} itself is the tuple of arguments passed to the
857    constructor for the union."""
858
859    def __str__ (self):
860        return six.u('No memberType of %s can be constructed from %s') % (self.type._Name(), self.value)
861
862@six.python_2_unicode_compatible
863class SimpleFacetValueError (SimpleTypeValueError):
864    """Raised when a simple type value does not satisfy a facet constraint.
865
866    This extends L{SimpleTypeValueError} with the L{facet} field which
867    can be used to determine why the value is unacceptable."""
868
869    type = None
870    """The L{pyxb.binding.basis.simpleTypeDefinition} that constrains values."""
871
872    value = None
873    """The value that violates the constraints of L{type}.  In some
874    cases this is a tuple of arguments passed to a constructor that
875    failed with a built-in exception likeC{ValueError} or
876    C{OverflowError}."""
877
878    facet = None
879    """The specific facet that is violated by the value."""
880
881    def __init__ (self, type, value, facet, location=None):
882        """@param type: the value for the L{type} attribute.
883        @param value: the value for the L{value} attribute.
884        @param facet: the value for the L{facet} attribute.
885        @param location: the value for the L{location} attribute.  Default taken from C{value} if possible."""
886        import pyxb.utils.utility
887
888        self.type = type
889        self.value = value
890        self.facet = facet
891        if (location is None) and isinstance(value, pyxb.utils.utility.Locatable_mixin):
892            location = value._location()
893        self.location = location
894        # Bypass immediate parent
895        super(SimpleTypeValueError, self).__init__(type, value, facet)
896
897    def __str__ (self):
898        return six.u('Type %s %s constraint violated by value %s') % (self.type._Name(), self.facet._Name, self.value)
899
900class SimplePluralValueError (SimpleTypeValueError):
901    """Raised when context requires a plural value.
902
903    Unlike L{SimpleListValueError}, in this case the plurality is
904    external to C{type}, for example when an element has simple
905    content and allows multiple occurrences."""
906    pass
907
908class AttributeValidationError (ValidationError):
909    """Raised when an attribute requirement is not satisfied."""
910
911    type = None
912    """The L{pyxb.binding.basis.complexTypeDefinition} subclass of the instance."""
913
914    tag = None
915    """The name of the attribute."""
916
917    instance = None
918    """The binding instance, if available."""
919
920    def __init__ (self, type, tag, instance=None, location=None):
921        """@param type: the value for the L{type} attribute.
922        @param tag: the value for the L{tag} attribute.
923        @param instance: the value for the L{instance} attribute.
924        @param location: the value for the L{location} attribute.  Default taken from C{instance} if possible.
925        """
926        import pyxb.utils.utility as utility
927        self.type = type
928        self.tag = tag
929        self.instance = instance
930        if (location is None) and isinstance(instance, utility.Locatable_mixin):
931            location = instance._location()
932        self.location = location
933        super(AttributeValidationError, self).__init__(type, tag, instance, location)
934
935@six.python_2_unicode_compatible
936class UnrecognizedAttributeError (AttributeValidationError):
937    """Attempt to reference an attribute not sanctioned by content model."""
938    def __str__ (self):
939        return six.u('Attempt to reference unrecognized attribute %s in type %s') % (self.tag, self.type)
940
941@six.python_2_unicode_compatible
942class ProhibitedAttributeError (AttributeValidationError):
943    """Raised when an attribute that is prohibited is set or referenced in an element."""
944    def __str__ (self):
945        return six.u('Attempt to reference prohibited attribute %s in type %s') % (self.tag, self.type)
946
947@six.python_2_unicode_compatible
948class MissingAttributeError (AttributeValidationError):
949    """Raised when an attribute that is required is missing in an element."""
950    def __str__ (self):
951        return six.u('Instance of %s lacks required attribute %s') % (self.type, self.tag)
952
953@six.python_2_unicode_compatible
954class AttributeChangeError (AttributeValidationError):
955    """Attempt to change an attribute that has a fixed value constraint."""
956    def __str__ (self):
957        return six.u('Cannot change fixed attribute %s in type %s') % (self.tag, self.type)
958
959class BindingError (PyXBException):
960    """Raised when the bindings are mis-used.
961
962    These are not validation errors, but rather structural errors.
963    For example, attempts to extract complex content from a type that
964    requires simple content, or vice versa.  """
965
966@six.python_2_unicode_compatible
967class NotSimpleContentError (BindingError):
968    """An operation that requires simple content was invoked on a
969    complex type instance that does not have simple content."""
970
971    instance = None
972    """The binding instance which should have had simple content."""
973
974    def __init__ (self, instance):
975        """@param instance: the binding instance that was mis-used.
976        This will be available in the L{instance} attribute."""
977        self.instance = instance
978        super(BindingError, self).__init__(instance)
979    pass
980
981    def __str__ (self):
982        return six.u('type %s does not have simple content') % (self.instance._Name(),)
983
984@six.python_2_unicode_compatible
985class NotComplexContentError (BindingError):
986    """An operation that requires a content model was invoked on a
987    complex type instance that has empty or simple content."""
988
989    instance = None
990    """The binding instance which should have had a content model."""
991
992    def __init__ (self, instance):
993        """@param instance: the binding instance that was mis-used.
994        This will be available in the L{instance} attribute."""
995        self.instance = instance
996        super(BindingError, self).__init__(instance)
997
998    def __str__ (self):
999        return six.u('type %s has simple/empty content') % (self.instance._Name(),)
1000
1001@six.python_2_unicode_compatible
1002class ReservedNameError (BindingError):
1003    """Reserved name set in binding instance."""
1004
1005    instance = None
1006    """The binding instance."""
1007
1008    name = None
1009    """The name that was caught being assigned"""
1010
1011    def __init__ (self, instance, name):
1012        """@param instance: the value for the L{instance} attribute.
1013        p@param name: the value for the L{name} attribute."""
1014        self.instance = instance
1015        self.name = name
1016        super(ReservedNameError, self).__init__(instance, name)
1017
1018    def __str__ (self):
1019        return six.u('%s is a reserved name within %s') % (self.name, self.instance._Name())
1020
1021class PyXBError (Exception):
1022    """Base class for exceptions that indicate a problem that the user probably can't fix."""
1023    pass
1024
1025class UsageError (PyXBError):
1026    """Raised when the code detects user violation of an API."""
1027
1028class LogicError (PyXBError):
1029    """Raised when the code detects an implementation problem."""
1030
1031class IncompleteImplementationError (LogicError):
1032    """Raised when required capability has not been implemented.
1033
1034    This is only used where it is reasonable to expect the capability
1035    to be present, such as a feature of XML schema that is not
1036    supported (e.g., the redefine directive)."""
1037