Skip to content

Commit 845f579

Browse files
author
fredrik.lundh
committed
added ElementTree core components to xml.etree
git-svn-id: http://svn.python.org/projects/python/trunk@41651 6015fed2-1504-0410-9fe1-9d1591cc4771
1 parent 68bf2b3 commit 845f579

4 files changed

Lines changed: 1621 additions & 0 deletions

File tree

Lib/xml/etree/ElementInclude.py

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
#
2+
# ElementTree
3+
# $Id: ElementInclude.py 1862 2004-06-18 07:31:02Z Fredrik $
4+
#
5+
# limited xinclude support for element trees
6+
#
7+
# history:
8+
# 2003-08-15 fl created
9+
# 2003-11-14 fl fixed default loader
10+
#
11+
# Copyright (c) 2003-2004 by Fredrik Lundh. All rights reserved.
12+
#
13+
# fredrik@pythonware.com
14+
# http://www.pythonware.com
15+
#
16+
# --------------------------------------------------------------------
17+
# The ElementTree toolkit is
18+
#
19+
# Copyright (c) 1999-2004 by Fredrik Lundh
20+
#
21+
# By obtaining, using, and/or copying this software and/or its
22+
# associated documentation, you agree that you have read, understood,
23+
# and will comply with the following terms and conditions:
24+
#
25+
# Permission to use, copy, modify, and distribute this software and
26+
# its associated documentation for any purpose and without fee is
27+
# hereby granted, provided that the above copyright notice appears in
28+
# all copies, and that both that copyright notice and this permission
29+
# notice appear in supporting documentation, and that the name of
30+
# Secret Labs AB or the author not be used in advertising or publicity
31+
# pertaining to distribution of the software without specific, written
32+
# prior permission.
33+
#
34+
# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
35+
# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
36+
# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR
37+
# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
38+
# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
39+
# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
40+
# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
41+
# OF THIS SOFTWARE.
42+
# --------------------------------------------------------------------
43+
44+
##
45+
# Limited XInclude support for the ElementTree package.
46+
##
47+
48+
import copy
49+
import ElementTree
50+
51+
XINCLUDE = "{http://www.w3.org/2001/XInclude}"
52+
53+
XINCLUDE_INCLUDE = XINCLUDE + "include"
54+
XINCLUDE_FALLBACK = XINCLUDE + "fallback"
55+
56+
##
57+
# Fatal include error.
58+
59+
class FatalIncludeError(SyntaxError):
60+
pass
61+
62+
##
63+
# Default loader. This loader reads an included resource from disk.
64+
#
65+
# @param href Resource reference.
66+
# @param parse Parse mode. Either "xml" or "text".
67+
# @param encoding Optional text encoding.
68+
# @return The expanded resource. If the parse mode is "xml", this
69+
# is an ElementTree instance. If the parse mode is "text", this
70+
# is a Unicode string. If the loader fails, it can return None
71+
# or raise an IOError exception.
72+
# @throws IOError If the loader fails to load the resource.
73+
74+
def default_loader(href, parse, encoding=None):
75+
file = open(href)
76+
if parse == "xml":
77+
data = ElementTree.parse(file).getroot()
78+
else:
79+
data = file.read()
80+
if encoding:
81+
data = data.decode(encoding)
82+
file.close()
83+
return data
84+
85+
##
86+
# Expand XInclude directives.
87+
#
88+
# @param elem Root element.
89+
# @param loader Optional resource loader. If omitted, it defaults
90+
# to {@link default_loader}. If given, it should be a callable
91+
# that implements the same interface as <b>default_loader</b>.
92+
# @throws FatalIncludeError If the function fails to include a given
93+
# resource, or if the tree contains malformed XInclude elements.
94+
# @throws IOError If the function fails to load a given resource.
95+
96+
def include(elem, loader=None):
97+
if loader is None:
98+
loader = default_loader
99+
# look for xinclude elements
100+
i = 0
101+
while i < len(elem):
102+
e = elem[i]
103+
if e.tag == XINCLUDE_INCLUDE:
104+
# process xinclude directive
105+
href = e.get("href")
106+
parse = e.get("parse", "xml")
107+
if parse == "xml":
108+
node = loader(href, parse)
109+
if node is None:
110+
raise FatalIncludeError(
111+
"cannot load %r as %r" % (href, parse)
112+
)
113+
node = copy.copy(node)
114+
if e.tail:
115+
node.tail = (node.tail or "") + e.tail
116+
elem[i] = node
117+
elif parse == "text":
118+
text = loader(href, parse, e.get("encoding"))
119+
if text is None:
120+
raise FatalIncludeError(
121+
"cannot load %r as %r" % (href, parse)
122+
)
123+
if i:
124+
node = elem[i-1]
125+
node.tail = (node.tail or "") + text
126+
else:
127+
elem.text = (elem.text or "") + text + (e.tail or "")
128+
del elem[i]
129+
continue
130+
else:
131+
raise FatalIncludeError(
132+
"unknown parse type in xi:include tag (%r)" % parse
133+
)
134+
elif e.tag == XINCLUDE_FALLBACK:
135+
raise FatalIncludeError(
136+
"xi:fallback tag must be child of xi:include (%r)" % e.tag
137+
)
138+
else:
139+
include(e, loader)
140+
i = i + 1
141+

