5 # limited xpath support for element trees
8 # 2003-05-23 fl created
9 # 2003-05-28 fl added support for // etc
10 # 2003-08-27 fl fixed parsing of periods in element names
12 # Copyright (c) 2003-2004 by Fredrik Lundh. All rights reserved.
14 # fredrik@pythonware.com
15 # http://www.pythonware.com
17 # --------------------------------------------------------------------
18 # The ElementTree toolkit is
20 # Copyright (c) 1999-2004 by Fredrik Lundh
22 # By obtaining, using, and/or copying this software and/or its
23 # associated documentation, you agree that you have read, understood,
24 # and will comply with the following terms and conditions:
26 # Permission to use, copy, modify, and distribute this software and
27 # its associated documentation for any purpose and without fee is
28 # hereby granted, provided that the above copyright notice appears in
29 # all copies, and that both that copyright notice and this permission
30 # notice appear in supporting documentation, and that the name of
31 # Secret Labs AB or the author not be used in advertising or publicity
32 # pertaining to distribution of the software without specific, written
35 # SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
36 # TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
37 # ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR
38 # BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
39 # DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
40 # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
41 # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
43 # --------------------------------------------------------------------
46 # Implementation module for XPath support. There's usually no reason
47 # to import this module directly; the <b>ElementTree</b> does this for
53 xpath_tokenizer = re.compile(
54 "(::|\.\.|\(\)|[/.*:\[\]\(\)@=])|((?:\{[^}]+\})?[^/:\[\]\(\)@=\s]+)|\s+"
57 class xpath_descendant_or_self:
61 # Wrapper for a compiled XPath.
66 # Create an Path instance from an XPath expression.
68 def __init__(self, path):
69 tokens = xpath_tokenizer(path)
70 # the current version supports 'path/path'-style expressions only
73 if tokens and tokens[0][0] == "/":
74 raise SyntaxError("cannot use absolute path on element")
76 op, tag = tokens.pop(0)
78 self.path.append(tag or op)
82 self.path.append(xpath_descendant_or_self())
85 raise SyntaxError("unsupported path syntax (%s)" % op)
87 op, tag = tokens.pop(0)
90 "expected path separator (%s)" % (op or tag)
92 if self.path and isinstance(self.path[-1], xpath_descendant_or_self):
93 raise SyntaxError("path cannot end with //")
94 if len(self.path) == 1 and isinstance(self.path[0], type("")):
95 self.tag = self.path[0]
98 # Find first matching object.
100 def find(self, element):
103 nodeset = self.findall(element)
113 # Find text for first matching object.
115 def findtext(self, element, default=None):
118 nodeset = self.findall(element)
121 return nodeset[0].text or ""
124 return elem.text or ""
128 # Find all matching objects.
130 def findall(self, element):
135 path = self.path[index]
140 if isinstance(path, xpath_descendant_or_self):
142 tag = self.path[index]
143 if not isinstance(tag, type("")):
148 tag = None # invalid path
150 new = list(node.getiterator(tag))
151 if new and new[0] is node:
158 if path == "*" or node.tag == path:
167 # (Internal) Compile path.
174 if len(_cache) >= 100:
180 # Find first matching object.
182 def find(element, path):
183 return _compile(path).find(element)
186 # Find text for first matching object.
188 def findtext(element, path, default=None):
189 return _compile(path).findtext(element, default)
192 # Find all matching objects.
194 def findall(element, path):
195 return _compile(path).findall(element)