annotate genshi/path.py @ 724:919809e55d16 experimental-match-fastpaths

update to trunk to track r847, fixing python 2.4 compatibility issues in speedup (and fixing copyrights, apparently :))
author aflett
date Mon, 21 Apr 2008 19:36:53 +0000
parents 3d7288f373bd
children
rev   line source
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
1 # -*- coding: utf-8 -*-
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
2 #
724
919809e55d16 update to trunk to track r847, fixing python 2.4 compatibility issues in speedup (and fixing copyrights, apparently :))
aflett
parents: 687
diff changeset
3 # Copyright (C) 2006-2008 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>''')
516
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
33 >>> print doc.select('items/item[@status="closed" and '
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
34 ... '(@resolution="invalid" or not(@resolution))]/summary/text()')
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
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
41 from math import ceil, floor
593
0af1b7982ba5 Minor, cosmetic tweaks.
cmlenz
parents: 534
diff changeset
42 import operator
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
43 import re
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
44
230
84168828b074 Renamed Markup to Genshi in repository.
cmlenz
parents: 228
diff changeset
45 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
46 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
47 START_CDATA, END_CDATA
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
48
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
49 __all__ = ['Path', 'PathSyntaxError']
425
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 386
diff changeset
50 __docformat__ = 'restructuredtext en'
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
51
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
52
114
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
53 class Axis(object):
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
54 """Defines constants for the various supported XPath axes."""
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
55
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
56 ATTRIBUTE = 'attribute'
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
57 CHILD = 'child'
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
58 DESCENDANT = 'descendant'
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
59 DESCENDANT_OR_SELF = 'descendant-or-self'
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
60 SELF = 'self'
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 def forname(cls, name):
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
63 """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
64 axis was defined.
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
65 """
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
66 return getattr(cls, name.upper().replace('-', '_'), None)
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
67 forname = classmethod(forname)
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
68
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
69
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
70 ATTRIBUTE = Axis.ATTRIBUTE
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
71 CHILD = Axis.CHILD
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
72 DESCENDANT = Axis.DESCENDANT
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
73 DESCENDANT_OR_SELF = Axis.DESCENDANT_OR_SELF
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
74 SELF = Axis.SELF
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
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
77 class Path(object):
26
3c1a022be04c * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
78 """Implements basic XPath support on streams.
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
79
26
3c1a022be04c * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
80 Instances of this class represent a "compiled" XPath expression, and provide
3c1a022be04c * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
81 methods for testing the path against a stream, as well as extracting a
3c1a022be04c * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
82 substream matching that path.
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
83 """
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
84
139
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
85 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
86 """Create the path object from a string.
3c1a022be04c * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
87
425
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 386
diff changeset
88 :param text: the path expression
498
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 425
diff changeset
89 :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
90 found (used in error messages)
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 425
diff changeset
91 :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
92 """
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
93 self.source = text
139
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
94 self.paths = PathParser(text, filename, lineno).parse()
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
95
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
96 def __repr__(self):
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
97 paths = []
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
98 for path in self.paths:
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
99 steps = []
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
100 for axis, nodetest, predicates in path:
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
101 steps.append('%s::%s' % (axis, nodetest))
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
102 for predicate in predicates:
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
103 steps[-1] += '[%s]' % predicate
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
104 paths.append('/'.join(steps))
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
105 return '<%s "%s">' % (self.__class__.__name__, '|'.join(paths))
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
106
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
107 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
108 """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
109
3c1a022be04c * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
110 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
111
230
84168828b074 Renamed Markup to Genshi in repository.
cmlenz
parents: 228
diff changeset
112 >>> from genshi.input import XML
33
1fdb7054fb42 Add doctests for path module.
cmlenz
parents: 27
diff changeset
113 >>> xml = XML('<root><elem><child>Text</child></elem></root>')
61
448792ab1303 Use a different namespace than Kid uses.
cmlenz
parents: 38
diff changeset
114
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
115 >>> print Path('.//child').select(xml)
33
1fdb7054fb42 Add doctests for path module.
cmlenz
parents: 27
diff changeset
116 <child>Text</child>
1fdb7054fb42 Add doctests for path module.
cmlenz
parents: 27
diff changeset
117
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
118 >>> print Path('.//child/text()').select(xml)
33
1fdb7054fb42 Add doctests for path module.
cmlenz
parents: 27
diff changeset
119 Text
1fdb7054fb42 Add doctests for path module.
cmlenz
parents: 27
diff changeset
120
425
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 386
diff changeset
121 :param stream: the stream to select from
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 386
diff changeset
122 :param namespaces: (optional) a mapping of namespace prefixes to URIs
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 386
diff changeset
123 :param variables: (optional) a mapping of variable names to values
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 386
diff changeset
124 :return: the substream matching the path, or an empty stream
498
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 425
diff changeset
125 :rtype: `Stream`
26
3c1a022be04c * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
126 """
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
127 if namespaces is None:
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
128 namespaces = {}
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
129 if variables is None:
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
130 variables = {}
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
131 stream = iter(stream)
26
3c1a022be04c * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
132 def _generate():
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
133 test = self.test()
305
60111a041e7c Various performance-oriented tweaks.
cmlenz
parents: 282
diff changeset
134 for event in stream:
60111a041e7c Various performance-oriented tweaks.
cmlenz
parents: 282
diff changeset
135 result = test(event, namespaces, variables)
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
136 if result is True:
305
60111a041e7c Various performance-oriented tweaks.
cmlenz
parents: 282
diff changeset
137 yield event
330
3e749eaa3100 XPath tests should never return event tuples, just values or booleans.
cmlenz
parents: 326
diff changeset
138 if event[0] is START:
3e749eaa3100 XPath tests should never return event tuples, just values or booleans.
cmlenz
parents: 326
diff changeset
139 depth = 1
3e749eaa3100 XPath tests should never return event tuples, just values or booleans.
cmlenz
parents: 326
diff changeset
140 while depth > 0:
3e749eaa3100 XPath tests should never return event tuples, just values or booleans.
cmlenz
parents: 326
diff changeset
141 subevent = stream.next()
3e749eaa3100 XPath tests should never return event tuples, just values or booleans.
cmlenz
parents: 326
diff changeset
142 if subevent[0] is START:
3e749eaa3100 XPath tests should never return event tuples, just values or booleans.
cmlenz
parents: 326
diff changeset
143 depth += 1
3e749eaa3100 XPath tests should never return event tuples, just values or booleans.
cmlenz
parents: 326
diff changeset
144 elif subevent[0] is END:
3e749eaa3100 XPath tests should never return event tuples, just values or booleans.
cmlenz
parents: 326
diff changeset
145 depth -= 1
3e749eaa3100 XPath tests should never return event tuples, just values or booleans.
cmlenz
parents: 326
diff changeset
146 yield subevent
3e749eaa3100 XPath tests should never return event tuples, just values or booleans.
cmlenz
parents: 326
diff changeset
147 test(subevent, namespaces, variables,
3e749eaa3100 XPath tests should never return event tuples, just values or booleans.
cmlenz
parents: 326
diff changeset
148 updateonly=True)
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
149 elif result:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
150 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
151 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
152 serializer=getattr(stream, 'serializer', None))
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
153
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
154 def test(self, ignore_context=False):
26
3c1a022be04c * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
155 """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
156 a specific stream event.
3c1a022be04c * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
157
425
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 386
diff changeset
158 The function returned expects the positional arguments ``event``,
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 386
diff changeset
159 ``namespaces`` and ``variables``. The first is a stream event, while the
305
60111a041e7c Various performance-oriented tweaks.
cmlenz
parents: 282
diff changeset
160 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
161 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
162 accepts an ``updateonly`` keyword argument that default to ``False``. If
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 386
diff changeset
163 it is set to ``True``, the function only updates its internal state,
306
095a754f95a8 Minor optimization for XPath evaluation.
cmlenz
parents: 305
diff changeset
164 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
165
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
166 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
167 example, a `START` or `TEXT` event.) Otherwise, it returns ``None``.
33
1fdb7054fb42 Add doctests for path module.
cmlenz
parents: 27
diff changeset
168
230
84168828b074 Renamed Markup to Genshi in repository.
cmlenz
parents: 228
diff changeset
169 >>> from genshi.input import XML
33
1fdb7054fb42 Add doctests for path module.
cmlenz
parents: 27
diff changeset
170 >>> xml = XML('<root><elem><child id="1"/></elem><child id="2"/></root>')
1fdb7054fb42 Add doctests for path module.
cmlenz
parents: 27
diff changeset
171 >>> test = Path('child').test()
305
60111a041e7c Various performance-oriented tweaks.
cmlenz
parents: 282
diff changeset
172 >>> for event in xml:
60111a041e7c Various performance-oriented tweaks.
cmlenz
parents: 282
diff changeset
173 ... if test(event, {}, {}):
386
c66370dfc41b Unit test fixes for Python 2.3.
cmlenz
parents: 384
diff changeset
174 ... print event[0], repr(event[1])
c66370dfc41b Unit test fixes for Python 2.3.
cmlenz
parents: 384
diff changeset
175 START (QName(u'child'), Attrs([(QName(u'id'), u'2')]))
498
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 425
diff changeset
176
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 425
diff changeset
177 :param ignore_context: if `True`, the path is interpreted like a pattern
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 425
diff changeset
178 in XSLT, meaning for example that it will match
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 425
diff changeset
179 at any depth
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 425
diff changeset
180 :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
181 stream against the path
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 425
diff changeset
182 :rtype: ``function``
26
3c1a022be04c * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
183 """
687
3d7288f373bd land first cut at fast-path matching - needs some cleanup
aflett
parents: 638
diff changeset
184
3d7288f373bd land first cut at fast-path matching - needs some cleanup
aflett
parents: 638
diff changeset
185 # paths is path state that is maintained across calls to _test()
3d7288f373bd land first cut at fast-path matching - needs some cleanup
aflett
parents: 638
diff changeset
186 # paths is a list of tuples, one for each segment in the path
3d7288f373bd land first cut at fast-path matching - needs some cleanup
aflett
parents: 638
diff changeset
187 paths = []
3d7288f373bd land first cut at fast-path matching - needs some cleanup
aflett
parents: 638
diff changeset
188 for p in self.paths:
3d7288f373bd land first cut at fast-path matching - needs some cleanup
aflett
parents: 638
diff changeset
189 if ignore_context:
3d7288f373bd land first cut at fast-path matching - needs some cleanup
aflett
parents: 638
diff changeset
190 p = [_DOTSLASHSLASH] + p
3d7288f373bd land first cut at fast-path matching - needs some cleanup
aflett
parents: 638
diff changeset
191 path = (p, len(p), [0], [], [0] * len(p))
3d7288f373bd land first cut at fast-path matching - needs some cleanup
aflett
parents: 638
diff changeset
192 paths.append(path)
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
193
306
095a754f95a8 Minor optimization for XPath evaluation.
cmlenz
parents: 305
diff changeset
194 def _test(event, namespaces, variables, updateonly=False):
330
3e749eaa3100 XPath tests should never return event tuples, just values or booleans.
cmlenz
parents: 326
diff changeset
195 kind, data, pos = event[:3]
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
196 retval = None
228
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
197 for steps, size, cursors, cutoff, counter in paths:
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
198 # Manage the stack that tells us "where we are" in the stream
211
e5151983df0d Fix another regression introduced in [258]: some kinds of cascaded match templates were broken, for example in the TurboGears example app.
cmlenz
parents: 179
diff changeset
199 if kind is END:
228
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
200 if cursors:
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
201 cursors.pop()
211
e5151983df0d Fix another regression introduced in [258]: some kinds of cascaded match templates were broken, for example in the TurboGears example app.
cmlenz
parents: 179
diff changeset
202 continue
223
9c199fcb9f31 Fix typo introduced in [272].
cmlenz
parents: 217
diff changeset
203 elif kind is START:
228
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
204 cursors.append(cursors and cursors[-1] or 0)
638
1f73e534e715 Fix for descendant-or-self XPath patterns when namespaces are involved.
cmlenz
parents: 605
diff changeset
205 elif kind is START_NS or kind is END_NS \
1f73e534e715 Fix for descendant-or-self XPath patterns when namespaces are involved.
cmlenz
parents: 605
diff changeset
206 or kind is START_CDATA or kind is END_CDATA:
1f73e534e715 Fix for descendant-or-self XPath patterns when namespaces are involved.
cmlenz
parents: 605
diff changeset
207 continue
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
208
306
095a754f95a8 Minor optimization for XPath evaluation.
cmlenz
parents: 305
diff changeset
209 if updateonly or retval or not cursors:
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
210 continue
228
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
211 cursor = cursors[-1]
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
212 depth = len(cursors)
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
213
228
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
214 if cutoff and depth + int(kind is not START) > cutoff[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
215 continue
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
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
217 ctxtnode = not ignore_context and kind is START \
228
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
218 and depth == 2
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
219 matched = None
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
220 while 1:
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
221 # Fetch the next location step
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
222 axis, nodetest, predicates = steps[cursor]
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
223
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
224 # If this is the start event for the context node, and the
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
225 # axis of the location step doesn't include the current
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
226 # element, skip the test
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
227 if ctxtnode and (axis is CHILD or axis is DESCENDANT):
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
228 break
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
229
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
230 # Is this the last step of the location path?
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
231 last_step = cursor + 1 == size
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
232
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
233 # Perform the actual node test
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
234 matched = nodetest(kind, data, pos, namespaces, variables)
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
235
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
236 # The node test matched
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
237 if matched:
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
238
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
239 # Check all the predicates for this step
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
240 if predicates:
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
241 for predicate in predicates:
228
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
242 pretval = predicate(kind, data, pos, namespaces,
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
243 variables)
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
244 if type(pretval) is float: # FIXME <- need to
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
245 # check this for
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
246 # other types that
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
247 # can be coerced to
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
248 # float
228
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
249 counter[cursor] += 1
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
250 if counter[cursor] != int(pretval):
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
251 pretval = False
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
252 if not pretval:
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
253 matched = None
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
254 break
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
255
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
256 # Both the node test and the predicates matched
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
257 if matched:
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
258 if last_step:
217
f150cff4da18 Fix `py:match` directive which would screw up in some scenarios due to incorrect handling of the substream. Closes #49.
cmlenz
parents: 216
diff changeset
259 if not ctxtnode or kind is not START \
f150cff4da18 Fix `py:match` directive which would screw up in some scenarios due to incorrect handling of the substream. Closes #49.
cmlenz
parents: 216
diff changeset
260 or axis is ATTRIBUTE or axis is SELF:
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
261 retval = matched
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
262 elif not ctxtnode or axis is SELF \
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
263 or axis is DESCENDANT_OR_SELF:
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
264 cursor += 1
228
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
265 cursors[-1] = cursor
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
266 cutoff[:] = []
228
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
267
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
268 if kind is START:
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
269 if last_step and not (axis is DESCENDANT or
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
270 axis is DESCENDANT_OR_SELF):
228
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
271 cutoff[:] = [depth]
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
272
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
273 elif steps[cursor][0] is ATTRIBUTE:
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
274 # If the axis of the next location step is the
363
37e4b4bb0b53 Parse template includes at parse time to avoid some runtime overhead.
cmlenz
parents: 333
diff changeset
275 # attribute axis, we need to move on to processing
37e4b4bb0b53 Parse template includes at parse time to avoid some runtime overhead.
cmlenz
parents: 333
diff changeset
276 # that step without waiting for the next markup
37e4b4bb0b53 Parse template includes at parse time to avoid some runtime overhead.
cmlenz
parents: 333
diff changeset
277 # event
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
278 continue
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
279
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
280 # We're done with this step if it's the last step or the
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
281 # axis isn't "self"
384
5b6e4335ee21 Fix for infinite loop in XPath test. Closes #82.
cmlenz
parents: 364
diff changeset
282 if not matched or last_step or not (
5b6e4335ee21 Fix for infinite loop in XPath test. Closes #82.
cmlenz
parents: 364
diff changeset
283 axis is SELF or axis is DESCENDANT_OR_SELF):
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
284 break
638
1f73e534e715 Fix for descendant-or-self XPath patterns when namespaces are involved.
cmlenz
parents: 605
diff changeset
285 if ctxtnode and axis is DESCENDANT_OR_SELF:
1f73e534e715 Fix for descendant-or-self XPath patterns when namespaces are involved.
cmlenz
parents: 605
diff changeset
286 ctxtnode = False
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
287
687
3d7288f373bd land first cut at fast-path matching - needs some cleanup
aflett
parents: 638
diff changeset
288 if (retval or not matched) and \
3d7288f373bd land first cut at fast-path matching - needs some cleanup
aflett
parents: 638
diff changeset
289 kind is START and \
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
290 not (axis is DESCENDANT or axis is DESCENDANT_OR_SELF):
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
291 # If this step is not a closure, it cannot be matched until
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
292 # the current element is closed... so we need to move the
114
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
293 # cursor back to the previous closure and retest that
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
294 # against the current element
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
295 backsteps = [(i, k, d, p) for i, (k, d, p)
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
296 in enumerate(steps[:cursor])
215
94120e6b0ce4 A couple of minor XPath fixes.
cmlenz
parents: 211
diff changeset
297 if k is DESCENDANT or k is DESCENDANT_OR_SELF]
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
298 backsteps.reverse()
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
299 for cursor, axis, nodetest, predicates in backsteps:
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
300 if nodetest(kind, data, pos, namespaces, variables):
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
301 cutoff[:] = []
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
302 break
228
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
303 cursors[-1] = cursor
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
304
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
305 return retval
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
306
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
307 return _test
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
308
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
309
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
310 class PathSyntaxError(Exception):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
311 """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
312
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
313 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
314 if filename:
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
315 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
316 Exception.__init__(self, message)
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
317 self.filename = filename
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
318 self.lineno = lineno
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
319 self.offset = offset
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
320
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
321
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
322 class PathParser(object):
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
323 """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
324
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
325 _QUOTES = (("'", "'"), ('"', '"'))
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
326 _TOKENS = ('::', ':', '..', '.', '//', '/', '[', ']', '()', '(', ')', '@',
179
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
327 '=', '!=', '!', '|', ',', '>=', '>', '<=', '<', '$')
163
9c023c395e44 Support for XPath number literals including decimal places.
cmlenz
parents: 162
diff changeset
328 _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
329 '|'.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
330 ''.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
331
139
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
332 def __init__(self, text, filename=None, lineno=-1):
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
333 self.filename = filename
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
334 self.lineno = lineno
163
9c023c395e44 Support for XPath number literals including decimal places.
cmlenz
parents: 162
diff changeset
335 self.tokens = filter(None, [dqstr or sqstr or number or token or name
9c023c395e44 Support for XPath number literals including decimal places.
cmlenz
parents: 162
diff changeset
336 for dqstr, sqstr, number, token, name in
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
337 self._tokenize(text)])
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
338 self.pos = 0
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
339
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
340 # Tokenizer
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
341
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
342 at_end = property(lambda self: self.pos == len(self.tokens) - 1)
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
343 cur_token = property(lambda self: self.tokens[self.pos])
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
344
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
345 def next_token(self):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
346 self.pos += 1
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
347 return self.tokens[self.pos]
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
348
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
349 def peek_token(self):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
350 if not self.at_end:
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
351 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
352 return None
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
353
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
354 # Recursive descent parser
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
355
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
356 def parse(self):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
357 """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
358 tests.
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
359
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
360 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
361 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
362 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
363
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
364 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
365 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
366 """
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
367 paths = [self._location_path()]
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
368 while self.cur_token == '|':
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
369 self.next_token()
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
370 paths.append(self._location_path())
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
371 if not self.at_end:
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
372 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
373 % 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
374 return paths
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
375
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
376 def _location_path(self):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
377 steps = []
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
378 while True:
215
94120e6b0ce4 A couple of minor XPath fixes.
cmlenz
parents: 211
diff changeset
379 if self.cur_token.startswith('/'):
94120e6b0ce4 A couple of minor XPath fixes.
cmlenz
parents: 211
diff changeset
380 if self.cur_token == '//':
94120e6b0ce4 A couple of minor XPath fixes.
cmlenz
parents: 211
diff changeset
381 steps.append((DESCENDANT_OR_SELF, NodeTest(), []))
94120e6b0ce4 A couple of minor XPath fixes.
cmlenz
parents: 211
diff changeset
382 elif not steps:
94120e6b0ce4 A couple of minor XPath fixes.
cmlenz
parents: 211
diff changeset
383 raise PathSyntaxError('Absolute location paths not '
94120e6b0ce4 A couple of minor XPath fixes.
cmlenz
parents: 211
diff changeset
384 'supported', self.filename,
94120e6b0ce4 A couple of minor XPath fixes.
cmlenz
parents: 211
diff changeset
385 self.lineno)
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
386 self.next_token()
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
387
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
388 axis, nodetest, predicates = self._location_step()
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
389 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
390 axis = CHILD
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
391 steps.append((axis, nodetest, predicates))
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
392
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
393 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
394 break
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
395
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
396 return steps
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
397
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
398 def _location_step(self):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
399 if self.cur_token == '@':
114
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
400 axis = ATTRIBUTE
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
401 self.next_token()
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
402 elif self.cur_token == '.':
114
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
403 axis = SELF
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
404 elif self.cur_token == '..':
139
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
405 raise PathSyntaxError('Unsupported axis "parent"', self.filename,
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
406 self.lineno)
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
407 elif self.peek_token() == '::':
114
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
408 axis = Axis.forname(self.cur_token)
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
409 if axis is None:
139
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
410 raise PathSyntaxError('Unsupport axis "%s"' % axis,
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
411 self.filename, self.lineno)
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
412 self.next_token()
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
413 self.next_token()
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
414 else:
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
415 axis = None
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
416 nodetest = self._node_test(axis or CHILD)
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
417 predicates = []
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
418 while self.cur_token == '[':
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
419 predicates.append(self._predicate())
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
420 return axis, nodetest, predicates
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
421
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
422 def _node_test(self, axis=None):
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
423 test = prefix = None
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
424 next_token = self.peek_token()
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
425 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
426 test = self._node_type()
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
427
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
428 elif next_token == ':': # Namespace prefix
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
429 prefix = self.cur_token
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
430 self.next_token()
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
431 localname = self.next_token()
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
432 if localname == '*':
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
433 test = QualifiedPrincipalTypeTest(axis, prefix)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
434 else:
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
435 test = QualifiedNameTest(axis, prefix, localname)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
436
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
437 else: # Name test
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
438 if self.cur_token == '*':
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
439 test = PrincipalTypeTest(axis)
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
440 elif self.cur_token == '.':
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
441 test = NodeTest()
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
442 else:
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
443 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
444
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
445 if not self.at_end:
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
446 self.next_token()
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
447 return test
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
448
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
449 def _node_type(self):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
450 name = self.cur_token
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
451 self.next_token()
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
452
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
453 args = []
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
454 if self.cur_token != '()':
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
455 # The processing-instruction() function optionally accepts the
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
456 # name of the PI as argument, which must be a literal string
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
457 self.next_token() # (
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
458 if self.cur_token != ')':
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
459 string = self.cur_token
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
460 if (string[0], string[-1]) in self._QUOTES:
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
461 string = string[1:-1]
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
462 args.append(string)
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
463
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
464 cls = _nodetest_map.get(name)
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
465 if not cls:
139
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
466 raise PathSyntaxError('%s() not allowed here' % name, self.filename,
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
467 self.lineno)
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
468 return cls(*args)
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
469
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
470 def _predicate(self):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
471 assert self.cur_token == '['
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
472 self.next_token()
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
473 expr = self._or_expr()
121
062e51ad7b19 Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents: 114
diff changeset
474 if self.cur_token != ']':
062e51ad7b19 Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents: 114
diff changeset
475 raise PathSyntaxError('Expected "]" to close predicate, '
139
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
476 'but found "%s"' % self.cur_token,
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
477 self.filename, self.lineno)
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
478 if not self.at_end:
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
479 self.next_token()
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
480 return expr
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
481
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
482 def _or_expr(self):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
483 expr = self._and_expr()
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
484 while self.cur_token == 'or':
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
485 self.next_token()
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
486 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
487 return expr
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
488
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
489 def _and_expr(self):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
490 expr = self._equality_expr()
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
491 while self.cur_token == 'and':
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
492 self.next_token()
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
493 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
494 return expr
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
495
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
496 def _equality_expr(self):
162
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
497 expr = self._relational_expr()
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
498 while self.cur_token in ('=', '!='):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
499 op = _operator_map[self.cur_token]
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
500 self.next_token()
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
501 expr = op(expr, self._relational_expr())
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
502 return expr
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
503
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
504 def _relational_expr(self):
516
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
505 expr = self._sub_expr()
162
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
506 while self.cur_token in ('>', '>=', '<', '>='):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
507 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
508 self.next_token()
516
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
509 expr = op(expr, self._sub_expr())
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
510 return expr
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
511
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
512 def _sub_expr(self):
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
513 token = self.cur_token
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
514 if token != '(':
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
515 return self._primary_expr()
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
516 self.next_token()
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
517 expr = self._or_expr()
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
518 if self.cur_token != ')':
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
519 raise PathSyntaxError('Expected ")" to close sub-expression, '
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
520 'but found "%s"' % self.cur_token,
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
521 self.filename, self.lineno)
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
522 self.next_token()
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
523 return expr
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
524
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
525 def _primary_expr(self):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
526 token = self.cur_token
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
527 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
528 self.next_token()
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
529 return StringLiteral(token[1:-1])
163
9c023c395e44 Support for XPath number literals including decimal places.
cmlenz
parents: 162
diff changeset
530 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
531 self.next_token()
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
532 return NumberLiteral(as_float(token))
179
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
533 elif token == '$':
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
534 token = self.next_token()
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
535 self.next_token()
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
536 return VariableReference(token)
121
062e51ad7b19 Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents: 114
diff changeset
537 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
538 return self._function_call()
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
539 else:
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
540 axis = None
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
541 if token == '@':
114
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
542 axis = ATTRIBUTE
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
543 self.next_token()
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
544 return self._node_test(axis)
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
545
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
546 def _function_call(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
547 name = self.cur_token
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
548 if self.next_token() == '()':
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
549 args = []
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
550 else:
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
551 assert self.cur_token == '('
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
552 self.next_token()
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
553 args = [self._or_expr()]
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
554 while self.cur_token == ',':
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
555 self.next_token()
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
556 args.append(self._or_expr())
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
557 if not self.cur_token == ')':
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
558 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
559 '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
560 self.filename, self.lineno)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
561 self.next_token()
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
562 cls = _function_map.get(name)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
563 if not cls:
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
564 raise PathSyntaxError('Unsupported function "%s"' % name,
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
565 self.filename, self.lineno)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
566 return cls(*args)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
567
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
568
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
569 # Type coercion
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
570
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
571 def as_scalar(value):
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
572 """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
573 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
574 if isinstance(value, Attrs):
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
575 assert len(value) == 1
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
576 return value[0][1]
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
577 else:
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
578 return value
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
579
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
580 def as_float(value):
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
581 # 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
582 # 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
583 return float(as_scalar(value))
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
584
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
585 def as_long(value):
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
586 return long(as_scalar(value))
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
587
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
588 def as_string(value):
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
589 value = as_scalar(value)
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
590 if value is False:
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
591 return u''
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
592 return unicode(value)
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
593
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
594 def as_bool(value):
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
595 return bool(as_scalar(value))
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
596
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
597
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
598 # Node tests
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
599
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
600 class PrincipalTypeTest(object):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
601 """Node test that matches any event with the given principal type."""
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
602 __slots__ = ['principal_type']
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
603 def __init__(self, principal_type):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
604 self.principal_type = principal_type
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
605 def __call__(self, kind, data, pos, namespaces, variables):
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
606 if kind is START:
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
607 if self.principal_type is ATTRIBUTE:
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
608 return data[1] or None
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
609 else:
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
610 return True
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
611 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
612 return '*'
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
613
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
614 class QualifiedPrincipalTypeTest(object):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
615 """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
616 specific namespace."""
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
617 __slots__ = ['principal_type', 'prefix']
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
618 def __init__(self, principal_type, prefix):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
619 self.principal_type = principal_type
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
620 self.prefix = prefix
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
621 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
622 namespace = Namespace(namespaces.get(self.prefix))
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
623 if kind is START:
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
624 if self.principal_type is ATTRIBUTE and data[1]:
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
625 return Attrs([(name, value) for name, value in data[1]
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
626 if name in namespace]) or None
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
627 else:
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
628 return data[0] in namespace
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
629 def __repr__(self):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
630 return '%s:*' % self.prefix
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
631
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
632 class LocalNameTest(object):
364
7dabedbb53fb Fix for #77: match templates were matching their own output.
cmlenz
parents: 363
diff changeset
633 """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
634 local name.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
635 """
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
636 __slots__ = ['principal_type', 'name']
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
637 def __init__(self, principal_type, name):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
638 self.principal_type = principal_type
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
639 self.name = name
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
640 def __call__(self, kind, data, pos, namespaces, variables):
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
641 if kind is START:
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
642 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
643 return Attrs([(self.name, data[1].get(self.name))])
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
644 else:
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
645 return data[0].localname == self.name
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
646 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
647 return self.name
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
648
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
649 class QualifiedNameTest(object):
364
7dabedbb53fb Fix for #77: match templates were matching their own output.
cmlenz
parents: 363
diff changeset
650 """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
651 qualified name.
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
652 """
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
653 __slots__ = ['principal_type', 'prefix', 'name']
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
654 def __init__(self, principal_type, prefix, name):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
655 self.principal_type = principal_type
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
656 self.prefix = prefix
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
657 self.name = name
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
658 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
659 qname = QName('%s}%s' % (namespaces.get(self.prefix), self.name))
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
660 if kind is START:
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
661 if self.principal_type is ATTRIBUTE and qname in data[1]:
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
662 return Attrs([(self.name, data[1].get(self.name))])
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
663 else:
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
664 return data[0] == qname
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
665 def __repr__(self):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
666 return '%s:%s' % (self.prefix, self.name)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
667
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
668 class CommentNodeTest(object):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
669 """Node test that matches any comment events."""
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
670 __slots__ = []
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
671 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
672 return kind is COMMENT
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
673 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
674 return 'comment()'
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
675
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
676 class NodeTest(object):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
677 """Node test that matches any node."""
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
678 __slots__ = []
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
679 def __call__(self, kind, data, pos, namespaces, variables):
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
680 if kind is START:
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
681 return True
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
682 return kind, data, pos
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
683 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
684 return 'node()'
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
685
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
686 class ProcessingInstructionNodeTest(object):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
687 """Node test that matches any processing instruction event."""
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
688 __slots__ = ['target']
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
689 def __init__(self, target=None):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
690 self.target = target
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
691 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
692 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
693 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
694 arg = ''
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
695 if self.target:
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
696 arg = '"' + self.target + '"'
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
697 return 'processing-instruction(%s)' % arg
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
698
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
699 class TextNodeTest(object):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
700 """Node test that matches any text event."""
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
701 __slots__ = []
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
702 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
703 return kind is TEXT
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
704 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
705 return 'text()'
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
706
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
707 _nodetest_map = {'comment': CommentNodeTest, 'node': NodeTest,
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
708 'processing-instruction': ProcessingInstructionNodeTest,
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
709 'text': TextNodeTest}
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
710
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
711 # Functions
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
712
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
713 class Function(object):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
714 """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
715
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
716 class BooleanFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
717 """The `boolean` function, which converts its argument to a boolean
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
718 value.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
719 """
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
720 __slots__ = ['expr']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
721 def __init__(self, expr):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
722 self.expr = expr
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
723 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
724 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
725 return as_bool(val)
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
726 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
727 return 'boolean(%r)' % self.expr
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
728
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
729 class CeilingFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
730 """The `ceiling` function, which returns the nearest lower integer number
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
731 for the given number.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
732 """
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
733 __slots__ = ['number']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
734 def __init__(self, number):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
735 self.number = number
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
736 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
737 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
738 return ceil(as_float(number))
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
739 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
740 return 'ceiling(%r)' % self.number
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
741
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
742 class ConcatFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
743 """The `concat` function, which concatenates (joins) the variable number of
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
744 strings it gets as arguments.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
745 """
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
746 __slots__ = ['exprs']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
747 def __init__(self, *exprs):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
748 self.exprs = exprs
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
749 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
750 strings = []
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
751 for item in [expr(kind, data, pos, namespaces, variables)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
752 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
753 strings.append(as_string(item))
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
754 return u''.join(strings)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
755 def __repr__(self):
169
dc6676d3b697 Fix syntax error in `path` module.
cmlenz
parents: 164
diff changeset
756 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
757
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
758 class ContainsFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
759 """The `contains` function, which returns whether a string contains a given
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
760 substring.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
761 """
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
762 __slots__ = ['string1', 'string2']
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
763 def __init__(self, string1, string2):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
764 self.string1 = string1
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
765 self.string2 = string2
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
766 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
767 string1 = self.string1(kind, data, pos, namespaces, variables)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
768 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
769 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
770 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
771 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
772
534
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
773 class MatchesFunction(Function):
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
774 """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
775 expression.
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
776 """
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
777 __slots__ = ['string1', 'string2']
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
778 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
779
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
780 def __init__(self, string1, string2, flags=''):
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
781 self.string1 = string1
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
782 self.string2 = string2
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
783 self.flags = self._map_flags(flags)
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
784 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
785 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
786 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
787 return re.search(string2, string1, self.flags)
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
788 def _map_flags(self, flags):
593
0af1b7982ba5 Minor, cosmetic tweaks.
cmlenz
parents: 534
diff changeset
789 return reduce(operator.or_,
534
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
790 [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
791 def __repr__(self):
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
792 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
793
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
794 class FalseFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
795 """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
796 __slots__ = []
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
797 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
798 return False
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
799 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
800 return 'false()'
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
801
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
802 class FloorFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
803 """The `ceiling` function, which returns the nearest higher integer number
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
804 for the given number.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
805 """
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
806 __slots__ = ['number']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
807 def __init__(self, number):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
808 self.number = number
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
809 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
810 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
811 return floor(as_float(number))
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
812 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
813 return 'floor(%r)' % self.number
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
814
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
815 class LocalNameFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
816 """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
817 element.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
818 """
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
819 __slots__ = []
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
820 def __call__(self, kind, data, pos, namespaces, variables):
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
821 if kind is START:
234
39c424b80edd * Minor simplification of XPath engine.
cmlenz
parents: 230
diff changeset
822 return data[0].localname
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
823 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
824 return 'local-name()'
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
825
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
826 class NameFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
827 """The `name` function, which returns the qualified name of the current
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
828 element.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
829 """
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
830 __slots__ = []
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
831 def __call__(self, kind, data, pos, namespaces, variables):
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
832 if kind is START:
234
39c424b80edd * Minor simplification of XPath engine.
cmlenz
parents: 230
diff changeset
833 return data[0]
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
834 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
835 return 'name()'
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
836
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
837 class NamespaceUriFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
838 """The `namespace-uri` function, which returns the namespace URI of the
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
839 current element.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
840 """
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
841 __slots__ = []
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
842 def __call__(self, kind, data, pos, namespaces, variables):
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
843 if kind is START:
234
39c424b80edd * Minor simplification of XPath engine.
cmlenz
parents: 230
diff changeset
844 return data[0].namespace
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
845 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
846 return 'namespace-uri()'
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
847
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
848 class NotFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
849 """The `not` function, which returns the negated boolean value of its
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
850 argument.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
851 """
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
852 __slots__ = ['expr']
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
853 def __init__(self, expr):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
854 self.expr = expr
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
855 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
856 return not as_bool(self.expr(kind, data, pos, namespaces, variables))
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
857 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
858 return 'not(%s)' % self.expr
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
859
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
860 class NormalizeSpaceFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
861 """The `normalize-space` function, which removes leading and trailing
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
862 whitespace in the given string, and replaces multiple adjacent whitespace
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
863 characters inside the string with a single space.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
864 """
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
865 __slots__ = ['expr']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
866 _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
867 def __init__(self, expr):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
868 self.expr = expr
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
869 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
870 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
871 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
872 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
873 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
874
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
875 class NumberFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
876 """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
877 __slots__ = ['expr']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
878 def __init__(self, expr):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
879 self.expr = expr
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
880 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
881 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
882 return as_float(val)
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
883 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
884 return 'number(%r)' % self.expr
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
885
162
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
886 class RoundFunction(Function):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
887 """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
888 given number.
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
889 """
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
890 __slots__ = ['number']
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
891 def __init__(self, number):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
892 self.number = number
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
893 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
894 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
895 return round(as_float(number))
162
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
896 def __repr__(self):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
897 return 'round(%r)' % self.number
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
898
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
899 class StartsWithFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
900 """The `starts-with` function that returns whether one string starts with
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
901 a given substring.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
902 """
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
903 __slots__ = ['string1', 'string2']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
904 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
905 self.string1 = string1
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
906 self.string2 = string2
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
907 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
908 string1 = self.string1(kind, data, pos, namespaces, variables)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
909 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
910 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
911 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
912 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
913
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
914 class StringLengthFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
915 """The `string-length` function that returns the length of the given
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
916 string.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
917 """
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
918 __slots__ = ['expr']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
919 def __init__(self, expr):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
920 self.expr = expr
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
921 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
922 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
923 return len(as_string(string))
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
924 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
925 return 'string-length(%r)' % self.expr
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
926
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
927 class SubstringFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
928 """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
929 at the given offset, and optionally limited to the given length.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
930 """
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
931 __slots__ = ['string', 'start', 'length']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
932 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
933 self.string = string
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
934 self.start = start
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
935 self.length = length
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
936 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
937 string = self.string(kind, data, pos, namespaces, variables)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
938 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
939 length = 0
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
940 if self.length is not None:
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
941 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
942 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
943 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
944 if self.length is not None:
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
945 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
946 self.length)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
947 else:
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
948 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
949
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
950 class SubstringAfterFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
951 """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
952 is found after the given substring.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
953 """
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
954 __slots__ = ['string1', 'string2']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
955 def __init__(self, string1, string2):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
956 self.string1 = string1
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
957 self.string2 = string2
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
958 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
959 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
960 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
961 index = string1.find(string2)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
962 if index >= 0:
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
963 return string1[index + len(string2):]
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
964 return u''
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
965 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
966 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
967
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
968 class SubstringBeforeFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
969 """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
970 is found before the given substring.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
971 """
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
972 __slots__ = ['string1', 'string2']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
973 def __init__(self, string1, string2):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
974 self.string1 = string1
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
975 self.string2 = string2
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
976 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
977 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
978 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
979 index = string1.find(string2)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
980 if index >= 0:
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
981 return string1[:index]
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
982 return u''
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
983 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
984 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
985
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
986 class TranslateFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
987 """The `translate` function that translates a set of characters in a
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
988 string to target set of characters.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
989 """
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
990 __slots__ = ['string', 'fromchars', 'tochars']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
991 def __init__(self, string, fromchars, tochars):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
992 self.string = string
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
993 self.fromchars = fromchars
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
994 self.tochars = tochars
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
995 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
996 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
997 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
998 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
999 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
1000 [ord(c) for c in tochars]))
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1001 return string.translate(table)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1002 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1003 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
1004 self.tochars)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1005
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1006 class TrueFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1007 """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
1008 __slots__ = []
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1009 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
1010 return True
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1011 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1012 return 'true()'
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1013
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1014 _function_map = {'boolean': BooleanFunction, 'ceiling': CeilingFunction,
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1015 'concat': ConcatFunction, 'contains': ContainsFunction,
534
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1016 'matches': MatchesFunction, 'false': FalseFunction, 'floor':
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1017 FloorFunction, 'local-name': LocalNameFunction, 'name':
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1018 NameFunction, 'namespace-uri': NamespaceUriFunction,
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1019 'normalize-space': NormalizeSpaceFunction, 'not': NotFunction,
162
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1020 'number': NumberFunction, 'round': RoundFunction,
534
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1021 'starts-with': StartsWithFunction, 'string-length':
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1022 StringLengthFunction, 'substring': SubstringFunction,
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1023 'substring-after': SubstringAfterFunction, 'substring-before':
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1024 SubstringBeforeFunction, 'translate': TranslateFunction,
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1025 'true': TrueFunction}
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1026
179
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
1027 # Literals & Variables
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1028
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1029 class Literal(object):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1030 """Abstract base class for literal nodes."""
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1031
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1032 class StringLiteral(Literal):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1033 """A string literal node."""
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1034 __slots__ = ['text']
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1035 def __init__(self, text):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1036 self.text = text
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1037 def __call__(self, kind, data, pos, namespaces, variables):
228
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
1038 return self.text
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1039 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1040 return '"%s"' % self.text
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1041
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1042 class NumberLiteral(Literal):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1043 """A number literal node."""
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1044 __slots__ = ['number']
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1045 def __init__(self, number):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1046 self.number = number
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1047 def __call__(self, kind, data, pos, namespaces, variables):
228
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
1048 return self.number
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1049 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1050 return str(self.number)
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1051
179
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
1052 class VariableReference(Literal):
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
1053 """A variable reference node."""
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
1054 __slots__ = ['name']
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
1055 def __init__(self, name):
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
1056 self.name = name
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1057 def __call__(self, kind, data, pos, namespaces, variables):
228
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
1058 return variables.get(self.name)
179
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
1059 def __repr__(self):
215
94120e6b0ce4 A couple of minor XPath fixes.
cmlenz
parents: 211
diff changeset
1060 return str(self.name)
179
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
1061
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1062 # Operators
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1063
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1064 class AndOperator(object):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1065 """The boolean operator `and`."""
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1066 __slots__ = ['lval', 'rval']
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1067 def __init__(self, lval, rval):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1068 self.lval = lval
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1069 self.rval = rval
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1070 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
1071 lval = as_bool(self.lval(kind, data, pos, namespaces, variables))
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1072 if not lval:
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1073 return False
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1074 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
1075 return as_bool(rval)
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1076 def __repr__(self):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1077 return '%s and %s' % (self.lval, self.rval)
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1078
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1079 class EqualsOperator(object):
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1080 """The equality operator `=`."""
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1081 __slots__ = ['lval', 'rval']
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1082 def __init__(self, lval, rval):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1083 self.lval = lval
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1084 self.rval = rval
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1085 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
1086 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
1087 rval = as_scalar(self.rval(kind, data, pos, namespaces, variables))
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1088 return lval == rval
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1089 def __repr__(self):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1090 return '%s=%s' % (self.lval, self.rval)
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1091
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1092 class NotEqualsOperator(object):
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1093 """The equality operator `!=`."""
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1094 __slots__ = ['lval', 'rval']
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1095 def __init__(self, lval, rval):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1096 self.lval = lval
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1097 self.rval = rval
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1098 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
1099 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
1100 rval = as_scalar(self.rval(kind, data, pos, namespaces, variables))
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1101 return lval != rval
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1102 def __repr__(self):
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1103 return '%s!=%s' % (self.lval, self.rval)
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1104
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1105 class OrOperator(object):
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1106 """The boolean operator `or`."""
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1107 __slots__ = ['lval', 'rval']
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1108 def __init__(self, lval, rval):
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1109 self.lval = lval
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1110 self.rval = rval
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1111 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
1112 lval = as_bool(self.lval(kind, data, pos, namespaces, variables))
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1113 if lval:
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1114 return True
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1115 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
1116 return as_bool(rval)
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1117 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1118 return '%s or %s' % (self.lval, self.rval)
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1119
162
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1120 class GreaterThanOperator(object):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1121 """The relational operator `>` (greater than)."""
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1122 __slots__ = ['lval', 'rval']
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1123 def __init__(self, lval, rval):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1124 self.lval = lval
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1125 self.rval = rval
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1126 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1127 lval = self.lval(kind, data, pos, namespaces, variables)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1128 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
1129 return as_float(lval) > as_float(rval)
162
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1130 def __repr__(self):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1131 return '%s>%s' % (self.lval, self.rval)
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1132
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1133 class GreaterThanOrEqualOperator(object):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1134 """The relational operator `>=` (greater than or equal)."""
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1135 __slots__ = ['lval', 'rval']
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1136 def __init__(self, lval, rval):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1137 self.lval = lval
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1138 self.rval = rval
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1139 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1140 lval = self.lval(kind, data, pos, namespaces, variables)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1141 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
1142 return as_float(lval) >= as_float(rval)
162
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1143 def __repr__(self):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1144 return '%s>=%s' % (self.lval, self.rval)
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1145
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1146 class LessThanOperator(object):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1147 """The relational operator `<` (less than)."""
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1148 __slots__ = ['lval', 'rval']
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1149 def __init__(self, lval, rval):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1150 self.lval = lval
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1151 self.rval = rval
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1152 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1153 lval = self.lval(kind, data, pos, namespaces, variables)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1154 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
1155 return as_float(lval) < as_float(rval)
162
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1156 def __repr__(self):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1157 return '%s<%s' % (self.lval, self.rval)
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1158
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1159 class LessThanOrEqualOperator(object):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1160 """The relational operator `<=` (less than or equal)."""
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1161 __slots__ = ['lval', 'rval']
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1162 def __init__(self, lval, rval):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1163 self.lval = lval
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1164 self.rval = rval
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1165 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1166 lval = self.lval(kind, data, pos, namespaces, variables)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1167 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
1168 return as_float(lval) <= as_float(rval)
162
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1169 def __repr__(self):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1170 return '%s<=%s' % (self.lval, self.rval)
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1171
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1172 _operator_map = {'=': EqualsOperator, '!=': NotEqualsOperator,
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1173 '>': GreaterThanOperator, '>=': GreaterThanOrEqualOperator,
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1174 '<': 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
1175
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
1176
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
1177 _DOTSLASHSLASH = (DESCENDANT_OR_SELF, PrincipalTypeTest(None), ())
Copyright (C) 2012-2017 Edgewall Software