1
2
3
4 __version__ = "0.2.0"
5 __authors__ = ["Sylvain Hellegouarch (sh@defuze.org)"]
6 __contributors__ = ['David Turner']
7 __date__ = "2006/12/08"
8 __copyright__ = """
9 Copyright (c) 2006 Sylvain Hellegouarch
10 All rights reserved.
11 """
12 __license__ = """
13 Redistribution and use in source and binary forms, with or without modification,
14 are permitted provided that the following conditions are met:
15
16 * Redistributions of source code must retain the above copyright notice,
17 this list of conditions and the following disclaimer.
18 * Redistributions in binary form must reproduce the above copyright notice,
19 this list of conditions and the following disclaimer in the documentation
20 and/or other materials provided with the distribution.
21 * Neither the name of Sylvain Hellegouarch nor the names of his contributors
22 may be used to endorse or promote products derived from this software
23 without specific prior written permission.
24
25 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
26 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
27 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
29 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
33 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 """
36
37 ENCODING = 'UTF-8'
38 DUMMY_URI = u'http://dummy.com'
39
40 import bridge.parser.bridge_default
41 from bridge.filter import fetch_child, fetch_children
42
43 from bridge.common import XML_NS, XMLNS_NS
44
45 __all__ = ['Attribute', 'Element', 'PI', 'Comment', 'Document']
46
48 - def __init__(self, target, data, parent=None):
49 self.target = target
50 self.data = data
51 self.xml_parent = parent
52
53 if self.xml_parent:
54 self.xml_parent.xml_children.append(self)
55
63
65 """
66 Maps the attribute of an XML element to a simple Python object.
67 """
68
69 encoding = ENCODING
70 as_attribute_of_element = None
71
72 - def __init__(self, name=None, value=None, prefix=None, namespace=None, parent=None):
73 """
74 Maps the attribute of an XML element to a simple Python object.
75
76 Keyword arguments:
77 name -- Name of the attribute
78 value -- content of the attribute
79 prefix -- XML prefix of the element
80 namespace -- XML namespace defining the prefix
81 parent -- element which this attribute belongs to
82 """
83 if value and not isinstance(value, unicode):
84 raise TypeError, "Attribute's value must be an unicode object or None"
85
86 self.xml_parent = parent
87 self.xml_name = name
88 self.xml_text = value
89 self.xml_prefix = prefix
90 self.xml_ns = namespace
91
92 self.as_attribute_of_element = {}
93 if self.xml_parent and self.xml_parent.xml_root.as_attribute_of_element:
94 self.as_attribute_of_element.update(self.xml_parent.xml_root.as_attribute_of_element)
95 elif isinstance(Attribute.as_attribute_of_element, dict):
96 self.as_attribute_of_element.update(Attribute.as_attribute_of_element)
97
98 if self.xml_parent:
99 self.xml_parent.xml_attributes.append(self)
100
101 attrs = self.as_attribute_of_element.get(self.xml_ns, [])
102 if self.xml_name in attrs:
103 name = self.xml_name.replace('-', '_')
104 name = name.replace('.', '_')
105 if not hasattr(self.xml_parent, name):
106 setattr(self.xml_parent, name, self.xml_text)
107
109 if self.xml_text:
110 return self.xml_text
111 return unicode(self.xml_text)
112
114 if self.xml_text:
115 return self.xml_text.encode(self.encoding)
116 return str(self.xml_text)
117
119 value = self.xml_text or ''
120 return '%s="%s" attribute at %s' % (self.xml_name, value, hex(id(self)))
121
123 """
124 Maps an XML element to a Python object.
125 """
126
127 parser = bridge.parser.bridge_default.Parser
128 encoding = ENCODING
129 as_list = None
130 as_attribute = None
131
132 - def __init__(self, name=None, content=None, attributes=None, prefix=None, namespace=None, parent=None):
133 """
134 Maps an XML element to a Python object.
135
136 Keyword arguments:
137 name -- Name of the XML element
138 content -- Content of the element
139 attributes -- dictionary of the form {local_name: value}
140 prefix -- XML prefix of the element
141 namespace -- XML namespace attached to that element
142 parent -- Parent element of this element.
143
144 If 'parent' is not None, 'self' will be added to the parent.xml_children
145
146 If 'Element.as_list' is set and if (name, namespace) belongs to it
147 then we will add a list to parent with the name of the element
148
149 If 'Element.as_attribute' is set and if (name, namespace) belongs to it
150 then we will add an attribute to parent with the name of the element
151 """
152 if content and not isinstance(content, unicode):
153 raise TypeError, "Element's content must be an unicode object or None"
154
155 self._root = None
156 self.xml_parent = parent
157 self.xml_prefix = prefix
158 self.xml_ns = namespace
159 self.xml_name = name
160 self.xml_text = content
161 self.xml_children = []
162 self.xml_attributes = []
163
164 if self.xml_root is None:
165 return
166
167 self.as_attribute = {}
168 if self.xml_root.as_attribute:
169 self.as_attribute.update(self.xml_root.as_attribute)
170 elif isinstance(Element.as_attribute, dict):
171 self.as_attribute.update(Element.as_attribute)
172
173 self.as_list= {}
174 if self.xml_root.as_list:
175 self.as_list.update(self.xml_root.as_list)
176 elif isinstance(Element.as_list, dict):
177 self.as_list.update(Element.as_list)
178
179 self.as_attribute_of_element = {}
180
181 if self.xml_parent:
182 self.xml_parent.xml_children.append(self)
183
184 as_attr_elts = self.as_attribute.get(self.xml_ns, [])
185 as_list_elts = self.as_list.get(self.xml_ns, [])
186
187 if self.xml_name in as_attr_elts:
188 name = self.xml_name.replace('-', '_')
189 name = name.replace('.', '_')
190 setattr(self.xml_parent, name, self)
191 elif self.xml_name in as_list_elts:
192 name = self.xml_name.replace('-', '_')
193 name = name.replace('.', '_')
194 if not hasattr(self.xml_parent, name):
195 setattr(self.xml_parent, name, [])
196 els = getattr(self.xml_parent, name)
197 els.append(self)
198
199 if attributes and isinstance(attributes, dict):
200 for name in attributes:
201 Attribute(name, attributes[name], parent=self)
202
204 prefix = self.xml_prefix
205 xmlns = self.xml_ns
206 if (prefix not in ('', None)) and xmlns:
207 return '<%s:%s xmlns:%s="%s" element at %s />' % (prefix, self.xml_name,
208 prefix, xmlns, hex(id(self)),)
209 else:
210 return "<%s element at %s />" % (self.xml_name, hex(id(self)))
211
213 if self.xml_text:
214 return self.xml_text
215 return unicode(None)
216
218 if self.xml_text:
219 return self.xml_text.encode(self.encoding)
220 return str(None)
221
223 return iter(self.xml_children)
224
227
230
232 """
233 deletes 'name' instance of Element. It will also removes it
234 from its parent children and attributes.
235 """
236 if not hasattr(self, name):
237 raise AttributeError, name
238
239 attr = getattr(self, name)
240 if isinstance(attr, Element):
241 if attr in self.xml_children:
242 self.xml_children.remove(attr)
243 elif isinstance(attr, Attribute):
244 if attr in self.xml_attributes:
245 self.xml_attributes.remove(attr)
246
247 del self.__dict__[name]
248
250 if self._root is not None:
251 return self._root
252
253 if isinstance(self.xml_parent, Document):
254 return self
255
256 if self.xml_parent is None:
257 return self
258 return self.xml_parent.get_root()
259 xml_root = property(get_root, doc="Retrieve the top level element")
260
262 for attr in self.xml_attributes:
263 if attr.xml_name == name:
264 return attr
265
267 for attr in self.xml_attributes:
268 if (attr.xml_name == name) and (attr.xml_ns == namespace):
269 return attr
270
272 """
273 Checks if this element has 'name' attribute
274
275 Keyword arguments:
276 name -- local name of the element
277 ns -- namespace of the element
278 """
279 obj = getattr(self, name, None)
280 if obj:
281 return obj.xml_ns == ns
282 return False
283
285 """
286 Checks if this element has a child named 'name' in its children elements
287
288 Keyword arguments:
289 name -- local name of the element
290 ns -- namespace of the element
291 """
292 return self.filtrate(fetch_child, child_name=name, child_ns=ns) != None
293
295 """
296 Returns the child element named 'name', None if not found.
297
298 Keyword arguments:
299 name -- local name of the element
300 ns -- namespace of the element
301 """
302 return self.filtrate(fetch_child, child_name=name, child_ns=ns)
303
305 """
306 Returns the all children of this element named 'name'
307
308 Keyword arguments:
309 name -- local name of the element
310 ns -- namespace of the element
311 recursive -- if True this will iterate through the entire tree
312 """
313 return self.filtrate(fetch_children, child_name=name, child_ns=ns, recursive=recursive)
314
316 """
317 deletes this instance of Element. It will also removes it
318 from its parent children and attributes.
319 """
320 if self.xml_parent:
321 if self in self.xml_parent.xml_children:
322 self.xml_parent.xml_children.remove(self)
323 if hasattr(self.xml_parent, self.xml_name):
324 obj = getattr(self.xml_parent, self.xml_name)
325 if isinstance(obj, list):
326 obj.remove(self)
327 elif isinstance(obj, Element):
328 del obj
329
331 """
332 Insert 'element' right before 'before_element'.
333 This only inserts the new element in self.xml_children
334
335 Keyword arguments:
336 before_element -- element pivot
337 element -- new element to insert
338 """
339 self.xml_children.insert(self.xml_children.index(before_element), element)
340
342 """
343 Insert 'element' right after 'after_element'.
344 This only inserts the new element in self.xml_children
345
346 Keyword arguments:
347 after_element -- element pivot
348 element -- new element to insert
349 """
350 self.xml_children.insert(self.xml_children.index(after_element) + 1, element)
351
352 - def xml(self, indent=True, encoding=ENCODING, prefixes=None, omit_declaration=False):
353 """
354 Serializes as a string this element
355
356 Keyword arguments
357 indent -- pretty print the XML string (defaut: True)
358 encoding -- encoding to use during the serialization process
359 prefixes -- dictionnary of prefixes of the form {'prefix': 'ns'}
360 omit_declaration -- prevent the result to start with the XML declaration
361 """
362 ser = self.parser()
363 return ser.serialize(self, indent=indent, encoding=encoding,
364 prefixes=prefixes, omit_declaration=omit_declaration)
365
366 - def load(self, source, prefixes=None, as_attribute=None, as_list=None,
367 as_attribute_of_element=None):
368 """
369 Load source into an Element instance
370
371 Keyword arguments:
372 source -- an XML string, a file path or a file object
373 prefixes -- dictionnary of prefixes of the form {'prefix': 'ns'}
374 as_attribute -- dictionary of element names to set as attribute of their parent
375 as_list -- dictionary of element names to set into a list of their parent
376 as_attribute_of_element -- dictionary of attribute names to set as attribute of their
377 parent
378
379 If any of those last three parameters are provided they will take
380 precedence over those set on the Element and Attribute class.
381
382 """
383 ser = self.parser()
384 return ser.deserialize(source, prefixes=prefixes, as_attribute=as_attribute,
385 as_list=as_list, as_attribute_of_element=as_attribute_of_element)
386 load = classmethod(load)
387