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 __ownerDatatype = None
67 """The PyXB_simpleTypeDefinition subclass to which this facet belongs."""
68 return self.__ownerDatatype
71
72
73
74
75 _ValueDatatype = None
76
77
78 __valueDatatype = None
80 """Get the datatype used to represent values of the facet.
81
82 This usually has nothing to do with the owner datatype; for
83 example, the length facet may apply to any STD but the value
84 of the facet is an integer. In generated bindings this is
85 usually set explicitly in the facet constructor; when
86 processing a schema, it is derived from the value's type
87 definition.
88 """
89 if self.__valueDatatype is None:
90 assert self.baseTypeDefinition() is not None
91 return self.baseTypeDefinition().pythonSupport()
92 return self.__valueDatatype
93
94 __value = None
97
98 __annotation = None
100
102 """Create a facet instance, initializing it from the keyword parameters."""
103 super(Facet, self).__init__(**kw)
104
105 assert Facet != self.__class__
106 self.setFromKeywords(_reset=True, _constructor=True, **kw)
107
109 """Configure values of the facet from a set of keywords.
110
111 This method is pre-extended; subclasses should invoke the
112 parent method after setting their local configuration.
113
114 @keyword _reset: If C{False} or missing, existing values will
115 be retained if they do not appear in the
116 keywords. If C{True}, members not defined in
117 the keywords are set to a default.
118 @keyword base_type_definition:
119 @keyword owner_type_definition:
120 @keyword owner_datatype:
121 @keyword value_datatype:
122 """
123
124 if not kw.get('_reset', False):
125 kw.setdefault('base_type_definition', self.__baseTypeDefinition)
126 kw.setdefault('owner_type_definition', self.__ownerTypeDefinition)
127 kw.setdefault('owner_datatype', self.__ownerDatatype)
128 kw.setdefault('value_datatype', self.__valueDatatype)
129 self.__baseTypeDefinition = kw.get('base_type_definition', None)
130 self.__ownerTypeDefinition = kw.get('owner_type_definition', None)
131 self.__ownerDatatype = kw.get('owner_datatype', None)
132 self.__valueDatatype = kw.get('value_datatype', self._ValueDatatype)
133
134
135
136 assert (self.__valueDatatype is not None) or (self.__baseTypeDefinition is not None)
137 super_fn = getattr(super(Facet, self), '_setFromKeywords_vb', lambda *a,**kw: self)
138 return super_fn(**kw)
139
141 """Public entrypoint to the _setFromKeywords_vb call hierarchy."""
142 return self._setFromKeywords_vb(**kw)
143
144 @classmethod
146 """Given the name of a facet, return the Facet subclass that represents it."""
147 assert cls != Facet
148 if 0 <= name.find(':'):
149 name = name.split(':', 1)[1]
150 facet_class = globals().get('%s_%s' % (cls._FacetPrefix, name), None)
151 if facet_class is None:
152 raise pyxb.LogicError('Unrecognized facet name %s: expect %s' % (name, ','.join([_f._Name for _f in cls.Facets])))
153 assert facet_class is not None
154 return facet_class
155
166
173
175 """One of the facets defined in section 4.3, which provide
176 constraints on the lexical space of a type definition."""
177
178
179
180
181
182 _FacetPrefix = 'CF'
183
186
189
191 """Return True iff the given value satisfies the constraint represented by this facet instance.
192
193 The actual test is delegated to the subclasses."""
194 return self._validateConstraint_vx(value)
195
197 kwv = kw.get('value', None)
198 if kwv is not None:
199 if not isinstance(kwv, self.valueDatatype()):
200 kwv = self.valueDatatype()(kwv)
201 self._value(kwv)
202
204 """Extend base class.
205
206 Additional keywords:
207 * value
208 """
209
210 super_fn = getattr(super(ConstrainingFacet, self), '_setFromKeywords_vb', lambda *a,**kw: self)
211 rv = super_fn(**kw)
212 self.__setFromKeywords(**kw)
213 return rv
214
216 """Marker class to indicate that the facet instance must be told
217 its datatype when it is constructed.
218
219 This is necessary for facets like L{CF_minInclusive} and
220 L{CF_minExclusive}, for which the value is determined by the base
221 type definition of the associated STD. In some cases the value
222 that must be used in the facet cannot be represented in the Python
223 type used for the facet; see L{LateDatatypeBindsSuperclass}.
224 """
225
226 _LateDatatypeBindsSuperclass = None
227 """The class variable that indicates that the Subclasses must
228 override this variable with a value of C{True} or C{False}. The
229 value is C{True} iff the value used for the facet is not within
230 the value space of the corresponding value datatype; for example,
231 L{CF_minExclusive}."""
232
233
234 @classmethod
242
243 @classmethod
264
267
269 """Mix-in to a constraining facet that adds support for the 'fixed' property."""
270 __fixed = None
272
274 if kw.get('_reset', False):
275 self.__fixed = None
276 kwv = kw.get('fixed', None)
277 if kwv is not None:
278 self.__fixed = datatypes.boolean(kwv)
279
281 """Extend base class.
282
283 Additional keywords:
284 * fixed
285 """
286 self.__setFromKeywords(**kw)
287 super_fn = getattr(super(_Fixed_mixin, self), '_setFromKeywords_vb', lambda *a,**kw: self)
288 return super_fn(**kw)
289
291 """Mix-in to handle facets whose values are collections, not scalars.
292
293 For example, the enumeration and pattern facets maintain a list of
294 enumeration values and patterns, respectively, as their value
295 space.
296
297 Subclasses must define a class variable _CollectionFacet_itemType
298 which is a reference to a class that is used to construct members
299 of the collection.
300 """
301
302 __items = None
304 """Extend base class.
305
306 @keyword _constructor: If C{False} or absent, the object being
307 set is a member of the collection. If
308 C{True}, the object being set is the
309 collection itself.
310 """
311 if kw.get('_reset', False):
312 self.__items = []
313 if not kw.get('_constructor', False):
314
315 self.__items.append(self._CollectionFacet_itemType(facet_instance=self, **kw))
316 super_fn = getattr(super(_CollectionFacet_mixin, self), '_setFromKeywords_vb', lambda *a,**kw: self)
317 return super_fn(**kw)
318
320 """The members of the collection."""
321
322 return self.__items
323
325 """A generator for members of the collection."""
326 for item in self.items():
327 yield item.value
328
329 -class CF_length (ConstrainingFacet, _Fixed_mixin):
340
342 """A facet that constrains the length of the lexical representation of a value.
343
344 See U{http://www.w3.org/TR/xmlschema-2/#rf-minLength}
345 """
346 _Name = 'minLength'
347 _ValueDatatype = datatypes.nonNegativeInteger
348
352
354 """A facet that constrains the length of the lexical representation of a value.
355
356 See U{http://www.w3.org/TR/xmlschema-2/#rf-minLength}
357 """
358 _Name = 'maxLength'
359 _ValueDatatype = datatypes.nonNegativeInteger
360
364
365 import pyxb.utils.xmlre
403
404 -class CF_pattern (ConstrainingFacet, _CollectionFacet_mixin):
405 """A facet that constrains the lexical representation of a value
406 to match one of a set of patterns.
407
408 See U{http://www.w3.org/TR/xmlschema-2/#rf-pattern}
409
410 @note: In PyXB, pattern constraints are ignored for any type with
411 a Python representation that does not derive from C{basestring}.
412 This is due to the difficulty in reconstructing the lexical
413 representation of a non-string type after it has been converted to
414 its value space.
415
416 @todo: On creating new instances of non-string simple types from
417 string representations, we could apply pattern constraints. That
418 would mean checking them prior to invoking the Factory method.
419 """
420 _Name = 'pattern'
421 _CollectionFacet_itemType = _PatternElement
422 _ValueDatatype = datatypes.string
423
424 __patternElements = None
426
430
435
447
449 """This class represents individual values that appear within a
450 L{CF_enumeration} collection."""
451
452 __value = None
454 """The Python value that is used for equality testing
455 against this enumeration.
456
457 This is an instance of L{enumeration.valueDatatype()<CF_enumeration.valueDatatype>},
458 initialized from the unicodeValue."""
459 return self.__value
460
461 __tag = None
463 """The Python identifier used for the named constant representing
464 the enumeration value.
465
466 This should include any desired prefix, since it must be
467 unique within its binding class. If C{None}, no enumeration
468 constant will be generated."""
469 return self.__tag
471 """Set the tag to be used for this enumeration."""
472 self.__tag = tag
473
474 __bindingTag = None
479
480 __enumeration = None
482 """A reference to the L{CF_enumeration} instance that owns this element."""
483 return self.__enumeration
484
485 __unicodeValue = None
487 """The unicode string that defines the enumeration value."""
488 return self.__unicodeValue
489
490 - def __init__ (self, enumeration=None, unicode_value=None,
491 description=None, annotation=None,
492 **kw):
514
517
518 -class CF_enumeration (ConstrainingFacet, _CollectionFacet_mixin, _LateDatatype_mixin):
519 """Capture a constraint that restricts valid values to a fixed set.
520
521 A STD that has an enumeration restriction should mix-in
522 L{pyxb.binding.basis.enumeration_mixin}, and should have a class
523 variable titled C{_CF_enumeration} that is an instance of this
524 class.
525
526 "unicode" refers to the Unicode string by which the value is
527 represented in XML.
528
529 "tag" refers to the Python member reference associated with the
530 enumeration. The value is derived from the unicode value of the
531 enumeration element and an optional prefix that identifies the
532 owning simple type when the tag is promoted to module-level
533 visibility.
534
535 "value" refers to the Python value held in the tag
536
537 See U{http://www.w3.org/TR/xmlschema-2/#rf-enumeration}
538 """
539 _Name = 'enumeration'
540 _CollectionFacet_itemType = _EnumerationElement
541 _LateDatatypeBindsSuperclass = False
542
543 __elements = None
544 __tagToElement = None
545 __valueToElement = None
546 __unicodeToElement = None
547
548
549
550 __enumPrefix = None
551
559
562
578
580 """Return the L{_EnumerationElement} instance that has the given value.
581
582 @raise KeyError: the value is not valid for the enumeration."""
583 return self.__valueToElement[value]
584
586 """Return the enumeration value corresponding to the given unicode string.
587
588 If ustr is not a valid option for this enumeration, return None."""
589 rv = self.__unicodeToElement.get(ustr, None)
590 if rv is not None:
591 rv = rv.value()
592 return rv
593
603
605 """Marker class to indicate that the generated binding has enumeration members."""
606 @classmethod
609
611 """The enumeration used to constrain the whiteSpace facet"""
612 pass
613 _WhiteSpace_enum._CF_enumeration = CF_enumeration(value_datatype=_WhiteSpace_enum)
614 _WhiteSpace_enum.preserve = _WhiteSpace_enum._CF_enumeration.addEnumeration(unicode_value=u'preserve')
615 _WhiteSpace_enum.replace = _WhiteSpace_enum._CF_enumeration.addEnumeration(unicode_value=u'replace')
616 _WhiteSpace_enum.collapse = _WhiteSpace_enum._CF_enumeration.addEnumeration(unicode_value=u'collapse')
617
618
619
620
621 _WhiteSpace_enum._InitializeFacetMap(_WhiteSpace_enum._CF_enumeration)
624 """Specify the value-space interpretation of whitespace.
625
626 See U{http://www.w3.org/TR/xmlschema-2/#rf-whiteSpace}
627 """
628 _Name = 'whiteSpace'
629 _ValueDatatype = _WhiteSpace_enum
630
631 __TabCRLF_re = re.compile("[\t\n\r]")
632 __MultiSpace_re = re.compile(" +")
643
645 """No validation rules for whitespace facet."""
646 return True
647
648 -class CF_minInclusive (ConstrainingFacet, _Fixed_mixin, _LateDatatype_mixin):
649 """Specify the minimum legal value for the constrained type.
650
651 See U{http://www.w3.org/TR/xmlschema-2/#rf-minInclusive}
652 """
653 _Name = 'minInclusive'
654 _LateDatatypeBindsSuperclass = False
655
658
659
660 -class CF_maxInclusive (ConstrainingFacet, _Fixed_mixin, _LateDatatype_mixin):
661 """Specify the maximum legal value for the constrained type.
662
663 See U{http://www.w3.org/TR/xmlschema-2/#rf-maxInclusive}
664 """
665 _Name = 'maxInclusive'
666 _LateDatatypeBindsSuperclass = False
667
670
671 -class CF_minExclusive (ConstrainingFacet, _Fixed_mixin, _LateDatatype_mixin):
672 """Specify the exclusive lower bound of legal values for the constrained type.
673
674 See U{http://www.w3.org/TR/xmlschema-2/#rf-minExclusive}
675 """
676 _Name = 'minExclusive'
677 _LateDatatypeBindsSuperclass = True
678
681
682 -class CF_maxExclusive (ConstrainingFacet, _Fixed_mixin, _LateDatatype_mixin):
683 """Specify the exclusive upper bound of legal values for the constrained type.
684
685 See U{http://www.w3.org/TR/xmlschema-2/#rf-maxExclusive}
686 """
687 _Name = 'maxExclusive'
688 _LateDatatypeBindsSuperclass = True
689
692
694 """Specify the number of digits in the *value* space of the type.
695
696 See U{http://www.w3.org/TR/xmlschema-2/#rf-totalDigits}
697 """
698 _Name = 'totalDigits'
699 _ValueDatatype = datatypes.positiveInteger
700
702 if self.value() is None:
703 return True
704 n = 0
705 scale = 1
706 match = False
707 v = None
708 while (n <= self.value()) and (not match):
709 v = long(value * scale)
710 match = ((value * scale) == v)
711 if self.value() == n:
712 break
713 n += 1
714 scale *= 10
715 while n < self.value():
716 n += 1
717 scale *= 10
718 return match and (v is not None) and (abs(v) < scale)
719
721 """Specify the number of sub-unit digits in the *value* space of the type.
722
723 See U{http://www.w3.org/TR/xmlschema-2/#rf-fractionDigits}
724 """
725 _Name = 'fractionDigits'
726 _ValueDatatype = datatypes.nonNegativeInteger
727
729 if self.value() is None:
730 return True
731 n = 0
732 scale = 1
733 while n <= self.value():
734 if ((value * scale) == long(value * scale)):
735 return True
736 n += 1
737 scale *= 10
738 return False
739
741 """A fundamental facet provides information on the value space of the associated type."""
742
743 _FacetPrefix = 'FF'
744
745 @classmethod
746 - def CreateFromDOM (cls, node, owner_type_definition, base_type_definition=None):
747 facet_class = cls.ClassForFacet(node.getAttribute('name'))
748 rv = facet_class(base_type_definition=base_type_definition,
749 owner_type_definition=owner_type_definition)
750 rv.updateFromDOM(node)
751
757
768
770 """Specifies that the associated type supports a notion of equality.
771
772 See U{http://www.w3.org/TR/xmlschema-2/#equal}
773 """
774
775 _Name = 'equal'
776
778 """Specifies that the associated type supports a notion of order.
779
780 See U{http://www.w3.org/TR/xmlschema-2/#rf-ordered}
781 """
782
783 _LegalValues = ( 'false', 'partial', 'total' )
784 _Name = 'ordered'
785 _ValueDatatype = datatypes.string
786
790
792 """Specifies that the associated type supports a notion of bounds.
793
794 See U{http://www.w3.org/TR/xmlschema-2/#rf-bounded}
795 """
796
797 _Name = 'bounded'
798 _ValueDatatype = datatypes.boolean
799
801 """Specifies that the associated type supports a notion of length.
802
803 See U{http://www.w3.org/TR/xmlschema-2/#rf-cardinality}
804 """
805
806 _LegalValues = ( 'finite', 'countably infinite' )
807 _Name = 'cardinality'
808 _ValueDatatype = datatypes.string
812
814 """Specifies that the associated type represents a number.
815
816 See U{http://www.w3.org/TR/xmlschema-2/#rf-numeric}
817 """
818
819 _Name = 'numeric'
820 _ValueDatatype = datatypes.boolean
821
822
823 ConstrainingFacet.Facets = [
824 CF_length, CF_minLength, CF_maxLength, CF_pattern, CF_enumeration,
825 CF_whiteSpace, CF_minInclusive, CF_maxInclusive, CF_minExclusive,
826 CF_maxExclusive, CF_totalDigits, CF_fractionDigits ]
827
828 FundamentalFacet.Facets = [
829 FF_equal, FF_ordered, FF_bounded, FF_cardinality, FF_numeric ]
830
831 Facet.Facets = []
832 Facet.Facets.extend(ConstrainingFacet.Facets)
833 Facet.Facets.extend(FundamentalFacet.Facets)
834