Beispiel #1
0
 def __init__(self, wsdl):
     """
     @param wsdl: A WSDL.
     @type wsdl: L{wsdl.Definitions}
     """
     self.wsdl = wsdl
     self.multiref = MultiRef()
Beispiel #2
0
 def __init__(self, wsdl):
     """
     @param wsdl: A wsdl.
     @type wsdl: L{wsdl.Definitions}
     """
     self.wsdl = wsdl
     self.schema = wsdl.schema
     self.options = Options()
     self.parser = Parser()
     self.multiref = MultiRef()
     self.xcodecs = (
         Unmarshaller(self.schema),
         Marshaller(self.schema),
     )
Beispiel #3
0
 def __init__(self, wsdl):
     """
     @param wsdl: A wsdl.
     @type wsdl: L{wsdl.Definitions}
     """
     self.wsdl = wsdl
     self.multiref = MultiRef()
Beispiel #4
0
 def __init__(self, wsdl):
     """
     @param wsdl: A wsdl.
     @type wsdl: L{wsdl.Definitions}
     """
     self.wsdl = wsdl
     self.schema = wsdl.schema
     self.options = Options()
     self.parser = Parser()
     self.multiref = MultiRef()
Beispiel #5
0
class Binding:
    """
    The soap binding class used to process outgoing and imcoming
    soap messages per the WSDL port binding.
    @cvar replyfilter: The reply filter function.
    @type replyfilter: (lambda s,r: r)
    @ivar wsdl: The wsdl.
    @type wsdl: L{suds.wsdl.Definitions}
    @ivar schema: The collective schema contained within the wsdl.
    @type schema: L{xsd.schema.Schema}
    @ivar options: A dictionary options.
    @type options: L{Options}
    """
    
    replyfilter = (lambda s,r: r)

    def __init__(self, wsdl):
        """
        @param wsdl: A wsdl.
        @type wsdl: L{wsdl.Definitions}
        """
        self.wsdl = wsdl
        self.multiref = MultiRef()
        
    def schema(self):
        return self.wsdl.schema
    
    def options(self):
        return self.wsdl.options
        
    def unmarshaller(self, typed=True):
        """
        Get the appropriate XML decoder.
        @return: Either the (basic|typed) unmarshaller.
        @rtype: L{UmxTyped}
        """
        if typed:
            return UmxTyped(self.schema())
        else:
            return UmxBasic()
        
    def marshaller(self):
        """
        Get the appropriate XML encoder.
        @return: An L{MxLiteral} marshaller.
        @rtype: L{MxLiteral}
        """
        return MxLiteral(self.schema(), self.options().xstq)
    
    def param_defs(self, method):
        """
        Get parameter definitions.  
        Each I{pdef} is a tuple (I{name}, L{xsd.sxbase.SchemaObject})
        @param method: A servic emethod.
        @type method: I{service.Method}
        @return: A collection of parameter definitions
        @rtype: [I{pdef},..]
        """
        raise Exception, 'not implemented'

    def get_message(self, method, args, kwargs):
        """
        Get the soap message for the specified method, args and soapheaders.
        This is the entry point for creating the outbound soap message.
        @param method: The method being invoked.
        @type method: I{service.Method}
        @param args: A list of args for the method invoked.
        @type args: list
        @param kwargs: Named (keyword) args for the method invoked.
        @type kwargs: dict
        @return: The soap envelope.
        @rtype: L{Document}
        """

        content = self.headercontent(method)
        header = self.header(content)
        content = self.bodycontent(method, args, kwargs)
        body = self.body(content)
        env = self.envelope(header, body)
        if self.options().prefixes:
            body.normalizePrefixes()
            env.promotePrefixes()
        else:
            env.refitPrefixes()
        return Document(env)
    
    def get_reply(self, method, reply):
        """
        Process the I{reply} for the specified I{method} by sax parsing the I{reply}
        and then unmarshalling into python object(s).
        @param method: The name of the invoked method.
        @type method: str
        @param reply: The reply XML received after invoking the specified method.
        @type reply: str
        @return: The unmarshalled reply.  The returned value is an L{Object} for a
            I{list} depending on whether the service returns a single object or a 
            collection.
        @rtype: tuple ( L{Element}, L{Object} )
        """
        reply = self.replyfilter(reply)
        try:
            reply = reply[reply.index("<soap:Envelope"):reply.index("</soap:Envelope>")+16]
        except:
            pass
        
        sax = Parser()
        replyroot = sax.parse(string=reply)
        plugins = PluginContainer(self.options().plugins)
        plugins.message.parsed(reply=replyroot)
        soapenv = replyroot.getChild('Envelope')
        soapenv.promotePrefixes()
        soapbody = soapenv.getChild('Body')
        self.detect_fault(soapbody)
        soapbody = self.multiref.process(soapbody)
        nodes = self.replycontent(method, soapbody)
        rtypes = self.returned_types(method)
        if len(rtypes) > 1:
            result = self.replycomposite(rtypes, nodes)
            return (replyroot, result)
        if len(rtypes) == 1:
            if rtypes[0].unbounded():
                result = self.replylist(rtypes[0], nodes)
                return (replyroot, result)
            if len(nodes):
                unmarshaller = self.unmarshaller()
                resolved = rtypes[0].resolve(nobuiltin=True)
                result = unmarshaller.process(nodes[0], resolved)
                return (replyroot, result)
        return (replyroot, None)
    
    def detect_fault(self, body):
        """
        Detect I{hidden} soapenv:Fault element in the soap body.
        @param body: The soap envelope body.
        @type body: L{Element}
        @raise WebFault: When found.
        """
        fault = body.getChild('Fault', envns)
        if fault is None:
            return
        unmarshaller = self.unmarshaller(False)
        p = unmarshaller.process(fault)
        if self.options().faults:
            raise WebFault(p, fault)
        return self
        
    
    def replylist(self, rt, nodes):
        """
        Construct a I{list} reply.  This mehod is called when it has been detected
        that the reply is a list.
        @param rt: The return I{type}.
        @type rt: L{suds.xsd.sxbase.SchemaObject}
        @param nodes: A collection of XML nodes.
        @type nodes: [L{Element},...]
        @return: A list of I{unmarshalled} objects.
        @rtype: [L{Object},...]
        """
        result = []
        resolved = rt.resolve(nobuiltin=True)
        unmarshaller = self.unmarshaller()
        for node in nodes:
            sobject = unmarshaller.process(node, resolved)
            result.append(sobject)
        return result
    
    def replycomposite(self, rtypes, nodes):
        """
        Construct a I{composite} reply.  This method is called when it has been
        detected that the reply has multiple root nodes.
        @param rtypes: A list of known return I{types}.
        @type rtypes: [L{suds.xsd.sxbase.SchemaObject},...]
        @param nodes: A collection of XML nodes.
        @type nodes: [L{Element},...]
        @return: The I{unmarshalled} composite object.
        @rtype: L{Object},...
        """
        dictionary = {}
        for rt in rtypes:
            dictionary[rt.name] = rt
        unmarshaller = self.unmarshaller()
        composite = Factory.object('reply')
        for node in nodes:
            tag = node.name
            rt = dictionary.get(tag, None)
            if rt is None:
                if node.get('id') is None:
                    raise Exception('<%s/> not mapped to message part' % tag)
                else:
                    continue
            resolved = rt.resolve(nobuiltin=True)
            sobject = unmarshaller.process(node, resolved)
            value = getattr(composite, tag, None)
            if value is None:
                if rt.unbounded():
                    value = []
                    setattr(composite, tag, value)
                    value.append(sobject)
                else:
                    setattr(composite, tag, sobject)
            else:
                if not isinstance(value, list):
                    value = [value,]
                    setattr(composite, tag, value)
                value.append(sobject)          
        return composite
    
    def get_fault(self, reply):
        """
        Extract the fault from the specified soap reply.  If I{faults} is True, an
        exception is raised.  Otherwise, the I{unmarshalled} fault L{Object} is
        returned.  This method is called when the server raises a I{web fault}.
        @param reply: A soap reply message.
        @type reply: str
        @return: A fault object.
        @rtype: tuple ( L{Element}, L{Object} )
        """
        reply = self.replyfilter(reply)
        sax = Parser()
        faultroot = sax.parse(string=reply)
        soapenv = faultroot.getChild('Envelope')
        soapbody = soapenv.getChild('Body')
        fault = soapbody.getChild('Fault')
        unmarshaller = self.unmarshaller(False)
        p = unmarshaller.process(fault)
        if self.options().faults:
            raise WebFault(p, faultroot)
        return (faultroot, p.detail)
    
    def mkparam(self, method, pdef, object):
        """
        Builds a parameter for the specified I{method} using the parameter
        definition (pdef) and the specified value (object).
        @param method: A method name.
        @type method: str
        @param pdef: A parameter definition.
        @type pdef: tuple: (I{name}, L{xsd.sxbase.SchemaObject})
        @param object: The parameter value.
        @type object: any
        @return: The parameter fragment.
        @rtype: L{Element}
        """
        marshaller = self.marshaller()
        content = \
            Content(tag=pdef[0],
                    value=object, 
                    type=pdef[1], 
                    real=pdef[1].resolve())
        return marshaller.process(content)
    
    def mkheader(self, method, hdef, object):
        """
        Builds a soapheader for the specified I{method} using the header
        definition (hdef) and the specified value (object).
        @param method: A method name.
        @type method: str
        @param hdef: A header definition.
        @type hdef: tuple: (I{name}, L{xsd.sxbase.SchemaObject})
        @param object: The header value.
        @type object: any
        @return: The parameter fragment.
        @rtype: L{Element}
        """
        marshaller = self.marshaller()
        if isinstance(object, (list, tuple)):
            tags = []
            for item in object:
                tags.append(self.mkheader(method, hdef, item))
            return tags
        content = Content(tag=hdef[0], value=object, type=hdef[1])
        return marshaller.process(content)
            
    def envelope(self, header, body):
        """
        Build the B{<Envelope/>} for an soap outbound message.
        @param header: The soap message B{header}.
        @type header: L{Element}
        @param body: The soap message B{body}.
        @type body: L{Element}
        @return: The soap envelope containing the body and header.
        @rtype: L{Element}
        """
        env = Element('Envelope', ns=envns)
        env.addPrefix(Namespace.xsins[0], Namespace.xsins[1])
        env.append(header)
        env.append(body)
        return env
    
    def header(self, content):
        """
        Build the B{<Body/>} for an soap outbound message.
        @param content: The header content.
        @type content: L{Element}
        @return: the soap body fragment.
        @rtype: L{Element}
        """
        header = Element('Header', ns=envns)
        header.append(content)
        return header
    
    def bodycontent(self, method, args, kwargs):
        """
        Get the content for the soap I{body} node.
        @param method: A service method.
        @type method: I{service.Method}
        @param args: method parameter values
        @type args: list
        @param kwargs: Named (keyword) args for the method invoked.
        @type kwargs: dict
        @return: The xml content for the <body/>
        @rtype: [L{Element},..]
        """
        raise Exception, 'not implemented'
    
    def headercontent(self, method):
        """
        Get the content for the soap I{Header} node.
        @param method: A service method.
        @type method: I{service.Method}
        @return: The xml content for the <body/>
        @rtype: [L{Element},..]
        """
        n = 0
        content = []
        wsse = self.options().wsse
        if wsse is not None:
            content.append(wsse.xml())
        headers = self.options().soapheaders
        if not isinstance(headers, (tuple,list,dict)):
            headers = (headers,)
        if len(headers) == 0:
            return content
        pts = self.headpart_types(method)
        if isinstance(headers, (tuple,list)):
            for header in headers:
                if isinstance(header, Element):
                    content.append(deepcopy(header))
                    continue
                if len(pts) == n: break
                h = self.mkheader(method, pts[n], header)
                ns = pts[n][1].namespace('ns0')
                h.setPrefix(ns[0], ns[1])
                content.append(h)
                n += 1
        else:
            for pt in pts:
                header = headers.get(pt[0])
                if header is None:
                    continue
                h = self.mkheader(method, pt, header)
                ns = pt[1].namespace('ns0')
                h.setPrefix(ns[0], ns[1])
                content.append(h)
        return content
    
    def replycontent(self, method, body):
        """
        Get the reply body content.
        @param method: A service method.
        @type method: I{service.Method}
        @param body: The soap body
        @type body: L{Element}
        @return: the body content
        @rtype: [L{Element},...]
        """
        raise Exception, 'not implemented'
    
    def body(self, content):
        """
        Build the B{<Body/>} for an soap outbound message.
        @param content: The body content.
        @type content: L{Element}
        @return: the soap body fragment.
        @rtype: L{Element}
        """
        body = Element('Body', ns=envns)
        body.append(content)
        return body
    
    def bodypart_types(self, method, input=True):
        """
        Get a list of I{parameter definitions} (pdef) defined for the specified method.
        Each I{pdef} is a tuple (I{name}, L{xsd.sxbase.SchemaObject})
        @param method: A service method.
        @type method: I{service.Method}
        @param input: Defines input/output message.
        @type input: boolean
        @return:  A list of parameter definitions
        @rtype: [I{pdef},]
        """
        result = []
        if input:
            parts = method.soap.input.body.parts
        else:
            parts = method.soap.output.body.parts
        for p in parts:
            if p.element is not None:
                query = ElementQuery(p.element)
            else:
                query = TypeQuery(p.type)
            pt = query.execute(self.schema())
            if pt is None:
                raise TypeNotFound(query.ref)
            if p.type is not None:
                pt = PartElement(p.name, pt)
            if input:
                if pt.name is None:
                    result.append((p.name, pt))
                else:
                    result.append((pt.name, pt))
            else:
                result.append(pt)
        return result
    
    def headpart_types(self, method, input=True):
        """
        Get a list of I{parameter definitions} (pdef) defined for the specified method.
        Each I{pdef} is a tuple (I{name}, L{xsd.sxbase.SchemaObject})
        @param method: A service method.
        @type method: I{service.Method}
        @param input: Defines input/output message.
        @type input: boolean
        @return:  A list of parameter definitions
        @rtype: [I{pdef},]
        """
        result = []
        if input:
            headers = method.soap.input.headers
        else:
            headers = method.soap.output.headers
        for header in headers:
            part = header.part
            if part.element is not None:
                query = ElementQuery(part.element)
            else:
                query = TypeQuery(part.type)
            pt = query.execute(self.schema())
            if pt is None:
                raise TypeNotFound(query.ref)
            if part.type is not None:
                pt = PartElement(part.name, pt)
            if input:
                if pt.name is None:
                    result.append((part.name, pt))
                else:
                    result.append((pt.name, pt))
            else:
                result.append(pt)
        return result
    
    def returned_types(self, method):
        """
        Get the L{xsd.sxbase.SchemaObject} returned by the I{method}.
        @param method: A service method.
        @type method: I{service.Method}
        @return: The name of the type return by the method.
        @rtype: [I{rtype},..]
        """
        result = []
        for rt in self.bodypart_types(method, input=False):
            result.append(rt)
        return result
