1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 """Classes related to XMLSchema facets.
16
17 The definitions herein are from sections U{4.2<http://www.w3.org/TR/xmlschema-2/index.html#rf-facets>}
18 and U{4.3<http://www.w3.org/TR/xmlschema-2/index.html#rf-facets>} of
19 U{XML Schema Part 2: Datatypes<http://www.w3.org/TR/xmlschema-2/>}.
20 Facets are attributes of a datatype that constrain its lexical and
21 value spaces.
22
23 """
24
25 from xml.dom import Node
26 import pyxb
27 import types
28 import datatypes
29 import basis
30 from pyxb.utils import utility
31 from pyxb.utils import domutils
32 import re
33
34 -class Facet (pyxb.cscRoot):
35 """The base class for facets.
36
37 This provides association with STDs, a name, and a value for the facet.
38 """
39
40 _Name = None
41 @classmethod
43 """The name of a facet is a class constant."""
44 return self._Name
45
46 __baseTypeDefinition = None
48 """The SimpleTypeDefinition component restricted by this facet.
49
50 Note: this is NOT the STD to which the facet belongs, but is
51 usually that STD's base type. I.e., this jumps us through all
52 the containing restrictions and extensions to get to the core
53 type definition."""
54 return self.__baseTypeDefinition
55
56 __ownerTypeDefinition = None
58 """The SimpleTypeDefinition component to which this facet belongs.
59
60 I.e., the one in which the hasFacet specification was found.
61 This value is None if the facet is not associated with an
62 STD."""
63 return self.__ownerTypeDefinition
64
65
66
67
68 _ValueDatatype = None
69
70
71 __valueDatatype = None
73 """Get the datatype used to represent values of the facet.
74
75 This usually has nothing to do with the owner datatype; for
76 example, the length facet may apply to any STD but the value
77 of the facet is an integer. In generated bindings this is
78 usually set explicitly in the facet constructor; when
79 processing a schema, it is derived from the value's type
80 definition.
81 """
82 if self.__valueDatatype is None:
83 assert self.baseTypeDefinition() is not None
84 return self.baseTypeDefinition().pythonSupport()
85 return self.__valueDatatype
86
87 __value = None
90
91 __annotation = None
93
95 """Create a facet instance, initializing it from the keyword parameters."""
96 super(Facet, self).__init__(**kw)
97
98 assert Facet != self.__class__
99 self.setFromKeywords(_reset=True, _constructor=True, **kw)
100
102 """Configure values of the facet from a set of keywords.
103
104 This method is pre-extended; subclasses should invoke the
105 parent method after setting their local configuration.
106
107 @keyword _reset: If C{False} or missing, existing values will
108 be retained if they do not appear in the
109 keywords. If C{True}, members not defined in
110 the keywords are set to a default.
111 @keyword base_type_definition:
112 @keyword owner_type_definition:
113 @keyword value_datatype:
114 """
115
116 if not kw.get('_reset', False):
117 kw.setdefault('base_type_definition', self.__baseTypeDefinition)
118 kw.setdefault('owner_type_definition', self.__ownerTypeDefinition)
119 kw.setdefault('value_datatype', self.__valueDatatype)
120 self.__baseTypeDefinition = kw.get('base_type_definition', None)
121 self.__ownerTypeDefinition = kw.get('owner_type_definition', None)
122 self.__valueDatatype = kw.get('value_datatype', self._ValueDatatype)
123
124
125
126 assert (self.__valueDatatype is not None) or (self.__baseTypeDefinition is not None)
127 super_fn = getattr(super(Facet, self), '_setFromKeywords_vb', lambda *a,**kw: self)
128 return super_fn(**kw)
129
131 """Public entrypoint to the _setFromKeywords_vb call hierarchy."""
132 return self._setFromKeywords_vb(**kw)
133
134 @classmethod
136 """Given the name of a facet, return the Facet subclass that represents it."""
137 assert cls != Facet
138 if 0 <= name.find(':'):
139 name = name.split(':', 1)[1]
140 facet_class = globals().get('%s_%s' % (cls._FacetPrefix, name), None)
141 if facet_class is None:
142 raise pyxb.LogicError('Unrecognized facet name %s: expect %s' % (name, ','.join([_f._Name for _f in cls.Facets])))
143 assert facet_class is not None
144 return facet_class
145
156
163
165 """One of the facets defined in section 4.3, which provide
166 constraints on the lexical space of a type definition."""
167
168
169
170
171
172 _FacetPrefix = 'CF'
173
176
179
181 """Return True iff the given value satisfies the constraint represented by this facet instance.
182
183 The actual test is delegated to the subclasses."""
184 return self._validateConstraint_vx(value)
185
187 kwv = kw.get('value', None)
188 if kwv is not None:
189 if not isinstance(kwv, self.valueDatatype()):
190 kwv = self.valueDatatype()(kwv)
191 self._value(kwv)
192
194 """Extend base class.
195
196 Additional keywords:
197 * value
198 """
199
200 super_fn = getattr(super(ConstrainingFacet, self), '_setFromKeywords_vb', lambda *a,**kw: self)
201 rv = super_fn(**kw)
202 self.__setFromKeywords(**kw)
203 return rv
204
206 """Marker class to indicate that the facet instance must be told
207 its datatype when it is constructed.
208
209 This is necessary for facets like L{CF_minInclusive} and
210 L{CF_minExclusive}, for which the value is determined by the base
211 type definition of the associated STD. In some cases the value
212 that must be used in the facet cannot be represented in the Python
213 type used for the facet; see L{LateDatatypeBindsSuperclass}.
214 """
215
216 _LateDatatypeBindsSuperclass = None
217 """The class variable that indicates that the Subclasses must
218 override this variable with a value of C{True} or C{False}. The
219 value is C{True} iff the value used for the facet is not within
220 the value space of the corresponding value datatype; for example,
221 L{CF_minExclusive}."""
222
223
224 @classmethod
232
233 @classmethod
254
257
259 """Mix-in to a constraining facet that adds support for the 'fixed' property."""
260 __fixed = None
262
264 if kw.get('_reset', False):
265 self.__fixed = None
266 kwv = kw.get('fixed', None)
267 if kwv is not None:
268 self.__fixed = datatypes.boolean(kwv)
269
271 """Extend base class.
272
273 Additional keywords:
274 * fixed
275 """
276 self.__setFromKeywords(**kw)
277 super_fn = getattr(super(_Fixed_mixin, self), '_setFromKeywords_vb', lambda *a,**kw: self)
278 return super_fn(**kw)
279
281 """Mix-in to handle facets whose values are collections, not scalars.
282
283 For example, the enumeration and pattern facets maintain a list of
284 enumeration values and patterns, respectively, as their value
285 space.
286
287 Subclasses must define a class variable _CollectionFacet_itemType
288 which is a reference to a class that is used to construct members
289 of the collection.
290 """
291
292 __items = None
294 """Extend base class.
295
296 @keyword _constructor: If C{False} or absent, the object being
297 set is a member of the collection. If
298 C{True}, the object being set is the
299 collection itself.
300 """
301 if kw.get('_reset', False):
302 self.__items = []
303 if not kw.get('_constructor', False):
304
305 self.__items.append(self._CollectionFacet_itemType(facet_instance=self, **kw))
306 super_fn = getattr(super(_CollectionFacet_mixin, self), '_setFromKeywords_vb', lambda *a,**kw: self)
307 return super_fn(**kw)
308
310 """The members of the collection, as a reference."""
311 return self.__items
312
314 """The members of the collection, as a copy."""
315 return self.__items[:]
316
318 """The members of the collection as an iterator"""
319 return iter(self.__items)
320
321 -class CF_length (ConstrainingFacet, _Fixed_mixin):
332
334 """A facet that constrains the length of the lexical representation of a value.
335
336 See U{http://www.w3.org/TR/xmlschema-2/#rf-minLength}
337 """
338 _Name = 'minLength'
339 _ValueDatatype = datatypes.nonNegativeInteger
340
344
346 """A facet that constrains the length of the lexical representation of a value.
347
348 See U{http://www.w3.org/TR/xmlschema-2/#rf-minLength}
349 """
350 _Name = 'maxLength'
351 _ValueDatatype = datatypes.nonNegativeInteger
352
356
357 import pyxb.utils.xmlre
395
396 -class CF_pattern (ConstrainingFacet, _CollectionFacet_mixin):
397 """A facet that constrains the lexical representation of a value
398 to match one of a set of patterns.
399
400 See U{http://www.w3.org/TR/xmlschema-2/#rf-pattern}
401
402 @note: In PyXB, pattern constraints are ignored for any type with
403 a Python representation that does not derive from C{basestring}.
404 This is due to the difficulty in reconstructing the lexical
405 representation of a non-string type after it has been converted to
406 its value space.
407
408 @todo: On creating new instances of non-string simple types from
409 string representations, we could apply pattern constraints. That
410 would mean checking them prior to invoking the Factory method.
411 """
412 _Name = 'pattern'
413 _CollectionFacet_itemType = _PatternElement
414 _ValueDatatype = datatypes.string
415
416 __patternElements = None
418
422
427
439
441 """This class represents individual values that appear within a
442 L{CF_enumeration} collection."""
443
444 __value = None
446 """The Python value that is used for equality testing
447 against this enumeration.
448
449 This is an instance of L{enumeration.valueDatatype()<CF_enumeration.valueDatatype>},
450 initialized from the unicodeValue."""
451 return self.__value
452
453 __tag = None
455 """The Python identifier used for the named constant representing
456 the enumeration value.
457
458 This should include any desired prefix, since it must be
459 unique within its binding class. If C{None}, no enumeration
460 constant will be generated."""
461 return self.__tag
463 """Set the tag to be used for this enumeration."""
464 self.__tag = tag
465
466 __enumeration = None
468 """A reference to the L{CF_enumeration} instance that owns this element."""
469 return self.__enumeration
470
471 __unicodeValue = None
473 """The unicode string that defines the enumeration value."""
474 return self.__unicodeValue
475
476 - def __init__ (self, enumeration=None, unicode_value=None,
477 description=None, annotation=None, tag=None,
478 **kw):
501
504
505 -class CF_enumeration (ConstrainingFacet, _CollectionFacet_mixin, _LateDatatype_mixin):
506 """Capture a constraint that restricts valid values to a fixed set.
507
508 A STD that has an enumeration restriction should mix-in
509 L{pyxb.binding.basis.enumeration_mixin}, and should have a class
510 variable titled C{_CF_enumeration} that is an instance of this
511 class.
512
513 "unicode" refers to the Unicode string by which the value is
514 represented in XML.
515
516 "tag" refers to the Python member reference associated with the
517 enumeration. The value is derived from the unicode value of the
518 enumeration element and an optional prefix that identifies the
519 owning simple type when the tag is promoted to module-level
520 visibility.
521
522 "value" refers to the Python value held in the tag
523
524 See U{http://www.w3.org/TR/xmlschema-2/#rf-enumeration}
525 """
526 _Name = 'enumeration'
527 _CollectionFacet_itemType = _EnumerationElement
528 _LateDatatypeBindsSuperclass = False
529
530 __tagToElement = None
531 __valueToElement = None
532 __unicodeToElement = None
533
534
535
536 __enumPrefix = None
537
544
547
549 """@deprecated: Use L{items} or L{iteritems} instead."""
550 return self.items()
551
553 """Return a list of enumeration values."""
554 return [ _ee.value() for _ee in self.iteritems() ]
555
557 """Generate the enumeration values."""
558 for ee in self.iteritems():
559 yield ee.value()
560
576
578 """Return the L{_EnumerationElement} instance that has the given value.
579
580 @raise KeyError: the value is not valid for the enumeration."""
581 return self.__valueToElement[value]
582
584 """Return the enumeration value corresponding to the given unicode string.
585
586 If ustr is not a valid option for this enumeration, return None."""
587 rv = self.__unicodeToElement.get(ustr, None)
588 if rv is not None:
589 rv = rv.value()
590 return rv
591
593
594
595 if 0 == len(self._items()):
596 return True
597 for ee in self.iteritems():
598 if ee.value() == value:
599 return True
600 return False
601
603 """Marker class to indicate that the generated binding has enumeration members."""
604 @classmethod
607
609 """The enumeration used to constrain the whiteSpace facet"""
610 pass
611 _WhiteSpace_enum._CF_enumeration = CF_enumeration(value_datatype=_WhiteSpace_enum)
612 _WhiteSpace_enum.preserve = _WhiteSpace_enum._CF_enumeration.addEnumeration(unicode_value=u'preserve', tag='preserve')
613 _WhiteSpace_enum.replace = _WhiteSpace_enum._CF_enumeration.addEnumeration(unicode_value=u'replace', tag='replace')
614 _WhiteSpace_enum.collapse = _WhiteSpace_enum._CF_enumeration.addEnumeration(unicode_value=u'collapse', tag='collapse')
615
616
617
618
619 _WhiteSpace_enum._InitializeFacetMap(_WhiteSpace_enum._CF_enumeration)
622 """Specify the value-space interpretation of whitespace.
623
624 See U{http://www.w3.org/TR/xmlschema-2/#rf-whiteSpace}
625 """
626 _Name = 'whiteSpace'
627 _ValueDatatype = _WhiteSpace_enum
628
629 __TabCRLF_re = re.compile("[\t\n\r]")
630 __MultiSpace_re = re.compile(" +")
641
643 """No validation rules for whitespace facet."""
644 return True
645
646 -class CF_minInclusive (ConstrainingFacet, _Fixed_mixin, _LateDatatype_mixin):
647 """Specify the minimum legal value for the constrained type.
648
649 See U{http://www.w3.org/TR/xmlschema-2/#rf-minInclusive}
650 """
651 _Name = 'minInclusive'
652 _LateDatatypeBindsSuperclass = False
653
656
657
658 -class CF_maxInclusive (ConstrainingFacet, _Fixed_mixin, _LateDatatype_mixin):
659 """Specify the maximum legal value for the constrained type.
660
661 See U{http://www.w3.org/TR/xmlschema-2/#rf-maxInclusive}
662 """
663 _Name = 'maxInclusive'
664 _LateDatatypeBindsSuperclass = False
665
668
669 -class CF_minExclusive (ConstrainingFacet, _Fixed_mixin, _LateDatatype_mixin):
670 """Specify the exclusive lower bound of legal values for the constrained type.
671
672 See U{http://www.w3.org/TR/xmlschema-2/#rf-minExclusive}
673 """
674 _Name = 'minExclusive'
675 _LateDatatypeBindsSuperclass = True
676
679
680 -class CF_maxExclusive (ConstrainingFacet, _Fixed_mixin, _LateDatatype_mixin):
681 """Specify the exclusive upper bound of legal values for the constrained type.
682
683 See U{http://www.w3.org/TR/xmlschema-2/#rf-maxExclusive}
684 """
685 _Name = 'maxExclusive'
686 _LateDatatypeBindsSuperclass = True
687
690
692 """Specify the number of digits in the *value* space of the type.
693
694 See U{http://www.w3.org/TR/xmlschema-2/#rf-totalDigits}
695 """
696 _Name = 'totalDigits'
697 _ValueDatatype = datatypes.positiveInteger
698
700 if self.value() is None:
701 return True
702 n = 0
703 scale = 1
704 match = False
705 v = None
706 while (n <= self.value()) and (not match):
707 v = long(value * scale)
708 match = ((value * scale) == v)
709 if self.value() == n:
710 break
711 n += 1
712 scale *= 10
713 while n < self.value():
714 n += 1
715 scale *= 10
716 return match and (v is not None) and (abs(v) < scale)
717
719 """Specify the number of sub-unit digits in the *value* space of the type.
720
721 See U{http://www.w3.org/TR/xmlschema-2/#rf-fractionDigits}
722 """
723 _Name = 'fractionDigits'
724 _ValueDatatype = datatypes.nonNegativeInteger
725
727 if self.value() is None:
728 return True
729 n = 0
730 scale = 1
731 while n <= self.value():
732 if ((value * scale) == long(value * scale)):
733 return True
734 n += 1
735 scale *= 10
736 return False
737
739 """A fundamental facet provides information on the value space of the associated type."""
740
741 _FacetPrefix = 'FF'
742
743 @classmethod
744 - def CreateFromDOM (cls, node, owner_type_definition, base_type_definition=None):
745 facet_class = cls.ClassForFacet(node.getAttribute('name'))
746 rv = facet_class(base_type_definition=base_type_definition,
747 owner_type_definition=owner_type_definition)
748 rv.updateFromDOM(node)
749
755
766
768 """Specifies that the associated type supports a notion of equality.
769
770 See U{http://www.w3.org/TR/xmlschema-2/#equal}
771 """
772
773 _Name = 'equal'
774
776 """Specifies that the associated type supports a notion of order.
777
778 See U{http://www.w3.org/TR/xmlschema-2/#rf-ordered}
779 """
780
781 _LegalValues = ( 'false', 'partial', 'total' )
782 _Name = 'ordered'
783 _ValueDatatype = datatypes.string
784
788
790 """Specifies that the associated type supports a notion of bounds.
791
792 See U{http://www.w3.org/TR/xmlschema-2/#rf-bounded}
793 """
794
795 _Name = 'bounded'
796 _ValueDatatype = datatypes.boolean
797
799 """Specifies that the associated type supports a notion of length.
800
801 See U{http://www.w3.org/TR/xmlschema-2/#rf-cardinality}
802 """
803
804 _LegalValues = ( 'finite', 'countably infinite' )
805 _Name = 'cardinality'
806 _ValueDatatype = datatypes.string
810
812 """Specifies that the associated type represents a number.
813
814 See U{http://www.w3.org/TR/xmlschema-2/#rf-numeric}
815 """
816
817 _Name = 'numeric'
818 _ValueDatatype = datatypes.boolean
819
820
821 ConstrainingFacet.Facets = [
822 CF_length, CF_minLength, CF_maxLength, CF_pattern, CF_enumeration,
823 CF_whiteSpace, CF_minInclusive, CF_maxInclusive, CF_minExclusive,
824 CF_maxExclusive, CF_totalDigits, CF_fractionDigits ]
825
826 FundamentalFacet.Facets = [
827 FF_equal, FF_ordered, FF_bounded, FF_cardinality, FF_numeric ]
828
829 Facet.Facets = []
830 Facet.Facets.extend(ConstrainingFacet.Facets)
831 Facet.Facets.extend(FundamentalFacet.Facets)
832