diff options
author | jortel <devnull@localhost> | 2008-04-28 15:41:27 +0000 |
---|---|---|
committer | jortel <devnull@localhost> | 2008-04-28 15:41:27 +0000 |
commit | 68d6751bfc706a0b2f77bcc1ba94221bb694afc7 (patch) | |
tree | f6780f5a6e668e3a6e416836903224341a298a0c | |
parent | 25893aa7efbf5233465b4594b89cf0cae3e8e5e0 (diff) | |
download | suds-68d6751bfc706a0b2f77bcc1ba94221bb694afc7.tar.gz |
refactor ServiceProxy into ServiceProxy, Method, Factory and Client classes; change 'http_proxy' kwarg to 'proxy' and change expected value to be a dict() with key<protocol>=proxy.
-rw-r--r-- | README | 41 | ||||
-rw-r--r-- | suds/schema.py | 1 | ||||
-rw-r--r-- | suds/serviceproxy.py | 191 | ||||
-rw-r--r-- | test/test.py | 5 |
4 files changed, 163 insertions, 75 deletions
@@ -372,5 +372,44 @@ version-0.1.7 (04-08-08): for lots of collaboration on #suds. -version-0.1.8 (in-progress): +version-0.1.8 (04-28-08): + * Contains the first cut at the rpc/encoded soap style. + + * Replaced Property class with suds.sudsobject.Object. The Property class was developed a long time + ago with a slightly different purpose. The suds Object is a simpler (more straight forward) approach that + requires less code and works better in the debugger. + + * The Binding and the encoding is selected on a per-method basis which is more consistent with the wsdl. + + * The (nil_supported) and (faults) flag(s) passed into the service proxy using **kwargs. In addition to these + flags, a (http_proxy) flag has been added and is passed to the urllib2.Request object. The following args + are supported: + * faults = Raise faults raised by server (default:True), else return tuple from service method invocation + as (http code, object). + * nil_supported = The bindings will set the xsi:nil="true" on nodes that have a value=None when this + flag is True (default:True). Otherwise, an empty node <x/> is sent. + * proxy = An http proxy to be specified on requests (default:{}). + The proxy is defined as {protocol:proxy,} + + * Http proxy supported (see above). + + * ServiceProxy refactored to delegate to a SoapClient. Since the service proxy exposes web services via getattr(), + any attribute (including methods) provided by the ServiceProxy class hides WS operations defined by the + wsdl. So, by moving everything to the SoapClient, wsdl operations are no longer hidden without + having to use *hoky* names for attributes and methods in the service proxy. Instead, the service proxy has + __client__ and __factory__ attributes (which really should be at low risk for name collision). For now the + get_instance() and get_enum() methods have not been moved to preserve backward compatibility. Although, + the prefered API change would to replace: + + > service = ServiceProxy('myurl') + > person = service.get_instance('person') + + ... with something like ... + + > service = ServiceProxy('myurl') + > factory = service.__factory__ + > person = factory.get_instance('person') + + After a few releases giving time for users to switch the new API, the get_instance() and get_enum() + methods may be removed with a notice in big letters. diff --git a/suds/schema.py b/suds/schema.py index ba2aa28..0b35519 100644 --- a/suds/schema.py +++ b/suds/schema.py @@ -14,7 +14,6 @@ # written by: Jeff Ortel ( jortel@redhat.com ) from suds import * -from suds.property import Property from sax import Parser, splitPrefix, defns from urlparse import urljoin diff --git a/suds/serviceproxy.py b/suds/serviceproxy.py index 2587eb0..8a19c63 100644 --- a/suds/serviceproxy.py +++ b/suds/serviceproxy.py @@ -13,7 +13,7 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # written by: Jeff Ortel ( jortel@redhat.com ) -from urllib2 import Request, urlopen, HTTPError +from urllib2 import Request, urlopen, urlparse, HTTPError from suds import * from suds.sudsobject import Object from suds.builder import Builder @@ -22,31 +22,83 @@ from suds.wsdl import WSDL class ServiceProxy(object): - """ a lightweight soap based web service proxy""" + """ + A lightweight soap based web service proxy. + Flags: + * faults = Raise faults raised by server (default:True), else return tuple from service method invocation + as (http code, object). + * nil_supported = The bindings will set the xsi:nil="true" on nodes that have a value=None when this + flag is True (default:True). Otherwise, an empty node <x/> is sent. + * proxy = An http proxy to be specified on requests (default:{}). + The proxy is defined as {protocol:proxy,} + """ def __init__(self, url, **kwargs): - self.__kwargs = kwargs - self.__wsdl = WSDL(url) - self.__schema = self.__wsdl.get_schema() - self.__builder = Builder(self.__schema) - self._log = logger('serviceproxy') + client = Client(url, **kwargs) + self.__client__ = client + self.__factory__ = Factory(client.schema) + + def get_instance(self, name): + """get an instance of an meta-object by type.""" + return self.__factory__.get_instance(name) + + def get_enum(self, name): + """ get an enumeration """ + return self.__factory__.get_enum(name) + + def __str__(self): + return str(self.__client__) - def _get_methods(self): - """get a list of methods provided by this service""" - list = [] - for op in self.__wsdl.get_operations(): - method = op.attribute('name') - binding = self.__wsdl.get_binding(method, **self.__kwargs) - ptypes = binding.get_ptypes(method) - params = ['%s{%s}' % (t[0], t[1].asref()[0]) for t in ptypes] - m = '%s(%s)' % (method, ', '.join(params)) - list.append(m) - return list + def __unicode__(self): + return unicode(self.__client__) + + def __getattr__(self, name): + builtin = name.startswith('__') and name.endswith('__') + if builtin: + return self.__dict__[name] + operation = self.__client__.wsdl.get_operation(name) + if operation is None: + raise MethodNotFound(name) + method = Method(self.__client__, name) + return method + + +class Method(object): + + """method wrapper""" + + def __init__(self, client, name): + self.client = client + self.name = name + self.log = client.log + + def __call__(self, *args): + """call the method""" + result = None + try: + result = self.client.send(self, *args) + except WebFault, e: + if self.client.faults: + self.log.debug('raising (%s)', e) + raise e + else: + self.log.debug('fault (%s)', e) + result = (500, e) + return result + + +class Factory: + + """ A factory for instantiating types defined in the wsdl """ + def __init__(self, schema): + self.schema = schema + self.builder = Builder(schema) + def get_instance(self, name): """get an instance of an meta-object by type.""" try: - return self.__builder.build(name) + return self.builder.build(name) except TypeNotFound, e: raise e except: @@ -54,7 +106,7 @@ class ServiceProxy(object): def get_enum(self, name): """ get an enumeration """ - type = self.__schema.find(name) + type = self.schema.find(name) if type is None: raise TypeNotFound(name) data = Object.instance(name) @@ -62,20 +114,31 @@ class ServiceProxy(object): enum = e.get_name() setattr(data, enum, enum) return data + + +class Client: + + """ a lightweight soap based web client""" + + def __init__(self, url, **kwargs): + self.kwargs = kwargs + self.faults = self.kwargs.get('faults', True) + self.wsdl = WSDL(url) + self.schema = self.wsdl.get_schema() + self.builder = Builder(self.schema) + self.log = logger('serviceproxy') - def _send(self, method, *args): + def send(self, method, *args): """"send the required soap message to invoke the specified method""" result = None - binding = self.__wsdl.get_binding(method.name, **self.__kwargs) + binding = self.wsdl.get_binding(method.name, **self.kwargs) headers = self.__headers(method.name) - location = self.__wsdl.get_location().encode('utf-8') - proxy = self.__kwargs.get('http_proxy', None) + location = self.wsdl.get_location().encode('utf-8') msg = binding.get_message(method.name, *args) - self._log.debug('sending to (%s)\nmessage:\n%s', location, msg) + self.log.debug('sending to (%s)\nmessage:\n%s', location, msg) try: request = Request(location, msg, headers) - if proxy is not None: - request.set_proxy(proxy) + self.__set_proxies(location, request) fp = urlopen(request) reply = fp.read() result = self.__succeeded(binding, method, reply) @@ -83,23 +146,28 @@ class ServiceProxy(object): result = self.__failed(binding, method, e) return result - def _faults(self): - """ get whether the proxy should raise web faults on error """ - return self.__kwargs.get('faults', True) + def __set_proxies(self, location, request): + """ set the proxies for the request """ + proxies = self.kwargs.get('proxy', {}) + protocol = urlparse.urlparse(location).scheme + proxy = proxies.get(protocol, None) + if proxy is not None: + self.log.info('proxy %s used for %s', proxy, location) + request.set_proxy(proxy, protocol) def __headers(self, method): """ get http headers """ - action = self.__wsdl.get_soap_action(method) + action = self.wsdl.get_soap_action(method) return \ { 'SOAPAction': action, 'Content-Type' : 'text/xml' } def __succeeded(self, binding, method, reply): """ request succeeded, process reply """ - self._log.debug('http succeeded:\n%s', reply) + self.log.debug('http succeeded:\n%s', reply) if len(reply) > 0: p = binding.get_reply(method.name, reply) - if self._faults(): + if self.faults: return p else: return (200, p) @@ -110,65 +178,44 @@ class ServiceProxy(object): """ request failed, process reply based on reason """ status, reason = (error.code, error.msg) reply = error.fp.read() - self._log.debug('http failed:\n%s', reply) + self.log.debug('http failed:\n%s', reply) if status == 500: if len(reply) > 0: return (status, binding.get_fault(reply)) else: return (status, None) - if self._faults(): + if self.faults: raise Exception((status, reason)) else: return (status, None) + def __get_methods(self): + """get a list of methods provided by this service""" + list = [] + for op in self.wsdl.get_operations(): + method = op.attribute('name') + binding = self.wsdl.get_binding(method, **self.kwargs) + ptypes = binding.get_ptypes(method) + params = ['%s{%s}' % (t[0], t[1].asref()[0]) for t in ptypes] + m = '%s(%s)' % (method, ', '.join(params)) + list.append(m) + return list + def __str__(self): return unicode(self).encode('utf-8') def __unicode__(self): try: - s = 'service (%s)' % self.__wsdl.get_servicename() + s = 'service (%s)' % self.wsdl.get_servicename() s += '\n\tprefixes:\n' - prefixes = self.__wsdl.mapped_prefixes() + prefixes = self.wsdl.mapped_prefixes() prefixes.sort() for p in prefixes: s += '\t\t%s = "%s"\n' % p s += '\n\tmethods:\n' - for m in self._get_methods(): + for m in self.__get_methods(): s += '\t\t%s\n' % m return unicode(s) except Exception, e: - self._log.exception(e) + self.log.exception(e) return u'service not available' - - def __repr__(self): - return unicode(self).encode('utf-8') - - def __getattr__(self, name): - try: - return self.__getattribute__(name) - except: - pass - if self.__wsdl.get_operation(name) is None: - raise MethodNotFound(name) - return self.Method(self, name) - - class Method(object): - """method wrapper""" - def __init__(self, proxy, name): - self.proxy = proxy - self.name = name - self.log = proxy._log - - def __call__(self, *args): - """call the method""" - result = None - try: - result = self.proxy._send(self, *args) - except WebFault, e: - if self.proxy._faults(): - self.log.debug('raising (%s)', e) - raise e - else: - self.log.debug('fault (%s)', e) - result = (500, e) - return result diff --git a/test/test.py b/test/test.py index 21141af..59a8dfe 100644 --- a/test/test.py +++ b/test/test.py @@ -47,7 +47,10 @@ class Test: def test_misc(self): - service = ServiceProxy('https://sec.neurofuzz-software.com/paos/genSSHA-SOAP.php?wsdl', faults=True, nil_supported=False) + service = ServiceProxy('https://sec.neurofuzz-software.com/paos/genSSHA-SOAP.php?wsdl', + faults=True, + nil_supported=False, + proxy={'http':'10.1.1.1'}) print service print service.genSSHA('hello', 'sha1') |