Beispiel #6
0
class Binding:
    """
    The SOAP binding class used to process outgoing and incoming SOAP messages
    per the WSDL port binding.
    @cvar replyfilter: The reply filter function.
    @type replyfilter: (lambda s,r: r)
    @ivar wsdl: The WSDL.
    @type wsdl: L{suds.wsdl.Definitions}
    @ivar schema: The collective schema contained within the WSDL.
    @type schema: L{xsd.schema.Schema}
    @ivar options: A dictionary options.
    @type options: L{Options}
    """

    replyfilter = (lambda s, r: r)

    def __init__(self, wsdl):
        """
        @param wsdl: A WSDL.
        @type wsdl: L{wsdl.Definitions}
        """
        self.wsdl = wsdl
        self.multiref = MultiRef()

    def schema(self):
        return self.wsdl.schema

    def options(self):
        return self.wsdl.options

    def unmarshaller(self, typed=True):
        """
        Get the appropriate XML decoder.
        @return: Either the (basic|typed) unmarshaller.
        @rtype: L{UmxTyped}
        """
        if typed:
            return UmxTyped(self.schema())
        else:
            return UmxBasic()

    def marshaller(self):
        """
        Get the appropriate XML encoder.
        @return: An L{MxLiteral} marshaller.
        @rtype: L{MxLiteral}
        """
        return MxLiteral(self.schema(), self.options().xstq)

    def param_defs(self, method):
        """
        Get parameter definitions.
        Each I{pdef} is a tuple (I{name}, L{xsd.sxbase.SchemaObject})
        @param method: A service method.
        @type method: I{service.Method}
        @return: A collection of parameter definitions
        @rtype: [I{pdef},..]
        """
        raise Exception, 'not implemented'

    def get_message(self, method, args, kwargs):
        """
        Get a SOAP message for the specified method, args and SOAP headers.
        This is the entry point for creating an outbound SOAP message.
        @param method: The method being invoked.
        @type method: I{service.Method}
        @param args: A list of args for the method invoked.
        @type args: list
        @param kwargs: Named (keyword) args for the method invoked.
        @type kwargs: dict
        @return: The SOAP envelope.
        @rtype: L{Document}
        """

        content = self.headercontent(method)
        header = self.header(content)
        content = self.bodycontent(method, args, kwargs)
        body = self.body(content)
        env = self.envelope(header, body)
        if self.options().prefixes:
            body.normalizePrefixes()
            env.promotePrefixes()
        else:
            env.refitPrefixes()
        return Document(env)

    def get_reply(self, method, reply):
        """
        Process the I{reply} for the specified I{method} by sax parsing the
        I{reply} and then unmarshalling into Python object(s).
        @param method: The name of the invoked method.
        @type method: str
        @param reply: The reply XML received after invoking the specified
            method.
        @type reply: str
        @return: The unmarshalled reply.  The returned value is an L{Object}
            for a I{list} depending on whether the service returns a single
            object or a collection.
        @rtype: tuple ( L{Element}, L{Object} )
        """
        reply = self.replyfilter(reply)
        sax = Parser()
        replyroot = sax.parse(string=reply)
        plugins = PluginContainer(self.options().plugins)
        plugins.message.parsed(reply=replyroot)
        soapenv = replyroot.getChild('Envelope')
        soapenv.promotePrefixes()
        soapbody = soapenv.getChild('Body')
        self.detect_fault(soapbody)
        soapbody = self.multiref.process(soapbody)
        nodes = self.replycontent(method, soapbody)
        rtypes = self.returned_types(method)
        if len(rtypes) > 1:
            result = self.replycomposite(rtypes, nodes)
            return (replyroot, result)
        if len(rtypes) == 1:
            if rtypes[0].multi_occurrence():
                result = self.replylist(rtypes[0], nodes)
                return (replyroot, result)
            if len(nodes):
                unmarshaller = self.unmarshaller()
                resolved = rtypes[0].resolve(nobuiltin=True)
                result = unmarshaller.process(nodes[0], resolved)
                return (replyroot, result)
        return (replyroot, None)

    def detect_fault(self, body):
        """
        Detect I{hidden} soapenv:Fault element in the SOAP body.
        @param body: The SOAP envelope body.
        @type body: L{Element}
        @raise WebFault: When found.
        """
        fault = body.getChild('Fault', envns)
        if fault is None:
            return
        unmarshaller = self.unmarshaller(False)
        p = unmarshaller.process(fault)
        if self.options().faults:
            raise WebFault(p, fault)
        return self

    def replylist(self, rt, nodes):
        """
        Construct a I{list} reply.  This mehod is called when it has been detected
        that the reply is a list.
        @param rt: The return I{type}.
        @type rt: L{suds.xsd.sxbase.SchemaObject}
        @param nodes: A collection of XML nodes.
        @type nodes: [L{Element},...]
        @return: A list of I{unmarshalled} objects.
        @rtype: [L{Object},...]
        """
        result = []
        resolved = rt.resolve(nobuiltin=True)
        unmarshaller = self.unmarshaller()
        for node in nodes:
            sobject = unmarshaller.process(node, resolved)
            result.append(sobject)
        return result

    def replycomposite(self, rtypes, nodes):
        """
        Construct a I{composite} reply.  This method is called when it has been
        detected that the reply has multiple root nodes.
        @param rtypes: A list of known return I{types}.
        @type rtypes: [L{suds.xsd.sxbase.SchemaObject},...]
        @param nodes: A collection of XML nodes.
        @type nodes: [L{Element},...]
        @return: The I{unmarshalled} composite object.
        @rtype: L{Object},...
        """
        dictionary = {}
        for rt in rtypes:
            dictionary[rt.name] = rt
        unmarshaller = self.unmarshaller()
        composite = Factory.object('reply')
        for node in nodes:
            tag = node.name
            rt = dictionary.get(tag, None)
            if rt is None:
                if node.get('id') is None:
                    raise Exception('<%s/> not mapped to message part' % tag)
                else:
                    continue
            resolved = rt.resolve(nobuiltin=True)
            sobject = unmarshaller.process(node, resolved)
            value = getattr(composite, tag, None)
            if value is None:
                if rt.multi_occurrence():
                    value = []
                    setattr(composite, tag, value)
                    value.append(sobject)
                else:
                    setattr(composite, tag, sobject)
            else:
                if not isinstance(value, list):
                    value = [
                        value,
                    ]
                    setattr(composite, tag, value)
                value.append(sobject)
        return composite

    def get_fault(self, reply):
        """
        Extract the fault from the specified SOAP reply.  If I{faults} is True, an
        exception is raised.  Otherwise, the I{unmarshalled} fault L{Object} is
        returned.  This method is called when the server raises a I{web fault}.
        @param reply: A SOAP reply message.
        @type reply: str
        @return: A fault object.
        @rtype: tuple ( L{Element}, L{Object} )
        """
        _reply = self.replyfilter(reply)
        sax = Parser()
        faultroot = sax.parse(string=_reply)
        soapenv = faultroot.getChild('Envelope')
        soapbody = soapenv.getChild('Body')
        fault = soapbody.getChild('Fault')
        unmarshaller = self.unmarshaller(False)
        if fault:
            p = unmarshaller.process(fault)
            if self.options().faults:
                raise WebFault(p, faultroot)
            return (True, faultroot, p.detail)
        else:
            #p = unmarshaller.process(soapbody)
            #if self.options().faults:
            #raise WebFault(p, faultroot)
            return (False, faultroot, reply)

    def mkparam(self, method, pdef, object):
        """
        Builds a parameter for the specified I{method} using the parameter
        definition (pdef) and the specified value (object).
        @param method: A method name.
        @type method: str
        @param pdef: A parameter definition.
        @type pdef: tuple: (I{name}, L{xsd.sxbase.SchemaObject})
        @param object: The parameter value.
        @type object: any
        @return: The parameter fragment.
        @rtype: L{Element}
        """
        marshaller = self.marshaller()
        content = \
            Content(tag=pdef[0],
                    value=object,
                    type=pdef[1],
                    real=pdef[1].resolve())
        return marshaller.process(content)

    def mkheader(self, method, hdef, object):
        """
        Builds a soapheader for the specified I{method} using the header
        definition (hdef) and the specified value (object).
        @param method: A method name.
        @type method: str
        @param hdef: A header definition.
        @type hdef: tuple: (I{name}, L{xsd.sxbase.SchemaObject})
        @param object: The header value.
        @type object: any
        @return: The parameter fragment.
        @rtype: L{Element}
        """
        marshaller = self.marshaller()
        if isinstance(object, (list, tuple)):
            tags = []
            for item in object:
                tags.append(self.mkheader(method, hdef, item))
            return tags
        content = Content(tag=hdef[0], value=object, type=hdef[1])
        return marshaller.process(content)

    def envelope(self, header, body):
        """
        Build the B{<Envelope/>} for a SOAP outbound message.
        @param header: The SOAP message B{header}.
        @type header: L{Element}
        @param body: The SOAP message B{body}.
        @type body: L{Element}
        @return: The SOAP envelope containing the body and header.
        @rtype: L{Element}
        """
        env = Element('Envelope', ns=envns)
        env.addPrefix(Namespace.xsins[0], Namespace.xsins[1])
        env.append(header)
        env.append(body)
        return env

    def header(self, content):
        """
        Build the B{<Body/>} for a SOAP outbound message.
        @param content: The header content.
        @type content: L{Element}
        @return: the SOAP body fragment.
        @rtype: L{Element}
        """
        header = Element('Header', ns=envns)
        header.append(content)
        return header

    def bodycontent(self, method, args, kwargs):
        """
        Get the content for the SOAP I{body} node.
        @param method: A service method.
        @type method: I{service.Method}
        @param args: method parameter values
        @type args: list
        @param kwargs: Named (keyword) args for the method invoked.
        @type kwargs: dict
        @return: The XML content for the <body/>
        @rtype: [L{Element},..]
        """
        raise Exception, 'not implemented'

    def headercontent(self, method):
        """
        Get the content for the SOAP I{Header} node.
        @param method: A service method.
        @type method: I{service.Method}
        @return: The XML content for the <body/>
        @rtype: [L{Element},..]
        """
        n = 0
        content = []
        wsse = self.options().wsse
        if wsse is not None:
            content.append(wsse.xml())
        headers = self.options().soapheaders
        if not isinstance(headers, (tuple, list, dict)):
            headers = (headers, )
        if len(headers) == 0:
            return content
        pts = self.headpart_types(method)
        if isinstance(headers, (tuple, list)):
            for header in headers:
                if isinstance(header, Element):
                    content.append(deepcopy(header))
                    continue
                if len(pts) == n: break
                h = self.mkheader(method, pts[n], header)
                ns = pts[n][1].namespace('ns0')
                h.setPrefix(ns[0], ns[1])
                content.append(h)
                n += 1
        else:
            for pt in pts:
                header = headers.get(pt[0])
                if header is None:
                    continue
                h = self.mkheader(method, pt, header)
                ns = pt[1].namespace('ns0')
                h.setPrefix(ns[0], ns[1])
                content.append(h)
        return content

    def replycontent(self, method, body):
        """
        Get the reply body content.
        @param method: A service method.
        @type method: I{service.Method}
        @param body: The SOAP body.
        @type body: L{Element}
        @return: The body content.
        @rtype: [L{Element},...]
        """
        raise Exception, 'not implemented'

    def body(self, content):
        """
        Build the B{<Body/>} for a SOAP outbound message.
        @param content: The body content.
        @type content: L{Element}
        @return: The SOAP body fragment.
        @rtype: L{Element}
        """
        body = Element('Body', ns=envns)
        body.append(content)
        return body

    def bodypart_types(self, method, input=True):
        """
        Get a list of I{parameter definitions} (pdef) defined for the specified method.
        Each I{pdef} is a tuple (I{name}, L{xsd.sxbase.SchemaObject})
        @param method: A service method.
        @type method: I{service.Method}
        @param input: Defines input/output message.
        @type input: boolean
        @return:  A list of parameter definitions
        @rtype: [I{pdef},]
        """
        result = []
        if input:
            parts = method.soap.input.body.parts
        else:
            parts = method.soap.output.body.parts
        for p in parts:
            if p.element is not None:
                query = ElementQuery(p.element)
            else:
                query = TypeQuery(p.type)
            pt = query.execute(self.schema())
            if pt is None:
                raise TypeNotFound(query.ref)
            if p.type is not None:
                pt = PartElement(p.name, pt)
            if input:
                if pt.name is None:
                    result.append((p.name, pt))
                else:
                    result.append((pt.name, pt))
            else:
                result.append(pt)
        return result

    def headpart_types(self, method, input=True):
        """
        Get a list of I{parameter definitions} (pdef) defined for the specified method.
        Each I{pdef} is a tuple (I{name}, L{xsd.sxbase.SchemaObject})
        @param method: A service method.
        @type method: I{service.Method}
        @param input: Defines input/output message.
        @type input: boolean
        @return:  A list of parameter definitions
        @rtype: [I{pdef},]
        """
        result = []
        if input:
            headers = method.soap.input.headers
        else:
            headers = method.soap.output.headers
        for header in headers:
            part = header.part
            if part.element is not None:
                query = ElementQuery(part.element)
            else:
                query = TypeQuery(part.type)
            pt = query.execute(self.schema())
            if pt is None:
                raise TypeNotFound(query.ref)
            if part.type is not None:
                pt = PartElement(part.name, pt)
            if input:
                if pt.name is None:
                    result.append((part.name, pt))
                else:
                    result.append((pt.name, pt))
            else:
                result.append(pt)
        return result

    def returned_types(self, method):
        """
        Get the L{xsd.sxbase.SchemaObject} returned by the I{method}.
        @param method: A service method.
        @type method: I{service.Method}
        @return: The name of the type return by the method.
        @rtype: [I{rtype},..]
        """
        result = []
        for rt in self.bodypart_types(method, input=False):
            result.append(rt)
        return result
