summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjortel <devnull@localhost>2009-08-20 23:56:52 +0000
committerjortel <devnull@localhost>2009-08-20 23:56:52 +0000
commit49f92210582415ca0ce0ae848a0f3ed868443c5d (patch)
tree8e2bccc92c543ef0dddb98abedceb30c0ac0577b
parent2402892309c6bd9c25cedd70411b91b68855df4f (diff)
downloadsuds-49f92210582415ca0ce0ae848a0f3ed868443c5d.tar.gz
Rework the wsdl, client and servicedefinition to handle wsdls with multiple services. To ensure symmetry, the API for multiple ports was modified and the implementation simplified. See https://fedorahosted.org/suds/wiki/Documentation. The client API also replaces the self.service & self.sd with self.services and self.sd is not a list. We may want to simplify the ServiceDefintion as it currently repeats all of the /types/ for each service. Fixes ticket #247.
-rw-r--r--suds/__init__.py6
-rw-r--r--suds/bindings/binding.py6
-rw-r--r--suds/client.py230
-rw-r--r--suds/mx/appender.py6
-rw-r--r--suds/servicedefinition.py19
-rw-r--r--suds/wsdl.py31
6 files changed, 121 insertions, 177 deletions
diff --git a/suds/__init__.py b/suds/__init__.py
index cb3768f..d1514d0 100644
--- a/suds/__init__.py
+++ b/suds/__init__.py
@@ -31,7 +31,7 @@ import socket
__version__ = '0.3.7'
-properties = dict(version=__version__, build="(beta) R549-20090819")
+properties = dict(version=__version__, build="(beta) R550-20090820")
#
# Exceptions
@@ -44,6 +44,10 @@ class MethodNotFound(Exception):
class PortNotFound(Exception):
def __init__(self, name):
Exception.__init__(self, "Port not found: '%s'" % name)
+
+class ServiceNotFound(Exception):
+ def __init__(self, name):
+ Exception.__init__(self, "Service not found: '%s'" % name)
class TypeNotFound(Exception):
def __init__(self, name):
diff --git a/suds/bindings/binding.py b/suds/bindings/binding.py
index b876795..662379c 100644
--- a/suds/bindings/binding.py
+++ b/suds/bindings/binding.py
@@ -72,7 +72,7 @@ class Binding:
"""
Get the appropriate XML decoder.
@return: Either the (basic|typed) unmarshaller.
- @rtype: L{Marshaller}
+ @rtype: L{UmxTyped}
"""
if typed:
return UmxTyped(self.schema)
@@ -82,8 +82,8 @@ class Binding:
def marshaller(self):
"""
Get the appropriate XML encoder.
- @return: Either L{literal} marshaller.
- @rtype: L{Marshaller}
+ @return: An L{MxLiteral} marshaller.
+ @rtype: L{MxLiteral}
"""
return MxLiteral(self.schema)
diff --git a/suds/client.py b/suds/client.py
index 991df63..07f8b98 100644
--- a/suds/client.py
+++ b/suds/client.py
@@ -104,9 +104,12 @@ class Client(object):
options.set(**kwargs)
self.options = options
self.wsdl = Definitions(url, options)
- self.service = Wrapper(Service(self))
self.factory = Factory(self.wsdl)
- self.sd = ServiceDefinition(self.wsdl)
+ self.service = ServiceIterator(self, self.wsdl.services)
+ self.sd = []
+ for s in self.wsdl.services:
+ sd = ServiceDefinition(self.wsdl, s)
+ self.sd.append(sd)
self.messages = dict(tx=None, rx=None)
def set_options(self, **kwargs):
@@ -162,7 +165,8 @@ class Client(object):
s.append('Suds ( https://fedorahosted.org/suds/ )')
s.append(' version: %s' % version)
s.append(' %s build: %s' % (build[0], build[1]))
- s.append('\n\n%s' % unicode(self.sd))
+ for sd in self.sd:
+ s.append('\n\n%s' % unicode(sd))
return ''.join(s)
@@ -219,164 +223,115 @@ class Factory:
"""
self.resolver = PathResolver(self.wsdl, ps)
-
-class Wrapper:
+
+class ServiceIterator:
"""
- Wrapper, translates to resolve() and call().
+ Services (namespace) iterator.
+ @ivar client: A client object.
+ @type client: L{Client}
+ @ivar services: A list of services.
+ @type services: list
"""
+ def __init__(self, client, services):
+ """
+ @param client: A client object.
+ @type client: L{Client}
+ @param services: A list of services.
+ @type services: list
+ """
+ self.client = client
+ self.services = services
- def __init__(self, wrapped):
- self.__wrapped__ = wrapped
-
def __getattr__(self, name):
- builtin = name.startswith('__') and name.endswith('__')
+ builtin = name.startswith('__') and name.endswith('__')
if builtin:
return self.__dict__[name]
+ if len(self.services):
+ s = self.services[0]
+ p = PortIterator(self.client, s.ports)
else:
- return self.__wrapped__.resolve(name)
-
- def __call__(self, *args, **kwargs):
- target = self.__wrapped__
- return target.call(*args, **kwargs)
+ raise Exception, 'No services defined'
+ return getattr(p, name)
- def __str__(self):
- return str(self.__wrapped__)
-
- def __unicode__(self):
- return unicode(self.__wrapped__)
-
+ def __getitem__(self, name):
+ if len(self.services) == 1:
+ s = self.services[0]
+ p = PortIterator(self.client, s.ports)
+ return p[name]
+ if isinstance(name, int):
+ s = self.services[name]
+ return PortIterator(self.client, s.ports)
+ for s in self.services:
+ if name == s.name:
+ return PortIterator(self.client, s.ports)
+ raise ServiceNotFound, name
+
-class Service:
+class PortIterator:
"""
- The I{service} (namespace) object.
+ Port (namespace) iterator.
@ivar client: A client object.
@type client: L{Client}
+ @ivar ports: A list of ports.
+ @type ports: list
"""
-
- def __init__(self, client):
+ def __init__(self, client, ports):
"""
@param client: A client object.
@type client: L{Client}
+ @param ports: A list of ports.
+ @type ports: list
"""
self.client = client
-
- def resolve(self, name):
- """
- Resolve I{name} to a service port.
- @param name: A port/method name.
- @type name: str
- @return: The I{wrapped} L{Port}
- """
- service = self.client.wsdl.service
- port = self.dport(service)
- if port is None:
- port = service.port(name)
- return Wrapper(Port(self.client, (name, port)))
+ self.ports = ports
- def dport(self, service):
- """
- The I{default} port as defined by L{Options}.
- @param service: A wsdl service object.
- @type service : L{wsdl.Service}
- @return: The I{raw} L{wsdl.Port}
- """
- name = self.client.options.port
- if name is not None:
- port = service.port(name)
- if port is None:
- raise PortNotFound(name)
+ def __getattr__(self, name):
+ builtin = name.startswith('__') and name.endswith('__')
+ if builtin:
+ return self.__dict__[name]
+ if len(self.ports):
+ p = self.ports[0]
+ m = MethodIterator(self.client, p.methods)
else:
- port = None
- return port
+ raise Exception, 'No ports defined'
+ return getattr(m, name)
- def __str__(self):
- return unicode(self)
+ def __getitem__(self, name):
+ if isinstance(name, int):
+ p = self.ports[name]
+ return MethodIterator(self.client, p.methods)
+ for p in self.ports:
+ if name == p.name:
+ return MethodIterator(self.client, p.methods)
+ raise PortNotFound(name)
- def __unicode__(self):
- return unicode(self.client.sd)
-
-class Port:
+class MethodIterator:
"""
- The I{port} (namespace) object.
+ Method (namespace) iterator.
@ivar client: A client object.
@type client: L{Client}
- @ivar port: A port tuple (name, L{wsdl.Port})
- @type port: tuple
+ @ivar methods: A dict of methods.
+ @type methods: dict
"""
-
- def __init__(self, client, port):
+ def __init__(self, client, methods):
"""
@param client: A client object.
@type client: L{Client}
- @param port: A port tuple (name, L{wsdl.Port})
- @type port: tuple
+ @param methods: A dict of methods.
+ @type methods: dict
"""
self.client = client
- self.port = port
-
- def resolve(self, name, strict=True):
- """
- Resolve I{name} to a service method.
- @param name: A method name.
- @type name: str
- @param strict: Make sure this is a real port.
- @type strict: bool
- @return: The I{wrapped} L{Method}
- """
- if strict and self.anyport():
- raise PortNotFound(self.name())
- finder = self.finder()
- method = finder.method(name)
- if method is None:
- raise MethodNotFound(name)
- return Wrapper(Method(self.client, method))
-
- def name(self):
- """
- The port name.
- @return: The port name.
- @rtype: str
- """
- return self.port[0]
-
- def realport(self):
- """
- The I{real} port.
- @return: The contained port object.
- @rtype: L{wsdl.Port}
- """
- return self.port[1]
-
- def anyport(self):
- """
- Get whether this port references a real port or just a
- placeholder for I{any} port.
- @return: True if real port reference is None.
- @rtype: bool
- """
- return ( self.realport() is None )
+ self.methods = methods
- def finder(self):
- """
- The method name I{finder}.
- @return: A method name resolver.
- @rtype: (L{wsdl.Port}|L{wsdl.Service})
- """
- if self.anyport():
- return self.client.wsdl.service
- else:
- return self.realport()
-
- def call(self, *args, **kwargs):
- """
- When called, this means that this port was returned by the
- Service.resolve() but the user intended to call a method.
- The name is used to lookup a method and forwards the invocation
- to the method.
- """
- method = self.resolve(self.name(), strict=False)
- return method(*args, **kwargs)
+ def __getattr__(self, name):
+ builtin = name.startswith('__') and name.endswith('__')
+ if builtin:
+ return self.__dict__[name]
+ m = self.methods.get(name)
+ if m is None:
+ raise MethodNotFound, name
+ return Method(self.client, m)
class Method:
@@ -397,19 +352,10 @@ class Method:
"""
self.client = client
self.method = method
-
- def resolve(self, name):
- """
- Error, not permitted.
- @param name: A method attribute to be resolved.
- @type name: str.
- @raise AttributeError: Always.
- """
- raise AttributeError(name)
- def call(self, *args, **kwargs):
+ def __call__(self, *args, **kwargs):
"""
- Method Invocation.
+ Invoke the method.
"""
clientclass = self.clientclass(kwargs)
client = clientclass(self.client, self.method)
@@ -422,9 +368,11 @@ class Method:
return client.invoke(args, kwargs)
def faults(self):
+ """ get faults option """
return self.client.options.faults
def clientclass(self, kwargs):
+ """ get soap client class """
if SimClient.simulation(kwargs):
return SimClient
else:
@@ -475,13 +423,13 @@ class SoapClient:
timer.stop()
metrics.log.debug(
"message for '%s' created: %s",
- self.method.qname, timer)
+ self.method.name, timer)
timer.start()
result = self.send(msg)
timer.stop()
metrics.log.debug(
"method '%s' invoked: %s",
- self.method.qname, timer)
+ self.method.name, timer)
return result
def send(self, msg):
diff --git a/suds/mx/appender.py b/suds/mx/appender.py
index 116f035..83813dd 100644
--- a/suds/mx/appender.py
+++ b/suds/mx/appender.py
@@ -61,7 +61,7 @@ class ContentAppender:
def __init__(self, marshaller):
"""
@param marshaller: A marshaller.
- @type marshaller: L{MBase}
+ @type marshaller: L{suds.mx.core.Core}
"""
self.default = PrimativeAppender(marshaller)
self.appenders = (
@@ -99,13 +99,13 @@ class Appender:
"""
An appender used by the marshaller to append content.
@ivar marshaller: A marshaller.
- @type marshaller: L{MBase}
+ @type marshaller: L{suds.mx.core.Core}
"""
def __init__(self, marshaller):
"""
@param marshaller: A marshaller.
- @type marshaller: L{MBase}
+ @type marshaller: L{suds.mx.core.Core}
"""
self.marshaller = marshaller
diff --git a/suds/servicedefinition.py b/suds/servicedefinition.py
index 46d4961..81b5a0d 100644
--- a/suds/servicedefinition.py
+++ b/suds/servicedefinition.py
@@ -41,14 +41,20 @@ class ServiceDefinition:
@type types: [I{Type},..]
"""
- def __init__(self, wsdl):
+ def __init__(self, wsdl, service):
+ """
+ @param wsdl: A wsdl object
+ @type wsdl: L{Definitions}
+ @param service: A service B{name}.
+ @type service: str
+ """
self.wsdl = wsdl
- self.service = wsdl.service
+ self.service = service
self.ports = []
self.params = []
self.types = []
self.prefixes = []
- self.addports(wsdl.service)
+ self.addports()
self.paramtypes()
self.publictypes()
self.getprefixes()
@@ -62,7 +68,7 @@ class ServiceDefinition:
for ns in self.prefixes:
self.wsdl.root.addPrefix(ns[0], ns[1])
- def addports(self, s):
+ def addports(self):
"""
Look through the list of service ports and construct a list of tuples where
each tuple is used to describe a port and it's list of methods as:
@@ -71,11 +77,10 @@ class ServiceDefinition:
"""
timer = metrics.Timer()
timer.start()
- for port in s.ports:
+ for port in self.service.ports:
p = self.findport(port)
for op in port.binding.operations.values():
- qname = ':'.join((port.name, op.name))
- m = s.method(qname)
+ m = p[0].method(op.name)
binding = m.binding.input
method = (m.name, binding.param_defs(m))
p[1].append(method)
diff --git a/suds/wsdl.py b/suds/wsdl.py
index b8ab8d2..aecd538 100644
--- a/suds/wsdl.py
+++ b/suds/wsdl.py
@@ -182,7 +182,7 @@ class Definitions(WObject):
self.messages = {}
self.port_types = {}
self.bindings = {}
- self.service = None
+ self.services = []
self.add_children(self.root)
self.children.sort()
pmd = self.__metadata__.__print__
@@ -193,8 +193,8 @@ class Definitions(WObject):
self.resolve()
self.build_schema()
self.set_wrapped()
- if self.service is not None:
- self.add_methods()
+ for s in self.services:
+ self.add_methods(s)
log.debug("wsdl at '%s' loaded:\n%s", url, self)
def mktns(self, root):
@@ -228,7 +228,7 @@ class Definitions(WObject):
self.bindings[child.qname] = child
continue
if isinstance(child, Service):
- self.service = child
+ self.services.append(child)
continue
def open_imports(self):
@@ -258,14 +258,14 @@ class Definitions(WObject):
self.schema.merge(s)
return self.schema
- def add_methods(self):
+ def add_methods(self, service):
""" Build method view for service """
bindings = {
'document/literal' : Document(self),
'rpc/literal' : RPC(self),
'rpc/encoded' : Encoded(self)
}
- for p in self.service.ports:
+ for p in service.ports:
binding = p.binding
ptype = p.binding.type
operations = p.binding.type.operations.values()
@@ -281,9 +281,7 @@ class Definitions(WObject):
key = '/'.join((op.soap.style, op.soap.output.body.use))
m.binding.output = bindings.get(key)
op = ptype.operation(name)
- m.qname = ':'.join((p.name, name))
- self.service.methods[m.name] = m
- self.service.methods[m.qname] = m
+ p.methods[name] = m
def set_wrapped(self):
""" set (wrapped|bare) flag on messages """
@@ -762,6 +760,7 @@ class Port(NamedObject):
self.binding = root.get('binding')
address = root.getChild('address')
self.location = address.get('location').encode('utf-8')
+ self.methods = {}
def method(self, name):
"""
@@ -771,8 +770,7 @@ class Port(NamedObject):
@return: The requested method object.
@rtype: I{Method}
"""
- qname = ':'.join((self.name, name))
- return self.__service.method(qname)
+ return self.methods.get(name)
class Service(NamedObject):
@@ -793,7 +791,6 @@ class Service(NamedObject):
"""
NamedObject.__init__(self, root, definitions)
self.ports = []
- self.methods = {}
for p in root.getChildren('port'):
port = Port(p, definitions, self)
self.ports.append(port)
@@ -811,16 +808,6 @@ class Service(NamedObject):
return p
return None
- def method(self, name):
- """
- Get a method defined in one of the portTypes by name.
- @param name: A method name.
- @type name: str
- @return: The requested method object.
- @rtype: I{Method}
- """
- return self.methods.get(name)
-
def setlocation(self, url, names=None):
"""
Override the invocation location (url) for service method.