From bb0c69a9038c12200a8a32552c07798c460e8ff3 Mon Sep 17 00:00:00 2001 From: Rich Salz Date: Thu, 24 Apr 2003 17:03:15 +0000 Subject: [PATCH] Import files from (now outdated) ZSI/wsdl directory --- ServiceProxy.py | 303 +++++++++++++++ TimeoutSocket.py | 176 +++++++++ Utility.py | 753 +++++++++++++++++++++++++++++++++++++ WSDLTools.py | 951 +++++++++++++++++++++++++++++++++++++++++++++++ XMLSchema.py | 331 +++++++++++++++++ ZPL | 61 +++ __init__.py | 12 + 7 files changed, 2587 insertions(+) create mode 100755 ServiceProxy.py create mode 100755 TimeoutSocket.py create mode 100755 Utility.py create mode 100755 WSDLTools.py create mode 100755 XMLSchema.py create mode 100644 ZPL create mode 100644 __init__.py diff --git a/ServiceProxy.py b/ServiceProxy.py new file mode 100755 index 0000000..fac3791 --- /dev/null +++ b/ServiceProxy.py @@ -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) diff --git a/TimeoutSocket.py b/TimeoutSocket.py new file mode 100755 index 0000000..d643ead --- /dev/null +++ b/TimeoutSocket.py @@ -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 + Lloyd Zusman + Phil Mayes + Piers Lauder + Radovan Garabik +""" +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 diff --git a/Utility.py b/Utility.py new file mode 100755 index 0000000..905692d --- /dev/null +++ b/Utility.py @@ -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') +# ('http://www.w3.org/2000/xmlns/', 'xmlns') +# +# 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 diff --git a/WSDLTools.py b/WSDLTools.py new file mode 100755 index 0000000..8e4f7f3 --- /dev/null +++ b/WSDLTools.py @@ -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 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 diff --git a/XMLSchema.py b/XMLSchema.py new file mode 100755 index 0000000..c1f5401 --- /dev/null +++ b/XMLSchema.py @@ -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 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 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 = '' diff --git a/ZPL b/ZPL new file mode 100644 index 0000000..ec12c9f --- /dev/null +++ b/ZPL @@ -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. + + diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..67cdf25 --- /dev/null +++ b/__init__.py @@ -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