diff options
author | jortel <devnull@localhost> | 2009-08-20 23:56:52 +0000 |
---|---|---|
committer | jortel <devnull@localhost> | 2009-08-20 23:56:52 +0000 |
commit | 49f92210582415ca0ce0ae848a0f3ed868443c5d (patch) | |
tree | 8e2bccc92c543ef0dddb98abedceb30c0ac0577b | |
parent | 2402892309c6bd9c25cedd70411b91b68855df4f (diff) | |
download | suds-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__.py | 6 | ||||
-rw-r--r-- | suds/bindings/binding.py | 6 | ||||
-rw-r--r-- | suds/client.py | 230 | ||||
-rw-r--r-- | suds/mx/appender.py | 6 | ||||
-rw-r--r-- | suds/servicedefinition.py | 19 | ||||
-rw-r--r-- | suds/wsdl.py | 31 |
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. |