Lib/xml/etree/ElementPath.py

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
#
2+
# ElementTree
3+
# $Id: ElementPath.py 1858 2004-06-17 21:31:41Z Fredrik $
4+
#
5+
# limited xpath support for element trees
6+
#
7+
# history:
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
11+
#
12+
# Copyright (c) 2003-2004 by Fredrik Lundh. All rights reserved.
13+
#
14+
# fredrik@pythonware.com
15+
# http://www.pythonware.com
16+
#
17+
# --------------------------------------------------------------------
18+
# The ElementTree toolkit is
19+
#
20+
# Copyright (c) 1999-2004 by Fredrik Lundh
21+
#
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:
25+
#
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
33+
# prior permission.
34+
#
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
42+
# OF THIS SOFTWARE.
43+
# --------------------------------------------------------------------
44+
45+
##
46+
# Implementation module for XPath support. There's usually no reason
47+
# to import this module directly; the <b>ElementTree</b> does this for
48+
# you, if needed.
49+
##
50+
51+
import re
52+
53+
xpath_tokenizer = re.compile(
54+
"(::|\.\.|\(\)|[/.*:\[\]\(\)@=])|((?:\{[^}]+\})?[^/:\[\]\(\)@=\s]+)|\s+"
55+
).findall
56+
57+
class xpath_descendant_or_self:
58+
pass
59+
60+
##
61+
# Wrapper for a compiled XPath.
62+
63+
class Path:
64+
65+
##
66+
# Create an Path instance from an XPath expression.
67+
68+
def __init__(self, path):
69+
tokens = xpath_tokenizer(path)
70+
# the current version supports 'path/path'-style expressions only
71+
self.path = []
72+
self.tag = None
73+
if tokens and tokens[0][0] == "/":
74+
raise SyntaxError("cannot use absolute path on element")
75+
while tokens:
76+
op, tag = tokens.pop(0)
77+
if tag or op == "*":
78+
self.path.append(tag or op)
79+
elif op == ".":
80+
pass
81+
elif op == "/":
82+
self.path.append(xpath_descendant_or_self())
83+
continue
84+
else:
85+
raise SyntaxError("unsupported path syntax (%s)" % op)
86+
if tokens:
87+
op, tag = tokens.pop(0)
88+
if op != "/":
89+
raise SyntaxError(
90+
"expected path separator (%s)" % (op or tag)
91+
)
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]
96+
97+
##
98+
# Find first matching object.
99+
100+
def find(self, element):
101+
tag = self.tag
102+
if tag is None:
103+
nodeset = self.findall(element)
104+
if not nodeset:
105+
return None
106+
return nodeset[0]
107+
for elem in element:
108+
if elem.tag == tag:
109+
return elem
110+
return None
111+
112+
##
113+
# Find text for first matching object.
114+
115+
def findtext(self, element, default=None):
116+
tag = self.tag
117+
if tag is None:
118+
nodeset = self.findall(element)
119+
if not nodeset:
120+
return default
121+
return nodeset[0].text or ""
122+
for elem in element:
123+
if elem.tag == tag:
124+
return elem.text or ""
125+
return default
126+
127+
##
128+
# Find all matching objects.
129+
130+
def findall(self, element):
131+
nodeset = [element]
132+
index = 0
133+
while 1:
134+
try:
135+
path = self.path[index]
136+
index = index + 1
137+
except IndexError:
138+
return nodeset
139+
set = []
140+
if isinstance(path, xpath_descendant_or_self):
141+
try:
142+
tag = self.path[index]
143+
if not isinstance(tag, type("")):
144+
tag = None
145+
else:
146+
index = index + 1
147+
except IndexError:
148+
tag = None # invalid path
149+
for node in nodeset:
150+
new = list(node.getiterator(tag))
151+
if new and new[0] is node:
152+
set.extend(new[1:])
153+
else:
154+
set.extend(new)
155+
else:
156+
for node in nodeset:
157+
for node in node:
158+
if path == "*" or node.tag == path:
159+
set.append(node)
160+
if not set:
161+
return []
162+
nodeset = set
163+
164+
_cache = {}
165+
166+
##
167+
# (Internal) Compile path.
168+
169+
def _compile(path):
170+
p = _cache.get(path)
171+
if p is not None:
172+
return p
173+
p = Path(path)
174+
if len(_cache) >= 100:
175+
_cache.clear()
176+
_cache[path] = p
177+
return p
178+
179+
##
180+
# Find first matching object.
181+
182+
def find(element, path):
183+
return _compile(path).find(element)
184+
185+
##
186+
# Find text for first matching object.
187+
188+
def findtext(element, path, default=None):
189+
return _compile(path).findtext(element, default)
190+
191+
##
192+
# Find all matching objects.
193+
194+
def findall(element, path):
195+
return _compile(path).findall(element)
196+

0 commit comments

Comments
 (0)