1.1 --- a/libxml2dom/soap.py Sun Sep 14 02:30:58 2008 +0200
1.2 +++ b/libxml2dom/soap.py Sun Sep 14 22:55:59 2008 +0200
1.3 @@ -6,7 +6,7 @@
1.4
1.5 See: http://www.w3.org/TR/2007/REC-soap12-part0-20070427/
1.6
1.7 -Copyright (C) 2007 Paul Boddie <paul@boddie.org.uk>
1.8 +Copyright (C) 2007, 2008 Paul Boddie <paul@boddie.org.uk>
1.9
1.10 This program is free software; you can redistribute it and/or modify it under
1.11 the terms of the GNU Lesser General Public License as published by the Free
1.12 @@ -26,13 +26,14 @@
1.13 The sending and receiving of SOAP messages can be done using traditional HTTP
1.14 libraries.
1.15
1.16 -See tests/soap_test.py for more details.
1.17 +See tests/test_soap.py for more details.
1.18 """
1.19
1.20 import libxml2dom
1.21 from libxml2dom.macrolib import *
1.22 from libxml2dom.macrolib import \
1.23 createDocument as Node_createDocument
1.24 +from libxml2dom.values import ContentValue, SequenceValue
1.25
1.26 # SOAP-related namespaces.
1.27
1.28 @@ -59,68 +60,6 @@
1.29 "SOAP-ENC" : OLD_SOAP_ENCODING_NAMESPACE
1.30 }
1.31
1.32 -class SOAPImplementation(libxml2dom.Implementation):
1.33 -
1.34 - "Contains a SOAP-specific implementation."
1.35 -
1.36 - # Wrapping of documents.
1.37 -
1.38 - def adoptDocument(self, node):
1.39 - return SOAPDocument(node, self)
1.40 -
1.41 - # Factory functions.
1.42 -
1.43 - def get_node(self, _node, context_node):
1.44 -
1.45 - """
1.46 - Get a libxml2dom node for the given low-level '_node' and libxml2dom
1.47 - 'context_node'.
1.48 - """
1.49 -
1.50 - if Node_nodeType(_node) == context_node.ELEMENT_NODE:
1.51 -
1.52 - # Make special envelope elements.
1.53 -
1.54 - if Node_namespaceURI(_node) in (SOAP_ENVELOPE_NAMESPACE, OLD_SOAP_ENVELOPE_NAMESPACE):
1.55 - if Node_localName(_node) == "Envelope":
1.56 - return SOAPEnvelopeElement(_node, self, context_node.ownerDocument)
1.57 - elif Node_localName(_node) == "Header":
1.58 - return SOAPHeaderElement(_node, self, context_node.ownerDocument)
1.59 - elif Node_localName(_node) == "Body":
1.60 - return SOAPBodyElement(_node, self, context_node.ownerDocument)
1.61 - elif Node_localName(_node) == "Fault":
1.62 - return SOAPFaultElement(_node, self, context_node.ownerDocument)
1.63 - elif Node_localName(_node) == "Code":
1.64 - return SOAPCodeElement(_node, self, context_node.ownerDocument)
1.65 - elif Node_localName(_node) == "Subcode":
1.66 - return SOAPSubcodeElement(_node, self, context_node.ownerDocument)
1.67 - elif Node_localName(_node) == "Value":
1.68 - return SOAPValueElement(_node, self, context_node.ownerDocument)
1.69 - elif Node_localName(_node) == "Text":
1.70 - return SOAPTextElement(_node, self, context_node.ownerDocument)
1.71 -
1.72 - # Detect the method element.
1.73 -
1.74 - if Node_parentNode(_node) and Node_localName(Node_parentNode(_node)) == "Body" and \
1.75 - Node_namespaceURI(Node_parentNode(_node)) in (SOAP_ENVELOPE_NAMESPACE, OLD_SOAP_ENVELOPE_NAMESPACE):
1.76 -
1.77 - return SOAPMethodElement(_node, self, context_node.ownerDocument)
1.78 -
1.79 - # Otherwise, make generic SOAP elements.
1.80 -
1.81 - return SOAPElement(_node, self, context_node.ownerDocument)
1.82 -
1.83 - else:
1.84 - return libxml2dom.Implementation.get_node(self, _node, context_node)
1.85 -
1.86 - # Convenience functions.
1.87 -
1.88 - def createSOAPMessage(self, namespaceURI, localName):
1.89 -
1.90 - "Create a new SOAP message document (fragment)."
1.91 -
1.92 - return SOAPDocument(Node_createDocument(namespaceURI, localName, None), self).documentElement
1.93 -
1.94 # Node classes.
1.95
1.96 class SOAPNode(libxml2dom.Node):
1.97 @@ -140,33 +79,63 @@
1.98 ns.update(namespaces or {})
1.99 return libxml2dom.Node.xpath(self, expr, variables, ns)
1.100
1.101 - # All nodes support convenience methods.
1.102 + def add_or_replace_element(self, new_element):
1.103 +
1.104 + """
1.105 + Add or replace the given 'new_element', using its localName to find any
1.106 + element to be replaced.
1.107 + """
1.108 +
1.109 + elements = self.xpath(new_element.localName)
1.110 + if elements:
1.111 + self.replaceChild(new_element, elements[0])
1.112 + else:
1.113 + self.appendChild(new_element)
1.114 +
1.115 +class SOAPElement(ContentValue, SequenceValue, SOAPNode):
1.116 +
1.117 + "A SOAP element."
1.118
1.119 def convert(self, node):
1.120 return node.textContent.strip()
1.121
1.122 + def values(self):
1.123 + return [v.contents for v in self.xpath("*")]
1.124 +
1.125 def _contents(self):
1.126 # NOTE: Should check whether this should be a leaf element.
1.127 if not self.xpath("*"):
1.128 return (self.localName, getattr(self.ownerDocument, "convert", self.convert)(self))
1.129 else:
1.130 - return (self.localName, SOAPContents(self))
1.131 + return (self.localName, self)
1.132
1.133 def __len__(self):
1.134 - return 2
1.135 -
1.136 - def __getitem__(self, i):
1.137 - return self.contents[i]
1.138 + if not self.xpath("*"):
1.139 + return 2
1.140 + else:
1.141 + return SequenceValue.__len__(self)
1.142
1.143 def __eq__(self, other):
1.144 - if hasattr(other, "contents"):
1.145 - return self.contents == other.contents
1.146 + if not self.xpath("*"):
1.147 + return ContentValue.__eq__(self, other)
1.148 else:
1.149 - return self.contents == other
1.150 + return SequenceValue.__eq__(self, other)
1.151 +
1.152 + def __ne__(self, other):
1.153 + if not self.xpath("*"):
1.154 + return ContentValue.__ne__(self, other)
1.155 + else:
1.156 + return SequenceValue.__ne__(self, other)
1.157 +
1.158 + def __repr__(self):
1.159 + if self.contents[1] is self:
1.160 + return "<%s: %r>" % (self.__class__.__name__, self.values())
1.161 + else:
1.162 + return "<%s: %r>" % (self.__class__.__name__, self.contents)
1.163
1.164 # Node construction methods.
1.165
1.166 - def createSOAPElement(self, localName):
1.167 + def createSOAPElement(self, localName, insert=0):
1.168
1.169 "Create an element with the appropriate namespace and prefix."
1.170
1.171 @@ -176,61 +145,45 @@
1.172 name = prefix + ":" + localName
1.173 else:
1.174 name = localName
1.175 - return self.createElementNS(ref_element.namespaceURI, name)
1.176 + element = self.createElementNS(ref_element.namespaceURI, name)
1.177 + if insert:
1.178 + self.appendChild(element)
1.179 + return element
1.180
1.181 contents = property(_contents)
1.182
1.183 -class SOAPContents(object):
1.184 -
1.185 - "A wrapper around another node in order to provide sequence-like access."
1.186 -
1.187 - def __init__(self, node):
1.188 - self.node = node
1.189 -
1.190 - def __len__(self):
1.191 - return len(self.node.xpath("*"))
1.192 -
1.193 - def __getitem__(self, i):
1.194 - return self.node.xpath("*")[i]
1.195 -
1.196 - def __eq__(self, other):
1.197 - for i, j in map(None, self, other):
1.198 - if i != j:
1.199 - return False
1.200 - return True
1.201 -
1.202 class SOAPDocument(libxml2dom._Document, SOAPNode):
1.203
1.204 "A SOAP document fragment."
1.205
1.206 def _envelope(self):
1.207 - return self.xpath("env:Envelope|SOAP-ENV:Envelope")[0]
1.208 + return (self.xpath("env:Envelope|SOAP-ENV:Envelope") or [None])[0]
1.209
1.210 envelope = property(_envelope)
1.211
1.212 # Convenience methods and properties.
1.213
1.214 def _fault(self):
1.215 - return self.envelope.body.fault
1.216 + if self.envelope is not None:
1.217 + return self.envelope.fault
1.218 + else:
1.219 + return None
1.220
1.221 def _method(self):
1.222 - return self.envelope.body.method
1.223 + if self.envelope is not None:
1.224 + return self.envelope.method
1.225 + else:
1.226 + return None
1.227
1.228 fault = property(_fault)
1.229 method = property(_method)
1.230
1.231 -class SOAPElement(SOAPNode):
1.232 -
1.233 - "A SOAP element."
1.234 -
1.235 - pass
1.236 -
1.237 -class SOAPEnvelopeElement(SOAPNode):
1.238 +class SOAPEnvelopeElement(SOAPElement):
1.239
1.240 "A SOAP envelope element."
1.241
1.242 def _body(self):
1.243 - return self.xpath("env:Body|SOAP-ENV:Body")[0]
1.244 + return (self.xpath("env:Body|SOAP-ENV:Body") or [None])[0]
1.245
1.246 def _setBody(self, body):
1.247 self.appendChild(body)
1.248 @@ -238,18 +191,40 @@
1.249 def _delBody(self):
1.250 self.removeChild(self.body)
1.251
1.252 - def createBody(self):
1.253 - return self.createSOAPElement("Body")
1.254 + # Convenience methods and properties.
1.255 +
1.256 + def _fault(self):
1.257 + if self.body is not None:
1.258 + return self.body.fault
1.259 + else:
1.260 + return None
1.261 +
1.262 + def _method(self):
1.263 + if self.body is not None:
1.264 + return self.body.method
1.265 + else:
1.266 + return None
1.267 +
1.268 + fault = property(_fault)
1.269 + method = property(_method)
1.270 +
1.271 + # Node construction methods.
1.272 +
1.273 + def createBody(self, insert=0):
1.274 + element = self.createSOAPElement("Body")
1.275 + if insert:
1.276 + self.add_or_replace_element(element)
1.277 + return element
1.278
1.279 body = property(_body, _setBody, _delBody)
1.280
1.281 -class SOAPHeaderElement(SOAPNode):
1.282 +class SOAPHeaderElement(SOAPElement):
1.283
1.284 "A SOAP header element."
1.285
1.286 pass
1.287
1.288 -class SOAPBodyElement(SOAPNode):
1.289 +class SOAPBodyElement(SOAPElement):
1.290
1.291 "A SOAP body element."
1.292
1.293 @@ -264,13 +239,24 @@
1.294
1.295 # Node construction methods.
1.296
1.297 - def createFault(self):
1.298 - return self.createSOAPElement("Fault")
1.299 + def createMethod(self, namespaceURI, name, insert=0):
1.300 + if self.method is not None:
1.301 + self.removeChild(self.method)
1.302 + element = self.createElementNS(namespaceURI, name)
1.303 + element.setAttributeNS(SOAP_ENVELOPE_NAMESPACE, "env:encodingStyle", SOAP_ENCODING_NAMESPACE)
1.304 + self.appendChild(element)
1.305 + return element
1.306 +
1.307 + def createFault(self, insert=0):
1.308 + element = self.createSOAPElement("Fault")
1.309 + if insert:
1.310 + self.add_or_replace_element(element)
1.311 + return element
1.312
1.313 fault = property(_fault)
1.314 method = property(_method)
1.315
1.316 -class SOAPMethodElement(SOAPNode):
1.317 +class SOAPMethodElement(SOAPElement):
1.318
1.319 "A SOAP method element."
1.320
1.321 @@ -291,15 +277,22 @@
1.322 else:
1.323 return None
1.324
1.325 + def _parameters(self):
1.326 + return self.xpath("*")
1.327 +
1.328 def _parameterValues(self):
1.329 - return [value.contents for value in self.xpath("*")]
1.330 + return self.values()
1.331 +
1.332 + def __repr__(self):
1.333 + return "<SOAPMethodElement: %r>" % self.parameters
1.334
1.335 methodName = property(_methodName)
1.336 resultParameter = property(_resultParameter)
1.337 resultParameterValue = property(_resultParameterValue)
1.338 parameterValues = property(_parameterValues)
1.339 + parameters = property(_parameters)
1.340
1.341 -class SOAPFaultElement(SOAPNode):
1.342 +class SOAPFaultElement(SOAPElement):
1.343
1.344 "A SOAP fault element."
1.345
1.346 @@ -323,15 +316,20 @@
1.347 def _detail(self):
1.348 return (self.xpath("env:Detail|SOAP-ENV:Detail") or [None])[0]
1.349
1.350 - def createCode(self):
1.351 - return self.createSOAPElement("Code")
1.352 + # Node construction methods.
1.353 +
1.354 + def createCode(self, insert=0):
1.355 + element = self.createSOAPElement("Code")
1.356 + if insert:
1.357 + self.add_or_replace_element(element)
1.358 + return element
1.359
1.360 code = property(_code)
1.361 subcode = property(_subcode)
1.362 reason = property(_reason)
1.363 detail = property(_detail)
1.364
1.365 -class SOAPSubcodeElement(SOAPNode):
1.366 +class SOAPSubcodeElement(SOAPElement):
1.367
1.368 "A SOAP subcode element."
1.369
1.370 @@ -351,10 +349,14 @@
1.371 self.appendChild(v)
1.372 v.value = value
1.373
1.374 - def createValue(self, value=None):
1.375 + # Node construction methods.
1.376 +
1.377 + def createValue(self, value=None, insert=0):
1.378 code_value = self.createSOAPElement("Value")
1.379 if value is not None:
1.380 code_value.value = code
1.381 + if insert:
1.382 + self.add_or_replace_element(code_value)
1.383 return code_value
1.384
1.385 value = property(_value, _setValue)
1.386 @@ -366,12 +368,17 @@
1.387 def _subcode(self):
1.388 return (self.xpath("env:Subcode|SOAP-ENV:Subcode") or [None])[0]
1.389
1.390 - def createSubcode(self):
1.391 - return self.createSOAPElement("Subcode")
1.392 + # Node construction methods.
1.393 +
1.394 + def createSubcode(self, insert=0):
1.395 + element = self.createSOAPElement("Subcode")
1.396 + if insert:
1.397 + self.add_or_replace_element(element)
1.398 + return element
1.399
1.400 subcode = property(_subcode)
1.401
1.402 -class SOAPValueElement(SOAPNode):
1.403 +class SOAPValueElement(SOAPElement):
1.404
1.405 "A SOAP value element."
1.406
1.407 @@ -398,13 +405,76 @@
1.408
1.409 lang = property(_lang, _setLang)
1.410
1.411 +# Implementation-related functionality.
1.412 +
1.413 +class SOAPImplementation(libxml2dom.Implementation):
1.414 +
1.415 + "Contains a SOAP-specific implementation."
1.416 +
1.417 + # Mapping of element names to wrappers.
1.418 +
1.419 + _class_for_name = {
1.420 + "Envelope" : SOAPEnvelopeElement,
1.421 + "Header" : SOAPHeaderElement,
1.422 + "Body" : SOAPBodyElement,
1.423 + "Fault" : SOAPFaultElement,
1.424 + "Code" : SOAPCodeElement,
1.425 + "Subcode" : SOAPSubcodeElement,
1.426 + "Value" : SOAPValueElement,
1.427 + "Text" : SOAPTextElement
1.428 + }
1.429 +
1.430 + # Wrapping of documents.
1.431 +
1.432 + def adoptDocument(self, node):
1.433 + return SOAPDocument(node, self)
1.434 +
1.435 + # Factory functions.
1.436 +
1.437 + def get_node(self, _node, context_node):
1.438 +
1.439 + """
1.440 + Get a libxml2dom node for the given low-level '_node' and libxml2dom
1.441 + 'context_node'.
1.442 + """
1.443 +
1.444 + if Node_nodeType(_node) == context_node.ELEMENT_NODE:
1.445 +
1.446 + # Make special envelope elements.
1.447 +
1.448 + if Node_namespaceURI(_node) in (SOAP_ENVELOPE_NAMESPACE, OLD_SOAP_ENVELOPE_NAMESPACE):
1.449 + cls = self._class_for_name[Node_localName(_node)]
1.450 + return cls(_node, self, context_node.ownerDocument)
1.451 +
1.452 + # Detect the method element.
1.453 +
1.454 + if Node_parentNode(_node) and Node_localName(Node_parentNode(_node)) == "Body" and \
1.455 + Node_namespaceURI(Node_parentNode(_node)) in (SOAP_ENVELOPE_NAMESPACE, OLD_SOAP_ENVELOPE_NAMESPACE):
1.456 +
1.457 + return SOAPMethodElement(_node, self, context_node.ownerDocument)
1.458 +
1.459 + # Otherwise, make generic SOAP elements.
1.460 +
1.461 + return SOAPElement(_node, self, context_node.ownerDocument)
1.462 +
1.463 + else:
1.464 + return libxml2dom.Implementation.get_node(self, _node, context_node)
1.465 +
1.466 + # Convenience functions.
1.467 +
1.468 + def createSOAPMessage(self):
1.469 +
1.470 + "Create a new SOAP message document (fragment)."
1.471 +
1.472 + return SOAPDocument(Node_createDocument(SOAP_ENVELOPE_NAMESPACE, "env:Envelope", None), self).documentElement
1.473 +
1.474 # Utility functions.
1.475
1.476 createDocument = libxml2dom.createDocument
1.477 createDocumentType = libxml2dom.createDocumentType
1.478
1.479 -def createSOAPMessage(namespaceURI, localName):
1.480 - return default_impl.createSOAPMessage(namespaceURI, localName)
1.481 +def createSOAPMessage():
1.482 + return default_impl.createSOAPMessage()
1.483
1.484 def parse(stream_or_string, html=0, htmlencoding=None, unfinished=0, impl=None):
1.485 return libxml2dom.parse(stream_or_string, html=html, htmlencoding=htmlencoding, unfinished=unfinished, impl=(impl or default_impl))