(which doesn't), so that SOAPpy can use wstools without needing ZSI around... which is kinda the point of generic common-code. :) class SOAPCallInfo: class ParameterInfo: class HeaderInfo(ParameterInfo): def callInfoFromWSDL(port, name): Next step is to move what's left of wstools/ServiceProxy.py into the ZSI module (and fix up the imports), so that wstools has *no* soap-stack-specific code in it.main
@@ -7,232 +7,11 @@ | |||
# 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 | |||
from urlparse import urlparse | |||
import weakref | |||
class ServiceProxy: | |||
"""A ServiceProxy provides a convenient way to call a remote web | |||
@@ -949,3 +949,221 @@ def FindExtension(object, kind, t_type=type(())): | |||
if isinstance(item, kind): | |||
return item | |||
return None | |||
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 |