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')
123 self.__ownerTypeDefinition = kw.get('owner_type_definition')
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))
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
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
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
575
577 """Return the L{_EnumerationElement} instance that has the given value.
578
579 @raise KeyError: the value is not valid for the enumeration."""
580 return self.__valueToElement[value]
581
583 """Return the enumeration value corresponding to the given unicode string.
584
585 If ustr is not a valid option for this enumeration, return None."""
586 rv = self.__unicodeToElement.get(ustr)
587 if rv is not None:
588 rv = rv.value()
589 return rv
590
592
593
594 if 0 == len(self._items()):
595 return True
596 for ee in self.iteritems():
597 if ee.value() == value:
598 return True
599 return False
600
602 """Marker class to indicate that the generated binding has enumeration members."""
603 @classmethod
606
608 """The enumeration used to constrain the whiteSpace facet"""
609 pass
610 _WhiteSpace_enum._CF_enumeration = CF_enumeration(value_datatype=_WhiteSpace_enum)
611 _WhiteSpace_enum.preserve = _WhiteSpace_enum._CF_enumeration.addEnumeration(unicode_value=u'preserve', tag='preserve')
612 _WhiteSpace_enum.replace = _WhiteSpace_enum._CF_enumeration.addEnumeration(unicode_value=u'replace', tag='replace')
613 _WhiteSpace_enum.collapse = _WhiteSpace_enum._CF_enumeration.addEnumeration(unicode_value=u'collapse', tag='collapse')
614
615
616
617
618 _WhiteSpace_enum._InitializeFacetMap(_WhiteSpace_enum._CF_enumeration)
621 """Specify the value-space interpretation of whitespace.
622
623 See U{http://www.w3.org/TR/xmlschema-2/#rf-whiteSpace}
624 """
625 _Name = 'whiteSpace'
626 _ValueDatatype = _WhiteSpace_enum
627
628 __TabCRLF_re = re.compile("[\t\n\r]")
629 __MultiSpace_re = re.compile(" +")
640
642 """No validation rules for whitespace facet."""
643 return True
644
645 -class CF_minInclusive (ConstrainingFacet, _Fixed_mixin, _LateDatatype_mixin):
646 """Specify the minimum legal value for the constrained type.
647
648 See U{http://www.w3.org/TR/xmlschema-2/#rf-minInclusive}
649 """
650 _Name = 'minInclusive'
651 _LateDatatypeBindsSuperclass = False
652
655
656
657 -class CF_maxInclusive (ConstrainingFacet, _Fixed_mixin, _LateDatatype_mixin):
658 """Specify the maximum legal value for the constrained type.
659
660 See U{http://www.w3.org/TR/xmlschema-2/#rf-maxInclusive}
661 """
662 _Name = 'maxInclusive'
663 _LateDatatypeBindsSuperclass = False
664
667
668 -class CF_minExclusive (ConstrainingFacet, _Fixed_mixin, _LateDatatype_mixin):
669 """Specify the exclusive lower bound of legal values for the constrained type.
670
671 See U{http://www.w3.org/TR/xmlschema-2/#rf-minExclusive}
672 """
673 _Name = 'minExclusive'
674 _LateDatatypeBindsSuperclass = True
675
678
679 -class CF_maxExclusive (ConstrainingFacet, _Fixed_mixin, _LateDatatype_mixin):
680 """Specify the exclusive upper bound of legal values for the constrained type.
681
682 See U{http://www.w3.org/TR/xmlschema-2/#rf-maxExclusive}
683 """
684 _Name = 'maxExclusive'
685 _LateDatatypeBindsSuperclass = True
686
689
691 """Specify the number of digits in the *value* space of the type.
692
693 See U{http://www.w3.org/TR/xmlschema-2/#rf-totalDigits}
694 """
695 _Name = 'totalDigits'
696 _ValueDatatype = datatypes.positiveInteger
697
699 if self.value() is None:
700 return True
701 n = 0
702 scale = 1
703 match = False
704 v = None
705 while (n <= self.value()) and (not match):
706 v = long(value * scale)
707 match = ((value * scale) == v)
708 if self.value() == n:
709 break
710 n += 1
711 scale *= 10
712 while n < self.value():
713 n += 1
714 scale *= 10
715 return match and (v is not None) and (abs(v) < scale)
716
718 """Specify the number of sub-unit digits in the *value* space of the type.
719
720 See U{http://www.w3.org/TR/xmlschema-2/#rf-fractionDigits}
721 """
722 _Name = 'fractionDigits'
723 _ValueDatatype = datatypes.nonNegativeInteger
724
726 if self.value() is None:
727 return True
728 n = 0
729 scale = 1
730 while n <= self.value():
731 if ((value * scale) == long(value * scale)):
732 return True
733 n += 1
734 scale *= 10
735 return False
736
738 """A fundamental facet provides information on the value space of the associated type."""
739
740 _FacetPrefix = 'FF'
741
742 @classmethod
743 - def CreateFromDOM (cls, node, owner_type_definition, base_type_definition=None):
748
754
765
767 """Specifies that the associated type supports a notion of equality.
768
769 See U{http://www.w3.org/TR/xmlschema-2/#equal}
770 """
771
772 _Name = 'equal'
773
775 """Specifies that the associated type supports a notion of order.
776
777 See U{http://www.w3.org/TR/xmlschema-2/#rf-ordered}
778 """
779
780 _LegalValues = ( 'false', 'partial', 'total' )
781 _Name = 'ordered'
782 _ValueDatatype = datatypes.string
783
787
789 """Specifies that the associated type supports a notion of bounds.
790
791 See U{http://www.w3.org/TR/xmlschema-2/#rf-bounded}
792 """
793
794 _Name = 'bounded'
795 _ValueDatatype = datatypes.boolean
796
798 """Specifies that the associated type supports a notion of length.
799
800 See U{http://www.w3.org/TR/xmlschema-2/#rf-cardinality}
801 """
802
803 _LegalValues = ( 'finite', 'countably infinite' )
804 _Name = 'cardinality'
805 _ValueDatatype = datatypes.string
809
811 """Specifies that the associated type represents a number.
812
813 See U{http://www.w3.org/TR/xmlschema-2/#rf-numeric}
814 """
815
816 _Name = 'numeric'
817 _ValueDatatype = datatypes.boolean
818
819
820 ConstrainingFacet.Facets = [
821 CF_length, CF_minLength, CF_maxLength, CF_pattern, CF_enumeration,
822 CF_whiteSpace, CF_minInclusive, CF_maxInclusive, CF_minExclusive,
823 CF_maxExclusive, CF_totalDigits, CF_fractionDigits ]
824
825 FundamentalFacet.Facets = [
826 FF_equal, FF_ordered, FF_bounded, FF_cardinality, FF_numeric ]
827
828 Facet.Facets = []
829 Facet.Facets.extend(ConstrainingFacet.Facets)
830 Facet.Facets.extend(FundamentalFacet.Facets)
831