Package xmlschema_acue ::
Package validators ::
Module identities
1
2
3
4
5
6
7
8
9
10
11 """
12 This module contains classes for other XML Schema identity constraints.
13 """
14 from __future__ import unicode_literals
15 import re
16 from collections import Counter
17 from elementpath import Selector, XPath1Parser, ElementPathSyntaxError, ElementPathKeyError
18
19 from xmlschema_acue.exceptions import XMLSchemaValueError
20 from xmlschema_acue.qnames import XSD_UNIQUE, XSD_KEY, XSD_KEYREF, XSD_SELECTOR, XSD_FIELD
21 from xmlschema_acue.helpers import get_qname, qname_to_prefixed
22 from xmlschema_acue.etree import etree_getpath
23 from xmlschema_acue.regex import get_python_regex
24
25 from xmlschema_acue.validators.exceptions import XMLSchemaValidationError
26 from xmlschema_acue.validators.xsdbase import XsdComponent
27
28 XSD_IDENTITY_XPATH_SYMBOLS = {
29 'processing-instruction', 'following-sibling', 'preceding-sibling',
30 'ancestor-or-self', 'attribute', 'following', 'namespace', 'preceding',
31 'ancestor', 'position', 'comment', 'parent', 'child', 'false', 'text', 'node',
32 'true', 'last', 'not', 'and', 'mod', 'div', 'or', '..', '//', '!=', '<=', '>=', '(', ')',
33 '[', ']', '.', '@', ',', '/', '|', '*', '-', '=', '+', '<', '>', ':', '(end)', '(name)',
34 '(string)', '(float)', '(decimal)', '(integer)', '::'
35 }
41
42
43 XsdIdentityXPathParser.build_tokenizer()
47 _admitted_tags = {XSD_SELECTOR}
48 pattern = re.compile(get_python_regex(
49 r"(\.//)?(((child::)?((\i\c*:)?(\i\c*|\*)))|\.)(/(((child::)?((\i\c*:)?(\i\c*|\*)))|\.))*(\|"
50 r"(\.//)?(((child::)?((\i\c*:)?(\i\c*|\*)))|\.)(/(((child::)?((\i\c*:)?(\i\c*|\*)))|\.))*)*"
51 ))
52
53 - def __init__(self, elem, schema, parent):
55
79
81 return '%s(path=%r)' % (self.__class__.__name__, self.path)
82
83 @property
86
89 _admitted_tags = {XSD_FIELD}
90 pattern = re.compile(get_python_regex(
91 r"(\.//)?((((child::)?((\i\c*:)?(\i\c*|\*)))|\.)/)*((((child::)?((\i\c*:)?(\i\c*|\*)))|\.)|"
92 r"((attribute::|@)((\i\c*:)?(\i\c*|\*))))(\|(\.//)?((((child::)?((\i\c*:)?(\i\c*|\*)))|\.)/)*"
93 r"((((child::)?((\i\c*:)?(\i\c*|\*)))|\.)|((attribute::|@)((\i\c*:)?(\i\c*|\*)))))*"
94 ))
95
98 - def __init__(self, elem, schema, parent):
100
102 super(XsdIdentity, self)._parse()
103 elem = self.elem
104 try:
105 self.name = get_qname(self.target_namespace, elem.attrib['name'])
106 except KeyError:
107 self.parse_error("missing required attribute 'name'", elem)
108 self.name = None
109
110 child = self._parse_component(elem, required=False, strict=False)
111 if child is None or child.tag != XSD_SELECTOR:
112 self.parse_error("missing 'selector' declaration.", elem)
113 self.selector = None
114 else:
115 self.selector = XsdSelector(child, self.schema, self)
116
117 self.fields = []
118 for child in self._iterparse_components(elem, start=int(self.selector is not None)):
119 if child.tag == XSD_FIELD:
120 self.fields.append(XsdFieldSelector(child, self.schema, self))
121 else:
122 self.parse_error("element %r not allowed here:" % child.tag, elem)
123
125 for xsd_element in self.selector.xpath_selector.iter_select(self.parent):
126 yield xsd_element
127
129 """
130 Get fields for a schema or instance context element.
131
132 :param context: Context Element or XsdElement
133 :param decoders: Context schema fields decoders.
134 :return: A tuple with field values. An empty field is replaced by `None`.
135 """
136 fields = []
137 for k, field in enumerate(self.fields):
138 result = field.xpath_selector.select(context)
139 if not result:
140 if isinstance(self, XsdKey):
141 raise XMLSchemaValueError("%r key field must have a value!" % field)
142 else:
143 fields.append(None)
144 elif len(result) == 1:
145 if decoders is None or decoders[k] is None:
146 fields.append(result[0])
147 else:
148 fields.append(decoders[k].decode(result[0], validation="skip"))
149 else:
150 raise XMLSchemaValueError("%r field selects multiple values!" % field)
151 return tuple(fields)
152
154 """
155 Iterate field values, excluding empty values (tuples with all `None` values).
156
157 :param elem: Instance XML element.
158 :return: N-Tuple with value fields.
159 """
160 current_path = ''
161 xsd_fields = None
162 for e in self.selector.xpath_selector.iter_select(elem):
163 path = etree_getpath(e, elem)
164 if current_path != path:
165
166 current_path = path
167 xsd_element = self.parent.find(path)
168 xsd_fields = self.get_fields(xsd_element)
169
170 if all(fld is None for fld in xsd_fields):
171 continue
172
173 try:
174 fields = self.get_fields(e, decoders=xsd_fields)
175 except XMLSchemaValueError as err:
176 yield XMLSchemaValidationError(self, e, reason=str(err))
177 else:
178 if any(fld is not None for fld in fields):
179 yield fields
180
181 @property
183 return self.selector.built and all([f.built for f in self.fields])
184
185 @property
187 if self.built:
188 return 'full'
189 elif self.selector.built or any([f.built for f in self.fields]):
190 return 'partial'
191 else:
192 return 'none'
193
195 for error in self.validator(*args, **kwargs):
196 yield error
197
199 values = Counter()
200 for v in self.iter_values(elem):
201 if isinstance(v, XMLSchemaValidationError):
202 yield v
203 else:
204 values[v] += 1
205
206 for value, count in values.items():
207 if count > 1:
208 yield XMLSchemaValidationError(self, elem, reason="duplicated value %r." % value)
209
213
214
215 -class XsdKey(XsdIdentity):
217
220 """
221 Implementation of xs:keyref.
222
223 :ivar refer: reference to a *xs:key* declaration that must be in the same element \
224 or in a descendant element.
225 """
226 _admitted_tags = {XSD_KEYREF}
227 refer = None
228 refer_path = '.'
229
231 return '%s(name=%r, refer=%r)' % (
232 self.__class__.__name__, self.prefixed_name, getattr(self.refer, 'prefixed_name', None)
233 )
234
243
276
284
306