summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjortel <devnull@localhost>2008-04-28 15:41:27 +0000
committerjortel <devnull@localhost>2008-04-28 15:41:27 +0000
commit68d6751bfc706a0b2f77bcc1ba94221bb694afc7 (patch)
treef6780f5a6e668e3a6e416836903224341a298a0c
parent25893aa7efbf5233465b4594b89cf0cae3e8e5e0 (diff)
downloadsuds-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--README41
-rw-r--r--suds/schema.py1
-rw-r--r--suds/serviceproxy.py191
-rw-r--r--test/test.py5
4 files changed, 163 insertions, 75 deletions
diff --git a/README b/README
index b9740d8..55f40a1 100644
--- a/README
+++ b/README
@@ -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')