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