Package xmlschema_acue ::
Module xpath
1
2
3
4
5
6
7
8
9
10
11 """
12 This module defines a mixin class for enabling XPath on schemas.
13 """
14 from __future__ import unicode_literals
15 from abc import abstractmethod
16 from elementpath import XPath2Parser, XPathContext
17
18 from xmlschema_acue.compat import Sequence
19 from xmlschema_acue.qnames import XSD_SCHEMA
20
21
22 -class ElementPathContext(XPathContext):
23 """
24 XPath dynamic context class for XMLSchema. Implements safe iteration methods for
25 schema elements that recognize circular references.
26 """
28 def safe_iter_descendants(context):
29 elem = context.item
30 yield elem
31 if elem.text is not None:
32 context.item = elem.text
33 yield context.item
34 if len(elem):
35 context.size = len(elem)
36 for context.position, context.item in enumerate(elem):
37 if context.item.is_global:
38 for item in safe_iter_descendants(context):
39 yield item
40 elif getattr(context.item, 'ref', None) is not None:
41 yield context.item
42 elif context.item not in local_items:
43 local_items.append(context.item)
44 for item in safe_iter_descendants(context):
45 yield item
46
47 local_items = []
48 return safe_iter_descendants(self)
49
50 - def _iter_context(self):
51 def safe_iter_context(context):
52 elem = context.item
53 yield elem
54 if elem.text is not None:
55 context.item = elem.text
56 yield context.item
57
58 for item in elem.attrib.items():
59 context.item = item
60 yield item
61
62 if len(elem):
63 context.size = len(elem)
64 for context.position, context.item in enumerate(elem):
65 if context.item.is_global:
66 for item in safe_iter_context(context):
67 yield item
68 elif getattr(context.item, 'ref', None) is not None:
69 yield context.item
70 elif context.item not in local_items:
71 local_items.append(context.item)
72 for item in safe_iter_context(context):
73 yield item
74
75 local_items = []
76 return safe_iter_context(self)
77
80 """
81 Mixin abstract class for enabling ElementTree and XPath API on XSD components.
82
83 :cvar text: The Element text. Its value is always `None`. For compatibility with the ElementTree API.
84 :cvar tail: The Element tail. Its value is always `None`. For compatibility with the ElementTree API.
85 """
86 _attrib = {}
87 text = None
88 tail = None
89 namespaces = {}
90 xpath_default_namespace = None
91
92 @abstractmethod
95
97 try:
98 return [e for e in self][i]
99 except AttributeError:
100 raise IndexError('child index out of range')
101
103 return reversed([e for e in self])
104
106 return len([e for e in self])
107
108 @property
110 """Alias of the *name* attribute. For compatibility with the ElementTree API."""
111 return getattr(self, 'name')
112
113 @property
115 """Returns the Element attributes. For compatibility with the ElementTree API."""
116 return getattr(self, 'attributes', self._attrib)
117
118 - def get(self, key, default=None):
119 """Gets an Element attribute. For compatibility with the ElementTree API."""
120 return self.attrib.get(key, default)
121
122 - def iterfind(self, path, namespaces=None):
123 """
124 Creates and iterator for all XSD subelements matching the path.
125
126 :param path: an XPath expression that considers the XSD component as the root element.
127 :param namespaces: is an optional mapping from namespace prefix to full name.
128 :return: an iterable yielding all matching XSD subelements in document order.
129 """
130 path = path.strip()
131 if path.startswith('/') and not path.startswith('//'):
132 path = ''.join(['/', XSD_SCHEMA, path])
133 if namespaces is None:
134 namespaces = {k: v for k, v in self.namespaces.items() if k}
135
136 parser = XPath2Parser(namespaces, strict=False, default_namespace=self.xpath_default_namespace)
137 root_token = parser.parse(path)
138 context = ElementPathContext(self)
139 return root_token.select(context)
140
141 - def find(self, path, namespaces=None):
142 """
143 Finds the first XSD subelement matching the path.
144
145 :param path: an XPath expression that considers the XSD component as the root element.
146 :param namespaces: an optional mapping from namespace prefix to full name.
147 :return: The first matching XSD subelement or ``None`` if there is not match.
148 """
149 path = path.strip()
150 if path.startswith('/') and not path.startswith('//'):
151 path = ''.join(['/', XSD_SCHEMA, path])
152 if namespaces is None:
153 namespaces = {k: v for k, v in self.namespaces.items() if k}
154 parser = XPath2Parser(namespaces, strict=False, default_namespace=self.xpath_default_namespace)
155 root_token = parser.parse(path)
156 context = ElementPathContext(self)
157 return next(root_token.select(context), None)
158
159 - def findall(self, path, namespaces=None):
160 """
161 Finds all XSD subelements matching the path.
162
163 :param path: an XPath expression that considers the XSD component as the root element.
164 :param namespaces: an optional mapping from namespace prefix to full name.
165 :return: a list containing all matching XSD subelements in document order, an empty \
166 list is returned if there is no match.
167 """
168 path = path.strip()
169 if path.startswith('/') and not path.startswith('//'):
170 path = ''.join(['/', XSD_SCHEMA, path])
171 if namespaces is None:
172 namespaces = {k: v for k, v in self.namespaces.items() if k}
173
174 parser = XPath2Parser(namespaces, strict=False, default_namespace=self.xpath_default_namespace)
175 root_token = parser.parse(path)
176 context = ElementPathContext(self)
177 return root_token.get_results(context)
178
179 - def iter(self, tag=None):
180 """
181 Creates an iterator for the XSD element and its subelements. If tag is not `None` or '*',
182 only XSD elements whose matches tag are returned from the iterator. Local elements are
183 expanded without repetitions. Element references are not expanded because the global
184 elements are not descendants of other elements.
185 """
186 def safe_iter(elem):
187 if tag is None or elem.is_matching(tag):
188 yield elem
189 for child in elem:
190 if child.is_global:
191 for e in safe_iter(child):
192 yield e
193 elif getattr(child, 'ref', None) is not None:
194 if tag is None or elem.is_matching(tag):
195 yield child
196 elif child not in local_elements:
197 local_elements.append(child)
198 for e in safe_iter(child):
199 yield e
200
201 if tag == '*':
202 tag = None
203 local_elements = []
204 return safe_iter(self)
205
207 """
208 Creates an iterator for the child elements of the XSD component. If *tag* is not `None`
209 or '*', only XSD elements whose name matches tag are returned from the iterator.
210 """
211 if tag == '*':
212 tag = None
213 for child in self:
214 if tag is None or child.is_matching(tag):
215 yield child
216