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."""
311
312 return self.__items
313
315 """A generator for members of the collection."""
316 for item in self.items():
317 yield item.value
318
319 -class CF_length (ConstrainingFacet, _Fixed_mixin):
330
332 """A facet that constrains the length of the lexical representation of a value.
333
334 See U{http://www.w3.org/TR/xmlschema-2/#rf-minLength}
335 """
336 _Name = 'minLength'
337 _ValueDatatype = datatypes.nonNegativeInteger
338
342
344 """A facet that constrains the length of the lexical representation of a value.
345
346 See U{http://www.w3.org/TR/xmlschema-2/#rf-minLength}
347 """
348 _Name = 'maxLength'
349 _ValueDatatype = datatypes.nonNegativeInteger
350
354
355 import pyxb.utils.xmlre
393
394 -class CF_pattern (ConstrainingFacet, _CollectionFacet_mixin):
395 """A facet that constrains the lexical representation of a value
396 to match one of a set of patterns.
397
398 See U{http://www.w3.org/TR/xmlschema-2/#rf-pattern}
399
400 @note: In PyXB, pattern constraints are ignored for any type with
401 a Python representation that does not derive from C{basestring}.
402 This is due to the difficulty in reconstructing the lexical
403 representation of a non-string type after it has been converted to
404 its value space.
405
406 @todo: On creating new instances of non-string simple types from
407 string representations, we could apply pattern constraints. That
408 would mean checking them prior to invoking the Factory method.
409 """
410 _Name = 'pattern'
411 _CollectionFacet_itemType = _PatternElement
412 _ValueDatatype = datatypes.string
413
414 __patternElements = None
416
420
425
437
439 """This class represents individual values that appear within a
440 L{CF_enumeration} collection."""
441
442 __value = None
444 """The Python value that is used for equality testing
445 against this enumeration.
446
447 This is an instance of L{enumeration.valueDatatype()<CF_enumeration.valueDatatype>},
448 initialized from the unicodeValue."""
449 return self.__value
450
451 __tag = None
453 """The Python identifier used for the named constant representing
454 the enumeration value.
455
456 This should include any desired prefix, since it must be
457 unique within its binding class. If C{None}, no enumeration
458 constant will be generated."""
459 return self.__tag
461 """Set the tag to be used for this enumeration."""
462 self.__tag = tag
463
464 __enumeration = None
466 """A reference to the L{CF_enumeration} instance that owns this element."""
467 return self.__enumeration
468
469 __unicodeValue = None
471 """The unicode string that defines the enumeration value."""
472 return self.__unicodeValue
473
474 - def __init__ (self, enumeration=None, unicode_value=None,
475 description=None, annotation=None, tag=None,
476 **kw):
499
502
503 -class CF_enumeration (ConstrainingFacet, _CollectionFacet_mixin, _LateDatatype_mixin):
504 """Capture a constraint that restricts valid values to a fixed set.
505
506 A STD that has an enumeration restriction should mix-in
507 L{pyxb.binding.basis.enumeration_mixin}, and should have a class
508 variable titled C{_CF_enumeration} that is an instance of this
509 class.
510
511 "unicode" refers to the Unicode string by which the value is
512 represented in XML.
513
514 "tag" refers to the Python member reference associated with the
515 enumeration. The value is derived from the unicode value of the
516 enumeration element and an optional prefix that identifies the
517 owning simple type when the tag is promoted to module-level
518 visibility.
519
520 "value" refers to the Python value held in the tag
521
522 See U{http://www.w3.org/TR/xmlschema-2/#rf-enumeration}
523 """
524 _Name = 'enumeration'
525 _CollectionFacet_itemType = _EnumerationElement
526 _LateDatatypeBindsSuperclass = False
527
528 __elements = None
529 __tagToElement = None
530 __valueToElement = None
531 __unicodeToElement = None
532
533
534
535 __enumPrefix = None
536
544
547
550
566
568 """Return the L{_EnumerationElement} instance that has the given value.
569
570 @raise KeyError: the value is not valid for the enumeration."""
571 return self.__valueToElement[value]
572
574 """Return the enumeration value corresponding to the given unicode string.
575
576 If ustr is not a valid option for this enumeration, return None."""
577 rv = self.__unicodeToElement.get(ustr, None)
578 if rv is not None:
579 rv = rv.value()
580 return rv
581
591
593 """Marker class to indicate that the generated binding has enumeration members."""
594 @classmethod
597
599 """The enumeration used to constrain the whiteSpace facet"""
600 pass
601 _WhiteSpace_enum._CF_enumeration = CF_enumeration(value_datatype=_WhiteSpace_enum)
602 _WhiteSpace_enum.preserve = _WhiteSpace_enum._CF_enumeration.addEnumeration(unicode_value=u'preserve', tag='preserve')
603 _WhiteSpace_enum.replace = _WhiteSpace_enum._CF_enumeration.addEnumeration(unicode_value=u'replace', tag='replace')
604 _WhiteSpace_enum.collapse = _WhiteSpace_enum._CF_enumeration.addEnumeration(unicode_value=u'collapse', tag='collapse')
605
606
607
608
609 _WhiteSpace_enum._InitializeFacetMap(_WhiteSpace_enum._CF_enumeration)
612 """Specify the value-space interpretation of whitespace.
613
614 See U{http://www.w3.org/TR/xmlschema-2/#rf-whiteSpace}
615 """
616 _Name = 'whiteSpace'
617 _ValueDatatype = _WhiteSpace_enum
618
619 __TabCRLF_re = re.compile("[\t\n\r]")
620 __MultiSpace_re = re.compile(" +")
631
633 """No validation rules for whitespace facet."""
634 return True
635
636 -class CF_minInclusive (ConstrainingFacet, _Fixed_mixin, _LateDatatype_mixin):
637 """Specify the minimum legal value for the constrained type.
638
639 See U{http://www.w3.org/TR/xmlschema-2/#rf-minInclusive}
640 """
641 _Name = 'minInclusive'
642 _LateDatatypeBindsSuperclass = False
643
646
647
648 -class CF_maxInclusive (ConstrainingFacet, _Fixed_mixin, _LateDatatype_mixin):
649 """Specify the maximum legal value for the constrained type.
650
651 See U{http://www.w3.org/TR/xmlschema-2/#rf-maxInclusive}
652 """
653 _Name = 'maxInclusive'
654 _LateDatatypeBindsSuperclass = False
655
658
659 -class CF_minExclusive (ConstrainingFacet, _Fixed_mixin, _LateDatatype_mixin):
660 """Specify the exclusive lower bound of legal values for the constrained type.
661
662 See U{http://www.w3.org/TR/xmlschema-2/#rf-minExclusive}
663 """
664 _Name = 'minExclusive'
665 _LateDatatypeBindsSuperclass = True
666
669
670 -class CF_maxExclusive (ConstrainingFacet, _Fixed_mixin, _LateDatatype_mixin):
671 """Specify the exclusive upper bound of legal values for the constrained type.
672
673 See U{http://www.w3.org/TR/xmlschema-2/#rf-maxExclusive}
674 """
675 _Name = 'maxExclusive'
676 _LateDatatypeBindsSuperclass = True
677
680
682 """Specify the number of digits in the *value* space of the type.
683
684 See U{http://www.w3.org/TR/xmlschema-2/#rf-totalDigits}
685 """
686 _Name = 'totalDigits'
687 _ValueDatatype = datatypes.positiveInteger
688
690 if self.value() is None:
691 return True
692 n = 0
693 scale = 1
694 match = False
695 v = None
696 while (n <= self.value()) and (not match):
697 v = long(value * scale)
698 match = ((value * scale) == v)
699 if self.value() == n:
700 break
701 n += 1
702 scale *= 10
703 while n < self.value():
704 n += 1
705 scale *= 10
706 return match and (v is not None) and (abs(v) < scale)
707
709 """Specify the number of sub-unit digits in the *value* space of the type.
710
711 See U{http://www.w3.org/TR/xmlschema-2/#rf-fractionDigits}
712 """
713 _Name = 'fractionDigits'
714 _ValueDatatype = datatypes.nonNegativeInteger
715
717 if self.value() is None:
718 return True
719 n = 0
720 scale = 1
721 while n <= self.value():
722 if ((value * scale) == long(value * scale)):
723 return True
724 n += 1
725 scale *= 10
726 return False
727
729 """A fundamental facet provides information on the value space of the associated type."""
730
731 _FacetPrefix = 'FF'
732
733 @classmethod
734 - def CreateFromDOM (cls, node, owner_type_definition, base_type_definition=None):
735 facet_class = cls.ClassForFacet(node.getAttribute('name'))
736 rv = facet_class(base_type_definition=base_type_definition,
737 owner_type_definition=owner_type_definition)
738 rv.updateFromDOM(node)
739
745
756
758 """Specifies that the associated type supports a notion of equality.
759
760 See U{http://www.w3.org/TR/xmlschema-2/#equal}
761 """
762
763 _Name = 'equal'
764
766 """Specifies that the associated type supports a notion of order.
767
768 See U{http://www.w3.org/TR/xmlschema-2/#rf-ordered}
769 """
770
771 _LegalValues = ( 'false', 'partial', 'total' )
772 _Name = 'ordered'
773 _ValueDatatype = datatypes.string
774
778
780 """Specifies that the associated type supports a notion of bounds.
781
782 See U{http://www.w3.org/TR/xmlschema-2/#rf-bounded}
783 """
784
785 _Name = 'bounded'
786 _ValueDatatype = datatypes.boolean
787
789 """Specifies that the associated type supports a notion of length.
790
791 See U{http://www.w3.org/TR/xmlschema-2/#rf-cardinality}
792 """
793
794 _LegalValues = ( 'finite', 'countably infinite' )
795 _Name = 'cardinality'
796 _ValueDatatype = datatypes.string
800
802 """Specifies that the associated type represents a number.
803
804 See U{http://www.w3.org/TR/xmlschema-2/#rf-numeric}
805 """
806
807 _Name = 'numeric'
808 _ValueDatatype = datatypes.boolean
809
810
811 ConstrainingFacet.Facets = [
812 CF_length, CF_minLength, CF_maxLength, CF_pattern, CF_enumeration,
813 CF_whiteSpace, CF_minInclusive, CF_maxInclusive, CF_minExclusive,
814 CF_maxExclusive, CF_totalDigits, CF_fractionDigits ]
815
816 FundamentalFacet.Facets = [
817 FF_equal, FF_ordered, FF_bounded, FF_cardinality, FF_numeric ]
818
819 Facet.Facets = []
820 Facet.Facets.extend(ConstrainingFacet.Facets)
821 Facet.Facets.extend(FundamentalFacet.Facets)
822