1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/libxml2dom/soap.py Sat Sep 29 22:40:42 2007 +0000
1.3 @@ -0,0 +1,305 @@
1.4 +#!/usr/bin/env python
1.5 +
1.6 +"""
1.7 +SOAP support using libxml2dom.
1.8 +
1.9 +See: http://www.w3.org/TR/2007/REC-soap12-part0-20070427/
1.10 +
1.11 +Copyright (C) 2007 Paul Boddie <paul@boddie.org.uk>
1.12 +
1.13 +This program is free software; you can redistribute it and/or modify it under
1.14 +the terms of the GNU Lesser General Public License as published by the Free
1.15 +Software Foundation; either version 3 of the License, or (at your option) any
1.16 +later version.
1.17 +
1.18 +This program is distributed in the hope that it will be useful, but WITHOUT
1.19 +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
1.20 +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
1.21 +details.
1.22 +
1.23 +You should have received a copy of the GNU Lesser General Public License along
1.24 +with this program. If not, see <http://www.gnu.org/licenses/>.
1.25 +
1.26 +--------
1.27 +
1.28 +The sending and receiving of SOAP messages can be done using traditional HTTP
1.29 +libraries.
1.30 +
1.31 +See tests/soap_test.py for more details.
1.32 +"""
1.33 +
1.34 +import libxml2dom
1.35 +from libxml2dom.macrolib import *
1.36 +from libxml2dom.macrolib import \
1.37 + createDocument as Node_createDocument
1.38 +
1.39 +# SOAP-related namespaces.
1.40 +
1.41 +SOAP_ENVELOPE_NAMESPACE = "http://www.w3.org/2003/05/soap-envelope"
1.42 +SOAP_ENCODING_NAMESPACE = "http://www.w3.org/2003/05/soap-encoding"
1.43 +SOAP_RPC_NAMESPACE = "http://www.w3.org/2003/05/soap-rpc"
1.44 +XS_NAMESPACE = "http://www.w3.org/2001/XMLSchema"
1.45 +XSI_NAMESPACE = "http://www.w3.org/2001/XMLSchema-instance"
1.46 +
1.47 +# Default namespace bindings for XPath.
1.48 +
1.49 +default_ns = {
1.50 + "env" : SOAP_ENVELOPE_NAMESPACE,
1.51 + "enc" : SOAP_ENCODING_NAMESPACE,
1.52 + "rpc" : SOAP_RPC_NAMESPACE,
1.53 + "xs" : XS_NAMESPACE,
1.54 + "xsi" : XSI_NAMESPACE
1.55 + }
1.56 +
1.57 +class SOAPImplementation(libxml2dom.Implementation):
1.58 +
1.59 + "Contains a SOAP-specific implementation."
1.60 +
1.61 + # Wrapping of documents.
1.62 +
1.63 + def adoptDocument(self, node):
1.64 + return SOAPDocument(node, self)
1.65 +
1.66 + # Factory functions.
1.67 +
1.68 + def get_node(self, _node, context_node):
1.69 +
1.70 + """
1.71 + Get a libxml2dom node for the given low-level '_node' and libxml2dom
1.72 + 'context_node'.
1.73 + """
1.74 +
1.75 + if Node_nodeType(_node) == context_node.ELEMENT_NODE:
1.76 +
1.77 + # Make special envelope elements.
1.78 +
1.79 + if Node_namespaceURI(_node) == SOAP_ENVELOPE_NAMESPACE:
1.80 + if Node_localName(_node) == "Envelope":
1.81 + return SOAPEnvelopeElement(_node, self, context_node.ownerDocument)
1.82 + elif Node_localName(_node) == "Header":
1.83 + return SOAPHeaderElement(_node, self, context_node.ownerDocument)
1.84 + elif Node_localName(_node) == "Body":
1.85 + return SOAPBodyElement(_node, self, context_node.ownerDocument)
1.86 + elif Node_localName(_node) == "Fault":
1.87 + return SOAPFaultElement(_node, self, context_node.ownerDocument)
1.88 + elif Node_localName(_node) == "Code":
1.89 + return SOAPCodeElement(_node, self, context_node.ownerDocument)
1.90 + elif Node_localName(_node) == "Subcode":
1.91 + return SOAPSubcodeElement(_node, self, context_node.ownerDocument)
1.92 + elif Node_localName(_node) == "Value":
1.93 + return SOAPValueElement(_node, self, context_node.ownerDocument)
1.94 + elif Node_localName(_node) == "Text":
1.95 + return SOAPTextElement(_node, self, context_node.ownerDocument)
1.96 +
1.97 + # Otherwise, make generic SOAP elements.
1.98 +
1.99 + return SOAPElement(_node, self, context_node.ownerDocument)
1.100 +
1.101 + else:
1.102 + return libxml2dom.Implementation.get_node(self, _node, context_node)
1.103 +
1.104 + # Convenience functions.
1.105 +
1.106 + def createSOAPMessage(self, namespaceURI, localName):
1.107 +
1.108 + "Create a new SOAP message document (fragment)."
1.109 +
1.110 + return SOAPDocument(Node_createDocument(namespaceURI, localName, None), self).documentElement
1.111 +
1.112 +# Node classes.
1.113 +
1.114 +class SOAPNode(libxml2dom.Node):
1.115 +
1.116 + "Convenience modifications to nodes specific to libxml2dom.soap."
1.117 +
1.118 + def xpath(self, expr, variables=None, namespaces=None):
1.119 +
1.120 + """
1.121 + Evaluate the given 'expr' using the optional 'variables' and
1.122 + 'namespaces'. If not otherwise specified, the prefixes given in the
1.123 + module global 'default_ns' will be bound as in that dictionary.
1.124 + """
1.125 +
1.126 + ns = {}
1.127 + ns.update(default_ns)
1.128 + ns.update(namespaces or {})
1.129 + return libxml2dom.Node.xpath(self, expr, variables, ns)
1.130 +
1.131 +class SOAPDocument(libxml2dom._Document, SOAPNode):
1.132 +
1.133 + "A SOAP document fragment."
1.134 +
1.135 + def _envelope(self):
1.136 + return self.xpath("./env:Envelope")[0]
1.137 +
1.138 + envelope = property(_envelope)
1.139 +
1.140 +class SOAPElement(SOAPNode):
1.141 +
1.142 + "A SOAP element."
1.143 +
1.144 + pass
1.145 +
1.146 +class SOAPEnvelopeElement(SOAPNode):
1.147 +
1.148 + "A SOAP envelope element."
1.149 +
1.150 + def _body(self):
1.151 + return self.xpath("./env:Body")[0]
1.152 +
1.153 + def _setBody(self, body):
1.154 + self.appendChild(body)
1.155 +
1.156 + def _delBody(self):
1.157 + self.removeChild(self.body)
1.158 +
1.159 + def createBody(self):
1.160 + return self.ownerDocument.createElementNS(SOAP_ENVELOPE_NAMESPACE, "env:Body")
1.161 +
1.162 + body = property(_body, _setBody, _delBody)
1.163 +
1.164 +class SOAPHeaderElement(SOAPNode):
1.165 +
1.166 + "A SOAP header element."
1.167 +
1.168 + pass
1.169 +
1.170 +class SOAPBodyElement(SOAPNode):
1.171 +
1.172 + "A SOAP body element."
1.173 +
1.174 + def _fault(self):
1.175 + return self.xpath("./env:Fault")[0]
1.176 +
1.177 + def _method(self):
1.178 + return self.xpath("./*[@env:encodingStyle = '%s']" % SOAP_ENCODING_NAMESPACE)[0]
1.179 +
1.180 + def _methodName(self):
1.181 + return self.method.localName
1.182 +
1.183 + def _resultParameter(self):
1.184 + return self.method.xpath(".//rpc:result")[0]
1.185 +
1.186 + def _resultParameterValue(self):
1.187 + name = self.resultParameter.textContent.strip()
1.188 + return self.method.xpath(".//%s" % name, namespaces={self.method.prefix : self.method.namespaceURI})[0].textContent.strip()
1.189 +
1.190 + def _parameters(self):
1.191 + return self.method.xpath(".//*[not(./*)]")
1.192 +
1.193 + def _parameterValues(self):
1.194 + values = {}
1.195 + for parameter in self.parameters:
1.196 + values[(parameter.namespaceURI, parameter.localName)] = parameter.textContent.strip()
1.197 + return values
1.198 +
1.199 + def createFault(self):
1.200 + return self.ownerDocument.createElementNS(SOAP_ENVELOPE_NAMESPACE, "env:Fault")
1.201 +
1.202 + fault = property(_fault)
1.203 + method = property(_method)
1.204 + methodName = property(_methodName)
1.205 + resultParameter = property(_resultParameter)
1.206 + resultParameterValue = property(_resultParameterValue)
1.207 + parameters = property(_parameters)
1.208 + parameterValues = property(_parameterValues)
1.209 +
1.210 +class SOAPFaultElement(SOAPNode):
1.211 +
1.212 + "A SOAP fault element."
1.213 +
1.214 + def _code(self):
1.215 + return self.xpath("./env:Code")[0]
1.216 +
1.217 + def _reason(self):
1.218 + return self.xpath("./env:Reason")[0]
1.219 +
1.220 + def _detail(self):
1.221 + return self.xpath("./env:Detail")[0]
1.222 +
1.223 + def createCode(self):
1.224 + return self.ownerDocument.createElementNS(SOAP_ENVELOPE_NAMESPACE, "env:Code")
1.225 +
1.226 + code = property(_code)
1.227 + reason = property(_reason)
1.228 + detail = property(_detail)
1.229 +
1.230 +class SOAPSubcodeElement(SOAPNode):
1.231 +
1.232 + "A SOAP subcode element."
1.233 +
1.234 + def _value(self):
1.235 + return self.xpath("./env:Value")[0]
1.236 +
1.237 + def createValue(self, value=None):
1.238 + code_value = self.ownerDocument.createElementNS(SOAP_ENVELOPE_NAMESPACE, "env:Value")
1.239 + if value is not None:
1.240 + code_value.value = code
1.241 + return code_value
1.242 +
1.243 + value = property(_value)
1.244 +
1.245 +class SOAPCodeElement(SOAPSubcodeElement):
1.246 +
1.247 + "A SOAP code element."
1.248 +
1.249 + def _subcode(self):
1.250 + return self.xpath("./env:Subcode")[0]
1.251 +
1.252 + def createSubcode(self):
1.253 + return self.ownerDocument.createElementNS(SOAP_ENVELOPE_NAMESPACE, "env:Subcode")
1.254 +
1.255 + subcode = property(_subcode)
1.256 +
1.257 +class SOAPValueElement(SOAPNode):
1.258 +
1.259 + "A SOAP value element."
1.260 +
1.261 + def _value(self):
1.262 + return self.textContent
1.263 +
1.264 + def _setValue(self, value):
1.265 + for node in self.childNodes:
1.266 + self.removeChild(node)
1.267 + text = self.ownerDocument.createTextNode(value)
1.268 + self.appendChild(text)
1.269 +
1.270 + value = property(_value, _setValue)
1.271 +
1.272 +class SOAPTextElement(SOAPValueElement):
1.273 +
1.274 + "A SOAP text element."
1.275 +
1.276 + def _lang(self):
1.277 + return self.getAttributeNS(libxml2dom.XML_NAMESPACE, "lang")
1.278 +
1.279 + def _setLang(self, value):
1.280 + self.setAttributeNS(libxml2dom.XML_NAMESPACE, "xml:lang", value)
1.281 +
1.282 + lang = property(_lang, _setLang)
1.283 +
1.284 +# Utility functions.
1.285 +
1.286 +createDocument = libxml2dom.createDocument
1.287 +createDocumentType = libxml2dom.createDocumentType
1.288 +
1.289 +def createSOAPMessage(namespaceURI, localName):
1.290 + return default_impl.createSOAPMessage(namespaceURI, localName)
1.291 +
1.292 +def parse(stream_or_string, html=0, htmlencoding=None, unfinished=0, impl=None):
1.293 + return libxml2dom.parse(stream_or_string, html=html, htmlencoding=htmlencoding, unfinished=unfinished, impl=(impl or default_impl))
1.294 +
1.295 +def parseFile(filename, html=0, htmlencoding=None, unfinished=0, impl=None):
1.296 + return libxml2dom.parseFile(filename, html=html, htmlencoding=htmlencoding, unfinished=unfinished, impl=(impl or default_impl))
1.297 +
1.298 +def parseString(s, html=0, htmlencoding=None, unfinished=0, impl=None):
1.299 + return libxml2dom.parseString(s, html=html, htmlencoding=htmlencoding, unfinished=unfinished, impl=(impl or default_impl))
1.300 +
1.301 +def parseURI(uri, html=0, htmlencoding=None, unfinished=0, impl=None):
1.302 + return libxml2dom.parseURI(uri, html=html, htmlencoding=htmlencoding, unfinished=unfinished, impl=(impl or default_impl))
1.303 +
1.304 +# Single instance of the implementation.
1.305 +
1.306 +default_impl = SOAPImplementation()
1.307 +
1.308 +# vim: tabstop=4 expandtab shiftwidth=4