| @@ -0,0 +1,303 @@ | |||
| # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved. | |||
| # | |||
| # This software is subject to the provisions of the Zope Public License, | |||
| # Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution. | |||
| # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED | |||
| # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |||
| # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS | |||
| # FOR A PARTICULAR PURPOSE. | |||
| from urlparse import urlparse | |||
| from ZSI import * | |||
| from ZSI.client import * | |||
| import weakref | |||
| from Utility import DOM | |||
| import WSDLTools | |||
| class SOAPCallInfo: | |||
| """SOAPCallInfo captures the important binding information about a | |||
| SOAP operation, in a structure that is easier to work with than | |||
| raw WSDL structures.""" | |||
| def __init__(self, methodName): | |||
| self.methodName = methodName | |||
| self.inheaders = [] | |||
| self.outheaders = [] | |||
| self.inparams = [] | |||
| self.outparams = [] | |||
| self.retval = None | |||
| encodingStyle = DOM.NS_SOAP_ENC | |||
| documentation = '' | |||
| soapAction = None | |||
| transport = None | |||
| namespace = None | |||
| location = None | |||
| use = 'encoded' | |||
| style = 'rpc' | |||
| def addInParameter(self, name, type, namespace=None, element_type=0): | |||
| """Add an input parameter description to the call info.""" | |||
| parameter = ParameterInfo(name, type, namespace, element_type) | |||
| self.inparams.append(parameter) | |||
| return parameter | |||
| def addOutParameter(self, name, type, namespace=None, element_type=0): | |||
| """Add an output parameter description to the call info.""" | |||
| parameter = ParameterInfo(name, type, namespace, element_type) | |||
| self.outparams.append(parameter) | |||
| return parameter | |||
| def setReturnParameter(self, name, type, namespace=None, element_type=0): | |||
| """Set the return parameter description for the call info.""" | |||
| parameter = ParameterInfo(name, type, namespace, element_type) | |||
| self.retval = parameter | |||
| return parameter | |||
| def addInHeaderInfo(self, name, type, namespace, element_type=0, | |||
| mustUnderstand=0): | |||
| """Add an input SOAP header description to the call info.""" | |||
| headerinfo = HeaderInfo(name, type, namespace, element_type) | |||
| if mustUnderstand: | |||
| headerinfo.mustUnderstand = 1 | |||
| self.inheaders.append(headerinfo) | |||
| return headerinfo | |||
| def addOutHeaderInfo(self, name, type, namespace, element_type=0, | |||
| mustUnderstand=0): | |||
| """Add an output SOAP header description to the call info.""" | |||
| headerinfo = HeaderInfo(name, type, namespace, element_type) | |||
| if mustUnderstand: | |||
| headerinfo.mustUnderstand = 1 | |||
| self.outheaders.append(headerinfo) | |||
| return headerinfo | |||
| def getInParameters(self): | |||
| """Return a sequence of the in parameters of the method.""" | |||
| return self.inparams | |||
| def getOutParameters(self): | |||
| """Return a sequence of the out parameters of the method.""" | |||
| return self.outparams | |||
| def getReturnParameter(self): | |||
| """Return param info about the return value of the method.""" | |||
| return self.retval | |||
| def getInHeaders(self): | |||
| """Return a sequence of the in headers of the method.""" | |||
| return self.inheaders | |||
| def getOutHeaders(self): | |||
| """Return a sequence of the out headers of the method.""" | |||
| return self.outheaders | |||
| class ParameterInfo: | |||
| """A ParameterInfo object captures parameter binding information.""" | |||
| def __init__(self, name, type, namespace=None, element_type=0): | |||
| if element_type: | |||
| self.element_type = 1 | |||
| if namespace is not None: | |||
| self.namespace = namespace | |||
| self.name = name | |||
| self.type = type | |||
| element_type = 0 | |||
| namespace = None | |||
| default = None | |||
| class HeaderInfo(ParameterInfo): | |||
| """A HeaderInfo object captures SOAP header binding information.""" | |||
| def __init__(self, name, type, namespace, element_type=None): | |||
| ParameterInfo.__init__(self, name, type, namespace, element_type) | |||
| mustUnderstand = 0 | |||
| actor = None | |||
| def callInfoFromWSDL(port, name): | |||
| """Return a SOAPCallInfo given a WSDL port and operation name.""" | |||
| wsdl = port.getService().getWSDL() | |||
| binding = port.getBinding() | |||
| portType = binding.getPortType() | |||
| operation = portType.operations[name] | |||
| opbinding = binding.operations[name] | |||
| messages = wsdl.messages | |||
| callinfo = SOAPCallInfo(name) | |||
| addrbinding = port.getAddressBinding() | |||
| if not isinstance(addrbinding, WSDLTools.SoapAddressBinding): | |||
| raise ValueError, 'Unsupported binding type.' | |||
| callinfo.location = addrbinding.location | |||
| soapbinding = binding.findBinding(WSDLTools.SoapBinding) | |||
| if soapbinding is None: | |||
| raise ValueError, 'Missing soap:binding element.' | |||
| callinfo.transport = soapbinding.transport | |||
| callinfo.style = soapbinding.style or 'document' | |||
| soap_op_binding = opbinding.findBinding(WSDLTools.SoapOperationBinding) | |||
| if soap_op_binding is not None: | |||
| callinfo.soapAction = soap_op_binding.soapAction | |||
| callinfo.style = soap_op_binding.style or callinfo.style | |||
| parameterOrder = operation.parameterOrder | |||
| if operation.input is not None: | |||
| message = messages[operation.input.message] | |||
| msgrole = opbinding.input | |||
| mime = msgrole.findBinding(WSDLTools.MimeMultipartRelatedBinding) | |||
| if mime is not None: | |||
| raise ValueError, 'Mime bindings are not supported.' | |||
| else: | |||
| for item in msgrole.findBindings(WSDLTools.SoapHeaderBinding): | |||
| part = messages[item.message].parts[item.part] | |||
| header = callinfo.addInHeaderInfo( | |||
| part.name, | |||
| part.element or part.type, | |||
| item.namespace, | |||
| element_type = part.element and 1 or 0 | |||
| ) | |||
| header.encodingStyle = item.encodingStyle | |||
| body = msgrole.findBinding(WSDLTools.SoapBodyBinding) | |||
| if body is None: | |||
| raise ValueError, 'Missing soap:body binding.' | |||
| callinfo.encodingStyle = body.encodingStyle | |||
| callinfo.namespace = body.namespace | |||
| callinfo.use = body.use | |||
| if body.parts is not None: | |||
| parts = [] | |||
| for name in body.parts: | |||
| parts.append(message.parts[name]) | |||
| else: | |||
| parts = message.parts.values() | |||
| for part in parts: | |||
| callinfo.addInParameter( | |||
| part.name, | |||
| part.element or part.type, | |||
| element_type = part.element and 1 or 0 | |||
| ) | |||
| if operation.output is not None: | |||
| message = messages[operation.output.message] | |||
| msgrole = opbinding.output | |||
| mime = msgrole.findBinding(WSDLTools.MimeMultipartRelatedBinding) | |||
| if mime is not None: | |||
| raise ValueError, 'Mime bindings are not supported.' | |||
| else: | |||
| for item in msgrole.findBindings(WSDLTools.SoapHeaderBinding): | |||
| part = messages[item.message].parts[item.part] | |||
| header = callinfo.addOutHeaderInfo( | |||
| part.name, | |||
| part.element or part.type, | |||
| item.namespace, | |||
| element_type = part.element and 1 or 0 | |||
| ) | |||
| header.encodingStyle = item.encodingStyle | |||
| body = msgrole.findBinding(WSDLTools.SoapBodyBinding) | |||
| if body is None: | |||
| raise ValueError, 'Missing soap:body binding.' | |||
| callinfo.encodingStyle = body.encodingStyle | |||
| callinfo.namespace = body.namespace | |||
| callinfo.use = body.use | |||
| if body.parts is not None: | |||
| parts = [] | |||
| for name in body.parts: | |||
| parts.append(message.parts[name]) | |||
| else: | |||
| parts = message.parts.values() | |||
| if parts: | |||
| callinfo.setReturnParameter( | |||
| parts[0].name, | |||
| parts[0].element or parts[0].type, | |||
| element_type = parts[0].element and 1 or 0 | |||
| ) | |||
| for part in parts[1:]: | |||
| callinfo.addOutParameter( | |||
| part.name, | |||
| part.element or part.type, | |||
| element_type = part.element and 1 or 0 | |||
| ) | |||
| return callinfo | |||
| class ServiceProxy: | |||
| """A ServiceProxy provides a convenient way to call a remote web | |||
| service that is described with WSDL. The proxy exposes methods | |||
| that reflect the methods of the remote web service.""" | |||
| def __init__(self, wsdl, service=None, port=None, tracefile=None, | |||
| typesmodule=None, nsdict=None): | |||
| if not hasattr(wsdl, 'targetNamespace'): | |||
| wsdl = WSDLTools.WSDLReader().loadFromURL(wsdl) | |||
| # for item in wsdl.types.items(): | |||
| # self._serializer.loadSchema(item) | |||
| self._service = wsdl.services[service or 0] | |||
| self.__doc__ = self._service.documentation | |||
| self._port = self._service.ports[port or 0] | |||
| self._name = self._service.name | |||
| self._wsdl = wsdl | |||
| self._tracefile = tracefile | |||
| self._typesmodule = typesmodule | |||
| self._nsdict = nsdict | |||
| binding = self._port.getBinding() | |||
| portType = binding.getPortType() | |||
| for item in portType.operations: | |||
| callinfo = callInfoFromWSDL(self._port, item.name) | |||
| method = MethodProxy(self, callinfo) | |||
| setattr(self, item.name, method) | |||
| def _call(self, name, *args, **kwargs): | |||
| """Call the named remote web service method.""" | |||
| if len(args) and len(kwargs): | |||
| raise TypeError( | |||
| 'Use positional or keyword argument only.' | |||
| ) | |||
| callinfo = getattr(self, name).callinfo | |||
| url = callinfo.location | |||
| (protocol, host, uri, query, fragment, identifier) = urlparse(url) | |||
| port = 80 | |||
| if host.find(':') >= 0: | |||
| host, port = host.split(':') | |||
| params = callinfo.getInParameters() | |||
| host = str(host) | |||
| port = str(port) | |||
| binding = Binding(host=host, tracefile=self._tracefile, | |||
| ssl=(protocol == 'https'), | |||
| port=port, url=uri, typesmodule=self._typesmodule, | |||
| nsdict=self._nsdict) | |||
| apply(getattr(binding, callinfo.methodName), args) | |||
| #print binding.ReceiveRaw() | |||
| return binding.Receive() | |||
| class MethodProxy: | |||
| """ """ | |||
| def __init__(self, parent, callinfo): | |||
| self.__name__ = callinfo.methodName | |||
| self.__doc__ = callinfo.documentation | |||
| self.callinfo = callinfo | |||
| self.parent = weakref.ref(parent) | |||
| def __call__(self, *args, **kwargs): | |||
| return self.parent()._call(self.__name__, *args, **kwargs) | |||
| @@ -0,0 +1,176 @@ | |||
| """Based on code from timeout_socket.py, with some tweaks for compatibility. | |||
| These tweaks should really be rolled back into timeout_socket, but it's | |||
| not totally clear who is maintaining it at this point. In the meantime, | |||
| we'll use a different module name for our tweaked version to avoid any | |||
| confusion. | |||
| The original timeout_socket is by: | |||
| Scott Cotton <scott@chronis.pobox.com> | |||
| Lloyd Zusman <ljz@asfast.com> | |||
| Phil Mayes <pmayes@olivebr.com> | |||
| Piers Lauder <piers@cs.su.oz.au> | |||
| Radovan Garabik <garabik@melkor.dnp.fmph.uniba.sk> | |||
| """ | |||
| import string, socket, select, errno | |||
| WSAEINVAL = getattr(errno, 'WSAEINVAL', 10022) | |||
| class TimeoutSocket: | |||
| """A socket imposter that supports timeout limits.""" | |||
| def __init__(self, timeout=20, sock=None): | |||
| self.timeout = float(timeout) | |||
| self.inbuf = '' | |||
| if sock is None: | |||
| sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |||
| self.sock = sock | |||
| self.sock.setblocking(0) | |||
| self._rbuf = '' | |||
| self._wbuf = '' | |||
| def __getattr__(self, name): | |||
| # Delegate to real socket attributes. | |||
| return getattr(self.sock, name) | |||
| def connect(self, *addr): | |||
| timeout = self.timeout | |||
| sock = self.sock | |||
| try: | |||
| # Non-blocking mode | |||
| sock.setblocking(0) | |||
| apply(sock.connect, addr) | |||
| sock.setblocking(timeout != 0) | |||
| return 1 | |||
| except socket.error,why: | |||
| if not timeout: | |||
| raise | |||
| sock.setblocking(1) | |||
| if len(why.args) == 1: | |||
| code = 0 | |||
| else: | |||
| code, why = why | |||
| if code not in ( | |||
| errno.EINPROGRESS, errno.EALREADY, errno.EWOULDBLOCK | |||
| ): | |||
| raise | |||
| r,w,e = select.select([],[sock],[],timeout) | |||
| if w: | |||
| try: | |||
| apply(sock.connect, addr) | |||
| return 1 | |||
| except socket.error,why: | |||
| if len(why.args) == 1: | |||
| code = 0 | |||
| else: | |||
| code, why = why | |||
| if code in (errno.EISCONN, WSAEINVAL): | |||
| return 1 | |||
| raise | |||
| raise TimeoutError('socket connect() timeout.') | |||
| def send(self, data, flags=0): | |||
| total = len(data) | |||
| next = 0 | |||
| while 1: | |||
| r, w, e = select.select([],[self.sock], [], self.timeout) | |||
| if w: | |||
| buff = data[next:next + 8192] | |||
| sent = self.sock.send(buff, flags) | |||
| next = next + sent | |||
| if next == total: | |||
| return total | |||
| continue | |||
| raise TimeoutError('socket send() timeout.') | |||
| def recv(self, amt, flags=0): | |||
| if select.select([self.sock], [], [], self.timeout)[0]: | |||
| return self.sock.recv(amt, flags) | |||
| raise TimeoutError('socket recv() timeout.') | |||
| buffsize = 4096 | |||
| handles = 1 | |||
| def makefile(self, mode="r", buffsize=-1): | |||
| self.handles = self.handles + 1 | |||
| self.mode = mode | |||
| return self | |||
| def close(self): | |||
| self.handles = self.handles - 1 | |||
| if self.handles == 0 and self.sock.fileno() >= 0: | |||
| self.sock.close() | |||
| def read(self, n=-1): | |||
| if not isinstance(n, type(1)): | |||
| n = -1 | |||
| if n >= 0: | |||
| k = len(self._rbuf) | |||
| if n <= k: | |||
| data = self._rbuf[:n] | |||
| self._rbuf = self._rbuf[n:] | |||
| return data | |||
| n = n - k | |||
| L = [self._rbuf] | |||
| self._rbuf = "" | |||
| while n > 0: | |||
| new = self.recv(max(n, self.buffsize)) | |||
| if not new: break | |||
| k = len(new) | |||
| if k > n: | |||
| L.append(new[:n]) | |||
| self._rbuf = new[n:] | |||
| break | |||
| L.append(new) | |||
| n = n - k | |||
| return "".join(L) | |||
| k = max(4096, self.buffsize) | |||
| L = [self._rbuf] | |||
| self._rbuf = "" | |||
| while 1: | |||
| new = self.recv(k) | |||
| if not new: break | |||
| L.append(new) | |||
| k = min(k*2, 1024**2) | |||
| return "".join(L) | |||
| def readline(self, limit=-1): | |||
| data = "" | |||
| i = self._rbuf.find('\n') | |||
| while i < 0 and not (0 < limit <= len(self._rbuf)): | |||
| new = self.recv(self.buffsize) | |||
| if not new: break | |||
| i = new.find('\n') | |||
| if i >= 0: i = i + len(self._rbuf) | |||
| self._rbuf = self._rbuf + new | |||
| if i < 0: i = len(self._rbuf) | |||
| else: i = i+1 | |||
| if 0 <= limit < len(self._rbuf): i = limit | |||
| data, self._rbuf = self._rbuf[:i], self._rbuf[i:] | |||
| return data | |||
| def readlines(self, sizehint = 0): | |||
| total = 0 | |||
| list = [] | |||
| while 1: | |||
| line = self.readline() | |||
| if not line: break | |||
| list.append(line) | |||
| total += len(line) | |||
| if sizehint and total >= sizehint: | |||
| break | |||
| return list | |||
| def writelines(self, list): | |||
| self.send(''.join(list)) | |||
| def write(self, data): | |||
| self.send(data) | |||
| def flush(self): | |||
| pass | |||
| class TimeoutError(Exception): | |||
| pass | |||
| @@ -0,0 +1,753 @@ | |||
| # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved. | |||
| # | |||
| # This software is subject to the provisions of the Zope Public License, | |||
| # Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution. | |||
| # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED | |||
| # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |||
| # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS | |||
| # FOR A PARTICULAR PURPOSE. | |||
| from string import join, strip, split | |||
| from UserDict import UserDict | |||
| from StringIO import StringIO | |||
| import xml.dom.minidom, weakref | |||
| import string, httplib, smtplib, urllib, socket | |||
| from TimeoutSocket import TimeoutSocket, TimeoutError | |||
| from StringIO import StringIO | |||
| from urlparse import urlparse | |||
| from httplib import HTTPConnection, HTTPSConnection | |||
| class HTTPResponse: | |||
| """Captures the information in an HTTP response message.""" | |||
| def __init__(self, response): | |||
| self.status = response.status | |||
| self.reason = response.reason | |||
| self.headers = response.msg | |||
| self.body = response.read() or None | |||
| response.close() | |||
| class TimeoutHTTP(HTTPConnection): | |||
| """A custom http connection object that supports socket timeout.""" | |||
| def __init__(self, host, port=None, timeout=20): | |||
| HTTPConnection.__init__(self, host, port) | |||
| self.timeout = timeout | |||
| def connect(self): | |||
| self.sock = TimeoutSocket(self.timeout) | |||
| self.sock.connect((self.host, self.port)) | |||
| class TimeoutHTTPS(HTTPSConnection): | |||
| """A custom https object that supports socket timeout. Note that this | |||
| is not really complete. The builtin SSL support in the Python socket | |||
| module requires a real socket (type) to be passed in to be hooked to | |||
| SSL. That means our fake socket won't work and our timeout hacks are | |||
| bypassed for send and recv calls. Since our hack _is_ in place at | |||
| connect() time, it should at least provide some timeout protection.""" | |||
| def __init__(self, host, port=None, timeout=20, **kwargs): | |||
| if not hasattr(socket, 'ssl'): | |||
| raise ValueError( | |||
| 'This Python installation does not have SSL support.' | |||
| ) | |||
| HTTPSConnection.__init__(self, str(host), port, **kwargs) | |||
| self.timeout = timeout | |||
| def connect(self): | |||
| sock = TimeoutSocket(self.timeout) | |||
| sock.connect((self.host, self.port)) | |||
| realsock = getattr(sock.sock, '_sock', sock.sock) | |||
| ssl = socket.ssl(realsock, self.key_file, self.cert_file) | |||
| self.sock = httplib.FakeSocket(sock, ssl) | |||
| def urlopen(url, timeout=20, redirects=None): | |||
| """A minimal urlopen replacement hack that supports timeouts for http. | |||
| Note that this supports GET only.""" | |||
| scheme, host, path, params, query, frag = urlparse(url) | |||
| if not scheme in ('http', 'https'): | |||
| return urllib.urlopen(url) | |||
| if params: path = '%s;%s' % (path, params) | |||
| if query: path = '%s?%s' % (path, query) | |||
| if frag: path = '%s#%s' % (path, frag) | |||
| if scheme == 'https': | |||
| if not hasattr(socket, 'ssl'): | |||
| raise ValueError( | |||
| 'This Python installation does not have SSL support.' | |||
| ) | |||
| conn = TimeoutHTTPS(host, None, timeout) | |||
| else: | |||
| conn = TimeoutHTTP(host, None, timeout) | |||
| conn.putrequest('GET', path) | |||
| conn.putheader('Connection', 'close') | |||
| conn.endheaders() | |||
| response = None | |||
| while 1: | |||
| response = conn.getresponse() | |||
| if response.status != 100: | |||
| break | |||
| conn._HTTPConnection__state = httplib._CS_REQ_SENT | |||
| conn._HTTPConnection__response = None | |||
| status = response.status | |||
| # If we get an HTTP redirect, we will follow it automatically. | |||
| if status >= 300 and status < 400: | |||
| location = response.msg.getheader('location') | |||
| if location is not None: | |||
| response.close() | |||
| if redirects is not None and redirects.has_key(location): | |||
| raise RecursionError( | |||
| 'Circular HTTP redirection detected.' | |||
| ) | |||
| if redirects is None: | |||
| redirects = {} | |||
| redirects[location] = 1 | |||
| return urlopen(location, timeout, redirects) | |||
| raise HTTPResponse(response) | |||
| if not (status >= 200 and status < 300): | |||
| raise HTTPResponse(response) | |||
| body = StringIO(response.read()) | |||
| response.close() | |||
| return body | |||
| class DOM: | |||
| """The DOM singleton defines a number of XML related constants and | |||
| provides a number of utility methods for DOM related tasks. It | |||
| also provides some basic abstractions so that the rest of the | |||
| package need not care about actual DOM implementation in use.""" | |||
| # Namespace stuff related to the SOAP specification. | |||
| NS_SOAP_ENV_1_1 = 'http://schemas.xmlsoap.org/soap/envelope/' | |||
| NS_SOAP_ENC_1_1 = 'http://schemas.xmlsoap.org/soap/encoding/' | |||
| NS_SOAP_ENV_1_2 = 'http://www.w3.org/2001/06/soap-envelope' | |||
| NS_SOAP_ENC_1_2 = 'http://www.w3.org/2001/06/soap-encoding' | |||
| NS_SOAP_ENV_ALL = (NS_SOAP_ENV_1_1, NS_SOAP_ENV_1_2) | |||
| NS_SOAP_ENC_ALL = (NS_SOAP_ENC_1_1, NS_SOAP_ENC_1_2) | |||
| NS_SOAP_ENV = NS_SOAP_ENV_1_1 | |||
| NS_SOAP_ENC = NS_SOAP_ENC_1_1 | |||
| _soap_uri_mapping = { | |||
| NS_SOAP_ENV_1_1 : '1.1', | |||
| NS_SOAP_ENV_1_2 : '1.2', | |||
| } | |||
| SOAP_ACTOR_NEXT_1_1 = 'http://schemas.xmlsoap.org/soap/actor/next' | |||
| SOAP_ACTOR_NEXT_1_2 = 'http://www.w3.org/2001/06/soap-envelope/actor/next' | |||
| SOAP_ACTOR_NEXT_ALL = (SOAP_ACTOR_NEXT_1_1, SOAP_ACTOR_NEXT_1_2) | |||
| def SOAPUriToVersion(self, uri): | |||
| """Return the SOAP version related to an envelope uri.""" | |||
| value = self._soap_uri_mapping.get(uri) | |||
| if value is not None: | |||
| return value | |||
| raise ValueError( | |||
| 'Unsupported SOAP envelope uri: %s' % uri | |||
| ) | |||
| def GetSOAPEnvUri(self, version): | |||
| """Return the appropriate SOAP envelope uri for a given | |||
| human-friendly SOAP version string (e.g. '1.1').""" | |||
| attrname = 'NS_SOAP_ENV_%s' % join(split(version, '.'), '_') | |||
| value = getattr(self, attrname, None) | |||
| if value is not None: | |||
| return value | |||
| raise ValueError( | |||
| 'Unsupported SOAP version: %s' % version | |||
| ) | |||
| def GetSOAPEncUri(self, version): | |||
| """Return the appropriate SOAP encoding uri for a given | |||
| human-friendly SOAP version string (e.g. '1.1').""" | |||
| attrname = 'NS_SOAP_ENC_%s' % join(split(version, '.'), '_') | |||
| value = getattr(self, attrname, None) | |||
| if value is not None: | |||
| return value | |||
| raise ValueError( | |||
| 'Unsupported SOAP version: %s' % version | |||
| ) | |||
| def GetSOAPActorNextUri(self, version): | |||
| """Return the right special next-actor uri for a given | |||
| human-friendly SOAP version string (e.g. '1.1').""" | |||
| attrname = 'SOAP_ACTOR_NEXT_%s' % join(split(version, '.'), '_') | |||
| value = getattr(self, attrname, None) | |||
| if value is not None: | |||
| return value | |||
| raise ValueError( | |||
| 'Unsupported SOAP version: %s' % version | |||
| ) | |||
| # Namespace stuff related to XML Schema. | |||
| NS_XSD_99 = 'http://www.w3.org/1999/XMLSchema' | |||
| NS_XSI_99 = 'http://www.w3.org/1999/XMLSchema-instance' | |||
| NS_XSD_00 = 'http://www.w3.org/2000/10/XMLSchema' | |||
| NS_XSI_00 = 'http://www.w3.org/2000/10/XMLSchema-instance' | |||
| NS_XSD_01 = 'http://www.w3.org/2001/XMLSchema' | |||
| NS_XSI_01 = 'http://www.w3.org/2001/XMLSchema-instance' | |||
| NS_XSD_ALL = (NS_XSD_99, NS_XSD_00, NS_XSD_01) | |||
| NS_XSI_ALL = (NS_XSI_99, NS_XSI_00, NS_XSI_01) | |||
| NS_XSD = NS_XSD_01 | |||
| NS_XSI = NS_XSI_01 | |||
| _xsd_uri_mapping = { | |||
| NS_XSD_99 : NS_XSI_99, | |||
| NS_XSD_00 : NS_XSI_00, | |||
| NS_XSD_01 : NS_XSI_01, | |||
| } | |||
| for key, value in _xsd_uri_mapping.items(): | |||
| _xsd_uri_mapping[value] = key | |||
| def InstanceUriForSchemaUri(self, uri): | |||
| """Return the appropriate matching XML Schema instance uri for | |||
| the given XML Schema namespace uri.""" | |||
| return self._xsd_uri_mapping.get(uri) | |||
| def SchemaUriForInstanceUri(self, uri): | |||
| """Return the appropriate matching XML Schema namespace uri for | |||
| the given XML Schema instance namespace uri.""" | |||
| return self._xsd_uri_mapping.get(uri) | |||
| # Namespace stuff related to WSDL. | |||
| NS_WSDL_1_1 = 'http://schemas.xmlsoap.org/wsdl/' | |||
| NS_WSDL_ALL = (NS_WSDL_1_1,) | |||
| NS_WSDL = NS_WSDL_1_1 | |||
| NS_SOAP_BINDING_1_1 = 'http://schemas.xmlsoap.org/wsdl/soap/' | |||
| NS_HTTP_BINDING_1_1 = 'http://schemas.xmlsoap.org/wsdl/http/' | |||
| NS_MIME_BINDING_1_1 = 'http://schemas.xmlsoap.org/wsdl/mime/' | |||
| NS_SOAP_BINDING_ALL = (NS_SOAP_BINDING_1_1,) | |||
| NS_HTTP_BINDING_ALL = (NS_HTTP_BINDING_1_1,) | |||
| NS_MIME_BINDING_ALL = (NS_MIME_BINDING_1_1,) | |||
| NS_SOAP_BINDING = NS_SOAP_BINDING_1_1 | |||
| NS_HTTP_BINDING = NS_HTTP_BINDING_1_1 | |||
| NS_MIME_BINDING = NS_MIME_BINDING_1_1 | |||
| NS_SOAP_HTTP_1_1 = 'http://schemas.xmlsoap.org/soap/http' | |||
| NS_SOAP_HTTP_ALL = (NS_SOAP_HTTP_1_1,) | |||
| NS_SOAP_HTTP = NS_SOAP_HTTP_1_1 | |||
| _wsdl_uri_mapping = { | |||
| NS_WSDL_1_1 : '1.1', | |||
| } | |||
| def WSDLUriToVersion(self, uri): | |||
| """Return the WSDL version related to a WSDL namespace uri.""" | |||
| value = self._wsdl_uri_mapping.get(uri) | |||
| if value is not None: | |||
| return value | |||
| raise ValueError( | |||
| 'Unsupported SOAP envelope uri: %s' % uri | |||
| ) | |||
| def GetWSDLUri(self, version): | |||
| attr = 'NS_WSDL_%s' % join(split(version, '.'), '_') | |||
| value = getattr(self, attr, None) | |||
| if value is not None: | |||
| return value | |||
| raise ValueError( | |||
| 'Unsupported WSDL version: %s' % version | |||
| ) | |||
| def GetWSDLSoapBindingUri(self, version): | |||
| attr = 'NS_SOAP_BINDING_%s' % join(split(version, '.'), '_') | |||
| value = getattr(self, attr, None) | |||
| if value is not None: | |||
| return value | |||
| raise ValueError( | |||
| 'Unsupported WSDL version: %s' % version | |||
| ) | |||
| def GetWSDLHttpBindingUri(self, version): | |||
| attr = 'NS_HTTP_BINDING_%s' % join(split(version, '.'), '_') | |||
| value = getattr(self, attr, None) | |||
| if value is not None: | |||
| return value | |||
| raise ValueError( | |||
| 'Unsupported WSDL version: %s' % version | |||
| ) | |||
| def GetWSDLMimeBindingUri(self, version): | |||
| attr = 'NS_MIME_BINDING_%s' % join(split(version, '.'), '_') | |||
| value = getattr(self, attr, None) | |||
| if value is not None: | |||
| return value | |||
| raise ValueError( | |||
| 'Unsupported WSDL version: %s' % version | |||
| ) | |||
| def GetWSDLHttpTransportUri(self, version): | |||
| attr = 'NS_SOAP_HTTP_%s' % join(split(version, '.'), '_') | |||
| value = getattr(self, attr, None) | |||
| if value is not None: | |||
| return value | |||
| raise ValueError( | |||
| 'Unsupported WSDL version: %s' % version | |||
| ) | |||
| # Other xml namespace constants. | |||
| NS_XMLNS = 'http://www.w3.org/2000/xmlns/' | |||
| def isElement(self, node, name, nsuri=None): | |||
| """Return true if the given node is an element with the given | |||
| name and optional namespace uri.""" | |||
| if node.nodeType == node.ELEMENT_NODE: | |||
| return 0 | |||
| return node.localName == name and \ | |||
| (nsuri is None or self.nsUriMatch(node.namespaceURI, nsuri)) | |||
| def getElement(self, node, name, nsuri=None, default=join): | |||
| """Return the first child of node with a matching name and | |||
| namespace uri, or the default if one is provided.""" | |||
| nsmatch = self.nsUriMatch | |||
| ELEMENT_NODE = node.ELEMENT_NODE | |||
| for child in node.childNodes: | |||
| if child.nodeType == ELEMENT_NODE: | |||
| if ((child.localName == name or name is None) and | |||
| (nsuri is None or nsmatch(child.namespaceURI, nsuri)) | |||
| ): | |||
| return child | |||
| if default is not join: | |||
| return default | |||
| raise KeyError, name | |||
| def getElementById(self, node, id, default=join): | |||
| """Return the first child of node matching an id reference.""" | |||
| attrget = self.getAttr | |||
| ELEMENT_NODE = node.ELEMENT_NODE | |||
| for child in node.childNodes: | |||
| if child.nodeType == ELEMENT_NODE: | |||
| if attrget(child, 'id') == id: | |||
| return child | |||
| if default is not join: | |||
| return default | |||
| raise KeyError, name | |||
| def getMappingById(self, document, depth=None, element=None, | |||
| mapping=None, level=1): | |||
| """Create an id -> element mapping of those elements within a | |||
| document that define an id attribute. The depth of the search | |||
| may be controlled by using the (1-based) depth argument.""" | |||
| if document is not None: | |||
| element = document.documentElement | |||
| mapping = {} | |||
| attr = element._attrs.get('id', None) | |||
| if attr is not None: | |||
| mapping[attr.value] = element | |||
| if depth is None or depth > level: | |||
| level = level + 1 | |||
| ELEMENT_NODE = element.ELEMENT_NODE | |||
| for child in element.childNodes: | |||
| if child.nodeType == ELEMENT_NODE: | |||
| self.getMappingById(None, depth, child, mapping, level) | |||
| return mapping | |||
| def getElements(self, node, name, nsuri=None): | |||
| """Return a sequence of the child elements of the given node that | |||
| match the given name and optional namespace uri.""" | |||
| nsmatch = self.nsUriMatch | |||
| result = [] | |||
| ELEMENT_NODE = node.ELEMENT_NODE | |||
| for child in node.childNodes: | |||
| if child.nodeType == ELEMENT_NODE: | |||
| if ((child.localName == name or name is None) and ( | |||
| (nsuri is None) or nsmatch(child.namespaceURI, nsuri))): | |||
| result.append(child) | |||
| return result | |||
| def hasAttr(self, node, name, nsuri=None): | |||
| """Return true if element has attribute with the given name and | |||
| optional nsuri. If nsuri is not specified, returns true if an | |||
| attribute exists with the given name with any namespace.""" | |||
| if nsuri is None: | |||
| if node._attrs.has_key(name): | |||
| return 1 | |||
| for item in node._attrsNS.keys(): | |||
| if item[1] == name: | |||
| return 1 | |||
| return 0 | |||
| return node.attrsNS.has_key((nsuri, name)) | |||
| def getAttr(self, node, name, nsuri=None, default=join): | |||
| """Return the value of the attribute named 'name' with the | |||
| optional nsuri, or the default if one is specified. If | |||
| nsuri is not specified, an attribute that matches the | |||
| given name will be returned regardless of namespace.""" | |||
| if nsuri is None: | |||
| result = node._attrs.get(name, None) | |||
| if result is None: | |||
| for item in node._attrsNS.keys(): | |||
| if item[1] == name: | |||
| result = node._attrsNS[item] | |||
| break | |||
| else: | |||
| result = node._attrsNS.get((nsuri, name), None) | |||
| if result is not None: | |||
| return result.value | |||
| if default is not join: | |||
| return default | |||
| return '' | |||
| def getElementText(self, node, preserve_ws=None): | |||
| """Return the text value of an xml element node. Leading and trailing | |||
| whitespace is stripped from the value unless the preserve_ws flag | |||
| is passed with a true value.""" | |||
| result = [] | |||
| for child in node.childNodes: | |||
| nodetype = child.nodeType | |||
| if nodetype == child.TEXT_NODE or \ | |||
| nodetype == child.CDATA_SECTION_NODE: | |||
| result.append(child.nodeValue) | |||
| value = join(result, '') | |||
| if preserve_ws is None: | |||
| value = strip(value) | |||
| return value | |||
| def findNamespaceURI(self, prefix, node): | |||
| """Find a namespace uri given a prefix and a context node.""" | |||
| attrkey = (self.NS_XMLNS, prefix) | |||
| DOCUMENT_NODE = node.DOCUMENT_NODE | |||
| ELEMENT_NODE = node.ELEMENT_NODE | |||
| while 1: | |||
| if node.nodeType != ELEMENT_NODE: | |||
| node = node.parentNode | |||
| continue | |||
| result = node._attrsNS.get(attrkey, None) | |||
| if result is not None: | |||
| return result.value | |||
| if hasattr(node, '__imported__'): | |||
| raise DOMException('Value for prefix %s not found.' % prefix) | |||
| node = node.parentNode | |||
| if node.nodeType == DOCUMENT_NODE: | |||
| raise DOMException('Value for prefix %s not found.' % prefix) | |||
| def findDefaultNS(self, node): | |||
| """Return the current default namespace uri for the given node.""" | |||
| attrkey = (self.NS_XMLNS, 'xmlns') | |||
| DOCUMENT_NODE = node.DOCUMENT_NODE | |||
| ELEMENT_NODE = node.ELEMENT_NODE | |||
| while 1: | |||
| if node.nodeType != ELEMENT_NODE: | |||
| node = node.parentNode | |||
| continue | |||
| result = node._attrsNS.get(attrkey, None) | |||
| if result is not None: | |||
| return result.value | |||
| if hasattr(node, '__imported__'): | |||
| raise DOMException('Cannot determine default namespace.') | |||
| node = node.parentNode | |||
| if node.nodeType == DOCUMENT_NODE: | |||
| raise DOMException('Cannot determine default namespace.') | |||
| def findTargetNS(self, node): | |||
| """Return the defined target namespace uri for the given node.""" | |||
| attrget = self.getAttr | |||
| attrkey = (self.NS_XMLNS, 'xmlns') | |||
| DOCUMENT_NODE = node.DOCUMENT_NODE | |||
| ELEMENT_NODE = node.ELEMENT_NODE | |||
| while 1: | |||
| if node.nodeType != ELEMENT_NODE: | |||
| node = node.parentNode | |||
| continue | |||
| result = attrget(node, 'targetNamespace', default=None) | |||
| if result is not None: | |||
| return result | |||
| node = node.parentNode | |||
| if node.nodeType == DOCUMENT_NODE: | |||
| raise DOMException('Cannot determine target namespace.') | |||
| def getTypeRef(self, element): | |||
| """Return (namespaceURI, name) for a type attribue of the given | |||
| element, or None if the element does not have a type attribute.""" | |||
| typeattr = self.getAttr(element, 'type', default=None) | |||
| if typeattr is None: | |||
| return None | |||
| parts = typeattr.split(':', 1) | |||
| if len(parts) == 2: | |||
| nsuri = self.findNamespaceURI(parts[0], element) | |||
| else: | |||
| nsuri = self.findDefaultNS(element) | |||
| return (nsuri, parts[1]) | |||
| def importNode(self, document, node, deep=0): | |||
| """Implements (well enough for our purposes) DOM node import.""" | |||
| nodetype = node.nodeType | |||
| if nodetype in (node.DOCUMENT_NODE, node.DOCUMENT_TYPE_NODE): | |||
| raise DOMException('Illegal node type for importNode') | |||
| if nodetype == node.ENTITY_REFERENCE_NODE: | |||
| deep = 0 | |||
| clone = node.cloneNode(deep) | |||
| self._setOwnerDoc(document, clone) | |||
| clone.__imported__ = 1 | |||
| return clone | |||
| def _setOwnerDoc(self, document, node): | |||
| node.ownerDocument = document | |||
| for child in node.childNodes: | |||
| self._setOwnerDoc(document, child) | |||
| def nsUriMatch(self, value, wanted, strict=0, tt=type(())): | |||
| """Return a true value if two namespace uri values match.""" | |||
| if value == wanted or (type(wanted) is tt) and value in wanted: | |||
| return 1 | |||
| if not strict: | |||
| wanted = type(wanted) is tt and wanted or (wanted,) | |||
| value = value[-1:] != '/' and value or value[:-1] | |||
| for item in wanted: | |||
| if item == value or item[:-1] == value: | |||
| return 1 | |||
| return 0 | |||
| def createDocument(self, nsuri, qname, doctype=None): | |||
| """Create a new writable DOM document object.""" | |||
| impl = xml.dom.minidom.getDOMImplementation() | |||
| return impl.createDocument(nsuri, qname, doctype) | |||
| def loadDocument(self, data): | |||
| """Load an xml file from a file-like object and return a DOM | |||
| document instance.""" | |||
| return xml.dom.minidom.parse(data) | |||
| def loadFromURL(self, url): | |||
| """Load an xml file from a URL and return a DOM document.""" | |||
| file = urlopen(url) | |||
| try: result = self.loadDocument(file) | |||
| finally: file.close() | |||
| return result | |||
| class DOMException(Exception): | |||
| pass | |||
| DOM = DOM() | |||
| class Collection(UserDict): | |||
| """Helper class for maintaining ordered named collections.""" | |||
| def __init__(self, parent): | |||
| UserDict.__init__(self) | |||
| self.parent = weakref.ref(parent) | |||
| self.list = [] | |||
| def __getitem__(self, key): | |||
| if type(key) is type(1): | |||
| return self.list[key] | |||
| return self.data[key] | |||
| def __setitem__(self, key, item): | |||
| item.parent = weakref.ref(self) | |||
| self.list.append(item) | |||
| self.data[key] = item | |||
| def keys(self): | |||
| return map(lambda i: i.name, self.list) | |||
| def items(self): | |||
| return map(lambda i: (i.name, i), self.list) | |||
| def values(self): | |||
| return self.list | |||
| # This is a runtime guerilla patch for pulldom (used by minidom) so | |||
| # that xml namespace declaration attributes are not lost in parsing. | |||
| # We need them to do correct QName linking for XML Schema and WSDL. | |||
| # The patch has been submitted to SF for the next Python version. | |||
| from xml.dom.pulldom import PullDOM, START_ELEMENT | |||
| if 1: | |||
| def startPrefixMapping(self, prefix, uri): | |||
| if not hasattr(self, '_xmlns_attrs'): | |||
| self._xmlns_attrs = [] | |||
| self._xmlns_attrs.append((prefix or 'xmlns', uri)) | |||
| self._ns_contexts.append(self._current_context.copy()) | |||
| self._current_context[uri] = prefix or '' | |||
| PullDOM.startPrefixMapping = startPrefixMapping | |||
| def startElementNS(self, name, tagName , attrs): | |||
| # Retrieve xml namespace declaration attributes. | |||
| xmlns_uri = 'http://www.w3.org/2000/xmlns/' | |||
| xmlns_attrs = getattr(self, '_xmlns_attrs', None) | |||
| if xmlns_attrs is not None: | |||
| for aname, value in xmlns_attrs: | |||
| attrs._attrs[(xmlns_uri, aname)] = value | |||
| self._xmlns_attrs = [] | |||
| uri, localname = name | |||
| if uri: | |||
| # When using namespaces, the reader may or may not | |||
| # provide us with the original name. If not, create | |||
| # *a* valid tagName from the current context. | |||
| if tagName is None: | |||
| prefix = self._current_context[uri] | |||
| if prefix: | |||
| tagName = prefix + ":" + localname | |||
| else: | |||
| tagName = localname | |||
| if self.document: | |||
| node = self.document.createElementNS(uri, tagName) | |||
| else: | |||
| node = self.buildDocument(uri, tagName) | |||
| else: | |||
| # When the tagname is not prefixed, it just appears as | |||
| # localname | |||
| if self.document: | |||
| node = self.document.createElement(localname) | |||
| else: | |||
| node = self.buildDocument(None, localname) | |||
| for aname,value in attrs.items(): | |||
| a_uri, a_localname = aname | |||
| if a_uri == xmlns_uri: | |||
| if a_localname == 'xmlns': | |||
| qname = a_localname | |||
| else: | |||
| qname = 'xmlns:' + a_localname | |||
| attr = self.document.createAttributeNS(a_uri, qname) | |||
| node.setAttributeNodeNS(attr) | |||
| elif a_uri: | |||
| prefix = self._current_context[a_uri] | |||
| if prefix: | |||
| qname = prefix + ":" + a_localname | |||
| else: | |||
| qname = a_localname | |||
| attr = self.document.createAttributeNS(a_uri, qname) | |||
| node.setAttributeNodeNS(attr) | |||
| else: | |||
| attr = self.document.createAttribute(a_localname) | |||
| node.setAttributeNode(attr) | |||
| attr.value = value | |||
| self.lastEvent[1] = [(START_ELEMENT, node), None] | |||
| self.lastEvent = self.lastEvent[1] | |||
| self.push(node) | |||
| PullDOM.startElementNS = startElementNS | |||
| # | |||
| # This is a runtime guerilla patch for minidom so | |||
| # that xmlns prefixed attributes dont raise AttributeErrors | |||
| # during cloning. | |||
| # | |||
| # Namespace declarations can appear in any start-tag, must look for xmlns | |||
| # prefixed attribute names during cloning. | |||
| # | |||
| # key (attr.namespaceURI, tag) | |||
| # ('http://www.w3.org/2000/xmlns/', u'xsd') <xml.dom.minidom.Attr instance at 0x82227c4> | |||
| # ('http://www.w3.org/2000/xmlns/', 'xmlns') <xml.dom.minidom.Attr instance at 0x8414b3c> | |||
| # | |||
| # xml.dom.minidom.Attr.nodeName = xmlns:xsd | |||
| # xml.dom.minidom.Attr.value = = http://www.w3.org/2001/XMLSchema | |||
| if 1: | |||
| def _clone_node(node, deep, newOwnerDocument): | |||
| """ | |||
| Clone a node and give it the new owner document. | |||
| Called by Node.cloneNode and Document.importNode | |||
| """ | |||
| if node.ownerDocument.isSameNode(newOwnerDocument): | |||
| operation = xml.dom.UserDataHandler.NODE_CLONED | |||
| else: | |||
| operation = xml.dom.UserDataHandler.NODE_IMPORTED | |||
| if node.nodeType == xml.dom.minidom.Node.ELEMENT_NODE: | |||
| clone = newOwnerDocument.createElementNS(node.namespaceURI, | |||
| node.nodeName) | |||
| for attr in node.attributes.values(): | |||
| clone.setAttributeNS(attr.namespaceURI, attr.nodeName, attr.value) | |||
| prefix, tag = xml.dom.minidom._nssplit(attr.nodeName) | |||
| if prefix == 'xmlns': | |||
| a = clone.getAttributeNodeNS(attr.namespaceURI, tag) | |||
| else: | |||
| a = clone.getAttributeNodeNS(attr.namespaceURI, attr.nodeName) | |||
| a.specified = attr.specified | |||
| if deep: | |||
| for child in node.childNodes: | |||
| c = xml.dom.minidom._clone_node(child, deep, newOwnerDocument) | |||
| clone.appendChild(c) | |||
| elif node.nodeType == xml.dom.minidom.Node.DOCUMENT_FRAGMENT_NODE: | |||
| clone = newOwnerDocument.createDocumentFragment() | |||
| if deep: | |||
| for child in node.childNodes: | |||
| c = xml.dom.minidom._clone_node(child, deep, newOwnerDocument) | |||
| clone.appendChild(c) | |||
| elif node.nodeType == xml.dom.minidom.Node.TEXT_NODE: | |||
| clone = newOwnerDocument.createTextNode(node.data) | |||
| elif node.nodeType == xml.dom.minidom.Node.CDATA_SECTION_NODE: | |||
| clone = newOwnerDocument.createCDATASection(node.data) | |||
| elif node.nodeType == xml.dom.minidom.Node.PROCESSING_INSTRUCTION_NODE: | |||
| clone = newOwnerDocument.createProcessingInstruction(node.target, | |||
| node.data) | |||
| elif node.nodeType == xml.dom.minidom.Node.COMMENT_NODE: | |||
| clone = newOwnerDocument.createComment(node.data) | |||
| elif node.nodeType == xml.dom.minidom.Node.ATTRIBUTE_NODE: | |||
| clone = newOwnerDocument.createAttributeNS(node.namespaceURI, | |||
| node.nodeName) | |||
| clone.specified = True | |||
| clone.value = node.value | |||
| elif node.nodeType == xml.dom.minidom.Node.DOCUMENT_TYPE_NODE: | |||
| assert node.ownerDocument is not newOwnerDocument | |||
| operation = xml.dom.UserDataHandler.NODE_IMPORTED | |||
| clone = newOwnerDocument.implementation.createDocumentType( | |||
| node.name, node.publicId, node.systemId) | |||
| clone.ownerDocument = newOwnerDocument | |||
| if deep: | |||
| clone.entities._seq = [] | |||
| clone.notations._seq = [] | |||
| for n in node.notations._seq: | |||
| notation = Notation(n.nodeName, n.publicId, n.systemId) | |||
| notation.ownerDocument = newOwnerDocument | |||
| clone.notations._seq.append(notation) | |||
| if hasattr(n, '_call_user_data_handler'): | |||
| n._call_user_data_handler(operation, n, notation) | |||
| for e in node.entities._seq: | |||
| entity = Entity(e.nodeName, e.publicId, e.systemId, | |||
| e.notationName) | |||
| entity.actualEncoding = e.actualEncoding | |||
| entity.encoding = e.encoding | |||
| entity.version = e.version | |||
| entity.ownerDocument = newOwnerDocument | |||
| clone.entities._seq.append(entity) | |||
| if hasattr(e, '_call_user_data_handler'): | |||
| e._call_user_data_handler(operation, n, entity) | |||
| else: | |||
| # Note the cloning of Document and DocumentType nodes is | |||
| # implemenetation specific. minidom handles those cases | |||
| # directly in the cloneNode() methods. | |||
| raise xml.dom.NotSupportedErr("Cannot clone node %s" % repr(node)) | |||
| # Check for _call_user_data_handler() since this could conceivably | |||
| # used with other DOM implementations (one of the FourThought | |||
| # DOMs, perhaps?). | |||
| if hasattr(node, '_call_user_data_handler'): | |||
| node._call_user_data_handler(operation, node, clone) | |||
| return clone | |||
| xml.dom.minidom._clone_node = _clone_node | |||
| @@ -0,0 +1,951 @@ | |||
| # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved. | |||
| # | |||
| # This software is subject to the provisions of the Zope Public License, | |||
| # Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution. | |||
| # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED | |||
| # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |||
| # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS | |||
| # FOR A PARTICULAR PURPOSE. | |||
| from Utility import DOM, Collection | |||
| from XMLSchema import XMLSchema | |||
| from StringIO import StringIO | |||
| import urllib | |||
| class WSDLReader: | |||
| """A WSDLReader creates WSDL instances from urls and xml data.""" | |||
| # Custom subclasses of WSDLReader may wish to implement a caching | |||
| # strategy or other optimizations. Because application needs vary | |||
| # so widely, we don't try to provide any caching by default. | |||
| def loadFromStream(self, file): | |||
| """Return a WSDL instance loaded from a file object.""" | |||
| document = DOM.loadDocument(file) | |||
| wsdl = WSDL() | |||
| wsdl.load(document) | |||
| return wsdl | |||
| def loadFromURL(self, url): | |||
| """Return a WSDL instance loaded from the given url.""" | |||
| document = DOM.loadFromURL(url) | |||
| wsdl = WSDL() | |||
| wsdl.location = url | |||
| wsdl.load(document) | |||
| return wsdl | |||
| def loadFromString(self, data): | |||
| """Return a WSDL instance loaded from an xml string.""" | |||
| return self.loadFromStream(StringIO(data)) | |||
| def loadFromFile(self, filename): | |||
| """Return a WSDL instance loaded from the given file.""" | |||
| file = open(filename, 'rb') | |||
| try: | |||
| wsdl = self.loadFromStream(file) | |||
| finally: | |||
| file.close() | |||
| return wsdl | |||
| class WSDL: | |||
| """A WSDL object models a WSDL service description. WSDL objects | |||
| may be created manually or loaded from an xml representation | |||
| using a WSDLReader instance.""" | |||
| def __init__(self, targetNamespace=None): | |||
| self.targetNamespace = targetNamespace or 'urn:this-document.wsdl' | |||
| self.documentation = '' | |||
| self.location = None | |||
| self.document = None | |||
| self.name = None | |||
| self.services = Collection(self) | |||
| self.messages = Collection(self) | |||
| self.portTypes = Collection(self) | |||
| self.bindings = Collection(self) | |||
| self.imports = Collection(self) | |||
| self.types = Types(self) | |||
| self.extensions = [] | |||
| def __del__(self): | |||
| if self.document is not None: | |||
| self.document.unlink() | |||
| self.document = None | |||
| version = '1.1' | |||
| def addService(self, name, documentation=''): | |||
| if self.services.has_key(name): | |||
| raise WSDLError( | |||
| 'Duplicate service element: %s' % name | |||
| ) | |||
| item = Service(name, documentation) | |||
| self.services[name] = item | |||
| return item | |||
| def addMessage(self, name, documentation=''): | |||
| if self.messages.has_key(name): | |||
| raise WSDLError( | |||
| 'Duplicate message element: %s.' % name | |||
| ) | |||
| item = Message(name, documentation) | |||
| self.messages[name] = item | |||
| return item | |||
| def addPortType(self, name, documentation=''): | |||
| if self.portTypes.has_key(name): | |||
| raise WSDLError( | |||
| 'Duplicate portType element: name' | |||
| ) | |||
| item = PortType(name, documentation) | |||
| self.portTypes[name] = item | |||
| return item | |||
| def addBinding(self, name, type, documentation=''): | |||
| if self.bindings.has_key(name): | |||
| raise WSDLError( | |||
| 'Duplicate binding element: %s' % name | |||
| ) | |||
| item = Binding(name, type, documentation) | |||
| self.bindings[name] = item | |||
| return item | |||
| def addImport(self, namespace, location): | |||
| item = ImportElement(namespace, location) | |||
| self.imports[namespace] = item | |||
| return item | |||
| def load(self, document): | |||
| # We save a reference to the DOM document to ensure that elements | |||
| # saved as "extensions" will continue to have a meaningful context | |||
| # for things like namespace references. The lifetime of the DOM | |||
| # document is bound to the lifetime of the WSDL instance. | |||
| self.document = document | |||
| definitions = DOM.getElement(document, 'definitions', None, None) | |||
| if definitions is None: | |||
| raise WSDLError( | |||
| 'Missing <definitions> element.' | |||
| ) | |||
| self.version = DOM.WSDLUriToVersion(definitions.namespaceURI) | |||
| NS_WSDL = DOM.GetWSDLUri(self.version) | |||
| self.targetNamespace = DOM.getAttr(definitions, 'targetNamespace', | |||
| None, None) | |||
| self.name = DOM.getAttr(definitions, 'name', None, None) | |||
| self.documentation = GetDocumentation(definitions) | |||
| # Resolve (recursively) any import elements in the document. | |||
| imported = {} | |||
| while 1: | |||
| imports = [] | |||
| for element in DOM.getElements(definitions, 'import', NS_WSDL): | |||
| location = DOM.getAttr(element, 'location') | |||
| if not imported.has_key(location): | |||
| imports.append(element) | |||
| if not imports: | |||
| break | |||
| for element in imports: | |||
| self._import(document, element) | |||
| location = DOM.getAttr(element, 'location') | |||
| imported[location] = 1 | |||
| for element in DOM.getElements(definitions, None, None): | |||
| localName = element.localName | |||
| if not DOM.nsUriMatch(element.namespaceURI, NS_WSDL): | |||
| if localName == 'schema': | |||
| self.types.addSchema(XMLSchema(element)) | |||
| else: | |||
| self.extensions.append(element) | |||
| continue | |||
| elif localName == 'message': | |||
| name = DOM.getAttr(element, 'name') | |||
| docs = GetDocumentation(element) | |||
| message = self.addMessage(name, docs) | |||
| parts = DOM.getElements(element, 'part', NS_WSDL) | |||
| message.load(parts) | |||
| continue | |||
| elif localName == 'portType': | |||
| name = DOM.getAttr(element, 'name') | |||
| docs = GetDocumentation(element) | |||
| ptype = self.addPortType(name, docs) | |||
| operations = DOM.getElements(element, 'operation', NS_WSDL) | |||
| ptype.load(operations) | |||
| continue | |||
| elif localName == 'binding': | |||
| name = DOM.getAttr(element, 'name') | |||
| type = DOM.getAttr(element, 'type', default=None) | |||
| if type is None: | |||
| raise WSDLError( | |||
| 'Missing type attribute for binding %s.' % name | |||
| ) | |||
| type = type.split(':', 1)[-1] | |||
| docs = GetDocumentation(element) | |||
| binding = self.addBinding(name, type, docs) | |||
| operations = DOM.getElements(element, 'operation', NS_WSDL) | |||
| binding.load(operations) | |||
| binding.load_ex(GetExtensions(element)) | |||
| continue | |||
| elif localName == 'service': | |||
| name = DOM.getAttr(element, 'name') | |||
| docs = GetDocumentation(element) | |||
| service = self.addService(name, docs) | |||
| ports = DOM.getElements(element, 'port', NS_WSDL) | |||
| service.load(ports) | |||
| service.load_ex(GetExtensions(element)) | |||
| continue | |||
| elif localName == 'types': | |||
| self.types.documentation = GetDocumentation(element) | |||
| for item in DOM.getElements(element, None, None): | |||
| if item.localName == 'schema': | |||
| self.types.addSchema(XMLSchema(item)) | |||
| else: | |||
| self.types.addExtension(item) | |||
| continue | |||
| def _import(self, document, element): | |||
| namespace = DOM.getAttr(element, 'namespace', default=None) | |||
| location = DOM.getAttr(element, 'location', default=None) | |||
| if namespace is None or location is None: | |||
| raise WSDLError( | |||
| 'Invalid import element (missing namespace or location).' | |||
| ) | |||
| # Sort-of support relative locations to simplify unit testing. The | |||
| # WSDL specification actually doesn't allow relative URLs, so its | |||
| # ok that this only works with urls relative to the initial document. | |||
| location = urllib.basejoin(self.location, location) | |||
| obimport = self.addImport(namespace, location) | |||
| obimport._loaded = 1 | |||
| importdoc = DOM.loadFromURL(location) | |||
| try: | |||
| if location.find('#') > -1: | |||
| idref = location.split('#')[-1] | |||
| imported = DOM.getElementById(importdoc, idref) | |||
| else: | |||
| imported = importdoc.documentElement | |||
| if imported is None: | |||
| raise WSDLError( | |||
| 'Import target element not found for: %s' % location | |||
| ) | |||
| imported_tns = DOM.findTargetNS(imported) | |||
| if imported_tns != namespace: | |||
| return | |||
| if imported.localName == 'definitions': | |||
| imported_nodes = imported.childNodes | |||
| else: | |||
| imported_nodes = [imported] | |||
| parent = element.parentNode | |||
| for node in imported_nodes: | |||
| if node.nodeType != node.ELEMENT_NODE: | |||
| continue | |||
| child = DOM.importNode(document, node, 1) | |||
| parent.appendChild(child) | |||
| child.setAttribute('targetNamespace', namespace) | |||
| attrsNS = imported._attrsNS | |||
| for attrkey in attrsNS.keys(): | |||
| if attrkey[0] == DOM.NS_XMLNS: | |||
| attr = attrsNS[attrkey].cloneNode(1) | |||
| child.setAttributeNode(attr) | |||
| finally: | |||
| importdoc.unlink() | |||
| class Element: | |||
| """A class that provides common functions for WSDL element classes.""" | |||
| def __init__(self, name=None, documentation=''): | |||
| self.name = name | |||
| self.documentation = documentation | |||
| self.extensions = [] | |||
| def addExtension(self, item): | |||
| self.extensions.append(item) | |||
| class ImportElement(Element): | |||
| def __init__(self, namespace, location): | |||
| self.namespace = namespace | |||
| self.location = location | |||
| _loaded = None | |||
| class Types(Collection): | |||
| def __init__(self, parent): | |||
| Collection.__init__(self, parent) | |||
| self.documentation = '' | |||
| self.extensions = [] | |||
| def addSchema(self, schema): | |||
| name = schema.targetNamespace | |||
| self[name] = schema | |||
| return schema | |||
| def keys(self): | |||
| return map(lambda i: i.targetNamespace, self.list) | |||
| def items(self): | |||
| return map(lambda i: i.targetNamespace, self.list) | |||
| def addExtension(self, item): | |||
| self.extensions.append(item) | |||
| class Message(Element): | |||
| def __init__(self, name, documentation=''): | |||
| Element.__init__(self, name, documentation) | |||
| self.parts = Collection(self) | |||
| def addPart(self, name, type=None, element=None): | |||
| if self.parts.has_key(name): | |||
| raise WSDLError( | |||
| 'Duplicate message part element: %s' % name | |||
| ) | |||
| if type is None and element is None: | |||
| raise WSDLError( | |||
| 'Missing type or element attribute for part: %s' % name | |||
| ) | |||
| item = MessagePart(name) | |||
| item.element = element | |||
| item.type = type | |||
| self.parts[name] = item | |||
| return item | |||
| def load(self, elements): | |||
| for element in elements: | |||
| name = DOM.getAttr(element, 'name') | |||
| part = MessagePart(name) | |||
| self.parts[name] = part | |||
| elemref = DOM.getAttr(element, 'element', default=None) | |||
| typeref = DOM.getAttr(element, 'type', default=None) | |||
| if typeref is None and elemref is None: | |||
| raise WSDLError( | |||
| 'No type or element attribute for part: %s' % name | |||
| ) | |||
| if typeref is not None: | |||
| part.type = ParseTypeRef(typeref, element) | |||
| if elemref is not None: | |||
| part.element = ParseTypeRef(elemref, element) | |||
| class MessagePart(Element): | |||
| def __init__(self, name): | |||
| Element.__init__(self, name, '') | |||
| self.element = None | |||
| self.type = None | |||
| class PortType(Element): | |||
| def __init__(self, name, documentation=''): | |||
| Element.__init__(self, name, documentation) | |||
| self.operations = Collection(self) | |||
| def getWSDL(self): | |||
| return self.parent().parent() | |||
| def addOperation(self, name, documentation='', parameterOrder=None): | |||
| item = Operation(name, documentation, parameterOrder) | |||
| self.operations[name] = item | |||
| return item | |||
| def load(self, elements): | |||
| for element in elements: | |||
| name = DOM.getAttr(element, 'name') | |||
| docs = GetDocumentation(element) | |||
| param_order = DOM.getAttr(element, 'parameterOrder', default=None) | |||
| if param_order is not None: | |||
| param_order = param_order.split(' ') | |||
| operation = self.addOperation(name, docs, param_order) | |||
| item = DOM.getElement(element, 'input', None, None) | |||
| if item is not None: | |||
| name = DOM.getAttr(item, 'name') | |||
| docs = GetDocumentation(item) | |||
| msgref = DOM.getAttr(item, 'message') | |||
| message = msgref.split(':', 1)[-1] | |||
| operation.setInput(message, name, docs) | |||
| item = DOM.getElement(element, 'output', None, None) | |||
| if item is not None: | |||
| name = DOM.getAttr(item, 'name') | |||
| docs = GetDocumentation(item) | |||
| msgref = DOM.getAttr(item, 'message') | |||
| message = msgref.split(':', 1)[-1] | |||
| operation.setOutput(message, name, docs) | |||
| for item in DOM.getElements(element, 'fault', None): | |||
| name = DOM.getAttr(item, 'name') | |||
| docs = GetDocumentation(item) | |||
| msgref = DOM.getAttr(item, 'message') | |||
| message = msgref.split(':', 1)[-1] | |||
| operation.addFault(message, name, docs) | |||
| class Operation(Element): | |||
| def __init__(self, name, documentation='', parameterOrder=None): | |||
| Element.__init__(self, name, documentation) | |||
| self.parameterOrder = parameterOrder | |||
| self.faults = Collection(self) | |||
| self.input = None | |||
| self.output = None | |||
| def getPortType(self): | |||
| return self.parent().parent() | |||
| def getInputMessage(self): | |||
| if self.input is None: | |||
| return None | |||
| wsdl = self.getPortType().getWSDL() | |||
| return wsdl.messages[self.input.message] | |||
| def getOutputMessage(self): | |||
| if self.output is None: | |||
| return None | |||
| wsdl = self.getPortType().getWSDL() | |||
| return wsdl.messages[self.output.message] | |||
| def getFaultMessage(self, name): | |||
| wsdl = self.getPortType().getWSDL() | |||
| return wsdl.messages[self.faults[name].message] | |||
| def addFault(self, name, message, documentation=''): | |||
| if self.faults.has_key(name): | |||
| raise WSDLError( | |||
| 'Duplicate fault element: %s' % name | |||
| ) | |||
| item = MessageRole('fault', message, name, documentation) | |||
| self.faults[name] = item | |||
| return item | |||
| def setInput(self, message, name='', documentation=''): | |||
| self.input = MessageRole('input', message, name, documentation) | |||
| return self.input | |||
| def setOutput(self, message, name='', documentation=''): | |||
| self.output = MessageRole('output', message, name, documentation) | |||
| return self.output | |||
| class MessageRole(Element): | |||
| def __init__(self, type, message, name='', documentation=''): | |||
| Element.__init__(self, name, documentation) | |||
| self.message = message | |||
| self.type = type | |||
| class Binding(Element): | |||
| def __init__(self, name, type, documentation=''): | |||
| Element.__init__(self, name, documentation) | |||
| self.operations = Collection(self) | |||
| self.type = type | |||
| def getWSDL(self): | |||
| """Return the WSDL object that contains this binding.""" | |||
| return self.parent().parent() | |||
| def getPortType(self): | |||
| """Return the PortType object associated with this binding.""" | |||
| return self.getWSDL().portTypes[self.type] | |||
| def findBinding(self, kind): | |||
| for item in self.extensions: | |||
| if isinstance(item, kind): | |||
| return item | |||
| return None | |||
| def findBindings(self, kind): | |||
| return [ item for item in self.extensions if isinstance(item, kind) ] | |||
| def addOperationBinding(self, name, documentation=''): | |||
| item = OperationBinding(name, documentation) | |||
| self.operations[name] = item | |||
| return item | |||
| def load(self, elements): | |||
| for element in elements: | |||
| name = DOM.getAttr(element, 'name') | |||
| docs = GetDocumentation(element) | |||
| opbinding = self.addOperationBinding(name, docs) | |||
| opbinding.load_ex(GetExtensions(element)) | |||
| item = DOM.getElement(element, 'input', None, None) | |||
| if item is not None: | |||
| mbinding = MessageRoleBinding('input') | |||
| mbinding.documentation = GetDocumentation(item) | |||
| opbinding.input = mbinding | |||
| mbinding.load_ex(GetExtensions(item)) | |||
| item = DOM.getElement(element, 'output', None, None) | |||
| if item is not None: | |||
| mbinding = MessageRoleBinding('output') | |||
| mbinding.documentation = GetDocumentation(item) | |||
| opbinding.output = mbinding | |||
| mbinding.load_ex(GetExtensions(item)) | |||
| for item in DOM.getElements(element, 'fault', None): | |||
| name = DOM.getAttr(item, 'name') | |||
| mbinding = MessageRoleBinding('fault', name) | |||
| mbinding.documentation = GetDocumentation(item) | |||
| opbinding.faults[name] = mbinding | |||
| mbinding.load_ex(GetExtensions(item)) | |||
| def load_ex(self, elements): | |||
| for e in elements: | |||
| ns, name = e.namespaceURI, e.localName | |||
| if ns in DOM.NS_SOAP_BINDING_ALL and name == 'binding': | |||
| transport = DOM.getAttr(e, 'transport', default=None) | |||
| style = DOM.getAttr(e, 'style', default='document') | |||
| ob = SoapBinding(transport, style) | |||
| self.addExtension(ob) | |||
| continue | |||
| elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'binding': | |||
| verb = DOM.getAttr(e, 'verb') | |||
| ob = HttpBinding(verb) | |||
| self.addExtension(ob) | |||
| continue | |||
| else: | |||
| self.addExtension(e) | |||
| class OperationBinding(Element): | |||
| def __init__(self, name, documentation=''): | |||
| Element.__init__(self, name, documentation) | |||
| self.input = None | |||
| self.output = None | |||
| self.faults = Collection(self) | |||
| def getBinding(self): | |||
| """Return the parent Binding object of the operation binding.""" | |||
| return self.parent().parent() | |||
| def getOperation(self): | |||
| """Return the abstract Operation associated with this binding.""" | |||
| return self.getBinding().getPortType().operations[self.name] | |||
| def findBinding(self, kind): | |||
| for item in self.extensions: | |||
| if isinstance(item, kind): | |||
| return item | |||
| return None | |||
| def findBindings(self, kind): | |||
| return [ item for item in self.extensions if isinstance(item, kind) ] | |||
| def addInputBinding(self, binding): | |||
| if self.input is None: | |||
| self.input = MessageRoleBinding('input') | |||
| self.input.addExtension(binding) | |||
| return binding | |||
| def addOutputBinding(self, binding): | |||
| if self.output is None: | |||
| self.output = MessageRoleBinding('output') | |||
| self.output.addExtension(binding) | |||
| return binding | |||
| def addFaultBinding(self, name, binding): | |||
| fault = self.get(name, None) | |||
| if fault is None: | |||
| fault = MessageRoleBinding('fault', name) | |||
| fault.addExtension(binding) | |||
| return binding | |||
| def load_ex(self, elements): | |||
| for e in elements: | |||
| ns, name = e.namespaceURI, e.localName | |||
| if ns in DOM.NS_SOAP_BINDING_ALL and name == 'operation': | |||
| soapaction = DOM.getAttr(e, 'soapAction', default=None) | |||
| style = DOM.getAttr(e, 'style', default=None) | |||
| ob = SoapOperationBinding(soapaction, style) | |||
| self.addExtension(ob) | |||
| continue | |||
| elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'operation': | |||
| location = DOM.getAttr(e, 'location') | |||
| ob = HttpOperationBinding(location) | |||
| self.addExtension(ob) | |||
| continue | |||
| else: | |||
| self.addExtension(e) | |||
| class MessageRoleBinding(Element): | |||
| def __init__(self, type, name='', documentation=''): | |||
| Element.__init__(self, name, documentation) | |||
| self.type = type | |||
| def findBinding(self, kind): | |||
| for item in self.extensions: | |||
| if isinstance(item, kind): | |||
| return item | |||
| return None | |||
| def findBindings(self, kind): | |||
| return [ item for item in self.extensions if isinstance(item, kind) ] | |||
| def load_ex(self, elements): | |||
| for e in elements: | |||
| ns, name = e.namespaceURI, e.localName | |||
| if ns in DOM.NS_SOAP_BINDING_ALL and name == 'body': | |||
| encstyle = DOM.getAttr(e, 'encodingStyle', default=None) | |||
| namespace = DOM.getAttr(e, 'namespace', default=None) | |||
| parts = DOM.getAttr(e, 'parts', default=None) | |||
| use = DOM.getAttr(e, 'use', default=None) | |||
| if use is None: | |||
| raise WSDLError( | |||
| 'Invalid soap:body binding element.' | |||
| ) | |||
| ob = SoapBodyBinding(use, namespace, encstyle, parts) | |||
| self.addExtension(ob) | |||
| continue | |||
| elif ns in DOM.NS_SOAP_BINDING_ALL and name == 'fault': | |||
| encstyle = DOM.getAttr(e, 'encodingStyle', default=None) | |||
| namespace = DOM.getAttr(e, 'namespace', default=None) | |||
| name = DOM.getAttr(e, 'name', default=None) | |||
| use = DOM.getAttr(e, 'use', default=None) | |||
| if use is None or name is None: | |||
| raise WSDLError( | |||
| 'Invalid soap:fault binding element.' | |||
| ) | |||
| ob = SoapFaultBinding(name, use, namespace, encstyle) | |||
| self.addExtension(ob) | |||
| continue | |||
| elif ns in DOM.NS_SOAP_BINDING_ALL and name in ( | |||
| 'header', 'headerfault' | |||
| ): | |||
| encstyle = DOM.getAttr(e, 'encodingStyle', default=None) | |||
| namespace = DOM.getAttr(e, 'namespace', default=None) | |||
| message = DOM.getAttr(e, 'message') | |||
| part = DOM.getAttr(e, 'part') | |||
| use = DOM.getAttr(e, 'use') | |||
| if name == 'header': | |||
| _class = SoapHeaderBinding | |||
| else: | |||
| _class = SoapHeaderFaultBinding | |||
| ob = _class(message, part, use, namespace, encstyle) | |||
| self.addExtension(ob) | |||
| continue | |||
| elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'urlReplacement': | |||
| ob = HttpUrlReplacementBinding() | |||
| self.addExtension(ob) | |||
| continue | |||
| elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'urlEncoded': | |||
| ob = HttpUrlEncodedBinding() | |||
| self.addExtension(ob) | |||
| continue | |||
| elif ns in DOM.NS_MIME_BINDING_ALL and name == 'multipartRelated': | |||
| ob = MimeMultipartRelatedBinding() | |||
| self.addExtension(ob) | |||
| ob.load_ex(GetExtensions(e)) | |||
| continue | |||
| elif ns in DOM.NS_MIME_BINDING_ALL and name == 'content': | |||
| part = DOM.getAttr(e, 'part', default=None) | |||
| type = DOM.getAttr(e, 'type', default=None) | |||
| ob = MimeContentBinding(part, type) | |||
| self.addExtension(ob) | |||
| continue | |||
| elif ns in DOM.NS_MIME_BINDING_ALL and name == 'mimeXml': | |||
| part = DOM.getAttr(e, 'part', default=None) | |||
| ob = MimeXmlBinding(part) | |||
| self.addExtension(ob) | |||
| continue | |||
| else: | |||
| self.addExtension(e) | |||
| class Service(Element): | |||
| def __init__(self, name, documentation=''): | |||
| Element.__init__(self, name, documentation) | |||
| self.ports = Collection(self) | |||
| def getWSDL(self): | |||
| return self.parent().parent() | |||
| def addPort(self, name, binding, documentation=''): | |||
| item = Port(name, binding, documentation) | |||
| self.ports[name] = item | |||
| return item | |||
| def load(self, elements): | |||
| for element in elements: | |||
| name = DOM.getAttr(element, 'name', default=None) | |||
| docs = GetDocumentation(element) | |||
| binding = DOM.getAttr(element, 'binding', default=None) | |||
| if name is None or binding is None: | |||
| raise WSDLError( | |||
| 'Invalid port element.' | |||
| ) | |||
| binding = binding.split(':', 1)[-1] | |||
| port = self.addPort(name, binding, docs) | |||
| port.load_ex(GetExtensions(element)) | |||
| def load_ex(self, elements): | |||
| for e in elements: | |||
| self.addExtension(e) | |||
| class Port(Element): | |||
| def __init__(self, name, binding, documentation=''): | |||
| Element.__init__(self, name, documentation) | |||
| self.binding = binding | |||
| def getService(self): | |||
| """Return the Service object associated with this port.""" | |||
| return self.parent().parent() | |||
| def getBinding(self): | |||
| """Return the Binding object that is referenced by this port.""" | |||
| wsdl = self.getService().getWSDL() | |||
| return wsdl.bindings[self.binding] | |||
| def getPortType(self): | |||
| """Return the PortType object that is referenced by this port.""" | |||
| wsdl = self.getService().getWSDL() | |||
| binding = wsdl.bindings[self.binding] | |||
| return wsdl.portTypes[binding.type] | |||
| def getAddressBinding(self): | |||
| """A convenience method to obtain the extension element used | |||
| as the address binding for the port, or None if undefined.""" | |||
| for item in self.extensions: | |||
| if isinstance(item, SoapAddressBinding) or \ | |||
| isinstance(item, HttpAddressBinding): | |||
| return item | |||
| raise WSDLError( | |||
| 'No address binding found in port.' | |||
| ) | |||
| def load_ex(self, elements): | |||
| for e in elements: | |||
| ns, name = e.namespaceURI, e.localName | |||
| if ns in DOM.NS_SOAP_BINDING_ALL and name == 'address': | |||
| location = DOM.getAttr(e, 'location', default=None) | |||
| ob = SoapAddressBinding(location) | |||
| self.addExtension(ob) | |||
| continue | |||
| elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'address': | |||
| location = DOM.getAttr(e, 'location', default=None) | |||
| ob = HttpAddressBinding(location) | |||
| self.addExtension(ob) | |||
| continue | |||
| else: | |||
| self.addExtension(e) | |||
| class SoapBinding: | |||
| def __init__(self, transport, style='rpc'): | |||
| self.transport = transport | |||
| self.style = style | |||
| class SoapAddressBinding: | |||
| def __init__(self, location): | |||
| self.location = location | |||
| class SoapOperationBinding: | |||
| def __init__(self, soapAction=None, style=None): | |||
| self.soapAction = soapAction | |||
| self.style = style | |||
| class SoapBodyBinding: | |||
| def __init__(self, use, namespace=None, encodingStyle=None, parts=None): | |||
| if not use in ('literal', 'encoded'): | |||
| raise WSDLError( | |||
| 'Invalid use attribute value: %s' % use | |||
| ) | |||
| self.encodingStyle = encodingStyle | |||
| self.namespace = namespace | |||
| if type(parts) in (type(''), type(u'')): | |||
| raise WSDLError( | |||
| 'The parts argument must be a sequence.' | |||
| ) | |||
| self.parts = parts | |||
| self.use = use | |||
| class SoapFaultBinding: | |||
| def __init__(self, name, use, namespace=None, encodingStyle=None): | |||
| if not use in ('literal', 'encoded'): | |||
| raise WSDLError( | |||
| 'Invalid use attribute value: %s' % use | |||
| ) | |||
| self.encodingStyle = encodingStyle | |||
| self.namespace = namespace | |||
| self.name = name | |||
| self.use = use | |||
| class SoapHeaderBinding: | |||
| def __init__(self, message, part, use, namespace=None, encodingStyle=None): | |||
| if not use in ('literal', 'encoded'): | |||
| raise WSDLError( | |||
| 'Invalid use attribute value: %s' % use | |||
| ) | |||
| self.encodingStyle = encodingStyle | |||
| self.namespace = namespace | |||
| self.message = message | |||
| self.part = part | |||
| self.use = use | |||
| tagname = 'header' | |||
| class SoapHeaderFaultBinding(SoapHeaderBinding): | |||
| tagname = 'headerfault' | |||
| class HttpBinding: | |||
| def __init__(self, verb): | |||
| self.verb = verb | |||
| class HttpAddressBinding: | |||
| def __init__(self, location): | |||
| self.location = location | |||
| class HttpOperationBinding: | |||
| def __init__(self, location): | |||
| self.location = location | |||
| class HttpUrlReplacementBinding: | |||
| pass | |||
| class HttpUrlEncodedBinding: | |||
| pass | |||
| class MimeContentBinding: | |||
| def __init__(self, part=None, type=None): | |||
| self.part = part | |||
| self.type = type | |||
| class MimeXmlBinding: | |||
| def __init__(self, part=None): | |||
| self.part = part | |||
| class MimeMultipartRelatedBinding: | |||
| def __init__(self): | |||
| self.parts = [] | |||
| def load_ex(self, elements): | |||
| for e in elements: | |||
| ns, name = e.namespaceURI, e.localName | |||
| if ns in DOM.NS_MIME_BINDING_ALL and name == 'part': | |||
| self.parts.append(MimePartBinding()) | |||
| continue | |||
| class MimePartBinding: | |||
| def __init__(self): | |||
| self.items = [] | |||
| def load_ex(self, elements): | |||
| for e in elements: | |||
| ns, name = e.namespaceURI, e.localName | |||
| if ns in DOM.NS_MIME_BINDING_ALL and name == 'content': | |||
| part = DOM.getAttr(e, 'part', default=None) | |||
| type = DOM.getAttr(e, 'type', default=None) | |||
| ob = MimeContentBinding(part, type) | |||
| self.items.append(ob) | |||
| continue | |||
| elif ns in DOM.NS_MIME_BINDING_ALL and name == 'mimeXml': | |||
| part = DOM.getAttr(e, 'part', default=None) | |||
| ob = MimeXmlBinding(part) | |||
| self.items.append(ob) | |||
| continue | |||
| elif ns in DOM.NS_SOAP_BINDING_ALL and name == 'body': | |||
| encstyle = DOM.getAttr(e, 'encodingStyle', default=None) | |||
| namespace = DOM.getAttr(e, 'namespace', default=None) | |||
| parts = DOM.getAttr(e, 'parts', default=None) | |||
| use = DOM.getAttr(e, 'use', default=None) | |||
| if use is None: | |||
| raise WSDLError( | |||
| 'Invalid soap:body binding element.' | |||
| ) | |||
| ob = SoapBodyBinding(use, namespace, encstyle, parts) | |||
| self.items.append(ob) | |||
| continue | |||
| class WSDLError(Exception): | |||
| pass | |||
| def DeclareNSPrefix(writer, prefix, nsuri): | |||
| if writer.hasNSPrefix(nsuri): | |||
| return | |||
| writer.declareNSPrefix(prefix, nsuri) | |||
| def ParseTypeRef(value, element): | |||
| parts = value.split(':', 1) | |||
| if len(parts) == 1: | |||
| return (DOM.findTargetNS(element), value) | |||
| nsuri = DOM.findNamespaceURI(parts[0], element) | |||
| return (nsuri, parts[1]) | |||
| def ParseQName(value, element): | |||
| nameref = value.split(':', 1) | |||
| if len(nameref) == 2: | |||
| nsuri = DOM.findNamespaceURI(nameref[0], element) | |||
| name = nameref[-1] | |||
| else: | |||
| nsuri = DOM.findTargetNS(element) | |||
| name = nameref[-1] | |||
| return nsuri, name | |||
| def GetDocumentation(element): | |||
| docnode = DOM.getElement(element, 'documentation', None, None) | |||
| if docnode is not None: | |||
| return DOM.getElementText(docnode) | |||
| return '' | |||
| def GetExtensions(element): | |||
| return [ item for item in DOM.getElements(element, None, None) | |||
| if item.namespaceURI != DOM.NS_WSDL ] | |||
| def FindExtensions(object, kind, t_type=type(())): | |||
| if isinstance(kind, t_type): | |||
| result = [] | |||
| namespaceURI, name = kind | |||
| return [ item for item in object.extensions | |||
| if hasattr(item, 'nodeType') \ | |||
| and DOM.nsUriMatch(namespaceURI, item.namespaceURI) \ | |||
| and item.name == name ] | |||
| return [ item for item in object.extensions if isinstance(item, kind) ] | |||
| def FindExtension(object, kind, t_type=type(())): | |||
| if isinstance(kind, t_type): | |||
| namespaceURI, name = kind | |||
| for item in object.extensions: | |||
| if hasattr(item, 'nodeType') \ | |||
| and DOM.nsUriMatch(namespaceURI, item.namespaceURI) \ | |||
| and item.name == name: | |||
| return item | |||
| else: | |||
| for item in object.extensions: | |||
| if isinstance(item, kind): | |||
| return item | |||
| return None | |||
| @@ -0,0 +1,331 @@ | |||
| # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved. | |||
| # | |||
| # This software is subject to the provisions of the Zope Public License, | |||
| # Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution. | |||
| # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED | |||
| # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |||
| # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS | |||
| # FOR A PARTICULAR PURPOSE. | |||
| import string, types, base64, re | |||
| from Utility import DOM, Collection | |||
| from StringIO import StringIO | |||
| class SchemaReader: | |||
| """A SchemaReader creates XMLSchema objects from urls and xml data.""" | |||
| def loadFromStream(self, file): | |||
| """Return an XMLSchema instance loaded from a file object.""" | |||
| document = DOM.loadDocument(file) | |||
| schema = XMLSchema() | |||
| schema.load(document) | |||
| return schema | |||
| def loadFromString(self, data): | |||
| """Return an XMLSchema instance loaded from an xml string.""" | |||
| return self.loadFromStream(StringIO(data)) | |||
| def loadFromURL(self, url): | |||
| """Return an XMLSchema instance loaded from the given url.""" | |||
| document = DOM.loadFromURL(url) | |||
| schema = XMLSchema() | |||
| schema.location = url | |||
| schema.load(document) | |||
| return schema | |||
| def loadFromFile(self, filename): | |||
| """Return an XMLSchema instance loaded from the given file.""" | |||
| file = open(filename, 'rb') | |||
| try: schema = self.loadFromStream(file) | |||
| finally: file.close() | |||
| return schema | |||
| class XMLSchema: | |||
| # This is temporary, for the benefit of WSDL until the real thing works. | |||
| def __init__(self, element): | |||
| self.targetNamespace = DOM.getAttr(element, 'targetNamespace') | |||
| self.element = element | |||
| class realXMLSchema: | |||
| """A schema is a collection of schema components derived from one | |||
| or more schema documents, that is, one or more <schema> element | |||
| information items. It represents the abstract notion of a schema | |||
| rather than a single schema document (or other representation).""" | |||
| def __init__(self): | |||
| self.simpleTypes = Collection(self) | |||
| self.complexTypes = Collection(self) | |||
| self.attributes = Collection(self) | |||
| self.elements = Collection(self) | |||
| self.attrGroups = Collection(self) | |||
| self.idConstraints=None | |||
| self.modelGroups = None | |||
| self.notations = None | |||
| self.extensions = [] | |||
| targetNamespace = None | |||
| attributeFormDefault = 'unqualified' | |||
| elementFormDefault = 'unqualified' | |||
| blockDefault = None | |||
| finalDefault = None | |||
| location = None | |||
| version = None | |||
| id = None | |||
| def load(self, document): | |||
| if document.nodeType == document.DOCUMENT_NODE: | |||
| schema = DOM.getElement(document, 'schema', None, None) | |||
| else: | |||
| schema = document | |||
| if schema is None: | |||
| raise SchemaError('Missing <schema> element.') | |||
| self.namespace = namespace = schema.namespaceURI | |||
| if not namespace in DOM.NS_XSD_ALL: | |||
| raise SchemaError( | |||
| 'Unknown XML schema namespace: %s.' % self.namespace | |||
| ) | |||
| for attrname in ( | |||
| 'targetNamespace', 'attributeFormDefault', 'elementFormDefault', | |||
| 'blockDefault', 'finalDefault', 'version', 'id' | |||
| ): | |||
| value = DOM.getAttr(schema, attrname, None, None) | |||
| if attr is not None: | |||
| setattr(self, attrname, value) | |||
| # Resolve imports and includes here? | |||
| ## imported = {} | |||
| ## while 1: | |||
| ## imports = [] | |||
| ## for element in DOM.getElements(definitions, 'import', NS_WSDL): | |||
| ## location = DOM.getAttr(element, 'location') | |||
| ## if not imported.has_key(location): | |||
| ## imports.append(element) | |||
| ## if not imports: | |||
| ## break | |||
| ## for element in imports: | |||
| ## self._import(document, element) | |||
| ## imported[location] = 1 | |||
| for element in DOM.getElements(schema, None, None): | |||
| localName = element.localName | |||
| if not DOM.nsUriMatch(element.namespaceURI, namespace): | |||
| self.extensions.append(element) | |||
| continue | |||
| elif localName == 'message': | |||
| name = DOM.getAttr(element, 'name') | |||
| docs = GetDocumentation(element) | |||
| message = self.addMessage(name, docs) | |||
| parts = DOM.getElements(element, 'part', NS_WSDL) | |||
| message.load(parts) | |||
| continue | |||
| def _import(self, document, element): | |||
| namespace = DOM.getAttr(element, 'namespace', default=None) | |||
| location = DOM.getAttr(element, 'location', default=None) | |||
| if namespace is None or location is None: | |||
| raise WSDLError( | |||
| 'Invalid import element (missing namespace or location).' | |||
| ) | |||
| # Sort-of support relative locations to simplify unit testing. The | |||
| # WSDL specification actually doesn't allow relative URLs, so its | |||
| # ok that this only works with urls relative to the initial document. | |||
| location = urllib.basejoin(self.location, location) | |||
| obimport = self.addImport(namespace, location) | |||
| obimport._loaded = 1 | |||
| importdoc = DOM.loadFromURL(location) | |||
| try: | |||
| if location.find('#') > -1: | |||
| idref = location.split('#')[-1] | |||
| imported = DOM.getElementById(importdoc, idref) | |||
| else: | |||
| imported = importdoc.documentElement | |||
| if imported is None: | |||
| raise WSDLError( | |||
| 'Import target element not found for: %s' % location | |||
| ) | |||
| imported_tns = DOM.getAttr(imported, 'targetNamespace') | |||
| importer_tns = namespace | |||
| if imported_tns != importer_tns: | |||
| return | |||
| if imported.localName == 'definitions': | |||
| imported_nodes = imported.childNodes | |||
| else: | |||
| imported_nodes = [imported] | |||
| parent = element.parentNode | |||
| for node in imported_nodes: | |||
| if node.nodeType != node.ELEMENT_NODE: | |||
| continue | |||
| child = DOM.importNode(document, node, 1) | |||
| parent.appendChild(child) | |||
| child.setAttribute('targetNamespace', importer_tns) | |||
| attrsNS = imported._attrsNS | |||
| for attrkey in attrsNS.keys(): | |||
| if attrkey[0] == DOM.NS_XMLNS: | |||
| attr = attrsNS[attrkey].cloneNode(1) | |||
| child.setAttributeNode(attr) | |||
| finally: | |||
| importdoc.unlink() | |||
| class Element: | |||
| """Common base class for element representation classes.""" | |||
| def __init__(self, name=None, documentation=''): | |||
| self.name = name | |||
| self.documentation = documentation | |||
| self.extensions = [] | |||
| def addExtension(self, item): | |||
| self.extensions.append(item) | |||
| class SimpleTypeDefinition: | |||
| """Represents an xml schema simple type definition.""" | |||
| class ComplexTypeDefinition: | |||
| """Represents an xml schema complex type definition.""" | |||
| class AttributeDeclaration: | |||
| """Represents an xml schema attribute declaration.""" | |||
| class ElementDeclaration: | |||
| """Represents an xml schema element declaration.""" | |||
| def __init__(self, name, type=None, targetNamespace=None): | |||
| self.name = name | |||
| targetNamespace = None | |||
| annotation = None | |||
| nillable = 0 | |||
| abstract = 0 | |||
| default = None | |||
| fixed = None | |||
| scope = 'global' | |||
| type = None | |||
| form = 0 | |||
| # Things we will not worry about for now. | |||
| id_constraint_defs = None | |||
| sub_group_exclude = None | |||
| sub_group_affils = None | |||
| disallowed_subs = None | |||
| class AttributeGroupDefinition: | |||
| """Represents an xml schema attribute group definition.""" | |||
| class IdentityConstraintDefinition: | |||
| """Represents an xml schema identity constraint definition.""" | |||
| class ModelGroupDefinition: | |||
| """Represents an xml schema model group definition.""" | |||
| class NotationDeclaration: | |||
| """Represents an xml schema notation declaration.""" | |||
| class Annotation: | |||
| """Represents an xml schema annotation.""" | |||
| class ModelGroup: | |||
| """Represents an xml schema model group.""" | |||
| class Particle: | |||
| """Represents an xml schema particle.""" | |||
| class WildCard: | |||
| """Represents an xml schema wildcard.""" | |||
| class AttributeUse: | |||
| """Represents an xml schema attribute use.""" | |||
| class ElementComponent: | |||
| namespace = '' | |||
| name = '' | |||
| type = None | |||
| form = 'qualified | unqualified' | |||
| scope = 'global or complex def' | |||
| constraint = ('value', 'default | fixed') | |||
| nillable = 0 | |||
| id_constraint_defs = None | |||
| sub_group_affil = None | |||
| sub_group_exclusions = None | |||
| disallowed_subs = 'substitution, extension, restriction' | |||
| abstract = 0 | |||
| minOccurs = 1 | |||
| maxOccurs = 1 | |||
| ref = '' | |||
| class AttributeThing: | |||
| name = '' | |||
| namespace = '' | |||
| typeName = '' | |||
| typeUri = '' | |||
| scope = 'global | local to complex def' | |||
| constraint = ('value:default', 'value:fixed') | |||
| use = 'optional | prohibited | required' | |||
| class ElementDataType: | |||
| namespace = '' | |||
| name = '' | |||
| element_form = 'qualified | unqualified' | |||
| attr_form = None | |||
| type_name = '' | |||
| type_uri = '' | |||
| def __init__(self, name, namespace, type_name, type_uri): | |||
| self.namespace = namespace | |||
| self.name = name | |||
| # type may be anonymous... | |||
| self.type_name = type_name | |||
| self.type_uri = type_uri | |||
| def checkValue(self, value, context): | |||
| # Delegate value checking to the type of the element. | |||
| typeref = (self.type_uri, self.type_name) | |||
| handler = context.serializer.getType(typeref) | |||
| return handler.checkValue(value, context) | |||
| def serialize(self, name, namespace, value, context, **kwargs): | |||
| if context.check_values: | |||
| self.checkValue(value, context) | |||
| # Delegate serialization to the type of the element. | |||
| typeref = (self.type_uri, self.type_name) | |||
| handler = context.serializer.getType(typeref) | |||
| return handler.serialize(self.name, self.namespace, value, context) | |||
| def deserialize(self, element, context): | |||
| if element_is_null(element, context): | |||
| return None | |||
| # Delegate deserialization to the type of the element. | |||
| typeref = (self.type_uri, self.type_name) | |||
| handler = context.serializer.getType(typeref) | |||
| return handler.deserialize(element, context) | |||
| def parse_schema(data): | |||
| targetNS = '' | |||
| attributeFormDefault = 0 | |||
| elementFormDefault = 0 | |||
| blockDefault = '' | |||
| finalDefault = '' | |||
| language = None | |||
| version = None | |||
| id = '' | |||
| @@ -0,0 +1,61 @@ | |||
| Zope Public License (ZPL) Version 2.0 | |||
| ----------------------------------------------- | |||
| This software is Copyright (c) Zope Corporation (tm) and | |||
| Contributors. All rights reserved. | |||
| This license has been certified as open source. It has also | |||
| been designated as GPL compatible by the Free Software | |||
| Foundation (FSF). | |||
| Redistribution and use in source and binary forms, with or | |||
| without modification, are permitted provided that the | |||
| following conditions are met: | |||
| 1. Redistributions in source code must retain the above | |||
| copyright notice, this list of conditions, and the following | |||
| disclaimer. | |||
| 2. Redistributions in binary form must reproduce the above | |||
| copyright notice, this list of conditions, and the following | |||
| disclaimer in the documentation and/or other materials | |||
| provided with the distribution. | |||
| 3. The name Zope Corporation (tm) must not be used to | |||
| endorse or promote products derived from this software | |||
| without prior written permission from Zope Corporation. | |||
| 4. The right to distribute this software or to use it for | |||
| any purpose does not give you the right to use Servicemarks | |||
| (sm) or Trademarks (tm) of Zope Corporation. Use of them is | |||
| covered in a separate agreement (see | |||
| http://www.zope.com/Marks). | |||
| 5. If any files are modified, you must cause the modified | |||
| files to carry prominent notices stating that you changed | |||
| the files and the date of any change. | |||
| Disclaimer | |||
| THIS SOFTWARE IS PROVIDED BY ZOPE CORPORATION ``AS IS'' | |||
| AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT | |||
| NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY | |||
| AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN | |||
| NO EVENT SHALL ZOPE CORPORATION OR ITS CONTRIBUTORS BE | |||
| LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |||
| EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
| LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |||
| LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |||
| HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |||
| CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE | |||
| OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
| SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | |||
| DAMAGE. | |||
| This software consists of contributions made by Zope | |||
| Corporation and many individuals on behalf of Zope | |||
| Corporation. Specific attributions are listed in the | |||
| accompanying credits file. | |||
| @@ -0,0 +1,12 @@ | |||
| # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved. | |||
| # | |||
| # This software is subject to the provisions of the Zope Public License, | |||
| # Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution. | |||
| # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED | |||
| # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |||
| # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS | |||
| # FOR A PARTICULAR PURPOSE. | |||
| """WSDL parsing services package for Web Services for Python.""" | |||
| from ServiceProxy import ServiceProxy | |||