annotate genshi/path.py @ 1043:a21009a2bc3a trunk

Return correct value and properly namespaced attribute name when matching namespaced attributes with XPath expressions (fixes #572; thanks to Olemis Lang <olemis+trac@gmail.com> for bug report and suggestion for fix).
author hodgestar
date Thu, 20 Mar 2014 12:58:48 +0000
parents 16d55698006a
children
rev   line source
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
1 # -*- coding: utf-8 -*-
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
2 #
854
4d9bef447df9 More work on reducing the size of the diff produced by 2to3.
cmlenz
parents: 853
diff changeset
3 # Copyright (C) 2006-2009 Edgewall Software
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
4 # All rights reserved.
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
5 #
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
6 # This software is licensed as described in the file COPYING, which
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
7 # you should have received as part of this distribution. The terms
230
84168828b074 Renamed Markup to Genshi in repository.
cmlenz
parents: 228
diff changeset
8 # are also available at http://genshi.edgewall.org/wiki/License.
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
9 #
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
10 # This software consists of voluntary contributions made by many
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
11 # individuals. For the exact contribution history, see the revision
230
84168828b074 Renamed Markup to Genshi in repository.
cmlenz
parents: 228
diff changeset
12 # history and logs, available at http://genshi.edgewall.org/log/.
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
13
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
14 """Basic support for evaluating XPath expressions against streams.
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
15
230
84168828b074 Renamed Markup to Genshi in repository.
cmlenz
parents: 228
diff changeset
16 >>> from genshi.input import XML
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
17 >>> doc = XML('''<doc>
516
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
18 ... <items count="4">
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
19 ... <item status="new">
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
20 ... <summary>Foo</summary>
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
21 ... </item>
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
22 ... <item status="closed">
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
23 ... <summary>Bar</summary>
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
24 ... </item>
516
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
25 ... <item status="closed" resolution="invalid">
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
26 ... <summary>Baz</summary>
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
27 ... </item>
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
28 ... <item status="closed" resolution="fixed">
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
29 ... <summary>Waz</summary>
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
30 ... </item>
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
31 ... </items>
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
32 ... </doc>''')
853
f33ecf3c319e Convert a bunch of print statements to py3k compatible syntax.
cmlenz
parents: 852
diff changeset
33 >>> print(doc.select('items/item[@status="closed" and '
f33ecf3c319e Convert a bunch of print statements to py3k compatible syntax.
cmlenz
parents: 852
diff changeset
34 ... '(@resolution="invalid" or not(@resolution))]/summary/text()'))
516
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
35 BarBaz
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
36
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
37 Because the XPath engine operates on markup streams (as opposed to tree
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
38 structures), it only implements a subset of the full XPath 1.0 language.
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
39 """
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
40
818
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
41 from collections import deque
792
25b8ffa7fe65 Apply patch to silence a -3 warning about `reduce` removal. Closes #279.
cmlenz
parents: 719
diff changeset
42 try:
856
21308bd343b8 Add a couple of fallback imports for Python 3.0.
cmlenz
parents: 854
diff changeset
43 reduce # builtin in Python < 3
21308bd343b8 Add a couple of fallback imports for Python 3.0.
cmlenz
parents: 854
diff changeset
44 except NameError:
792
25b8ffa7fe65 Apply patch to silence a -3 warning about `reduce` removal. Closes #279.
cmlenz
parents: 719
diff changeset
45 from functools import reduce
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
46 from math import ceil, floor
593
0af1b7982ba5 Minor, cosmetic tweaks.
cmlenz
parents: 534
diff changeset
47 import operator
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
48 import re
818
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
49 from itertools import chain
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
50
230
84168828b074 Renamed Markup to Genshi in repository.
cmlenz
parents: 228
diff changeset
51 from genshi.core import Stream, Attrs, Namespace, QName
638
1f73e534e715 Fix for descendant-or-self XPath patterns when namespaces are involved.
cmlenz
parents: 605
diff changeset
52 from genshi.core import START, END, TEXT, START_NS, END_NS, COMMENT, PI, \
1f73e534e715 Fix for descendant-or-self XPath patterns when namespaces are involved.
cmlenz
parents: 605
diff changeset
53 START_CDATA, END_CDATA
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
54
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
55 __all__ = ['Path', 'PathSyntaxError']
425
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 386
diff changeset
56 __docformat__ = 'restructuredtext en'
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
57
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
58
114
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
59 class Axis(object):
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
60 """Defines constants for the various supported XPath axes."""
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
61
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
62 ATTRIBUTE = 'attribute'
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
63 CHILD = 'child'
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
64 DESCENDANT = 'descendant'
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
65 DESCENDANT_OR_SELF = 'descendant-or-self'
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
66 SELF = 'self'
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
67
822
70fddd2262f5 Get rid of some Python 2.3 legacy that's no longer needed now that 2.4 is the baseline.
cmlenz
parents: 818
diff changeset
68 @classmethod
114
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
69 def forname(cls, name):
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
70 """Return the axis constant for the given name, or `None` if no such
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
71 axis was defined.
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
72 """
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
73 return getattr(cls, name.upper().replace('-', '_'), None)
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
74
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
75
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
76 ATTRIBUTE = Axis.ATTRIBUTE
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
77 CHILD = Axis.CHILD
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
78 DESCENDANT = Axis.DESCENDANT
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
79 DESCENDANT_OR_SELF = Axis.DESCENDANT_OR_SELF
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
80 SELF = Axis.SELF
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
81
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
82
818
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
83 class GenericStrategy(object):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
84
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
85 @classmethod
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
86 def supports(cls, path):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
87 return True
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
88
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
89 def __init__(self, path):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
90 self.path = path
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
91
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
92 def test(self, ignore_context):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
93 p = self.path
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
94 if ignore_context:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
95 if p[0][0] is ATTRIBUTE:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
96 steps = [_DOTSLASHSLASH] + p
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
97 else:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
98 steps = [(DESCENDANT_OR_SELF, p[0][1], p[0][2])] + p[1:]
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
99 elif p[0][0] is CHILD or p[0][0] is ATTRIBUTE \
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
100 or p[0][0] is DESCENDANT:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
101 steps = [_DOTSLASH] + p
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
102 else:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
103 steps = p
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
104
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
105 # for node it contains all positions of xpath expression
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
106 # where its child should start checking for matches
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
107 # with list of corresponding context counters
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
108 # there can be many of them, because position that is from
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
109 # descendant-like axis can be achieved from different nodes
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
110 # for example <a><a><b/></a></a> should match both //a//b[1]
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
111 # and //a//b[2]
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
112 # positions always form increasing sequence (invariant)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
113 stack = [[(0, [[]])]]
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
114
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
115 def _test(event, namespaces, variables, updateonly=False):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
116 kind, data, pos = event[:3]
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
117 retval = None
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
118
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
119 # Manage the stack that tells us "where we are" in the stream
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
120 if kind is END:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
121 if stack:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
122 stack.pop()
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
123 return None
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
124 if kind is START_NS or kind is END_NS \
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
125 or kind is START_CDATA or kind is END_CDATA:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
126 # should we make namespaces work?
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
127 return None
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
128
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
129 pos_queue = deque([(pos, cou, []) for pos, cou in stack[-1]])
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
130 next_pos = []
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
131
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
132 # length of real part of path - we omit attribute axis
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
133 real_len = len(steps) - ((steps[-1][0] == ATTRIBUTE) or 1 and 0)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
134 last_checked = -1
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
135
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
136 # places where we have to check for match, are these
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
137 # provided by parent
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
138 while pos_queue:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
139 x, pcou, mcou = pos_queue.popleft()
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
140 axis, nodetest, predicates = steps[x]
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
141
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
142 # we need to push descendant-like positions from parent
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
143 # further
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
144 if (axis is DESCENDANT or axis is DESCENDANT_OR_SELF) and pcou:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
145 if next_pos and next_pos[-1][0] == x:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
146 next_pos[-1][1].extend(pcou)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
147 else:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
148 next_pos.append((x, pcou))
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
149
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
150 # nodetest first
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
151 if not nodetest(kind, data, pos, namespaces, variables):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
152 continue
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
153
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
154 # counters packs that were already bad
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
155 missed = set()
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
156 counters_len = len(pcou) + len(mcou)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
157
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
158 # number of counters - we have to create one
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
159 # for every context position based predicate
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
160 cnum = 0
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
161
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
162 # tells if we have match with position x
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
163 matched = True
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
164
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
165 if predicates:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
166 for predicate in predicates:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
167 pretval = predicate(kind, data, pos,
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
168 namespaces,
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
169 variables)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
170 if type(pretval) is float: # FIXME <- need to check
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
171 # this for other types that
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
172 # can be coerced to float
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
173
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
174 # each counter pack needs to be checked
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
175 for i, cou in enumerate(chain(pcou, mcou)):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
176 # it was bad before
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
177 if i in missed:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
178 continue
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
179
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
180 if len(cou) < cnum + 1:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
181 cou.append(0)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
182 cou[cnum] += 1
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
183
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
184 # it is bad now
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
185 if cou[cnum] != int(pretval):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
186 missed.add(i)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
187
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
188 # none of counters pack was good
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
189 if len(missed) == counters_len:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
190 pretval = False
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
191 cnum += 1
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
192
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
193 if not pretval:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
194 matched = False
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
195 break
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
196
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
197 if not matched:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
198 continue
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
199
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
200 # counter for next position with current node as context node
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
201 child_counter = []
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
202
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
203 if x + 1 == real_len:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
204 # we reached end of expression, because x + 1
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
205 # is equal to the length of expression
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
206 matched = True
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
207 axis, nodetest, predicates = steps[-1]
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
208 if axis is ATTRIBUTE:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
209 matched = nodetest(kind, data, pos, namespaces,
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
210 variables)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
211 if matched:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
212 retval = matched
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
213 else:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
214 next_axis = steps[x + 1][0]
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
215
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
216 # if next axis allows matching self we have
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
217 # to add next position to our queue
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
218 if next_axis is DESCENDANT_OR_SELF or next_axis is SELF:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
219 if not pos_queue or pos_queue[0][0] > x + 1:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
220 pos_queue.appendleft((x + 1, [], [child_counter]))
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
221 else:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
222 pos_queue[0][2].append(child_counter)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
223
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
224 # if axis is not self we have to add it to child's list
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
225 if next_axis is not SELF:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
226 next_pos.append((x + 1, [child_counter]))
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
227
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
228 if kind is START:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
229 stack.append(next_pos)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
230
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
231 return retval
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
232
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
233 return _test
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
234
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
235
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
236 class SimplePathStrategy(object):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
237 """Strategy for path with only local names, attributes and text nodes."""
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
238
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
239 @classmethod
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
240 def supports(cls, path):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
241 if path[0][0] is ATTRIBUTE:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
242 return False
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
243 allowed_tests = (LocalNameTest, CommentNodeTest, TextNodeTest)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
244 for _, nodetest, predicates in path:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
245 if predicates:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
246 return False
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
247 if not isinstance(nodetest, allowed_tests):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
248 return False
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
249 return True
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
250
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
251 def __init__(self, path):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
252 # fragments is list of tuples (fragment, pi, attr, self_beginning)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
253 # fragment is list of nodetests for fragment of path with only
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
254 # child:: axes between
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
255 # pi is KMP partial match table for this fragment
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
256 # attr is attribute nodetest if fragment ends with @ and None otherwise
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
257 # self_beginning is True if axis for first fragment element
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
258 # was self (first fragment) or descendant-or-self (farther fragment)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
259 self.fragments = []
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
260
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
261 self_beginning = False
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
262 fragment = []
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
263
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
264 def nodes_equal(node1, node2):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
265 """Tests if two node tests are equal"""
860
16d55698006a A bit of cleanup of the `Markup` Python implementation.
cmlenz
parents: 857
diff changeset
266 if type(node1) is not type(node2):
818
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
267 return False
860
16d55698006a A bit of cleanup of the `Markup` Python implementation.
cmlenz
parents: 857
diff changeset
268 if type(node1) == LocalNameTest:
818
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
269 return node1.name == node2.name
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
270 return True
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
271
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
272 def calculate_pi(f):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
273 """KMP prefix calculation for table"""
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
274 # the indexes in prefix table are shifted by one
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
275 # in comparision with common implementations
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
276 # pi[i] = NORMAL_PI[i + 1]
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
277 if len(f) == 0:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
278 return []
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
279 pi = [0]
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
280 s = 0
854
4d9bef447df9 More work on reducing the size of the diff produced by 2to3.
cmlenz
parents: 853
diff changeset
281 for i in range(1, len(f)):
818
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
282 while s > 0 and not nodes_equal(f[s], f[i]):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
283 s = pi[s-1]
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
284 if nodes_equal(f[s], f[i]):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
285 s += 1
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
286 pi.append(s)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
287 return pi
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
288
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
289 for axis in path:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
290 if axis[0] is SELF:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
291 if len(fragment) != 0:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
292 # if element is not first in fragment it has to be
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
293 # the same as previous one
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
294 # for example child::a/self::b is always wrong
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
295 if axis[1] != fragment[-1][1]:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
296 self.fragments = None
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
297 return
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
298 else:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
299 self_beginning = True
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
300 fragment.append(axis[1])
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
301 elif axis[0] is CHILD:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
302 fragment.append(axis[1])
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
303 elif axis[0] is ATTRIBUTE:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
304 pi = calculate_pi(fragment)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
305 self.fragments.append((fragment, pi, axis[1], self_beginning))
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
306 # attribute has always to be at the end, so we can jump out
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
307 return
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
308 else:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
309 pi = calculate_pi(fragment)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
310 self.fragments.append((fragment, pi, None, self_beginning))
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
311 fragment = [axis[1]]
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
312 if axis[0] is DESCENDANT:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
313 self_beginning = False
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
314 else: # DESCENDANT_OR_SELF
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
315 self_beginning = True
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
316 pi = calculate_pi(fragment)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
317 self.fragments.append((fragment, pi, None, self_beginning))
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
318
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
319 def test(self, ignore_context):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
320 # stack of triples (fid, p, ic)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
321 # fid is index of current fragment
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
322 # p is position in this fragment
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
323 # ic is if we ignore context in this fragment
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
324 stack = []
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
325 stack_push = stack.append
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
326 stack_pop = stack.pop
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
327 frags = self.fragments
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
328 frags_len = len(frags)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
329
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
330 def _test(event, namespaces, variables, updateonly=False):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
331 # expression found impossible during init
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
332 if frags is None:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
333 return None
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
334
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
335 kind, data, pos = event[:3]
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
336
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
337 # skip events we don't care about
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
338 if kind is END:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
339 if stack:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
340 stack_pop()
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
341 return None
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
342 if kind is START_NS or kind is END_NS \
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
343 or kind is START_CDATA or kind is END_CDATA:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
344 return None
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
345
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
346 if not stack:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
347 # root node, nothing on stack, special case
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
348 fid = 0
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
349 # skip empty fragments (there can be actually only one)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
350 while not frags[fid][0]:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
351 fid += 1
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
352 p = 0
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
353 # empty fragment means descendant node at beginning
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
354 ic = ignore_context or (fid > 0)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
355
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
356 # expression can match first node, if first axis is self::,
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
357 # descendant-or-self:: or if ignore_context is True and
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
358 # axis is not descendant::
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
359 if not frags[fid][3] and (not ignore_context or fid > 0):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
360 # axis is not self-beggining, we have to skip this node
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
361 stack_push((fid, p, ic))
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
362 return None
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
363 else:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
364 # take position of parent
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
365 fid, p, ic = stack[-1]
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
366
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
367 if fid is not None and not ic:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
368 # fragment not ignoring context - we can't jump back
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
369 frag, pi, attrib, _ = frags[fid]
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
370 frag_len = len(frag)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
371
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
372 if p == frag_len:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
373 # that probably means empty first fragment
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
374 pass
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
375 elif frag[p](kind, data, pos, namespaces, variables):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
376 # match, so we can go further
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
377 p += 1
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
378 else:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
379 # not matched, so there will be no match in subtree
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
380 fid, p = None, None
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
381
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
382 if p == frag_len and fid + 1 != frags_len:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
383 # we made it to end of fragment, we can go to following
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
384 fid += 1
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
385 p = 0
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
386 ic = True
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
387
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
388 if fid is None:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
389 # there was no match in fragment not ignoring context
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
390 if kind is START:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
391 stack_push((fid, p, ic))
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
392 return None
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
393
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
394 if ic:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
395 # we are in fragment ignoring context
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
396 while True:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
397 frag, pi, attrib, _ = frags[fid]
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
398 frag_len = len(frag)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
399
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
400 # KMP new "character"
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
401 while p > 0 and (p >= frag_len or not \
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
402 frag[p](kind, data, pos, namespaces, variables)):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
403 p = pi[p-1]
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
404 if frag[p](kind, data, pos, namespaces, variables):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
405 p += 1
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
406
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
407 if p == frag_len:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
408 # end of fragment reached
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
409 if fid + 1 == frags_len:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
410 # that was last fragment
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
411 break
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
412 else:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
413 fid += 1
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
414 p = 0
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
415 ic = True
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
416 if not frags[fid][3]:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
417 # next fragment not self-beginning
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
418 break
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
419 else:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
420 break
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
421
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
422 if kind is START:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
423 # we have to put new position on stack, for children
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
424
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
425 if not ic and fid + 1 == frags_len and p == frag_len:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
426 # it is end of the only, not context ignoring fragment
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
427 # so there will be no matches in subtree
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
428 stack_push((None, None, ic))
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
429 else:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
430 stack_push((fid, p, ic))
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
431
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
432 # have we reached the end of the last fragment?
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
433 if fid + 1 == frags_len and p == frag_len:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
434 if attrib: # attribute ended path, return value
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
435 return attrib(kind, data, pos, namespaces, variables)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
436 return True
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
437
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
438 return None
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
439
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
440 return _test
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
441
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
442
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
443 class SingleStepStrategy(object):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
444
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
445 @classmethod
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
446 def supports(cls, path):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
447 return len(path) == 1
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
448
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
449 def __init__(self, path):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
450 self.path = path
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
451
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
452 def test(self, ignore_context):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
453 steps = self.path
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
454 if steps[0][0] is ATTRIBUTE:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
455 steps = [_DOTSLASH] + steps
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
456 select_attr = steps[-1][0] is ATTRIBUTE and steps[-1][1] or None
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
457
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
458 # for every position in expression stores counters' list
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
459 # it is used for position based predicates
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
460 counters = []
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
461 depth = [0]
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
462
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
463 def _test(event, namespaces, variables, updateonly=False):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
464 kind, data, pos = event[:3]
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
465
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
466 # Manage the stack that tells us "where we are" in the stream
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
467 if kind is END:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
468 if not ignore_context:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
469 depth[0] -= 1
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
470 return None
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
471 elif kind is START_NS or kind is END_NS \
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
472 or kind is START_CDATA or kind is END_CDATA:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
473 # should we make namespaces work?
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
474 return None
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
475
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
476 if not ignore_context:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
477 outside = (steps[0][0] is SELF and depth[0] != 0) \
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
478 or (steps[0][0] is CHILD and depth[0] != 1) \
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
479 or (steps[0][0] is DESCENDANT and depth[0] < 1)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
480 if kind is START:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
481 depth[0] += 1
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
482 if outside:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
483 return None
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
484
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
485 axis, nodetest, predicates = steps[0]
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
486 if not nodetest(kind, data, pos, namespaces, variables):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
487 return None
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
488
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
489 if predicates:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
490 cnum = 0
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
491 for predicate in predicates:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
492 pretval = predicate(kind, data, pos, namespaces, variables)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
493 if type(pretval) is float: # FIXME <- need to check this
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
494 # for other types that can be
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
495 # coerced to float
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
496 if len(counters) < cnum + 1:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
497 counters.append(0)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
498 counters[cnum] += 1
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
499 if counters[cnum] != int(pretval):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
500 pretval = False
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
501 cnum += 1
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
502 if not pretval:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
503 return None
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
504
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
505 if select_attr:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
506 return select_attr(kind, data, pos, namespaces, variables)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
507
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
508 return True
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
509
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
510 return _test
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
511
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
512
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
513 class Path(object):
26
3c1a022be04c * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
514 """Implements basic XPath support on streams.
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
515
818
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
516 Instances of this class represent a "compiled" XPath expression, and
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
517 provide methods for testing the path against a stream, as well as
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
518 extracting a substream matching that path.
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
519 """
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
520
818
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
521 STRATEGIES = (SingleStepStrategy, SimplePathStrategy, GenericStrategy)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
522
139
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
523 def __init__(self, text, filename=None, lineno=-1):
26
3c1a022be04c * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
524 """Create the path object from a string.
3c1a022be04c * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
525
425
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 386
diff changeset
526 :param text: the path expression
498
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 425
diff changeset
527 :param filename: the name of the file in which the path expression was
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 425
diff changeset
528 found (used in error messages)
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 425
diff changeset
529 :param lineno: the line on which the expression was found
26
3c1a022be04c * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
530 """
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
531 self.source = text
139
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
532 self.paths = PathParser(text, filename, lineno).parse()
818
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
533 self.strategies = []
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
534 for path in self.paths:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
535 for strategy_class in self.STRATEGIES:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
536 if strategy_class.supports(path):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
537 self.strategies.append(strategy_class(path))
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
538 break
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
539 else:
854
4d9bef447df9 More work on reducing the size of the diff produced by 2to3.
cmlenz
parents: 853
diff changeset
540 raise NotImplemented('No strategy found for path')
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
541
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
542 def __repr__(self):
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
543 paths = []
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
544 for path in self.paths:
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
545 steps = []
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
546 for axis, nodetest, predicates in path:
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
547 steps.append('%s::%s' % (axis, nodetest))
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
548 for predicate in predicates:
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
549 steps[-1] += '[%s]' % predicate
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
550 paths.append('/'.join(steps))
860
16d55698006a A bit of cleanup of the `Markup` Python implementation.
cmlenz
parents: 857
diff changeset
551 return '<%s "%s">' % (type(self).__name__, '|'.join(paths))
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
552
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
553 def select(self, stream, namespaces=None, variables=None):
26
3c1a022be04c * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
554 """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
555
3c1a022be04c * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
556 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
557
230
84168828b074 Renamed Markup to Genshi in repository.
cmlenz
parents: 228
diff changeset
558 >>> from genshi.input import XML
33
1fdb7054fb42 Add doctests for path module.
cmlenz
parents: 27
diff changeset
559 >>> xml = XML('<root><elem><child>Text</child></elem></root>')
61
448792ab1303 Use a different namespace than Kid uses.
cmlenz
parents: 38
diff changeset
560
853
f33ecf3c319e Convert a bunch of print statements to py3k compatible syntax.
cmlenz
parents: 852
diff changeset
561 >>> print(Path('.//child').select(xml))
33
1fdb7054fb42 Add doctests for path module.
cmlenz
parents: 27
diff changeset
562 <child>Text</child>
1fdb7054fb42 Add doctests for path module.
cmlenz
parents: 27
diff changeset
563
853
f33ecf3c319e Convert a bunch of print statements to py3k compatible syntax.
cmlenz
parents: 852
diff changeset
564 >>> print(Path('.//child/text()').select(xml))
33
1fdb7054fb42 Add doctests for path module.
cmlenz
parents: 27
diff changeset
565 Text
1fdb7054fb42 Add doctests for path module.
cmlenz
parents: 27
diff changeset
566
425
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 386
diff changeset
567 :param stream: the stream to select from
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 386
diff changeset
568 :param namespaces: (optional) a mapping of namespace prefixes to URIs
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 386
diff changeset
569 :param variables: (optional) a mapping of variable names to values
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 386
diff changeset
570 :return: the substream matching the path, or an empty stream
498
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 425
diff changeset
571 :rtype: `Stream`
26
3c1a022be04c * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
572 """
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
573 if namespaces is None:
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
574 namespaces = {}
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
575 if variables is None:
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
576 variables = {}
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
577 stream = iter(stream)
818
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
578 def _generate(stream=stream, ns=namespaces, vs=variables):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
579 next = stream.next
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
580 test = self.test()
305
60111a041e7c Various performance-oriented tweaks.
cmlenz
parents: 282
diff changeset
581 for event in stream:
818
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
582 result = test(event, ns, vs)
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
583 if result is True:
305
60111a041e7c Various performance-oriented tweaks.
cmlenz
parents: 282
diff changeset
584 yield event
330
3e749eaa3100 XPath tests should never return event tuples, just values or booleans.
cmlenz
parents: 326
diff changeset
585 if event[0] is START:
3e749eaa3100 XPath tests should never return event tuples, just values or booleans.
cmlenz
parents: 326
diff changeset
586 depth = 1
3e749eaa3100 XPath tests should never return event tuples, just values or booleans.
cmlenz
parents: 326
diff changeset
587 while depth > 0:
818
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
588 subevent = next()
330
3e749eaa3100 XPath tests should never return event tuples, just values or booleans.
cmlenz
parents: 326
diff changeset
589 if subevent[0] is START:
3e749eaa3100 XPath tests should never return event tuples, just values or booleans.
cmlenz
parents: 326
diff changeset
590 depth += 1
3e749eaa3100 XPath tests should never return event tuples, just values or booleans.
cmlenz
parents: 326
diff changeset
591 elif subevent[0] is END:
3e749eaa3100 XPath tests should never return event tuples, just values or booleans.
cmlenz
parents: 326
diff changeset
592 depth -= 1
3e749eaa3100 XPath tests should never return event tuples, just values or booleans.
cmlenz
parents: 326
diff changeset
593 yield subevent
818
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
594 test(subevent, ns, vs, updateonly=True)
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
595 elif result:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
596 yield result
605
d0345c64da65 Text templates now default to rendering as plain text; it is no longer necessary to explicitly specify the "text" method to the `render()` or `serialize()` method of the generated markup stream. See tickets #62 and #118.
cmlenz
parents: 593
diff changeset
597 return Stream(_generate(),
d0345c64da65 Text templates now default to rendering as plain text; it is no longer necessary to explicitly specify the "text" method to the `render()` or `serialize()` method of the generated markup stream. See tickets #62 and #118.
cmlenz
parents: 593
diff changeset
598 serializer=getattr(stream, 'serializer', None))
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
599
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
600 def test(self, ignore_context=False):
26
3c1a022be04c * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
601 """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
602 a specific stream event.
3c1a022be04c * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
603
425
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 386
diff changeset
604 The function returned expects the positional arguments ``event``,
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 386
diff changeset
605 ``namespaces`` and ``variables``. The first is a stream event, while the
305
60111a041e7c Various performance-oriented tweaks.
cmlenz
parents: 282
diff changeset
606 latter two are a mapping of namespace prefixes to URIs, and a mapping
306
095a754f95a8 Minor optimization for XPath evaluation.
cmlenz
parents: 305
diff changeset
607 of variable names to values, respectively. In addition, the function
425
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 386
diff changeset
608 accepts an ``updateonly`` keyword argument that default to ``False``. If
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 386
diff changeset
609 it is set to ``True``, the function only updates its internal state,
306
095a754f95a8 Minor optimization for XPath evaluation.
cmlenz
parents: 305
diff changeset
610 but does not perform any tests or return a result.
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
611
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
612 If the path matches the event, the function returns the match (for
425
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 386
diff changeset
613 example, a `START` or `TEXT` event.) Otherwise, it returns ``None``.
33
1fdb7054fb42 Add doctests for path module.
cmlenz
parents: 27
diff changeset
614
230
84168828b074 Renamed Markup to Genshi in repository.
cmlenz
parents: 228
diff changeset
615 >>> from genshi.input import XML
33
1fdb7054fb42 Add doctests for path module.
cmlenz
parents: 27
diff changeset
616 >>> xml = XML('<root><elem><child id="1"/></elem><child id="2"/></root>')
1fdb7054fb42 Add doctests for path module.
cmlenz
parents: 27
diff changeset
617 >>> test = Path('child').test()
818
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
618 >>> namespaces, variables = {}, {}
305
60111a041e7c Various performance-oriented tweaks.
cmlenz
parents: 282
diff changeset
619 >>> for event in xml:
818
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
620 ... if test(event, namespaces, variables):
853
f33ecf3c319e Convert a bunch of print statements to py3k compatible syntax.
cmlenz
parents: 852
diff changeset
621 ... print('%s %r' % (event[0], event[1]))
857
129e54866a98 Avoid unicode literals in `repr`s of `QName` and `Namespace` when not necessary.
cmlenz
parents: 856
diff changeset
622 START (QName('child'), Attrs([(QName('id'), u'2')]))
498
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 425
diff changeset
623
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 425
diff changeset
624 :param ignore_context: if `True`, the path is interpreted like a pattern
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 425
diff changeset
625 in XSLT, meaning for example that it will match
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 425
diff changeset
626 at any depth
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 425
diff changeset
627 :return: a function that can be used to test individual events in a
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 425
diff changeset
628 stream against the path
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 425
diff changeset
629 :rtype: ``function``
26
3c1a022be04c * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
630 """
818
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
631 tests = [s.test(ignore_context) for s in self.strategies]
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
632 if len(tests) == 1:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
633 return tests[0]
216
636fe6766b4d Many fixes to XPath evaluation. Among other things, this should get rid of the bug that attributes were getting ?pulled up? by `py:match` directives using `py:attrs="select('@*')"` (see #50).
cmlenz
parents: 215
diff changeset
634
818
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
635 def _multi(event, namespaces, variables, updateonly=False):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
636 retval = None
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
637 for test in tests:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
638 val = test(event, namespaces, variables, updateonly=updateonly)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
639 if retval is None:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
640 retval = val
259
fe8dbe9066c1 Fix bug in evaluating XPath expressions using the union operator `|`, which caused any path but the first to get out of sync with the event stream, and the whole thing returning too few results.
cmlenz
parents: 250
diff changeset
641 return retval
818
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
642 return _multi
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
643
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
644
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
645 class PathSyntaxError(Exception):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
646 """Exception raised when an XPath expression is syntactically incorrect."""
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
647
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
648 def __init__(self, message, filename=None, lineno=-1, offset=-1):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
649 if filename:
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
650 message = '%s (%s, line %d)' % (message, filename, lineno)
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
651 Exception.__init__(self, message)
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
652 self.filename = filename
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
653 self.lineno = lineno
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
654 self.offset = offset
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
655
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
656
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
657 class PathParser(object):
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
658 """Tokenizes and parses an XPath expression."""
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
659
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
660 _QUOTES = (("'", "'"), ('"', '"'))
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
661 _TOKENS = ('::', ':', '..', '.', '//', '/', '[', ']', '()', '(', ')', '@',
179
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
662 '=', '!=', '!', '|', ',', '>=', '>', '<=', '<', '$')
163
9c023c395e44 Support for XPath number literals including decimal places.
cmlenz
parents: 162
diff changeset
663 _tokenize = re.compile('("[^"]*")|(\'[^\']*\')|((?:\d+)?\.\d+)|(%s)|([^%s\s]+)|\s+' % (
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
664 '|'.join([re.escape(t) for t in _TOKENS]),
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
665 ''.join([re.escape(t[0]) for t in _TOKENS]))).findall
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
666
139
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
667 def __init__(self, text, filename=None, lineno=-1):
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
668 self.filename = filename
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
669 self.lineno = lineno
854
4d9bef447df9 More work on reducing the size of the diff produced by 2to3.
cmlenz
parents: 853
diff changeset
670 self.tokens = [t for t in [dqstr or sqstr or number or token or name
4d9bef447df9 More work on reducing the size of the diff produced by 2to3.
cmlenz
parents: 853
diff changeset
671 for dqstr, sqstr, number, token, name in
4d9bef447df9 More work on reducing the size of the diff produced by 2to3.
cmlenz
parents: 853
diff changeset
672 self._tokenize(text)] if t]
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
673 self.pos = 0
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
674
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
675 # Tokenizer
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
676
822
70fddd2262f5 Get rid of some Python 2.3 legacy that's no longer needed now that 2.4 is the baseline.
cmlenz
parents: 818
diff changeset
677 @property
70fddd2262f5 Get rid of some Python 2.3 legacy that's no longer needed now that 2.4 is the baseline.
cmlenz
parents: 818
diff changeset
678 def at_end(self):
70fddd2262f5 Get rid of some Python 2.3 legacy that's no longer needed now that 2.4 is the baseline.
cmlenz
parents: 818
diff changeset
679 return self.pos == len(self.tokens) - 1
70fddd2262f5 Get rid of some Python 2.3 legacy that's no longer needed now that 2.4 is the baseline.
cmlenz
parents: 818
diff changeset
680
70fddd2262f5 Get rid of some Python 2.3 legacy that's no longer needed now that 2.4 is the baseline.
cmlenz
parents: 818
diff changeset
681 @property
70fddd2262f5 Get rid of some Python 2.3 legacy that's no longer needed now that 2.4 is the baseline.
cmlenz
parents: 818
diff changeset
682 def cur_token(self):
70fddd2262f5 Get rid of some Python 2.3 legacy that's no longer needed now that 2.4 is the baseline.
cmlenz
parents: 818
diff changeset
683 return self.tokens[self.pos]
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
684
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
685 def next_token(self):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
686 self.pos += 1
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
687 return self.tokens[self.pos]
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
688
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
689 def peek_token(self):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
690 if not self.at_end:
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
691 return self.tokens[self.pos + 1]
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
692 return None
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
693
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
694 # Recursive descent parser
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
695
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
696 def parse(self):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
697 """Parses the XPath expression and returns a list of location path
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
698 tests.
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
699
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
700 For union expressions (such as `*|text()`), this function returns one
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
701 test for each operand in the union. For patch expressions that don't
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
702 use the union operator, the function always returns a list of size 1.
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
703
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
704 Each path test in turn is a sequence of tests that correspond to the
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
705 location steps, each tuples of the form `(axis, testfunc, predicates)`
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
706 """
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
707 paths = [self._location_path()]
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
708 while self.cur_token == '|':
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
709 self.next_token()
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
710 paths.append(self._location_path())
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
711 if not self.at_end:
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
712 raise PathSyntaxError('Unexpected token %r after end of expression'
139
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
713 % self.cur_token, self.filename, self.lineno)
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
714 return paths
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
715
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
716 def _location_path(self):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
717 steps = []
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
718 while True:
215
94120e6b0ce4 A couple of minor XPath fixes.
cmlenz
parents: 211
diff changeset
719 if self.cur_token.startswith('/'):
818
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
720 if not steps:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
721 if self.cur_token == '//':
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
722 # hack to make //* match every node - also root
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
723 self.next_token()
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
724 axis, nodetest, predicates = self._location_step()
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
725 steps.append((DESCENDANT_OR_SELF, nodetest,
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
726 predicates))
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
727 if self.at_end or not self.cur_token.startswith('/'):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
728 break
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
729 continue
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
730 else:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
731 raise PathSyntaxError('Absolute location paths not '
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
732 'supported', self.filename,
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
733 self.lineno)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
734 elif self.cur_token == '//':
215
94120e6b0ce4 A couple of minor XPath fixes.
cmlenz
parents: 211
diff changeset
735 steps.append((DESCENDANT_OR_SELF, NodeTest(), []))
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
736 self.next_token()
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
737
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
738 axis, nodetest, predicates = self._location_step()
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
739 if not axis:
145
47bbd9d2a5af * Fix error in expression evaluation when the expression evaluates to an iterable that does not produce event tuples.
cmlenz
parents: 139
diff changeset
740 axis = CHILD
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
741 steps.append((axis, nodetest, predicates))
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
742 if self.at_end or not self.cur_token.startswith('/'):
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
743 break
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
744
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
745 return steps
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
746
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
747 def _location_step(self):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
748 if self.cur_token == '@':
114
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
749 axis = ATTRIBUTE
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
750 self.next_token()
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
751 elif self.cur_token == '.':
114
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
752 axis = SELF
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
753 elif self.cur_token == '..':
139
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
754 raise PathSyntaxError('Unsupported axis "parent"', self.filename,
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
755 self.lineno)
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
756 elif self.peek_token() == '::':
114
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
757 axis = Axis.forname(self.cur_token)
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
758 if axis is None:
139
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
759 raise PathSyntaxError('Unsupport axis "%s"' % axis,
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
760 self.filename, self.lineno)
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
761 self.next_token()
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
762 self.next_token()
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
763 else:
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
764 axis = None
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
765 nodetest = self._node_test(axis or CHILD)
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
766 predicates = []
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
767 while self.cur_token == '[':
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
768 predicates.append(self._predicate())
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
769 return axis, nodetest, predicates
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
770
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
771 def _node_test(self, axis=None):
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
772 test = prefix = None
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
773 next_token = self.peek_token()
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
774 if next_token in ('(', '()'): # Node type test
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
775 test = self._node_type()
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
776
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
777 elif next_token == ':': # Namespace prefix
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
778 prefix = self.cur_token
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
779 self.next_token()
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
780 localname = self.next_token()
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
781 if localname == '*':
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
782 test = QualifiedPrincipalTypeTest(axis, prefix)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
783 else:
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
784 test = QualifiedNameTest(axis, prefix, localname)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
785
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
786 else: # Name test
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
787 if self.cur_token == '*':
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
788 test = PrincipalTypeTest(axis)
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
789 elif self.cur_token == '.':
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
790 test = NodeTest()
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
791 else:
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
792 test = LocalNameTest(axis, self.cur_token)
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
793
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
794 if not self.at_end:
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
795 self.next_token()
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
796 return test
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
797
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
798 def _node_type(self):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
799 name = self.cur_token
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
800 self.next_token()
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
801
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
802 args = []
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
803 if self.cur_token != '()':
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
804 # The processing-instruction() function optionally accepts the
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
805 # name of the PI as argument, which must be a literal string
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
806 self.next_token() # (
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
807 if self.cur_token != ')':
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
808 string = self.cur_token
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
809 if (string[0], string[-1]) in self._QUOTES:
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
810 string = string[1:-1]
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
811 args.append(string)
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
812
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
813 cls = _nodetest_map.get(name)
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
814 if not cls:
139
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
815 raise PathSyntaxError('%s() not allowed here' % name, self.filename,
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
816 self.lineno)
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
817 return cls(*args)
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
818
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
819 def _predicate(self):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
820 assert self.cur_token == '['
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
821 self.next_token()
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
822 expr = self._or_expr()
121
062e51ad7b19 Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents: 114
diff changeset
823 if self.cur_token != ']':
062e51ad7b19 Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents: 114
diff changeset
824 raise PathSyntaxError('Expected "]" to close predicate, '
139
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
825 'but found "%s"' % self.cur_token,
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
826 self.filename, self.lineno)
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
827 if not self.at_end:
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
828 self.next_token()
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
829 return expr
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
830
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
831 def _or_expr(self):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
832 expr = self._and_expr()
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
833 while self.cur_token == 'or':
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
834 self.next_token()
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
835 expr = OrOperator(expr, self._and_expr())
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
836 return expr
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
837
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
838 def _and_expr(self):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
839 expr = self._equality_expr()
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
840 while self.cur_token == 'and':
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
841 self.next_token()
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
842 expr = AndOperator(expr, self._equality_expr())
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
843 return expr
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
844
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
845 def _equality_expr(self):
162
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
846 expr = self._relational_expr()
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
847 while self.cur_token in ('=', '!='):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
848 op = _operator_map[self.cur_token]
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
849 self.next_token()
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
850 expr = op(expr, self._relational_expr())
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
851 return expr
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
852
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
853 def _relational_expr(self):
516
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
854 expr = self._sub_expr()
162
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
855 while self.cur_token in ('>', '>=', '<', '>='):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
856 op = _operator_map[self.cur_token]
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
857 self.next_token()
516
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
858 expr = op(expr, self._sub_expr())
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
859 return expr
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
860
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
861 def _sub_expr(self):
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
862 token = self.cur_token
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
863 if token != '(':
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
864 return self._primary_expr()
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
865 self.next_token()
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
866 expr = self._or_expr()
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
867 if self.cur_token != ')':
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
868 raise PathSyntaxError('Expected ")" to close sub-expression, '
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
869 'but found "%s"' % self.cur_token,
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
870 self.filename, self.lineno)
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
871 self.next_token()
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
872 return expr
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
873
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
874 def _primary_expr(self):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
875 token = self.cur_token
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
876 if len(token) > 1 and (token[0], token[-1]) in self._QUOTES:
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
877 self.next_token()
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
878 return StringLiteral(token[1:-1])
163
9c023c395e44 Support for XPath number literals including decimal places.
cmlenz
parents: 162
diff changeset
879 elif token[0].isdigit() or token[0] == '.':
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
880 self.next_token()
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
881 return NumberLiteral(as_float(token))
179
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
882 elif token == '$':
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
883 token = self.next_token()
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
884 self.next_token()
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
885 return VariableReference(token)
121
062e51ad7b19 Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents: 114
diff changeset
886 elif not self.at_end and self.peek_token().startswith('('):
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
887 return self._function_call()
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
888 else:
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
889 axis = None
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
890 if token == '@':
114
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
891 axis = ATTRIBUTE
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
892 self.next_token()
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
893 return self._node_test(axis)
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
894
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
895 def _function_call(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
896 name = self.cur_token
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
897 if self.next_token() == '()':
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
898 args = []
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
899 else:
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
900 assert self.cur_token == '('
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
901 self.next_token()
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
902 args = [self._or_expr()]
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
903 while self.cur_token == ',':
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
904 self.next_token()
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
905 args.append(self._or_expr())
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
906 if not self.cur_token == ')':
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
907 raise PathSyntaxError('Expected ")" to close function argument '
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
908 'list, but found "%s"' % self.cur_token,
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
909 self.filename, self.lineno)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
910 self.next_token()
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
911 cls = _function_map.get(name)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
912 if not cls:
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
913 raise PathSyntaxError('Unsupported function "%s"' % name,
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
914 self.filename, self.lineno)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
915 return cls(*args)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
916
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
917
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
918 # Type coercion
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
919
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
920 def as_scalar(value):
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
921 """Convert value to a scalar. If a single element Attrs() object is passed
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
922 the value of the single attribute will be returned."""
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
923 if isinstance(value, Attrs):
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
924 assert len(value) == 1
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
925 return value[0][1]
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
926 else:
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
927 return value
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
928
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
929 def as_float(value):
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
930 # FIXME - if value is a bool it will be coerced to 0.0 and consequently
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
931 # compared as a float. This is probably not ideal.
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
932 return float(as_scalar(value))
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
933
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
934 def as_long(value):
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
935 return long(as_scalar(value))
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
936
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
937 def as_string(value):
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
938 value = as_scalar(value)
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
939 if value is False:
852
07f4339fecb0 Remove usage of unicode literals in a couple of places where they were not strictly necessary.
cmlenz
parents: 822
diff changeset
940 return ''
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
941 return unicode(value)
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
942
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
943 def as_bool(value):
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
944 return bool(as_scalar(value))
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
945
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
946
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
947 # Node tests
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
948
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
949 class PrincipalTypeTest(object):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
950 """Node test that matches any event with the given principal type."""
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
951 __slots__ = ['principal_type']
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
952 def __init__(self, principal_type):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
953 self.principal_type = principal_type
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
954 def __call__(self, kind, data, pos, namespaces, variables):
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
955 if kind is START:
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
956 if self.principal_type is ATTRIBUTE:
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
957 return data[1] or None
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
958 else:
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
959 return True
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
960 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
961 return '*'
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
962
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
963 class QualifiedPrincipalTypeTest(object):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
964 """Node test that matches any event with the given principal type in a
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
965 specific namespace."""
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
966 __slots__ = ['principal_type', 'prefix']
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
967 def __init__(self, principal_type, prefix):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
968 self.principal_type = principal_type
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
969 self.prefix = prefix
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
970 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
971 namespace = Namespace(namespaces.get(self.prefix))
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
972 if kind is START:
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
973 if self.principal_type is ATTRIBUTE and data[1]:
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
974 return Attrs([(name, value) for name, value in data[1]
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
975 if name in namespace]) or None
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
976 else:
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
977 return data[0] in namespace
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
978 def __repr__(self):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
979 return '%s:*' % self.prefix
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
980
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
981 class LocalNameTest(object):
364
7dabedbb53fb Fix for #77: match templates were matching their own output.
cmlenz
parents: 363
diff changeset
982 """Node test that matches any event with the given principal type and
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
983 local name.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
984 """
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
985 __slots__ = ['principal_type', 'name']
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
986 def __init__(self, principal_type, name):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
987 self.principal_type = principal_type
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
988 self.name = name
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
989 def __call__(self, kind, data, pos, namespaces, variables):
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
990 if kind is START:
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
991 if self.principal_type is ATTRIBUTE and self.name in data[1]:
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
992 return Attrs([(self.name, data[1].get(self.name))])
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
993 else:
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
994 return data[0].localname == self.name
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
995 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
996 return self.name
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
997
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
998 class QualifiedNameTest(object):
364
7dabedbb53fb Fix for #77: match templates were matching their own output.
cmlenz
parents: 363
diff changeset
999 """Node test that matches any event with the given principal type and
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1000 qualified name.
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1001 """
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1002 __slots__ = ['principal_type', 'prefix', 'name']
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1003 def __init__(self, principal_type, prefix, name):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1004 self.principal_type = principal_type
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1005 self.prefix = prefix
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1006 self.name = name
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1007 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1008 qname = QName('%s}%s' % (namespaces.get(self.prefix), self.name))
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1009 if kind is START:
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1010 if self.principal_type is ATTRIBUTE and qname in data[1]:
1043
a21009a2bc3a Return correct value and properly namespaced attribute name when matching namespaced attributes with XPath expressions (fixes #572; thanks to Olemis Lang <olemis+trac@gmail.com> for bug report and suggestion for fix).
hodgestar
parents: 860
diff changeset
1011 return Attrs([(qname, data[1].get(qname))])
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1012 else:
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1013 return data[0] == qname
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1014 def __repr__(self):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1015 return '%s:%s' % (self.prefix, self.name)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1016
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1017 class CommentNodeTest(object):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1018 """Node test that matches any comment events."""
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1019 __slots__ = []
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1020 def __call__(self, kind, data, pos, namespaces, variables):
330
3e749eaa3100 XPath tests should never return event tuples, just values or booleans.
cmlenz
parents: 326
diff changeset
1021 return kind is COMMENT
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1022 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1023 return 'comment()'
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1024
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1025 class NodeTest(object):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1026 """Node test that matches any node."""
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1027 __slots__ = []
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1028 def __call__(self, kind, data, pos, namespaces, variables):
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1029 if kind is START:
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1030 return True
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1031 return kind, data, pos
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1032 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1033 return 'node()'
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1034
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1035 class ProcessingInstructionNodeTest(object):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1036 """Node test that matches any processing instruction event."""
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1037 __slots__ = ['target']
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1038 def __init__(self, target=None):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1039 self.target = target
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1040 def __call__(self, kind, data, pos, namespaces, variables):
330
3e749eaa3100 XPath tests should never return event tuples, just values or booleans.
cmlenz
parents: 326
diff changeset
1041 return kind is PI and (not self.target or data[0] == self.target)
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1042 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1043 arg = ''
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1044 if self.target:
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1045 arg = '"' + self.target + '"'
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1046 return 'processing-instruction(%s)' % arg
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1047
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1048 class TextNodeTest(object):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1049 """Node test that matches any text event."""
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1050 __slots__ = []
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1051 def __call__(self, kind, data, pos, namespaces, variables):
330
3e749eaa3100 XPath tests should never return event tuples, just values or booleans.
cmlenz
parents: 326
diff changeset
1052 return kind is TEXT
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1053 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1054 return 'text()'
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1055
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1056 _nodetest_map = {'comment': CommentNodeTest, 'node': NodeTest,
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1057 'processing-instruction': ProcessingInstructionNodeTest,
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1058 'text': TextNodeTest}
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1059
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1060 # Functions
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1061
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1062 class Function(object):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1063 """Base class for function nodes in XPath expressions."""
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1064
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1065 class BooleanFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1066 """The `boolean` function, which converts its argument to a boolean
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1067 value.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1068 """
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1069 __slots__ = ['expr']
818
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
1070 _return_type = bool
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1071 def __init__(self, expr):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1072 self.expr = expr
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1073 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1074 val = self.expr(kind, data, pos, namespaces, variables)
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1075 return as_bool(val)
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1076 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1077 return 'boolean(%r)' % self.expr
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1078
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1079 class CeilingFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1080 """The `ceiling` function, which returns the nearest lower integer number
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1081 for the given number.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1082 """
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1083 __slots__ = ['number']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1084 def __init__(self, number):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1085 self.number = number
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1086 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1087 number = self.number(kind, data, pos, namespaces, variables)
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1088 return ceil(as_float(number))
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1089 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1090 return 'ceiling(%r)' % self.number
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1091
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1092 class ConcatFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1093 """The `concat` function, which concatenates (joins) the variable number of
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1094 strings it gets as arguments.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1095 """
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1096 __slots__ = ['exprs']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1097 def __init__(self, *exprs):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1098 self.exprs = exprs
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1099 def __call__(self, kind, data, pos, namespaces, variables):
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1100 strings = []
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1101 for item in [expr(kind, data, pos, namespaces, variables)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1102 for expr in self.exprs]:
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1103 strings.append(as_string(item))
852
07f4339fecb0 Remove usage of unicode literals in a couple of places where they were not strictly necessary.
cmlenz
parents: 822
diff changeset
1104 return ''.join(strings)
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1105 def __repr__(self):
169
dc6676d3b697 Fix syntax error in `path` module.
cmlenz
parents: 164
diff changeset
1106 return 'concat(%s)' % ', '.join([repr(expr) for expr in self.exprs])
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1107
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1108 class ContainsFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1109 """The `contains` function, which returns whether a string contains a given
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1110 substring.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1111 """
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1112 __slots__ = ['string1', 'string2']
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1113 def __init__(self, string1, string2):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1114 self.string1 = string1
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1115 self.string2 = string2
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1116 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1117 string1 = self.string1(kind, data, pos, namespaces, variables)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1118 string2 = self.string2(kind, data, pos, namespaces, variables)
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1119 return as_string(string2) in as_string(string1)
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1120 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1121 return 'contains(%r, %r)' % (self.string1, self.string2)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1122
534
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1123 class MatchesFunction(Function):
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1124 """The `matches` function, which returns whether a string matches a regular
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1125 expression.
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1126 """
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1127 __slots__ = ['string1', 'string2']
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1128 flag_mapping = {'s': re.S, 'm': re.M, 'i': re.I, 'x': re.X}
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1129
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1130 def __init__(self, string1, string2, flags=''):
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1131 self.string1 = string1
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1132 self.string2 = string2
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1133 self.flags = self._map_flags(flags)
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1134 def __call__(self, kind, data, pos, namespaces, variables):
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1135 string1 = as_string(self.string1(kind, data, pos, namespaces, variables))
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1136 string2 = as_string(self.string2(kind, data, pos, namespaces, variables))
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1137 return re.search(string2, string1, self.flags)
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1138 def _map_flags(self, flags):
593
0af1b7982ba5 Minor, cosmetic tweaks.
cmlenz
parents: 534
diff changeset
1139 return reduce(operator.or_,
534
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1140 [self.flag_map[flag] for flag in flags], re.U)
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1141 def __repr__(self):
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1142 return 'contains(%r, %r)' % (self.string1, self.string2)
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1143
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1144 class FalseFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1145 """The `false` function, which always returns the boolean `false` value."""
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1146 __slots__ = []
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1147 def __call__(self, kind, data, pos, namespaces, variables):
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1148 return False
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1149 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1150 return 'false()'
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1151
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1152 class FloorFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1153 """The `ceiling` function, which returns the nearest higher integer number
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1154 for the given number.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1155 """
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1156 __slots__ = ['number']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1157 def __init__(self, number):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1158 self.number = number
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1159 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1160 number = self.number(kind, data, pos, namespaces, variables)
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1161 return floor(as_float(number))
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1162 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1163 return 'floor(%r)' % self.number
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1164
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1165 class LocalNameFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1166 """The `local-name` function, which returns the local name of the current
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1167 element.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1168 """
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1169 __slots__ = []
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1170 def __call__(self, kind, data, pos, namespaces, variables):
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1171 if kind is START:
234
39c424b80edd * Minor simplification of XPath engine.
cmlenz
parents: 230
diff changeset
1172 return data[0].localname
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1173 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1174 return 'local-name()'
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1175
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1176 class NameFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1177 """The `name` function, which returns the qualified name of the current
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1178 element.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1179 """
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1180 __slots__ = []
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1181 def __call__(self, kind, data, pos, namespaces, variables):
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1182 if kind is START:
234
39c424b80edd * Minor simplification of XPath engine.
cmlenz
parents: 230
diff changeset
1183 return data[0]
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1184 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1185 return 'name()'
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1186
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1187 class NamespaceUriFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1188 """The `namespace-uri` function, which returns the namespace URI of the
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1189 current element.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1190 """
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1191 __slots__ = []
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1192 def __call__(self, kind, data, pos, namespaces, variables):
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1193 if kind is START:
234
39c424b80edd * Minor simplification of XPath engine.
cmlenz
parents: 230
diff changeset
1194 return data[0].namespace
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1195 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1196 return 'namespace-uri()'
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1197
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1198 class NotFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1199 """The `not` function, which returns the negated boolean value of its
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1200 argument.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1201 """
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1202 __slots__ = ['expr']
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1203 def __init__(self, expr):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1204 self.expr = expr
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1205 def __call__(self, kind, data, pos, namespaces, variables):
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1206 return not as_bool(self.expr(kind, data, pos, namespaces, variables))
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1207 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1208 return 'not(%s)' % self.expr
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1209
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1210 class NormalizeSpaceFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1211 """The `normalize-space` function, which removes leading and trailing
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1212 whitespace in the given string, and replaces multiple adjacent whitespace
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1213 characters inside the string with a single space.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1214 """
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1215 __slots__ = ['expr']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1216 _normalize = re.compile(r'\s{2,}').sub
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1217 def __init__(self, expr):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1218 self.expr = expr
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1219 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1220 string = self.expr(kind, data, pos, namespaces, variables)
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1221 return self._normalize(' ', as_string(string).strip())
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1222 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1223 return 'normalize-space(%s)' % repr(self.expr)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1224
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1225 class NumberFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1226 """The `number` function that converts its argument to a number."""
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1227 __slots__ = ['expr']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1228 def __init__(self, expr):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1229 self.expr = expr
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1230 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1231 val = self.expr(kind, data, pos, namespaces, variables)
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1232 return as_float(val)
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1233 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1234 return 'number(%r)' % self.expr
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1235
162
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1236 class RoundFunction(Function):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1237 """The `round` function, which returns the nearest integer number for the
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1238 given number.
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1239 """
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1240 __slots__ = ['number']
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1241 def __init__(self, number):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1242 self.number = number
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1243 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1244 number = self.number(kind, data, pos, namespaces, variables)
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1245 return round(as_float(number))
162
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1246 def __repr__(self):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1247 return 'round(%r)' % self.number
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1248
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1249 class StartsWithFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1250 """The `starts-with` function that returns whether one string starts with
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1251 a given substring.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1252 """
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1253 __slots__ = ['string1', 'string2']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1254 def __init__(self, string1, string2):
282
24b3cbbc1b1b Fix `starts-with()` XPath function so that it actually compares the two strings. Closes #61.
cmlenz
parents: 259
diff changeset
1255 self.string1 = string1
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1256 self.string2 = string2
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1257 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1258 string1 = self.string1(kind, data, pos, namespaces, variables)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1259 string2 = self.string2(kind, data, pos, namespaces, variables)
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1260 return as_string(string1).startswith(as_string(string2))
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1261 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1262 return 'starts-with(%r, %r)' % (self.string1, self.string2)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1263
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1264 class StringLengthFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1265 """The `string-length` function that returns the length of the given
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1266 string.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1267 """
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1268 __slots__ = ['expr']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1269 def __init__(self, expr):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1270 self.expr = expr
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1271 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1272 string = self.expr(kind, data, pos, namespaces, variables)
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1273 return len(as_string(string))
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1274 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1275 return 'string-length(%r)' % self.expr
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1276
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1277 class SubstringFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1278 """The `substring` function that returns the part of a string that starts
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1279 at the given offset, and optionally limited to the given length.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1280 """
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1281 __slots__ = ['string', 'start', 'length']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1282 def __init__(self, string, start, length=None):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1283 self.string = string
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1284 self.start = start
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1285 self.length = length
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1286 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1287 string = self.string(kind, data, pos, namespaces, variables)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1288 start = self.start(kind, data, pos, namespaces, variables)
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1289 length = 0
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1290 if self.length is not None:
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1291 length = self.length(kind, data, pos, namespaces, variables)
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1292 return string[as_long(start):len(as_string(string)) - as_long(length)]
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1293 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1294 if self.length is not None:
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1295 return 'substring(%r, %r, %r)' % (self.string, self.start,
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1296 self.length)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1297 else:
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1298 return 'substring(%r, %r)' % (self.string, self.start)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1299
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1300 class SubstringAfterFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1301 """The `substring-after` function that returns the part of a string that
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1302 is found after the given substring.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1303 """
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1304 __slots__ = ['string1', 'string2']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1305 def __init__(self, string1, string2):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1306 self.string1 = string1
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1307 self.string2 = string2
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1308 def __call__(self, kind, data, pos, namespaces, variables):
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1309 string1 = as_string(self.string1(kind, data, pos, namespaces, variables))
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1310 string2 = as_string(self.string2(kind, data, pos, namespaces, variables))
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1311 index = string1.find(string2)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1312 if index >= 0:
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1313 return string1[index + len(string2):]
852
07f4339fecb0 Remove usage of unicode literals in a couple of places where they were not strictly necessary.
cmlenz
parents: 822
diff changeset
1314 return ''
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1315 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1316 return 'substring-after(%r, %r)' % (self.string1, self.string2)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1317
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1318 class SubstringBeforeFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1319 """The `substring-before` function that returns the part of a string that
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1320 is found before the given substring.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1321 """
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1322 __slots__ = ['string1', 'string2']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1323 def __init__(self, string1, string2):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1324 self.string1 = string1
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1325 self.string2 = string2
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1326 def __call__(self, kind, data, pos, namespaces, variables):
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1327 string1 = as_string(self.string1(kind, data, pos, namespaces, variables))
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1328 string2 = as_string(self.string2(kind, data, pos, namespaces, variables))
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1329 index = string1.find(string2)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1330 if index >= 0:
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1331 return string1[:index]
852
07f4339fecb0 Remove usage of unicode literals in a couple of places where they were not strictly necessary.
cmlenz
parents: 822
diff changeset
1332 return ''
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1333 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1334 return 'substring-after(%r, %r)' % (self.string1, self.string2)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1335
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1336 class TranslateFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1337 """The `translate` function that translates a set of characters in a
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1338 string to target set of characters.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1339 """
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1340 __slots__ = ['string', 'fromchars', 'tochars']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1341 def __init__(self, string, fromchars, tochars):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1342 self.string = string
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1343 self.fromchars = fromchars
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1344 self.tochars = tochars
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1345 def __call__(self, kind, data, pos, namespaces, variables):
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1346 string = as_string(self.string(kind, data, pos, namespaces, variables))
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1347 fromchars = as_string(self.fromchars(kind, data, pos, namespaces, variables))
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1348 tochars = as_string(self.tochars(kind, data, pos, namespaces, variables))
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1349 table = dict(zip([ord(c) for c in fromchars],
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1350 [ord(c) for c in tochars]))
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1351 return string.translate(table)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1352 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1353 return 'translate(%r, %r, %r)' % (self.string, self.fromchars,
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1354 self.tochars)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1355
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1356 class TrueFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1357 """The `true` function, which always returns the boolean `true` value."""
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1358 __slots__ = []
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1359 def __call__(self, kind, data, pos, namespaces, variables):
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1360 return True
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1361 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1362 return 'true()'
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1363
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1364 _function_map = {'boolean': BooleanFunction, 'ceiling': CeilingFunction,
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1365 'concat': ConcatFunction, 'contains': ContainsFunction,
534
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1366 'matches': MatchesFunction, 'false': FalseFunction, 'floor':
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1367 FloorFunction, 'local-name': LocalNameFunction, 'name':
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1368 NameFunction, 'namespace-uri': NamespaceUriFunction,
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1369 'normalize-space': NormalizeSpaceFunction, 'not': NotFunction,
162
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1370 'number': NumberFunction, 'round': RoundFunction,
534
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1371 'starts-with': StartsWithFunction, 'string-length':
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1372 StringLengthFunction, 'substring': SubstringFunction,
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1373 'substring-after': SubstringAfterFunction, 'substring-before':
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1374 SubstringBeforeFunction, 'translate': TranslateFunction,
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1375 'true': TrueFunction}
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1376
179
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
1377 # Literals & Variables
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1378
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1379 class Literal(object):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1380 """Abstract base class for literal nodes."""
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1381
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1382 class StringLiteral(Literal):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1383 """A string literal node."""
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1384 __slots__ = ['text']
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1385 def __init__(self, text):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1386 self.text = text
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1387 def __call__(self, kind, data, pos, namespaces, variables):
228
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
1388 return self.text
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1389 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1390 return '"%s"' % self.text
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1391
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1392 class NumberLiteral(Literal):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1393 """A number literal node."""
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1394 __slots__ = ['number']
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1395 def __init__(self, number):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1396 self.number = number
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1397 def __call__(self, kind, data, pos, namespaces, variables):
228
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
1398 return self.number
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1399 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1400 return str(self.number)
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1401
179
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
1402 class VariableReference(Literal):
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
1403 """A variable reference node."""
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
1404 __slots__ = ['name']
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
1405 def __init__(self, name):
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
1406 self.name = name
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1407 def __call__(self, kind, data, pos, namespaces, variables):
228
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
1408 return variables.get(self.name)
179
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
1409 def __repr__(self):
215
94120e6b0ce4 A couple of minor XPath fixes.
cmlenz
parents: 211
diff changeset
1410 return str(self.name)
179
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
1411
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1412 # Operators
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1413
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1414 class AndOperator(object):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1415 """The boolean operator `and`."""
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1416 __slots__ = ['lval', 'rval']
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1417 def __init__(self, lval, rval):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1418 self.lval = lval
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1419 self.rval = rval
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1420 def __call__(self, kind, data, pos, namespaces, variables):
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1421 lval = as_bool(self.lval(kind, data, pos, namespaces, variables))
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1422 if not lval:
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1423 return False
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1424 rval = self.rval(kind, data, pos, namespaces, variables)
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1425 return as_bool(rval)
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1426 def __repr__(self):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1427 return '%s and %s' % (self.lval, self.rval)
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1428
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1429 class EqualsOperator(object):
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1430 """The equality operator `=`."""
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1431 __slots__ = ['lval', 'rval']
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1432 def __init__(self, lval, rval):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1433 self.lval = lval
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1434 self.rval = rval
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1435 def __call__(self, kind, data, pos, namespaces, variables):
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1436 lval = as_scalar(self.lval(kind, data, pos, namespaces, variables))
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1437 rval = as_scalar(self.rval(kind, data, pos, namespaces, variables))
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1438 return lval == rval
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1439 def __repr__(self):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1440 return '%s=%s' % (self.lval, self.rval)
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1441
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1442 class NotEqualsOperator(object):
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1443 """The equality operator `!=`."""
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1444 __slots__ = ['lval', 'rval']
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1445 def __init__(self, lval, rval):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1446 self.lval = lval
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1447 self.rval = rval
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1448 def __call__(self, kind, data, pos, namespaces, variables):
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1449 lval = as_scalar(self.lval(kind, data, pos, namespaces, variables))
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1450 rval = as_scalar(self.rval(kind, data, pos, namespaces, variables))
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1451 return lval != rval
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1452 def __repr__(self):
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1453 return '%s!=%s' % (self.lval, self.rval)
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1454
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1455 class OrOperator(object):
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1456 """The boolean operator `or`."""
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1457 __slots__ = ['lval', 'rval']
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1458 def __init__(self, lval, rval):
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1459 self.lval = lval
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1460 self.rval = rval
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1461 def __call__(self, kind, data, pos, namespaces, variables):
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1462 lval = as_bool(self.lval(kind, data, pos, namespaces, variables))
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1463 if lval:
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1464 return True
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1465 rval = self.rval(kind, data, pos, namespaces, variables)
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1466 return as_bool(rval)
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1467 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1468 return '%s or %s' % (self.lval, self.rval)
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1469
162
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1470 class GreaterThanOperator(object):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1471 """The relational operator `>` (greater than)."""
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1472 __slots__ = ['lval', 'rval']
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1473 def __init__(self, lval, rval):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1474 self.lval = lval
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1475 self.rval = rval
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1476 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1477 lval = self.lval(kind, data, pos, namespaces, variables)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1478 rval = self.rval(kind, data, pos, namespaces, variables)
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1479 return as_float(lval) > as_float(rval)
162
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1480 def __repr__(self):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1481 return '%s>%s' % (self.lval, self.rval)
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1482
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1483 class GreaterThanOrEqualOperator(object):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1484 """The relational operator `>=` (greater than or equal)."""
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1485 __slots__ = ['lval', 'rval']
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1486 def __init__(self, lval, rval):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1487 self.lval = lval
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1488 self.rval = rval
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1489 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1490 lval = self.lval(kind, data, pos, namespaces, variables)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1491 rval = self.rval(kind, data, pos, namespaces, variables)
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1492 return as_float(lval) >= as_float(rval)
162
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1493 def __repr__(self):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1494 return '%s>=%s' % (self.lval, self.rval)
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1495
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1496 class LessThanOperator(object):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1497 """The relational operator `<` (less than)."""
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1498 __slots__ = ['lval', 'rval']
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1499 def __init__(self, lval, rval):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1500 self.lval = lval
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1501 self.rval = rval
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1502 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1503 lval = self.lval(kind, data, pos, namespaces, variables)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1504 rval = self.rval(kind, data, pos, namespaces, variables)
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1505 return as_float(lval) < as_float(rval)
162
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1506 def __repr__(self):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1507 return '%s<%s' % (self.lval, self.rval)
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1508
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1509 class LessThanOrEqualOperator(object):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1510 """The relational operator `<=` (less than or equal)."""
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1511 __slots__ = ['lval', 'rval']
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1512 def __init__(self, lval, rval):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1513 self.lval = lval
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1514 self.rval = rval
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1515 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1516 lval = self.lval(kind, data, pos, namespaces, variables)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1517 rval = self.rval(kind, data, pos, namespaces, variables)
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1518 return as_float(lval) <= as_float(rval)
162
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1519 def __repr__(self):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1520 return '%s<=%s' % (self.lval, self.rval)
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1521
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1522 _operator_map = {'=': EqualsOperator, '!=': NotEqualsOperator,
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1523 '>': GreaterThanOperator, '>=': GreaterThanOrEqualOperator,
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1524 '<': LessThanOperator, '>=': LessThanOrEqualOperator}
333
e5ba8c5a30fe Fix XPath traversal in match templates. Previously, `div/p` would be treated the same as `div//p`, i.e. it would match all descendants and not just the immediate children.
cmlenz
parents: 330
diff changeset
1525
e5ba8c5a30fe Fix XPath traversal in match templates. Previously, `div/p` would be treated the same as `div//p`, i.e. it would match all descendants and not just the immediate children.
cmlenz
parents: 330
diff changeset
1526
e5ba8c5a30fe Fix XPath traversal in match templates. Previously, `div/p` would be treated the same as `div//p`, i.e. it would match all descendants and not just the immediate children.
cmlenz
parents: 330
diff changeset
1527 _DOTSLASHSLASH = (DESCENDANT_OR_SELF, PrincipalTypeTest(None), ())
818
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
1528 _DOTSLASH = (SELF, PrincipalTypeTest(None), ())
Copyright (C) 2012-2017 Edgewall Software