# HG changeset patch # User paulb # Date 1191105642 0 # Node ID ad75d02f3a5c80b1e184655c1362d4ecbf71823b # Parent 6d2cdee9b6615e11239b6074d888a66ae8df30fe [project @ 2007-09-29 22:40:42 by paulb] Added some SOAP support. diff -r 6d2cdee9b661 -r ad75d02f3a5c libxml2dom/soap.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libxml2dom/soap.py Sat Sep 29 22:40:42 2007 +0000 @@ -0,0 +1,305 @@ +#!/usr/bin/env python + +""" +SOAP support using libxml2dom. + +See: http://www.w3.org/TR/2007/REC-soap12-part0-20070427/ + +Copyright (C) 2007 Paul Boddie + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the Free +Software Foundation; either version 3 of the License, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. + +You should have received a copy of the GNU Lesser General Public License along +with this program. If not, see . + +-------- + +The sending and receiving of SOAP messages can be done using traditional HTTP +libraries. + +See tests/soap_test.py for more details. +""" + +import libxml2dom +from libxml2dom.macrolib import * +from libxml2dom.macrolib import \ + createDocument as Node_createDocument + +# SOAP-related namespaces. + +SOAP_ENVELOPE_NAMESPACE = "http://www.w3.org/2003/05/soap-envelope" +SOAP_ENCODING_NAMESPACE = "http://www.w3.org/2003/05/soap-encoding" +SOAP_RPC_NAMESPACE = "http://www.w3.org/2003/05/soap-rpc" +XS_NAMESPACE = "http://www.w3.org/2001/XMLSchema" +XSI_NAMESPACE = "http://www.w3.org/2001/XMLSchema-instance" + +# Default namespace bindings for XPath. + +default_ns = { + "env" : SOAP_ENVELOPE_NAMESPACE, + "enc" : SOAP_ENCODING_NAMESPACE, + "rpc" : SOAP_RPC_NAMESPACE, + "xs" : XS_NAMESPACE, + "xsi" : XSI_NAMESPACE + } + +class SOAPImplementation(libxml2dom.Implementation): + + "Contains a SOAP-specific implementation." + + # Wrapping of documents. + + def adoptDocument(self, node): + return SOAPDocument(node, self) + + # Factory functions. + + def get_node(self, _node, context_node): + + """ + Get a libxml2dom node for the given low-level '_node' and libxml2dom + 'context_node'. + """ + + if Node_nodeType(_node) == context_node.ELEMENT_NODE: + + # Make special envelope elements. + + if Node_namespaceURI(_node) == SOAP_ENVELOPE_NAMESPACE: + if Node_localName(_node) == "Envelope": + return SOAPEnvelopeElement(_node, self, context_node.ownerDocument) + elif Node_localName(_node) == "Header": + return SOAPHeaderElement(_node, self, context_node.ownerDocument) + elif Node_localName(_node) == "Body": + return SOAPBodyElement(_node, self, context_node.ownerDocument) + elif Node_localName(_node) == "Fault": + return SOAPFaultElement(_node, self, context_node.ownerDocument) + elif Node_localName(_node) == "Code": + return SOAPCodeElement(_node, self, context_node.ownerDocument) + elif Node_localName(_node) == "Subcode": + return SOAPSubcodeElement(_node, self, context_node.ownerDocument) + elif Node_localName(_node) == "Value": + return SOAPValueElement(_node, self, context_node.ownerDocument) + elif Node_localName(_node) == "Text": + return SOAPTextElement(_node, self, context_node.ownerDocument) + + # Otherwise, make generic SOAP elements. + + return SOAPElement(_node, self, context_node.ownerDocument) + + else: + return libxml2dom.Implementation.get_node(self, _node, context_node) + + # Convenience functions. + + def createSOAPMessage(self, namespaceURI, localName): + + "Create a new SOAP message document (fragment)." + + return SOAPDocument(Node_createDocument(namespaceURI, localName, None), self).documentElement + +# Node classes. + +class SOAPNode(libxml2dom.Node): + + "Convenience modifications to nodes specific to libxml2dom.soap." + + def xpath(self, expr, variables=None, namespaces=None): + + """ + Evaluate the given 'expr' using the optional 'variables' and + 'namespaces'. If not otherwise specified, the prefixes given in the + module global 'default_ns' will be bound as in that dictionary. + """ + + ns = {} + ns.update(default_ns) + ns.update(namespaces or {}) + return libxml2dom.Node.xpath(self, expr, variables, ns) + +class SOAPDocument(libxml2dom._Document, SOAPNode): + + "A SOAP document fragment." + + def _envelope(self): + return self.xpath("./env:Envelope")[0] + + envelope = property(_envelope) + +class SOAPElement(SOAPNode): + + "A SOAP element." + + pass + +class SOAPEnvelopeElement(SOAPNode): + + "A SOAP envelope element." + + def _body(self): + return self.xpath("./env:Body")[0] + + def _setBody(self, body): + self.appendChild(body) + + def _delBody(self): + self.removeChild(self.body) + + def createBody(self): + return self.ownerDocument.createElementNS(SOAP_ENVELOPE_NAMESPACE, "env:Body") + + body = property(_body, _setBody, _delBody) + +class SOAPHeaderElement(SOAPNode): + + "A SOAP header element." + + pass + +class SOAPBodyElement(SOAPNode): + + "A SOAP body element." + + def _fault(self): + return self.xpath("./env:Fault")[0] + + def _method(self): + return self.xpath("./*[@env:encodingStyle = '%s']" % SOAP_ENCODING_NAMESPACE)[0] + + def _methodName(self): + return self.method.localName + + def _resultParameter(self): + return self.method.xpath(".//rpc:result")[0] + + def _resultParameterValue(self): + name = self.resultParameter.textContent.strip() + return self.method.xpath(".//%s" % name, namespaces={self.method.prefix : self.method.namespaceURI})[0].textContent.strip() + + def _parameters(self): + return self.method.xpath(".//*[not(./*)]") + + def _parameterValues(self): + values = {} + for parameter in self.parameters: + values[(parameter.namespaceURI, parameter.localName)] = parameter.textContent.strip() + return values + + def createFault(self): + return self.ownerDocument.createElementNS(SOAP_ENVELOPE_NAMESPACE, "env:Fault") + + fault = property(_fault) + method = property(_method) + methodName = property(_methodName) + resultParameter = property(_resultParameter) + resultParameterValue = property(_resultParameterValue) + parameters = property(_parameters) + parameterValues = property(_parameterValues) + +class SOAPFaultElement(SOAPNode): + + "A SOAP fault element." + + def _code(self): + return self.xpath("./env:Code")[0] + + def _reason(self): + return self.xpath("./env:Reason")[0] + + def _detail(self): + return self.xpath("./env:Detail")[0] + + def createCode(self): + return self.ownerDocument.createElementNS(SOAP_ENVELOPE_NAMESPACE, "env:Code") + + code = property(_code) + reason = property(_reason) + detail = property(_detail) + +class SOAPSubcodeElement(SOAPNode): + + "A SOAP subcode element." + + def _value(self): + return self.xpath("./env:Value")[0] + + def createValue(self, value=None): + code_value = self.ownerDocument.createElementNS(SOAP_ENVELOPE_NAMESPACE, "env:Value") + if value is not None: + code_value.value = code + return code_value + + value = property(_value) + +class SOAPCodeElement(SOAPSubcodeElement): + + "A SOAP code element." + + def _subcode(self): + return self.xpath("./env:Subcode")[0] + + def createSubcode(self): + return self.ownerDocument.createElementNS(SOAP_ENVELOPE_NAMESPACE, "env:Subcode") + + subcode = property(_subcode) + +class SOAPValueElement(SOAPNode): + + "A SOAP value element." + + def _value(self): + return self.textContent + + def _setValue(self, value): + for node in self.childNodes: + self.removeChild(node) + text = self.ownerDocument.createTextNode(value) + self.appendChild(text) + + value = property(_value, _setValue) + +class SOAPTextElement(SOAPValueElement): + + "A SOAP text element." + + def _lang(self): + return self.getAttributeNS(libxml2dom.XML_NAMESPACE, "lang") + + def _setLang(self, value): + self.setAttributeNS(libxml2dom.XML_NAMESPACE, "xml:lang", value) + + lang = property(_lang, _setLang) + +# Utility functions. + +createDocument = libxml2dom.createDocument +createDocumentType = libxml2dom.createDocumentType + +def createSOAPMessage(namespaceURI, localName): + return default_impl.createSOAPMessage(namespaceURI, localName) + +def parse(stream_or_string, html=0, htmlencoding=None, unfinished=0, impl=None): + return libxml2dom.parse(stream_or_string, html=html, htmlencoding=htmlencoding, unfinished=unfinished, impl=(impl or default_impl)) + +def parseFile(filename, html=0, htmlencoding=None, unfinished=0, impl=None): + return libxml2dom.parseFile(filename, html=html, htmlencoding=htmlencoding, unfinished=unfinished, impl=(impl or default_impl)) + +def parseString(s, html=0, htmlencoding=None, unfinished=0, impl=None): + return libxml2dom.parseString(s, html=html, htmlencoding=htmlencoding, unfinished=unfinished, impl=(impl or default_impl)) + +def parseURI(uri, html=0, htmlencoding=None, unfinished=0, impl=None): + return libxml2dom.parseURI(uri, html=html, htmlencoding=htmlencoding, unfinished=unfinished, impl=(impl or default_impl)) + +# Single instance of the implementation. + +default_impl = SOAPImplementation() + +# vim: tabstop=4 expandtab shiftwidth=4