@@ -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 |