Beispiel #7
0
class Binding(object):
    """
    The SOAP binding class used to process outgoing and incoming SOAP messages
    per the WSDL port binding.

    @ivar wsdl: The WSDL.
    @type wsdl: L{suds.wsdl.Definitions}
    @ivar schema: The collective schema contained within the WSDL.
    @type schema: L{xsd.schema.Schema}
    @ivar options: A dictionary options.
    @type options: L{Options}

    """

    def __init__(self, wsdl):
        """
        @param wsdl: A WSDL.
        @type wsdl: L{wsdl.Definitions}

        """
        self.wsdl = wsdl
        self.multiref = MultiRef()

    def schema(self):
        return self.wsdl.schema

    def options(self):
        return self.wsdl.options

    def unmarshaller(self):
        """
        Get the appropriate schema based XML decoder.

        @return: Typed unmarshaller.
        @rtype: L{UmxTyped}

        """
        return UmxTyped(self.schema())

    def marshaller(self):
        """
        Get the appropriate XML encoder.

        @return: An L{MxLiteral} marshaller.
        @rtype: L{MxLiteral}

        """
        return MxLiteral(self.schema(), self.options().xstq)

    def param_defs(self, method):
        """
        Get parameter definitions.

        Each I{pdef} is a (I{name}, L{xsd.sxbase.SchemaObject}) tuple.

        @param method: A service method.
        @type method: I{service.Method}
        @return: A collection of parameter definitions
        @rtype: [I{pdef},...]

        """
        raise Exception("not implemented")

    def get_message(self, method, args, kwargs):
        """
        Get a SOAP message for the specified method, args and SOAP headers.

        This is the entry point for creating an outbound SOAP message.

        @param method: The method being invoked.
        @type method: I{service.Method}
        @param args: A list of args for the method invoked.
        @type args: list
        @param kwargs: Named (keyword) args for the method invoked.
        @type kwargs: dict
        @return: The SOAP envelope.
        @rtype: L{Document}

        """
        content = self.headercontent(method)
        header = self.header(content)
        content = self.bodycontent(method, args, kwargs)
        body = self.body(content)
        env = self.envelope(header, body)
        if self.options().prefixes:
            body.normalizePrefixes()
            env.promotePrefixes()
        else:
            env.refitPrefixes()
        return Document(env)

    def get_reply(self, method, replyroot):
        """
        Process the I{reply} for the specified I{method} by unmarshalling it
        into into Python object(s).

        @param method: The name of the invoked method.
        @type method: str
        @param replyroot: The reply XML root node received after invoking the
            specified method.
        @type replyroot: L{Element}
        @return: The unmarshalled reply. The returned value is an L{Object} or
            a I{list} depending on whether the service returns a single object
            or a collection.
        @rtype: L{Object} or I{list}

        """
        soapenv = replyroot.getChild("Envelope", envns)
        soapenv.promotePrefixes()
        soapbody = soapenv.getChild("Body", envns)
        soapbody = self.multiref.process(soapbody)
        nodes = self.replycontent(method, soapbody)
        rtypes = self.returned_types(method)
        if len(rtypes) > 1:
            return self.replycomposite(rtypes, nodes)
        if len(rtypes) == 0:
            return
        if rtypes[0].multi_occurrence():
            return self.replylist(rtypes[0], nodes)
        if len(nodes):
            resolved = rtypes[0].resolve(nobuiltin=True)
            return self.unmarshaller().process(nodes[0], resolved)

    def replylist(self, rt, nodes):
        """
        Construct a I{list} reply.

        Called for replies with possible multiple occurrences.

        @param rt: The return I{type}.
        @type rt: L{suds.xsd.sxbase.SchemaObject}
        @param nodes: A collection of XML nodes.
        @type nodes: [L{Element},...]
        @return: A list of I{unmarshalled} objects.
        @rtype: [L{Object},...]

        """
        resolved = rt.resolve(nobuiltin=True)
        unmarshaller = self.unmarshaller()
        return [unmarshaller.process(node, resolved) for node in nodes]

    def replycomposite(self, rtypes, nodes):
        """
        Construct a I{composite} reply.

        Called for replies with multiple output nodes.

        @param rtypes: A list of known return I{types}.
        @type rtypes: [L{suds.xsd.sxbase.SchemaObject},...]
        @param nodes: A collection of XML nodes.
        @type nodes: [L{Element},...]
        @return: The I{unmarshalled} composite object.
        @rtype: L{Object},...

        """
        dictionary = {}
        for rt in rtypes:
            dictionary[rt.name] = rt
        unmarshaller = self.unmarshaller()
        composite = Factory.object("reply")
        for node in nodes:
            tag = node.name
            rt = dictionary.get(tag)
            if rt is None:
                if node.get("id") is None:
                    message = "<%s/> not mapped to message part" % (tag,)
                    raise Exception(message)
                continue
            resolved = rt.resolve(nobuiltin=True)
            sobject = unmarshaller.process(node, resolved)
            value = getattr(composite, tag, None)
            if value is None:
                if rt.multi_occurrence():
                    value = []
                    setattr(composite, tag, value)
                    value.append(sobject)
                else:
                    setattr(composite, tag, sobject)
            else:
                if not isinstance(value, list):
                    value = [value,]
                    setattr(composite, tag, value)
                value.append(sobject)
        return composite

    def mkparam(self, method, pdef, object):
        """
        Builds a parameter for the specified I{method} using the parameter
        definition (pdef) and the specified value (object).

        @param method: A method name.
        @type method: str
        @param pdef: A parameter definition.
        @type pdef: tuple: (I{name}, L{xsd.sxbase.SchemaObject})
        @param object: The parameter value.
        @type object: any
        @return: The parameter fragment.
        @rtype: L{Element}

        """
        marshaller = self.marshaller()
        content = Content(tag=pdef[0], value=object, type=pdef[1],
            real=pdef[1].resolve())
        return marshaller.process(content)

    def mkheader(self, method, hdef, object):
        """
        Builds a soapheader for the specified I{method} using the header
        definition (hdef) and the specified value (object).

        @param method: A method name.
        @type method: str
        @param hdef: A header definition.
        @type hdef: tuple: (I{name}, L{xsd.sxbase.SchemaObject})
        @param object: The header value.
        @type object: any
        @return: The parameter fragment.
        @rtype: L{Element}

        """
        marshaller = self.marshaller()
        if isinstance(object, (list, tuple)):
            return [self.mkheader(method, hdef, item) for item in object]
        content = Content(tag=hdef[0], value=object, type=hdef[1])
        return marshaller.process(content)

    def envelope(self, header, body):
        """
        Build the B{<Envelope/>} for a SOAP outbound message.

        @param header: The SOAP message B{header}.
        @type header: L{Element}
        @param body: The SOAP message B{body}.
        @type body: L{Element}
        @return: The SOAP envelope containing the body and header.
        @rtype: L{Element}

        """
        env = Element("Envelope", ns=envns)
        env.addPrefix(Namespace.xsins[0], Namespace.xsins[1])
        env.append(header)
        env.append(body)
        return env

    def header(self, content):
        """
        Build the B{<Body/>} for a SOAP outbound message.

        @param content: The header content.
        @type content: L{Element}
        @return: The SOAP body fragment.
        @rtype: L{Element}

        """
        header = Element("Header", ns=envns)
        header.append(content)
        return header

    def bodycontent(self, method, args, kwargs):
        """
        Get the content for the SOAP I{body} node.

        @param method: A service method.
        @type method: I{service.Method}
        @param args: method parameter values.
        @type args: list
        @param kwargs: Named (keyword) args for the method invoked.
        @type kwargs: dict
        @return: The XML content for the <body/>.
        @rtype: [L{Element},...]

        """
        raise Exception("not implemented")

    def headercontent(self, method):
        """
        Get the content for the SOAP I{Header} node.

        @param method: A service method.
        @type method: I{service.Method}
        @return: The XML content for the <body/>.
        @rtype: [L{Element},...]

        """
        content = []
        wsse = self.options().wsse
        if wsse is not None:
            content.append(wsse.xml())
        headers = self.options().soapheaders
        if not isinstance(headers, (tuple, list, dict)):
            headers = (headers,)
        elif not headers:
            return content
        pts = self.headpart_types(method)
        if isinstance(headers, (tuple, list)):
            n = 0
            for header in headers:
                if isinstance(header, Element):
                    content.append(deepcopy(header))
                    continue
                if len(pts) == n:
                    break
                h = self.mkheader(method, pts[n], header)
                ns = pts[n][1].namespace("ns0")
                h.setPrefix(ns[0], ns[1])
                content.append(h)
                n += 1
        else:
            for pt in pts:
                header = headers.get(pt[0])
                if header is None:
                    continue
                h = self.mkheader(method, pt, header)
                ns = pt[1].namespace("ns0")
                h.setPrefix(ns[0], ns[1])
                content.append(h)
        return content

    def replycontent(self, method, body):
        """
        Get the reply body content.

        @param method: A service method.
        @type method: I{service.Method}
        @param body: The SOAP body.
        @type body: L{Element}
        @return: The body content.
        @rtype: [L{Element},...]

        """
        raise Exception("not implemented")

    def body(self, content):
        """
        Build the B{<Body/>} for a SOAP outbound message.

        @param content: The body content.
        @type content: L{Element}
        @return: The SOAP body fragment.
        @rtype: L{Element}

        """
        body = Element("Body", ns=envns)
        body.append(content)
        return body

    def bodypart_types(self, method, input=True):
        """
        Get a list of I{parameter definitions} (pdefs) defined for the
        specified method.

        An input I{pdef} is a (I{name}, L{xsd.sxbase.SchemaObject}) tuple,
        while an output I{pdef} is a L{xsd.sxbase.SchemaObject}.

        @param method: A service method.
        @type method: I{service.Method}
        @param input: Defines input/output message.
        @type input: boolean
        @return:  A list of parameter definitions
        @rtype: [I{pdef},...]

        """
        if input:
            parts = method.soap.input.body.parts
        else:
            parts = method.soap.output.body.parts
        return [self.__part_type(p, input) for p in parts]

    def headpart_types(self, method, input=True):
        """
        Get a list of header I{parameter definitions} (pdefs) defined for the
        specified method.

        An input I{pdef} is a (I{name}, L{xsd.sxbase.SchemaObject}) tuple,
        while an output I{pdef} is a L{xsd.sxbase.SchemaObject}.

        @param method: A service method.
        @type method: I{service.Method}
        @param input: Defines input/output message.
        @type input: boolean
        @return:  A list of parameter definitions
        @rtype: [I{pdef},...]

        """
        if input:
            headers = method.soap.input.headers
        else:
            headers = method.soap.output.headers
        return [self.__part_type(h.part, input) for h in headers]

    def returned_types(self, method):
        """
        Get the I{method} return value type(s).

        @param method: A service method.
        @type method: I{service.Method}
        @return: Method return value type.
        @rtype: [L{xsd.sxbase.SchemaObject},...]

        """
        return self.bodypart_types(method, input=False)

    def __part_type(self, part, input):
        """
        Get a I{parameter definition} (pdef) defined for a given body or header
        message part.

        An input I{pdef} is a (I{name}, L{xsd.sxbase.SchemaObject}) tuple,
        while an output I{pdef} is a L{xsd.sxbase.SchemaObject}.

        @param part: A service method input or output part.
        @type part: I{suds.wsdl.Part}
        @param input: Defines input/output message.
        @type input: boolean
        @return:  A list of parameter definitions
        @rtype: [I{pdef},...]

        """
        if part.element is None:
            query = TypeQuery(part.type)
        else:
            query = ElementQuery(part.element)
        part_type = query.execute(self.schema())
        if part_type is None:
            raise TypeNotFound(query.ref)
        if part.type is not None:
            part_type = PartElement(part.name, part_type)
        if not input:
            return part_type
        if part_type.name is None:
            return part.name, part_type
        return part_type.name, part_type
