annotate genshi/path.py @ 612:09de73cae3d5

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