annotate markup/path.py @ 33:0e1fc0211416

Add doctests for path module.
author cmlenz
date Wed, 28 Jun 2006 21:09:54 +0000
parents b8456279c444
children 224b0b41d1da
rev   line source
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
1 # -*- coding: utf-8 -*-
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
2 #
27
b8456279c444 * Fix the boilerplate in the Python source files.
cmlenz
parents: 26
diff changeset
3 # Copyright (C) 2006 Christopher Lenz
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
4 # All rights reserved.
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
5 #
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
6 # This software is licensed as described in the file COPYING, which
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
7 # you should have received as part of this distribution. The terms
27
b8456279c444 * Fix the boilerplate in the Python source files.
cmlenz
parents: 26
diff changeset
8 # are also available at http://markup.cmlenz.net/wiki/License.
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
9 #
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
10 # This software consists of voluntary contributions made by many
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
11 # individuals. For the exact contribution history, see the revision
27
b8456279c444 * Fix the boilerplate in the Python source files.
cmlenz
parents: 26
diff changeset
12 # history and logs, available at http://markup.cmlenz.net/log/.
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
13
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
14 """Basic support for evaluating XPath expressions against streams."""
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
15
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
16 import re
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
17
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
18 from markup.core import QName, Stream
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
19
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
20 __all__ = ['Path']
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
21
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
22
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
23 class Path(object):
26
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
24 """Implements basic XPath support on streams.
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
25
26
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
26 Instances of this class represent a "compiled" XPath expression, and provide
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
27 methods for testing the path against a stream, as well as extracting a
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
28 substream matching that path.
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
29 """
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
30 _TOKEN_RE = re.compile('(::|\.\.|\(\)|[/.:\[\]\(\)@=!])|'
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
31 '([^/:\[\]\(\)@=!\s]+)|'
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
32 '\s+')
26
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
33 _QUOTES = (("'", "'"), ('"', '"'))
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
34
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
35 def __init__(self, text):
26
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
36 """Create the path object from a string.
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
37
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
38 @param text: the path expression
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
39 """
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
40 self.source = text
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
41
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
42 steps = []
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
43 cur_op = ''
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
44 cur_tag = ''
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
45 in_predicate = False
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
46 for op, tag in self._TOKEN_RE.findall(text):
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
47 if op:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
48 if op == '[':
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
49 in_predicate = True
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
50 elif op == ']':
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
51 in_predicate = False
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
52 elif op.startswith('('):
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
53 if cur_tag == 'text':
26
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
54 steps[-1] = (False, self._FunctionText(), [])
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
55 else:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
56 raise NotImplementedError('XPath function "%s" not '
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
57 'supported' % cur_tag)
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
58 else:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
59 cur_op += op
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
60 cur_tag = ''
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
61 else:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
62 closure = cur_op in ('', '//')
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
63 if cur_op == '@':
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
64 if tag == '*':
26
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
65 node_test = self._AnyAttribute()
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
66 else:
26
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
67 node_test = self._AttributeByName(tag)
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
68 else:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
69 if tag == '*':
26
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
70 node_test = self._AnyElement()
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
71 elif in_predicate:
26
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
72 if len(tag) > 1 and (tag[0], tag[-1]) in self._QUOTES:
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
73 node_test = self._LiteralString(tag[1:-1])
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
74 if cur_op == '=':
26
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
75 node_test = self._OperatorEq(steps[-1][2][-1],
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
76 node_test)
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
77 steps[-1][2].pop()
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
78 elif cur_op == '!=':
26
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
79 node_test = self._OperatorNeq(steps[-1][2][-1],
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
80 node_test)
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
81 steps[-1][2].pop()
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
82 else:
26
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
83 node_test = self._ElementByName(tag)
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
84 if in_predicate:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
85 steps[-1][2].append(node_test)
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
86 else:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
87 steps.append([closure, node_test, []])
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
88 cur_op = ''
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
89 cur_tag = tag
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
90 self.steps = steps
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
91
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
92 def __repr__(self):
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
93 return '<%s "%s">' % (self.__class__.__name__, self.source)
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
94
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
95 def select(self, stream):
26
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
96 """Returns a substream of the given stream that matches the path.
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
97
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
98 If there are no matches, this method returns an empty stream.
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
99
33
0e1fc0211416 Add doctests for path module.
cmlenz
parents: 27
diff changeset
100 >>> from markup.input import XML
0e1fc0211416 Add doctests for path module.
cmlenz
parents: 27
diff changeset
101 >>> xml = XML('<root><elem><child>Text</child></elem></root>')
0e1fc0211416 Add doctests for path module.
cmlenz
parents: 27
diff changeset
102 >>> print Path('child').select(xml)
0e1fc0211416 Add doctests for path module.
cmlenz
parents: 27
diff changeset
103 <child>Text</child>
0e1fc0211416 Add doctests for path module.
cmlenz
parents: 27
diff changeset
104
0e1fc0211416 Add doctests for path module.
cmlenz
parents: 27
diff changeset
105 >>> xpath = Path('child')
0e1fc0211416 Add doctests for path module.
cmlenz
parents: 27
diff changeset
106 >>> print Path('child/text()').select(xml)
0e1fc0211416 Add doctests for path module.
cmlenz
parents: 27
diff changeset
107 Text
0e1fc0211416 Add doctests for path module.
cmlenz
parents: 27
diff changeset
108
26
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
109 @param stream: the stream to select from
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
110 @return: the substream matching the path, or an empty stream
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
111 """
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
112 stream = iter(stream)
26
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
113 def _generate():
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
114 test = self.test()
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
115 for kind, data, pos in stream:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
116 result = test(kind, data, pos)
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
117 if result is True:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
118 yield kind, data, pos
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
119 depth = 1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
120 while depth > 0:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
121 ev = stream.next()
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
122 if ev[0] is Stream.START:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
123 depth += 1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
124 elif ev[0] is Stream.END:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
125 depth -= 1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
126 yield ev
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
127 test(*ev)
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
128 elif result:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
129 yield result
26
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
130 return Stream(_generate())
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
131
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
132 def test(self):
26
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
133 """Returns a function that can be used to track whether the path matches
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
134 a specific stream event.
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
135
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
136 The function returned expects the positional arguments `kind`, `data`,
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
137 and `pos`, i.e. basically an unpacked stream event. If the path matches
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
138 the event, the function returns the match (for example, a `START` or
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
139 `TEXT` event.) Otherwise, it returns `None` or `False`.
33
0e1fc0211416 Add doctests for path module.
cmlenz
parents: 27
diff changeset
140
0e1fc0211416 Add doctests for path module.
cmlenz
parents: 27
diff changeset
141 >>> from markup.input import XML
0e1fc0211416 Add doctests for path module.
cmlenz
parents: 27
diff changeset
142 >>> xml = XML('<root><elem><child id="1"/></elem><child id="2"/></root>')
0e1fc0211416 Add doctests for path module.
cmlenz
parents: 27
diff changeset
143 >>> test = Path('child').test()
0e1fc0211416 Add doctests for path module.
cmlenz
parents: 27
diff changeset
144 >>> for kind, data, pos in xml:
0e1fc0211416 Add doctests for path module.
cmlenz
parents: 27
diff changeset
145 ... if test(kind, data, pos):
0e1fc0211416 Add doctests for path module.
cmlenz
parents: 27
diff changeset
146 ... print kind, data
0e1fc0211416 Add doctests for path module.
cmlenz
parents: 27
diff changeset
147 START (u'child', [(u'id', u'1')])
0e1fc0211416 Add doctests for path module.
cmlenz
parents: 27
diff changeset
148 START (u'child', [(u'id', u'2')])
26
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
149 """
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
150 stack = [0] # stack of cursors into the location path
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
151
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
152 def _test(kind, data, pos):
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
153 if not stack:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
154 return False
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
155
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
156 if kind is Stream.END:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
157 stack.pop()
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
158 return None
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
159
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
160 if kind is Stream.START:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
161 stack.append(stack[-1])
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
162
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
163 matched = False
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
164 closure, node_test, predicates = self.steps[stack[-1]]
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
165
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
166 matched = node_test(kind, data, pos)
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
167 if matched and predicates:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
168 for predicate in predicates:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
169 if not predicate(kind, data, pos):
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
170 matched = None
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
171 break
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
172
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
173 if matched:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
174 if stack[-1] == len(self.steps) - 1:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
175 return matched
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
176
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
177 stack[-1] += 1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
178
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
179 elif kind is Stream.START and not closure:
24
547e36f7ec94 Cosmetic (mostly whitespace) changes.
cmlenz
parents: 23
diff changeset
180 # If this step is not a closure, it cannot be matched until the
547e36f7ec94 Cosmetic (mostly whitespace) changes.
cmlenz
parents: 23
diff changeset
181 # current element is closed... so we need to move the cursor
547e36f7ec94 Cosmetic (mostly whitespace) changes.
cmlenz
parents: 23
diff changeset
182 # back to the last closure and retest that against the current
547e36f7ec94 Cosmetic (mostly whitespace) changes.
cmlenz
parents: 23
diff changeset
183 # element
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
184 closures = [step for step in self.steps[:stack[-1]] if step[0]]
25
c4201b794ab0 Oops. Fix typo in [25].
cmlenz
parents: 24
diff changeset
185 closures.reverse()
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
186 for closure, node_test, predicates in closures:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
187 stack[-1] -= 1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
188 if closure:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
189 matched = node_test(kind, data, pos)
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
190 if matched:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
191 stack[-1] += 1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
192 break
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
193
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
194 return None
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
195
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
196 return _test
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
197
26
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
198 class _AnyElement(object):
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
199 """Node test that matches any element."""
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
200 def __call__(self, kind, *_):
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
201 if kind is Stream.START:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
202 return True
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
203 return None
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
204 def __repr__(self):
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
205 return '<%s>' % self.__class__.__name__
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
206
26
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
207 class _ElementByName(object):
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
208 """Node test that matches an element with a specific tag name."""
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
209 def __init__(self, name):
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
210 self.name = QName(name)
26
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
211 def __call__(self, kind, data, _):
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
212 if kind is Stream.START:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
213 return data[0].localname == self.name
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
214 return None
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
215 def __repr__(self):
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
216 return '<%s "%s">' % (self.__class__.__name__, self.name)
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
217
26
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
218 class _AnyAttribute(object):
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
219 """Node test that matches any attribute."""
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
220 def __call__(self, kind, data, pos):
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
221 if kind is Stream.START:
26
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
222 text = ''.join([val for _, val in data[1]])
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
223 if text:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
224 return Stream.TEXT, text, pos
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
225 return None
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
226 return None
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
227 def __repr__(self):
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
228 return '<%s>' % (self.__class__.__name__)
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
229
26
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
230 class _AttributeByName(object):
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
231 """Node test that matches an attribute with a specific name."""
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
232 def __init__(self, name):
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
233 self.name = QName(name)
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
234 def __call__(self, kind, data, pos):
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
235 if kind is Stream.START:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
236 if self.name in data[1]:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
237 return Stream.TEXT, data[1].get(self.name), pos
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
238 return None
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
239 return None
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
240 def __repr__(self):
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
241 return '<%s "%s">' % (self.__class__.__name__, self.name)
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
242
26
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
243 class _FunctionText(object):
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
244 """Function that returns text content."""
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
245 def __call__(self, kind, data, pos):
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
246 if kind is Stream.TEXT:
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
247 return kind, data, pos
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
248 return None
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
249 def __repr__(self):
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
250 return '<%s>' % (self.__class__.__name__)
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
251
26
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
252 class _LiteralString(object):
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
253 """Always returns a literal string."""
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
254 def __init__(self, value):
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
255 self.value = value
26
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
256 def __call__(self, *_):
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
257 return Stream.TEXT, self.value, (-1, -1)
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
258 def __repr__(self):
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
259 return '<%s>' % (self.__class__.__name__)
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
260
26
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
261 class _OperatorEq(object):
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
262 """Equality comparison operator."""
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
263 def __init__(self, lval, rval):
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
264 self.lval = lval
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
265 self.rval = rval
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
266 def __call__(self, kind, data, pos):
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
267 lval = self.lval(kind, data, pos)
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
268 rval = self.rval(kind, data, pos)
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
269 return (lval and lval[1]) == (rval and rval[1])
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
270 def __repr__(self):
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
271 return '<%s %r = %r>' % (self.__class__.__name__, self.lval,
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
272 self.rval)
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
273
26
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
274 class _OperatorNeq(object):
039fc5b87405 * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
275 """Inequality comparison operator."""
1
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
276 def __init__(self, lval, rval):
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
277 self.lval = lval
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
278 self.rval = rval
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
279 def __call__(self, kind, data, pos):
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
280 lval = self.lval(kind, data, pos)
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
281 rval = self.rval(kind, data, pos)
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
282 return (lval and lval[1]) != (rval and rval[1])
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
283 def __repr__(self):
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
284 return '<%s %r != %r>' % (self.__class__.__name__, self.lval,
821114ec4f69 Initial import.
cmlenz
parents:
diff changeset
285 self.rval)
Copyright (C) 2012-2017 Edgewall Software