Beispiel #8
0
class Binding(object):
    """
    The SOAP binding class used to process outgoing and incoming SOAP messages
    per the WSDL port binding.

    @ivar wsdl: The WSDL.
    @type wsdl: L{suds.wsdl.Definitions}
    @ivar schema: The collective schema contained within the WSDL.
    @type schema: L{xsd.schema.Schema}
    @ivar options: A dictionary options.
    @type options: L{Options}

    """

    def __init__(self, wsdl):
        """
        @param wsdl: A WSDL.
        @type wsdl: L{wsdl.Definitions}

        """
        self.wsdl = wsdl
        self.multiref = MultiRef()

    def schema(self):
        return self.wsdl.schema

    def options(self):
        return self.wsdl.options

    def unmarshaller(self):
        """
        Get the appropriate schema based XML decoder.

        @return: Typed unmarshaller.
        @rtype: L{UmxTyped}

        """
        return UmxTyped(self.schema())

    def marshaller(self):
        """
        Get the appropriate XML encoder.

        @return: An L{MxLiteral} marshaller.
        @rtype: L{MxLiteral}

        """
        return MxLiteral(self.schema(), self.options().xstq)

    def param_defs(self, method):
        """
        Get parameter definitions.

        Each I{pdef} is a (I{name}, L{xsd.sxbase.SchemaObject}) tuple.

        @param method: A service method.
        @type method: I{service.Method}
        @return: A collection of parameter definitions
        @rtype: [I{pdef},...]

        """
        raise Exception("not implemented")

    def get_message(self, method, args, kwargs):
        """
        Get a SOAP message for the specified method, args and SOAP headers.

        This is the entry point for creating an outbound SOAP message.

        @param method: The method being invoked.
        @type method: I{service.Method}
        @param args: A list of args for the method invoked.
        @type args: list
        @param kwargs: Named (keyword) args for the method invoked.
        @type kwargs: dict
        @return: The SOAP envelope.
        @rtype: L{Document}

        """
        content = self.headercontent(method)
        header = self.header(content)
        content = self.bodycontent(method, args, kwargs)
        body = self.body(content)
        env = self.envelope(header, body)
        if self.options().prefixes:
            body.normalizePrefixes()
            env.promotePrefixes()
        else:
            env.refitPrefixes()
        return Document(env)

    def get_reply(self, method, replyroot):
        """
        Process the I{reply} for the specified I{method} by unmarshalling it
        into into Python object(s).

        @param method: The name of the invoked method.
        @type method: str
        @param replyroot: The reply XML root node received after invoking the
            specified method.
        @type replyroot: L{Element}
        @return: The unmarshalled reply. The returned value is an L{Object} or
            a I{list} depending on whether the service returns a single object
            or a collection.
        @rtype: L{Object} or I{list}

        """
        soapenv = replyroot.getChild("Envelope", envns)
        soapenv.promotePrefixes()
        soapbody = soapenv.getChild("Body", envns)
        soapbody = self.multiref.process(soapbody)
        nodes = self.replycontent(method, soapbody)
        rtypes = self.returned_types(method)
        if len(rtypes) > 1:
            return self.replycomposite(rtypes, nodes)
        if len(rtypes) == 0:
            return
        if rtypes[0].multi_occurrence():
            return self.replylist(rtypes[0], nodes)
        if len(nodes):
            resolved = rtypes[0].resolve(nobuiltin=True)
            return self.unmarshaller().process(nodes[0], resolved)

    def replylist(self, rt, nodes):
        """
        Construct a I{list} reply.

        Called for replies with possible multiple occurrences.

        @param rt: The return I{type}.
        @type rt: L{suds.xsd.sxbase.SchemaObject}
        @param nodes: A collection of XML nodes.
        @type nodes: [L{Element},...]
        @return: A list of I{unmarshalled} objects.
        @rtype: [L{Object},...]

        """
        resolved = rt.resolve(nobuiltin=True)
        unmarshaller = self.unmarshaller()
        return [unmarshaller.process(node, resolved) for node in nodes]

    def replycomposite(self, rtypes, nodes):
        """
        Construct a I{composite} reply.

        Called for replies with multiple output nodes.

        @param rtypes: A list of known return I{types}.
        @type rtypes: [L{suds.xsd.sxbase.SchemaObject},...]
        @param nodes: A collection of XML nodes.
        @type nodes: [L{Element},...]
        @return: The I{unmarshalled} composite object.
        @rtype: L{Object},...

        """
        dictionary = {}
        for rt in rtypes:
            dictionary[rt.name] = rt
        unmarshaller = self.unmarshaller()
        composite = Factory.object("reply")
        for node in nodes:
            tag = node.name
            rt = dictionary.get(tag)
            if rt is None:
                if node.get("id") is None:
                    message = "<%s/> not mapped to message part" % (tag,)
                    raise Exception(message)
                continue
            resolved = rt.resolve(nobuiltin=True)
            sobject = unmarshaller.process(node, resolved)
            value = getattr(composite, tag, None)
            if value is None:
                if rt.multi_occurrence():
                    value = []
                    setattr(composite, tag, value)
                    value.append(sobject)
                else:
                    setattr(composite, tag, sobject)
            else:
                if not isinstance(value, list):
                    value = [value,]
                    setattr(composite, tag, value)
                value.append(sobject)
        return composite

    def mkparam(self, method, pdef, object):
        """
        Builds a parameter for the specified I{method} using the parameter
        definition (pdef) and the specified value (object).

        @param method: A method name.
        @type method: str
        @param pdef: A parameter definition.
        @type pdef: tuple: (I{name}, L{xsd.sxbase.SchemaObject})
        @param object: The parameter value.
        @type object: any
        @return: The parameter fragment.
        @rtype: L{Element}

        """
        marshaller = self.marshaller()
        content = Content(tag=pdef[0], value=object, type=pdef[1],
            real=pdef[1].resolve())
        return marshaller.process(content)

    def mkheader(self, method, hdef, object):
        """
        Builds a soapheader for the specified I{method} using the header
        definition (hdef) and the specified value (object).

        @param method: A method name.
        @type method: str
        @param hdef: A header definition.
        @type hdef: tuple: (I{name}, L{xsd.sxbase.SchemaObject})
        @param object: The header value.
        @type object: any
        @return: The parameter fragment.
        @rtype: L{Element}

        """
        marshaller = self.marshaller()
        if isinstance(object, (list, tuple)):
            return [self.mkheader(method, hdef, item) for item in object]
        content = Content(tag=hdef[0], value=object, type=hdef[1])
        return marshaller.process(content)

    def envelope(self, header, body):
        """
        Build the B{<Envelope/>} for a SOAP outbound message.

        @param header: The SOAP message B{header}.
        @type header: L{Element}
        @param body: The SOAP message B{body}.
        @type body: L{Element}
        @return: The SOAP envelope containing the body and header.
        @rtype: L{Element}

        """
        env = Element("Envelope", ns=envns)
        env.addPrefix(Namespace.xsins[0], Namespace.xsins[1])
        env.append(header)
        env.append(body)
        return env

    def header(self, content):
        """
        Build the B{<Body/>} for a SOAP outbound message.

        @param content: The header content.
        @type content: L{Element}
        @return: The SOAP body fragment.
        @rtype: L{Element}

        """
        header = Element("Header", ns=envns)
        header.append(content)
        return header

    def bodycontent(self, method, args, kwargs):
        """
        Get the content for the SOAP I{body} node.

        @param method: A service method.
        @type method: I{service.Method}
        @param args: method parameter values.
        @type args: list
        @param kwargs: Named (keyword) args for the method invoked.
        @type kwargs: dict
        @return: The XML content for the <body/>.
        @rtype: [L{Element},...]

        """
        raise Exception("not implemented")

    def headercontent(self, method):
        """
        Get the content for the SOAP I{Header} node.

        @param method: A service method.
        @type method: I{service.Method}
        @return: The XML content for the <body/>.
        @rtype: [L{Element},...]

        """
        content = []
        wsse = self.options().wsse
        if wsse is not None:
            content.append(wsse.xml())
        headers = self.options().soapheaders
        if not isinstance(headers, (tuple, list, dict)):
            headers = (headers,)
        elif not headers:
            return content
        pts = self.headpart_types(method)
        if isinstance(headers, (tuple, list)):
            n = 0
            for header in headers:
                if isinstance(header, Element):
                    content.append(deepcopy(header))
                    continue
                if len(pts) == n:
                    break
                h = self.mkheader(method, pts[n], header)
                ns = pts[n][1].namespace("ns0")
                h.setPrefix(ns[0], ns[1])
                content.append(h)
                n += 1
        else:
            for pt in pts:
                header = headers.get(pt[0])
                if header is None:
                    continue
                h = self.mkheader(method, pt, header)
                ns = pt[1].namespace("ns0")
                h.setPrefix(ns[0], ns[1])
                content.append(h)
        return content

    def replycontent(self, method, body):
        """
        Get the reply body content.

        @param method: A service method.
        @type method: I{service.Method}
        @param body: The SOAP body.
        @type body: L{Element}
        @return: The body content.
        @rtype: [L{Element},...]

        """
        raise Exception("not implemented")

    def body(self, content):
        """
        Build the B{<Body/>} for a SOAP outbound message.

        @param content: The body content.
        @type content: L{Element}
        @return: The SOAP body fragment.
        @rtype: L{Element}

        """
        body = Element("Body", ns=envns)
        body.append(content)
        return body

    def bodypart_types(self, method, input=True):
        """
        Get a list of I{parameter definitions} (pdefs) defined for the
        specified method.

        An input I{pdef} is a (I{name}, L{xsd.sxbase.SchemaObject}) tuple,
        while an output I{pdef} is a L{xsd.sxbase.SchemaObject}.

        @param method: A service method.
        @type method: I{service.Method}
        @param input: Defines input/output message.
        @type input: boolean
        @return:  A list of parameter definitions
        @rtype: [I{pdef},...]

        """
        if input:
            parts = method.soap.input.body.parts
        else:
            parts = method.soap.output.body.parts
        return [self.__part_type(p, input) for p in parts]

    def headpart_types(self, method, input=True):
        """
        Get a list of header I{parameter definitions} (pdefs) defined for the
        specified method.

        An input I{pdef} is a (I{name}, L{xsd.sxbase.SchemaObject}) tuple,
        while an output I{pdef} is a L{xsd.sxbase.SchemaObject}.

        @param method: A service method.
        @type method: I{service.Method}
        @param input: Defines input/output message.
        @type input: boolean
        @return:  A list of parameter definitions
        @rtype: [I{pdef},...]

        """
        if input:
            headers = method.soap.input.headers
        else:
            headers = method.soap.output.headers
        return [self.__part_type(h.part, input) for h in headers]

    def returned_types(self, method):
        """
        Get the I{method} return value type(s).

        @param method: A service method.
        @type method: I{service.Method}
        @return: Method return value type.
        @rtype: [L{xsd.sxbase.SchemaObject},...]

        """
        return self.bodypart_types(method, input=False)

    def __part_type(self, part, input):
        """
        Get a I{parameter definition} (pdef) defined for a given body or header
        message part.

        An input I{pdef} is a (I{name}, L{xsd.sxbase.SchemaObject}) tuple,
        while an output I{pdef} is a L{xsd.sxbase.SchemaObject}.

        @param part: A service method input or output part.
        @type part: I{suds.wsdl.Part}
        @param input: Defines input/output message.
        @type input: boolean
        @return:  A list of parameter definitions
        @rtype: [I{pdef},...]

        """
        if part.element is None:
            query = TypeQuery(part.type)
        else:
            query = ElementQuery(part.element)
        part_type = query.execute(self.schema())
        if part_type is None:
            raise TypeNotFound(query.ref)
        if part.type is not None:
            part_type = PartElement(part.name, part_type)
        if not input:
            return part_type
        if part_type.name is None:
            return part.name, part_type
        return part_type.name, part_type