1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 """Classes related to XMLSchema facets.
17
18 The definitions herein are from sections U{4.2<http://www.w3.org/TR/xmlschema-2/index.html#rf-facets>}
19 and U{4.3<http://www.w3.org/TR/xmlschema-2/index.html#rf-facets>} of
20 U{XML Schema Part 2: Datatypes<http://www.w3.org/TR/xmlschema-2/>}.
21 Facets are attributes of a datatype that constrain its lexical and
22 value spaces.
23
24 """
25
26 import pyxb
27 import types
28 import datatypes
29 import basis
30 from pyxb.utils import utility
31 import re
32 import logging
33
34 _log = logging.getLogger(__name__)
35
36 -class Facet (pyxb.cscRoot):
37 """The base class for facets.
38
39 This provides association with STDs, a name, and a value for the facet.
40 """
41
42 _Name = None
43 @classmethod
45 """The name of a facet is a class constant."""
46 return self._Name
47
48 __baseTypeDefinition = None
50 """The SimpleTypeDefinition component restricted by this facet.
51
52 Note: this is NOT the STD to which the facet belongs, but is
53 usually that STD's base type. I.e., this jumps us through all
54 the containing restrictions and extensions to get to the core
55 type definition."""
56 return self.__baseTypeDefinition
57
58 __ownerTypeDefinition = None
60 """The SimpleTypeDefinition component to which this facet belongs.
61
62 I.e., the one in which the hasFacet specification was found.
63 This value is None if the facet is not associated with an
64 STD."""
65 return self.__ownerTypeDefinition
66
67
68
69
70 _ValueDatatype = None
71
72
73 __valueDatatype = None
75 """Get the datatype used to represent values of the facet.
76
77 This usually has nothing to do with the owner datatype; for
78 example, the length facet may apply to any STD but the value
79 of the facet is an integer. In generated bindings this is
80 usually set explicitly in the facet constructor; when
81 processing a schema, it is derived from the value's type
82 definition.
83 """
84 if self.__valueDatatype is None:
85 assert self.baseTypeDefinition() is not None
86 return self.baseTypeDefinition().pythonSupport()
87 return self.__valueDatatype
88
89 __value = None
92
93 __annotation = None
95
97 """Create a facet instance, initializing it from the keyword parameters."""
98 super(Facet, self).__init__(**kw)
99
100 assert Facet != self.__class__
101 self.setFromKeywords(_reset=True, _constructor=True, **kw)
102
104 """Configure values of the facet from a set of keywords.
105
106 This method is pre-extended; subclasses should invoke the
107 parent method after setting their local configuration.
108
109 @keyword _reset: If C{False} or missing, existing values will
110 be retained if they do not appear in the
111 keywords. If C{True}, members not defined in
112 the keywords are set to a default.
113 @keyword base_type_definition:
114 @keyword owner_type_definition:
115 @keyword value_datatype:
116 """
117
118 if not kw.get('_reset', False):
119 kw.setdefault('base_type_definition', self.__baseTypeDefinition)
120 kw.setdefault('owner_type_definition', self.__ownerTypeDefinition)
121 kw.setdefault('value_datatype', self.__valueDatatype)
122 self.__baseTypeDefinition = kw.get('base_type_definition', None)
123 self.__ownerTypeDefinition = kw.get('owner_type_definition', None)
124 self.__valueDatatype = kw.get('value_datatype', self._ValueDatatype)
125
126
127
128 assert (self.__valueDatatype is not None) or (self.__baseTypeDefinition is not None)
129 super_fn = getattr(super(Facet, self), '_setFromKeywords_vb', lambda *a,**kw: self)
130 return super_fn(**kw)
131
133 """Public entrypoint to the _setFromKeywords_vb call hierarchy."""
134 return self._setFromKeywords_vb(**kw)
135
136 @classmethod
138 """Given the name of a facet, return the Facet subclass that represents it."""
139 assert cls != Facet
140 if 0 <= name.find(':'):
141 name = name.split(':', 1)[1]
142 facet_class = globals().get('%s_%s' % (cls._FacetPrefix, name), None)
143 if facet_class is None:
144 raise pyxb.LogicError('Unrecognized facet name %s: expect %s' % (name, ','.join([_f._Name for _f in cls.Facets])))
145 assert facet_class is not None
146 return facet_class
147
158
165
167 """One of the facets defined in section 4.3, which provide
168 constraints on the lexical space of a type definition."""
169
170
171
172
173
174 _FacetPrefix = 'CF'
175
178
181
183 """Return True iff the given value satisfies the constraint represented by this facet instance.
184
185 The actual test is delegated to the subclasses."""
186 return self._validateConstraint_vx(value)
187
189 kwv = kw.get('value', None)
190 if kwv is not None:
191 if not isinstance(kwv, self.valueDatatype()):
192 kwv = self.valueDatatype()(kwv)
193 self._value(kwv)
194
196 """Extend base class.
197
198 Additional keywords:
199 * value
200 """
201
202 super_fn = getattr(super(ConstrainingFacet, self), '_setFromKeywords_vb', lambda *a,**kw: self)
203 rv = super_fn(**kw)
204 self.__setFromKeywords(**kw)
205 return rv
206
208 """Marker class to indicate that the facet instance must be told
209 its datatype when it is constructed.
210
211 This is necessary for facets like L{CF_minInclusive} and
212 L{CF_minExclusive}, for which the value is determined by the base
213 type definition of the associated STD. In some cases the value
214 that must be used in the facet cannot be represented in the Python
215 type used for the facet; see L{LateDatatypeBindsSuperclass}.
216 """
217
218 _LateDatatypeBindsSuperclass = None
219 """The class variable that indicates that the Subclasses must
220 override this variable with a value of C{True} or C{False}. The
221 value is C{True} iff the value used for the facet is not within
222 the value space of the corresponding value datatype; for example,
223 L{CF_minExclusive}."""
224
225
226 @classmethod
234
235 @classmethod
256
259
261 """Mix-in to a constraining facet that adds support for the 'fixed' property."""
262 __fixed = None
264
266 if kw.get('_reset', False):
267 self.__fixed = None
268 kwv = kw.get('fixed', None)
269 if kwv is not None:
270 self.__fixed = datatypes.boolean(kwv)
271
273 """Extend base class.
274
275 Additional keywords:
276 * fixed
277 """
278 self.__setFromKeywords(**kw)
279 super_fn = getattr(super(_Fixed_mixin, self), '_setFromKeywords_vb', lambda *a,**kw: self)
280 return super_fn(**kw)
281
283 """Mix-in to handle facets whose values are collections, not scalars.
284
285 For example, the enumeration and pattern facets maintain a list of
286 enumeration values and patterns, respectively, as their value
287 space.
288
289 Subclasses must define a class variable _CollectionFacet_itemType
290 which is a reference to a class that is used to construct members
291 of the collection.
292 """
293
294 __items = None
296 """Extend base class.
297
298 @keyword _constructor: If C{False} or absent, the object being
299 set is a member of the collection. If
300 C{True}, the object being set is the
301 collection itself.
302 """
303 if kw.get('_reset', False):
304 self.__items = []
305 if not kw.get('_constructor', False):
306 self.__items.append(self._CollectionFacet_itemType(facet_instance=self, **kw))
307 super_fn = getattr(super(_CollectionFacet_mixin, self), '_setFromKeywords_vb', lambda *a,**kw: self)
308 return super_fn(**kw)
309
311 """The members of the collection, as a reference."""
312 return self.__items
313
315 """The members of the collection, as a copy."""
316 return self.__items[:]
317
319 """The members of the collection as an iterator"""
320 return iter(self.__items)
321
322 -class CF_length (ConstrainingFacet, _Fixed_mixin):
333
335 """A facet that constrains the length of the lexical representation of a value.
336
337 See U{http://www.w3.org/TR/xmlschema-2/#rf-minLength}
338 """
339 _Name = 'minLength'
340 _ValueDatatype = datatypes.nonNegativeInteger
341
345
347 """A facet that constrains the length of the lexical representation of a value.
348
349 See U{http://www.w3.org/TR/xmlschema-2/#rf-minLength}
350 """
351 _Name = 'maxLength'
352 _ValueDatatype = datatypes.nonNegativeInteger
353
357
358 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