Libraries for Subliminal
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the (LGPL) GNU Lesser General Public License as
|
||||
# published by the Free Software Foundation; either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Library Lesser General Public License for more details at
|
||||
# ( http://www.gnu.org/licenses/lgpl.html ).
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
# written by: Jeff Ortel ( jortel@redhat.com )
|
||||
|
||||
"""
|
||||
Provides modules containing classes to support Web Services (SOAP)
|
||||
bindings.
|
||||
"""
|
||||
@@ -0,0 +1,538 @@
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the (LGPL) GNU Lesser General Public License as
|
||||
# published by the Free Software Foundation; either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Library Lesser General Public License for more details at
|
||||
# ( http://www.gnu.org/licenses/lgpl.html ).
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
# written by: Jeff Ortel ( jortel@redhat.com )
|
||||
|
||||
"""
|
||||
Provides classes for (WS) SOAP bindings.
|
||||
"""
|
||||
|
||||
from logging import getLogger
|
||||
from suds import *
|
||||
from suds.sax import Namespace
|
||||
from suds.sax.parser import Parser
|
||||
from suds.sax.document import Document
|
||||
from suds.sax.element import Element
|
||||
from suds.sudsobject import Factory, Object
|
||||
from suds.mx import Content
|
||||
from suds.mx.literal import Literal as MxLiteral
|
||||
from suds.umx.basic import Basic as UmxBasic
|
||||
from suds.umx.typed import Typed as UmxTyped
|
||||
from suds.bindings.multiref import MultiRef
|
||||
from suds.xsd.query import TypeQuery, ElementQuery
|
||||
from suds.xsd.sxbasic import Element as SchemaElement
|
||||
from suds.options import Options
|
||||
from suds.plugin import PluginContainer
|
||||
from copy import deepcopy
|
||||
|
||||
log = getLogger(__name__)
|
||||
|
||||
envns = ('SOAP-ENV', 'http://schemas.xmlsoap.org/soap/envelope/')
|
||||
|
||||
|
||||
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)
|
||||
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
|
||||
|
||||
|
||||
class PartElement(SchemaElement):
|
||||
"""
|
||||
A part used to represent a message part when the part
|
||||
references a schema type and thus assumes to be an element.
|
||||
@ivar resolved: The part type.
|
||||
@type resolved: L{suds.xsd.sxbase.SchemaObject}
|
||||
"""
|
||||
|
||||
def __init__(self, name, resolved):
|
||||
"""
|
||||
@param name: The part name.
|
||||
@type name: str
|
||||
@param resolved: The part type.
|
||||
@type resolved: L{suds.xsd.sxbase.SchemaObject}
|
||||
"""
|
||||
root = Element('element', ns=Namespace.xsdns)
|
||||
SchemaElement.__init__(self, resolved.schema, root)
|
||||
self.__resolved = resolved
|
||||
self.name = name
|
||||
self.form_qualified = False
|
||||
|
||||
def implany(self):
|
||||
return self
|
||||
|
||||
def optional(self):
|
||||
return True
|
||||
|
||||
def namespace(self, prefix=None):
|
||||
return Namespace.default
|
||||
|
||||
def resolve(self, nobuiltin=False):
|
||||
if nobuiltin and self.__resolved.builtin():
|
||||
return self
|
||||
else:
|
||||
return self.__resolved
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the (LGPL) GNU Lesser General Public License as
|
||||
# published by the Free Software Foundation; either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Library Lesser General Public License for more details at
|
||||
# ( http://www.gnu.org/licenses/lgpl.html ).
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
# written by: Jeff Ortel ( jortel@redhat.com )
|
||||
|
||||
"""
|
||||
Provides classes for the (WS) SOAP I{document/literal}.
|
||||
"""
|
||||
|
||||
from logging import getLogger
|
||||
from suds import *
|
||||
from suds.bindings.binding import Binding
|
||||
from suds.sax.element import Element
|
||||
|
||||
log = getLogger(__name__)
|
||||
|
||||
|
||||
class Document(Binding):
|
||||
"""
|
||||
The document/literal style. Literal is the only (@use) supported
|
||||
since document/encoded is pretty much dead.
|
||||
Although the soap specification supports multiple documents within the soap
|
||||
<body/>, it is very uncommon. As such, suds presents an I{RPC} view of
|
||||
service methods defined with a single document parameter. This is done so
|
||||
that the user can pass individual parameters instead of one, single document.
|
||||
To support the complete specification, service methods defined with multiple documents
|
||||
(multiple message parts), must present a I{document} view for that method.
|
||||
"""
|
||||
|
||||
def bodycontent(self, method, args, kwargs):
|
||||
#
|
||||
# The I{wrapped} vs I{bare} style is detected in 2 ways.
|
||||
# If there is 2+ parts in the message then it is I{bare}.
|
||||
# If there is only (1) part and that part resolves to a builtin then
|
||||
# it is I{bare}. Otherwise, it is I{wrapped}.
|
||||
#
|
||||
if not len(method.soap.input.body.parts):
|
||||
return ()
|
||||
wrapped = method.soap.input.body.wrapped
|
||||
if wrapped:
|
||||
pts = self.bodypart_types(method)
|
||||
root = self.document(pts[0])
|
||||
else:
|
||||
root = []
|
||||
n = 0
|
||||
for pd in self.param_defs(method):
|
||||
if n < len(args):
|
||||
value = args[n]
|
||||
else:
|
||||
value = kwargs.get(pd[0])
|
||||
n += 1
|
||||
p = self.mkparam(method, pd, value)
|
||||
if p is None:
|
||||
continue
|
||||
if not wrapped:
|
||||
ns = pd[1].namespace('ns0')
|
||||
p.setPrefix(ns[0], ns[1])
|
||||
root.append(p)
|
||||
return root
|
||||
|
||||
def replycontent(self, method, body):
|
||||
wrapped = method.soap.output.body.wrapped
|
||||
if wrapped:
|
||||
return body[0].children
|
||||
else:
|
||||
return body.children
|
||||
|
||||
def document(self, wrapper):
|
||||
"""
|
||||
Get the document root. For I{document/literal}, this is the
|
||||
name of the wrapper element qualifed by the schema tns.
|
||||
@param wrapper: The method name.
|
||||
@type wrapper: L{xsd.sxbase.SchemaObject}
|
||||
@return: A root element.
|
||||
@rtype: L{Element}
|
||||
"""
|
||||
tag = wrapper[1].name
|
||||
ns = wrapper[1].namespace('ns0')
|
||||
d = Element(tag, ns=ns)
|
||||
return d
|
||||
|
||||
def mkparam(self, method, pdef, object):
|
||||
#
|
||||
# Expand list parameters into individual parameters
|
||||
# each with the type information. This is because in document
|
||||
# arrays are simply unbounded elements.
|
||||
#
|
||||
if isinstance(object, (list, tuple)):
|
||||
tags = []
|
||||
for item in object:
|
||||
tags.append(self.mkparam(method, pdef, item))
|
||||
return tags
|
||||
else:
|
||||
return Binding.mkparam(self, method, pdef, object)
|
||||
|
||||
def param_defs(self, method):
|
||||
#
|
||||
# Get parameter definitions for document literal.
|
||||
# The I{wrapped} vs I{bare} style is detected in 2 ways.
|
||||
# If there is 2+ parts in the message then it is I{bare}.
|
||||
# If there is only (1) part and that part resolves to a builtin then
|
||||
# it is I{bare}. Otherwise, it is I{wrapped}.
|
||||
#
|
||||
pts = self.bodypart_types(method)
|
||||
wrapped = method.soap.input.body.wrapped
|
||||
if not wrapped:
|
||||
return pts
|
||||
result = []
|
||||
# wrapped
|
||||
for p in pts:
|
||||
resolved = p[1].resolve()
|
||||
for child, ancestry in resolved:
|
||||
if child.isattr():
|
||||
continue
|
||||
if self.bychoice(ancestry):
|
||||
log.debug(
|
||||
'%s\ncontained by <choice/>, excluded as param for %s()',
|
||||
child,
|
||||
method.name)
|
||||
continue
|
||||
result.append((child.name, child))
|
||||
return result
|
||||
|
||||
def returned_types(self, method):
|
||||
result = []
|
||||
wrapped = method.soap.output.body.wrapped
|
||||
rts = self.bodypart_types(method, input=False)
|
||||
if wrapped:
|
||||
for pt in rts:
|
||||
resolved = pt.resolve(nobuiltin=True)
|
||||
for child, ancestry in resolved:
|
||||
result.append(child)
|
||||
break
|
||||
else:
|
||||
result += rts
|
||||
return result
|
||||
|
||||
def bychoice(self, ancestry):
|
||||
"""
|
||||
The ancestry contains a <choice/>
|
||||
@param ancestry: A list of ancestors.
|
||||
@type ancestry: list
|
||||
@return: True if contains <choice/>
|
||||
@rtype: boolean
|
||||
"""
|
||||
for x in ancestry:
|
||||
if x.choice():
|
||||
return True
|
||||
return False
|
||||
@@ -0,0 +1,126 @@
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the (LGPL) GNU Lesser General Public License as
|
||||
# published by the Free Software Foundation; either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Library Lesser General Public License for more details at
|
||||
# ( http://www.gnu.org/licenses/lgpl.html ).
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
# written by: Jeff Ortel ( jortel@redhat.com )
|
||||
|
||||
"""
|
||||
Provides classes for handling soap multirefs.
|
||||
"""
|
||||
|
||||
from logging import getLogger
|
||||
from suds import *
|
||||
from suds.sax.element import Element
|
||||
|
||||
log = getLogger(__name__)
|
||||
|
||||
soapenc = (None, 'http://schemas.xmlsoap.org/soap/encoding/')
|
||||
|
||||
class MultiRef:
|
||||
"""
|
||||
Resolves and replaces multirefs.
|
||||
@ivar nodes: A list of non-multiref nodes.
|
||||
@type nodes: list
|
||||
@ivar catalog: A dictionary of multiref nodes by id.
|
||||
@type catalog: dict
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.nodes = []
|
||||
self.catalog = {}
|
||||
|
||||
def process(self, body):
|
||||
"""
|
||||
Process the specified soap envelope body and replace I{multiref} node
|
||||
references with the contents of the referenced node.
|
||||
@param body: A soap envelope body node.
|
||||
@type body: L{Element}
|
||||
@return: The processed I{body}
|
||||
@rtype: L{Element}
|
||||
"""
|
||||
self.nodes = []
|
||||
self.catalog = {}
|
||||
self.build_catalog(body)
|
||||
self.update(body)
|
||||
body.children = self.nodes
|
||||
return body
|
||||
|
||||
def update(self, node):
|
||||
"""
|
||||
Update the specified I{node} by replacing the I{multiref} references with
|
||||
the contents of the referenced nodes and remove the I{href} attribute.
|
||||
@param node: A node to update.
|
||||
@type node: L{Element}
|
||||
@return: The updated node
|
||||
@rtype: L{Element}
|
||||
"""
|
||||
self.replace_references(node)
|
||||
for c in node.children:
|
||||
self.update(c)
|
||||
return node
|
||||
|
||||
def replace_references(self, node):
|
||||
"""
|
||||
Replacing the I{multiref} references with the contents of the
|
||||
referenced nodes and remove the I{href} attribute. Warning: since
|
||||
the I{ref} is not cloned,
|
||||
@param node: A node to update.
|
||||
@type node: L{Element}
|
||||
"""
|
||||
href = node.getAttribute('href')
|
||||
if href is None:
|
||||
return
|
||||
id = href.getValue()
|
||||
ref = self.catalog.get(id)
|
||||
if ref is None:
|
||||
log.error('soap multiref: %s, not-resolved', id)
|
||||
return
|
||||
node.append(ref.children)
|
||||
node.setText(ref.getText())
|
||||
for a in ref.attributes:
|
||||
if a.name != 'id':
|
||||
node.append(a)
|
||||
node.remove(href)
|
||||
|
||||
def build_catalog(self, body):
|
||||
"""
|
||||
Create the I{catalog} of multiref nodes by id and the list of
|
||||
non-multiref nodes.
|
||||
@param body: A soap envelope body node.
|
||||
@type body: L{Element}
|
||||
"""
|
||||
for child in body.children:
|
||||
if self.soaproot(child):
|
||||
self.nodes.append(child)
|
||||
id = child.get('id')
|
||||
if id is None: continue
|
||||
key = '#%s' % id
|
||||
self.catalog[key] = child
|
||||
|
||||
def soaproot(self, node):
|
||||
"""
|
||||
Get whether the specified I{node} is a soap encoded root.
|
||||
This is determined by examining @soapenc:root='1'.
|
||||
The node is considered to be a root when the attribute
|
||||
is not specified.
|
||||
@param node: A node to evaluate.
|
||||
@type node: L{Element}
|
||||
@return: True if a soap encoded root.
|
||||
@rtype: bool
|
||||
"""
|
||||
root = node.getAttribute('root', ns=soapenc)
|
||||
if root is None:
|
||||
return True
|
||||
else:
|
||||
return ( root.value == '1' )
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the (LGPL) GNU Lesser General Public License as
|
||||
# published by the Free Software Foundation; either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Library Lesser General Public License for more details at
|
||||
# ( http://www.gnu.org/licenses/lgpl.html ).
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
# written by: Jeff Ortel ( jortel@redhat.com )
|
||||
|
||||
"""
|
||||
Provides classes for the (WS) SOAP I{rpc/literal} and I{rpc/encoded} bindings.
|
||||
"""
|
||||
|
||||
from logging import getLogger
|
||||
from suds import *
|
||||
from suds.mx.encoded import Encoded as MxEncoded
|
||||
from suds.umx.encoded import Encoded as UmxEncoded
|
||||
from suds.bindings.binding import Binding, envns
|
||||
from suds.sax.element import Element
|
||||
|
||||
log = getLogger(__name__)
|
||||
|
||||
|
||||
encns = ('SOAP-ENC', 'http://schemas.xmlsoap.org/soap/encoding/')
|
||||
|
||||
class RPC(Binding):
|
||||
"""
|
||||
RPC/Literal binding style.
|
||||
"""
|
||||
|
||||
def param_defs(self, method):
|
||||
return self.bodypart_types(method)
|
||||
|
||||
def envelope(self, header, body):
|
||||
env = Binding.envelope(self, header, body)
|
||||
env.addPrefix(encns[0], encns[1])
|
||||
env.set('%s:encodingStyle' % envns[0],
|
||||
'http://schemas.xmlsoap.org/soap/encoding/')
|
||||
return env
|
||||
|
||||
def bodycontent(self, method, args, kwargs):
|
||||
n = 0
|
||||
root = self.method(method)
|
||||
for pd in self.param_defs(method):
|
||||
if n < len(args):
|
||||
value = args[n]
|
||||
else:
|
||||
value = kwargs.get(pd[0])
|
||||
p = self.mkparam(method, pd, value)
|
||||
if p is not None:
|
||||
root.append(p)
|
||||
n += 1
|
||||
return root
|
||||
|
||||
def replycontent(self, method, body):
|
||||
return body[0].children
|
||||
|
||||
def method(self, method):
|
||||
"""
|
||||
Get the document root. For I{rpc/(literal|encoded)}, this is the
|
||||
name of the method qualifed by the schema tns.
|
||||
@param method: A service method.
|
||||
@type method: I{service.Method}
|
||||
@return: A root element.
|
||||
@rtype: L{Element}
|
||||
"""
|
||||
ns = method.soap.input.body.namespace
|
||||
if ns[0] is None:
|
||||
ns = ('ns0', ns[1])
|
||||
method = Element(method.name, ns=ns)
|
||||
return method
|
||||
|
||||
|
||||
class Encoded(RPC):
|
||||
"""
|
||||
RPC/Encoded (section 5) binding style.
|
||||
"""
|
||||
|
||||
def marshaller(self):
|
||||
return MxEncoded(self.schema())
|
||||
|
||||
def unmarshaller(self, typed=True):
|
||||
"""
|
||||
Get the appropriate XML decoder.
|
||||
@return: Either the (basic|typed) unmarshaller.
|
||||
@rtype: L{UmxTyped}
|
||||
"""
|
||||
if typed:
|
||||
return UmxEncoded(self.schema())
|
||||
else:
|
||||
return RPC.unmarshaller(self, typed)
|
||||
Reference in New Issue
Block a user