From e1f87183cd08a0e3d76129ff5429e2452585bdb2 Mon Sep 17 00:00:00 2001 From: jortel Date: Tue, 12 Jan 2010 23:15:00 +0000 Subject: Move transport caching to document-based caching. This approach leverages the performance of pickling/unpickling of sax DOM object graphs. Also, ensures that only valid/complete XML documents are cached. The NoCache class moved as well so users disabling the cache as Client(url, cache=NoCache) should switch to the preferred: Client(url, cache=None). --- suds/__init__.py | 8 +- suds/bindings/binding.py | 11 +- suds/cache.py | 290 +++++++++++++++++++++++++++++++++++++++++++++ suds/client.py | 7 +- suds/options.py | 10 +- suds/reader.py | 37 ++++++ suds/sax/__init__.py | 2 +- suds/transport/__init__.py | 68 ----------- suds/transport/cache.py | 158 ------------------------ suds/transport/http.py | 11 +- suds/transport/https.py | 3 - suds/transport/options.py | 4 - suds/wsdl.py | 8 +- suds/xsd/sxbasic.py | 16 +-- tests/axis2.py | 2 +- 15 files changed, 360 insertions(+), 275 deletions(-) create mode 100644 suds/cache.py create mode 100644 suds/reader.py delete mode 100644 suds/transport/cache.py diff --git a/suds/__init__.py b/suds/__init__.py index ec16846..e3f2db4 100644 --- a/suds/__init__.py +++ b/suds/__init__.py @@ -15,10 +15,8 @@ # written by: Jeff Ortel ( jortel@redhat.com ) """ -Suds is a lightweight SOAP python client that provides a service proxy -for Web Services. -@var properties: Library properties. -@type properties: dict +Suds is a lightweight SOAP python client that provides a +service proxy for Web Services. """ import os @@ -29,7 +27,7 @@ import sys # __version__ = '0.3.9' -__build__="(beta) R634-20100105" +__build__="(beta) R637-20100112" # # Exceptions diff --git a/suds/bindings/binding.py b/suds/bindings/binding.py index 1b828d1..c2e7810 100644 --- a/suds/bindings/binding.py +++ b/suds/bindings/binding.py @@ -51,8 +51,6 @@ class Binding: @type schema: L{xsd.schema.Schema} @ivar options: A dictionary options. @type options: L{Options} - @ivar parser: A sax parser. - @type parser: L{suds.sax.parser.Parser} """ replyfilter = (lambda s,r: r) @@ -64,8 +62,7 @@ class Binding: """ self.wsdl = wsdl self.schema = wsdl.schema - self.options = Options() - self.parser = Parser() + self.options = wsdl.options self.multiref = MultiRef() def unmarshaller(self, typed=True): @@ -138,7 +135,8 @@ class Binding: @rtype: tuple ( L{Element}, L{Object} ) """ reply = self.replyfilter(reply) - replyroot = self.parser.parse(string=reply) + sax = Parser() + replyroot = sax.parse(string=reply) soapenv = replyroot.getChild('Envelope') soapenv.promotePrefixes() soapbody = soapenv.getChild('Body') @@ -225,7 +223,8 @@ class Binding: @rtype: tuple ( L{Element}, L{Object} ) """ reply = self.replyfilter(reply) - faultroot = self.parser.parse(string=reply) + sax = Parser() + faultroot = sax.parse(string=reply) soapenv = faultroot.getChild('Envelope') soapbody = soapenv.getChild('Body') fault = soapbody.getChild('Fault') diff --git a/suds/cache.py b/suds/cache.py new file mode 100644 index 0000000..02f7e55 --- /dev/null +++ b/suds/cache.py @@ -0,0 +1,290 @@ +# 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 ) + +""" +Contains basic caching classes. +""" + +import os +from tempfile import gettempdir as tmp +from suds.transport import * +from datetime import datetime as dt +from datetime import timedelta +from cStringIO import StringIO +from logging import getLogger +try: + import cPickle as pickle +except: + import pickle + +log = getLogger(__name__) + + +class ByteCache: + """ + The URL caching object. + """ + + def put(self, id, fp): + """ + Put an item into the cache. + @param id: A file ID. + @type id: str + @param fp: A file stream. + @type fp: stream + @return: The stream. + @rtype: stream + """ + raise Exception('not-implemented') + + def get(self, id): + """ + Get an item from the cache by id. + @param id: A file ID. + @type id: str + @return: A stream when found, else None. + @rtype: stream + """ + raise Exception('not-implemented') + + def purge(self, id): + """ + Purge a file from the cache by id. + @param id: A file ID. + @type id: str + """ + raise Exception('not-implemented') + + def clear(self): + """ + @param id: A file ID. + @type id: str + """ + raise Exception('not-implemented') + + +class FileCache(ByteCache): + """ + A file-based URL cache. + @cvar fnprefix: The file name prefix. + @type fnprefix: str + @ivar fnsuffix: The file name suffix. + @type fnsuffix: str + @ivar duration: The cached file duration which defines how + long the file will be cached. + @type duration: (unit, value) + @ivar location: The directory for the cached files. + @type location: str + """ + fnprefix = 'suds' + units = ('months', 'weeks', 'days', 'hours', 'minutes', 'seconds') + + def __init__(self, location=None, **duration): + """ + @param location: The directory for the cached files. + @type location: str + @param duration: The cached file duration which defines how + long the file will be cached. A duration=0 means forever. + The duration may be: (months|weeks|days|hours|minutes|seconds). + @type duration: {unit:value} + """ + self.fnsuffix = 'xml' + if location is None: + location = os.path.join(tmp(), 'suds') + self.location = location + self.duration = (None, 0) + self.setduration(**duration) + + def setduration(self, **duration): + """ + Set the caching duration which defines how long the + file will be cached. + @param duration: The cached file duration which defines how + long the file will be cached. A duration=0 means forever. + The duration may be: (months|weeks|days|hours|minutes|seconds). + @type duration: {unit:value} + """ + if len(duration) == 1: + arg = duration.items()[0] + if not arg[0] in self.units: + raise Exception('must be: %s' % str(self.units)) + self.duration = arg + return self + + def setlocation(self, location): + """ + Set the location (directory) for the cached files. + @param location: The directory for the cached files. + @type location: str + """ + self.location = location + + def mktmp(self): + """ + Make the I{location} directory if it doesn't already exits. + """ + try: + if not os.path.isdir(self.location): + os.makedirs(self.location) + except: + log.debug(self.location, exc_info=1) + return self + + def put(self, id, fp): + try: + fn = self.__fn(id) + f = self.open(fn, 'w') + f.write(fp.read()) + f.close() + return open(fn) + except: + log.debug(id, exc_info=1) + return fp + + def get(self, id): + try: + fn = self.__fn(id) + self.validate(fn) + return self.open(fn) + except: + pass + + def validate(self, fn): + """ + Validate that the file has not expired based on the I{duration}. + @param fn: The file name. + @type fn: str + """ + if self.duration[1] < 1: + return + created = dt.fromtimestamp(os.path.getctime(fn)) + d = {self.duration[0] : self.duration[1]} + expired = created+timedelta(**d) + if expired < dt.now(): + log.debug('%s expired, deleted', fn) + os.remove(fn) + + def clear(self): + for fn in os.listdir(self.location): + if os.path.isdir(fn): + continue + if fn.startswith(self.fnprefix) and fn.endswith(self.fnsuffix): + log.debug('deleted: %s', fn) + os.remove(os.path.join(self.location, fn)) + + def purge(self, id): + fn = self.__fn(id) + try: + os.remove(fn) + except: + pass + + def open(self, fn, *args): + """ + Open the cache file making sure the directory is created. + """ + self.mktmp() + return open(fn, *args) + + def __fn(self, id): + fn = '%s-%s.%s' % (self.fnprefix, abs(hash(id)), self.fnsuffix) + return os.path.join(self.location, fn) + + +class Cache: + """ + The XML document cache. + """ + + def get(self, id): + """ + Get a document from the store by ID. + @param id: The document ID. + @type id: str + @return: The document, else None + @rtype: I{Document} + """ + raise Exception('not-implemented') + + def put(self, id, document): + """ + Put a document into the store. + @param id: The document ID. + @type id: str + @param document: The document to add. + @type document: I{Document} + """ + raise Exception('not-implemented') + + def purge(self, id): + """ + Purge a document from the cache by id. + @param id: A document ID. + @type id: str + """ + raise Exception('not-implemented') + + def clear(self): + """ + Clear all documents from the cache. + """ + raise Exception('not-implemented') + + +class NoCache(Cache): + """ + The passthru document cache. + """ + + def get(self, id): + return None + + def put(self, id, document): + pass + + +class DocumentStore(Cache): + + def __init__(self, location=None, **duration): + """ + @param location: The directory for the cached documents. + @type location: str + @param duration: The cached file duration which defines how + long the document will be cached. A duration=0 means forever. + The duration may be: (months|weeks|days|hours|minutes|seconds). + @type duration: {unit:value} + """ + cache = FileCache(location, **duration) + cache.fnsuffix = 'pxd' + self.cache = cache + + def get(self, id): + try: + fp = self.cache.get(id) + if fp is None: + return None + else: + return pickle.load(fp) + except: + self.cache.purge(id) + + def put(self, id, document): + ostr = StringIO() + pickle.dump(document, ostr) + istr = StringIO(ostr.getvalue()) + fp = self.cache.put(id, istr) + fp.close() + return document diff --git a/suds/client.py b/suds/client.py index 22d5cfc..0d7c7f3 100644 --- a/suds/client.py +++ b/suds/client.py @@ -25,7 +25,6 @@ from cookielib import CookieJar from suds import * from suds.transport import TransportError, Request from suds.transport.https import HttpAuthenticated -from suds.transport.cache import FileCache from suds.servicedefinition import ServiceDefinition from suds import sudsobject from sudsobject import Factory as InstFactory @@ -33,6 +32,7 @@ from sudsobject import Object from suds.resolver import PathResolver from suds.builder import Builder from suds.wsdl import Definitions +from suds.cache import DocumentStore from suds.sax.document import Document from suds.sax.parser import Parser from suds.options import Options @@ -104,7 +104,7 @@ class Client(object): options = Options() options.transport = HttpAuthenticated() self.options = options - options.cache = FileCache(days=1) + options.cache = DocumentStore(days=1) self.set_options(**kwargs) self.wsdl = Definitions(url, options) self.factory = Factory(self.wsdl) @@ -743,7 +743,8 @@ class SimClient(SoapClient): if fault is not None: return self.__fault(fault) raise Exception('(reply|fault) expected when msg=None') - msg = Parser().parse(string=msg) + sax = Parser() + msg = sax.parse(string=msg) return self.send(msg) def __reply(self, reply, args, kwargs): diff --git a/suds/options.py b/suds/options.py index 27f87f4..d7a711e 100644 --- a/suds/options.py +++ b/suds/options.py @@ -15,13 +15,14 @@ # written by: Jeff Ortel ( jortel@redhat.com ) """ -Suds base pptions classes. +Suds basic options classes. """ from suds.properties import * from suds.wsse import Security from suds.xsd.doctor import Doctor from suds.transport import Transport +from suds.cache import Cache, NoCache class TpLinker(AutoLinker): @@ -42,6 +43,9 @@ class TpLinker(AutoLinker): class Options(Skin): """ Options: + - B{cache} - The XML document cache. May be set (None) for no caching. + - type: L{Cache} + - default: L{NoCache} - B{faults} - Raise faults raised by server, else return tuple from service method invocation as (httpcode, object). - type: I{bool} @@ -84,10 +88,14 @@ class Options(Skin): WSDL import each other. B{**Experimental**}. - type: I{bool} - default: False + - B{store} - The SAX document store. + - type: L{DocumentStore} + - default: None """ def __init__(self, **kwargs): domain = __name__ definitions = [ + Definition('cache', Cache, NoCache()), Definition('faults', bool, True), Definition('transport', Transport, None, TpLinker()), Definition('service', (int, basestring), None), diff --git a/suds/reader.py b/suds/reader.py new file mode 100644 index 0000000..0925012 --- /dev/null +++ b/suds/reader.py @@ -0,0 +1,37 @@ +# 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 ) + +""" +Contains xml document reader classes. +""" + + +from suds.sax.parser import Parser +from suds.transport import Request + +class DocumentReader: + + def __init__(self, options): + self.options = options + + def open(self, url): + d = self.options.cache.get(url) + if d is None: + fp = self.options.transport.open(Request(url)) + sax = Parser() + d = sax.parse(file=fp) + self.options.cache.put(url, d) + return d diff --git a/suds/sax/__init__.py b/suds/sax/__init__.py index c50cbae..b4d343e 100644 --- a/suds/sax/__init__.py +++ b/suds/sax/__init__.py @@ -102,4 +102,4 @@ class Namespace: return isinstance(ns, tuple) and len(ns) == len(cls.default) except: pass - return False \ No newline at end of file + return False diff --git a/suds/transport/__init__.py b/suds/transport/__init__.py index 9bf5674..e1e00d7 100644 --- a/suds/transport/__init__.py +++ b/suds/transport/__init__.py @@ -128,71 +128,3 @@ class Transport: @raise TransportError: On all transport errors. """ raise Exception('not-implemented') - - -class Cache: - """ - The URL caching object. - """ - - def put(self, url, fp): - """ - Put an item into the cache. - @param url: A url. - @type url: str - @param fp: A file stream. - @type fp: stream - @return: The stream. - @rtype: stream - """ - raise Exception('not-implemented') - - def get(self, url): - """ - Get an item from the cache by url. - @param url: A url. - @type url: str - @return: A stream when found, else None. - @rtype: stream - """ - raise Exception('not-implemented') - - def clear(self): - """ - Clear the cached items. - """ - raise Exception('not-implemented') - - -class NoCache(Cache): - """ - The NO caching implementation. - """ - - def put(self, url, fp): - """ - Put an item into the cache. - @param url: A url. - @type url: str - @param fp: A file stream. - @type fp: stream - @return: The stream. - @rtype: stream - """ - return fp - - def get(self, url): - """ - Get an item from the cache by url. - @param url: A url. - @type url: str - @return: A stream when found, else None. - @rtype: stream - """ - return None - - def clear(self): - """ - Clear the cached items. - """ - pass diff --git a/suds/transport/cache.py b/suds/transport/cache.py deleted file mode 100644 index e96d41e..0000000 --- a/suds/transport/cache.py +++ /dev/null @@ -1,158 +0,0 @@ -# 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 ) - -""" -Contains transport interface (classes) and reference implementation. -""" - -import os -from tempfile import gettempdir as tmp -from urlparse import urlparse -from suds.transport import * -from datetime import datetime as dt -from datetime import timedelta -from logging import getLogger - -log = getLogger(__name__) - - -class FileCache(Cache): - """ - A file-based URL cache. - @cvar fnprefix: The file name prefix. - @type fnprefix: str - @cvar fnsuffix: The file name suffix. - @type fnsuffix: str - @ivar duration: The cached file duration which defines how - long the file will be cached. - @type duration: (unit, value) - @ivar location: The directory for the cached files. - @type location: str - """ - - fnprefix = 'suds' - fnsuffix = 'http' - units = ('months', 'weeks', 'days', 'hours', 'minutes', 'seconds') - - def __init__(self, location=None, **duration): - """ - @param location: The directory for the cached files. - @type location: str - @param duration: The cached file duration which defines how - long the file will be cached. A duration=0 means forever. - The duration may be: (months|weeks|days|hours|minutes|seconds). - @type duration: {unit:value} - """ - if location is None: - location = os.path.join(tmp(), 'suds') - self.location = location - self.duration = (None, 0) - self.setduration(**duration) - - def setduration(self, **duration): - """ - Set the caching duration which defines how long the - file will be cached. - @param duration: The cached file duration which defines how - long the file will be cached. A duration=0 means forever. - The duration may be: (months|weeks|days|hours|minutes|seconds). - @type duration: {unit:value} - """ - if len(duration) == 1: - arg = duration.items()[0] - if not arg[0] in self.units: - raise Exception('must be: %s' % str(self.units)) - self.duration = arg - return self - - def setlocation(self, location): - """ - Set the location (directory) for the cached files. - @param location: The directory for the cached files. - @type location: str - """ - self.location = location - - def mktmp(self): - """ - Make the I{location} directory if it doesn't already exits. - """ - try: - if not os.path.isdir(self.location): - os.makedirs(self.location) - except: - log.debug(self.location, exc_info=1) - return self - - def put(self, url, fp): - try: - fn = self.__fn(url) - f = self.open(fn, 'w') - f.write(fp.read()) - f.close() - return open(fn) - except: - log.debug(url, exc_info=1) - return fp - - def get(self, url): - try: - fn = self.__fn(url) - self.validate(fn) - return self.open(fn) - except: - pass - - def validate(self, fn): - """ - Validate that the file has not expired based on the I{duration}. - @param fn: The file name. - @type fn: str - """ - if self.duration[1] < 1: - return - created = dt.fromtimestamp(os.path.getctime(fn)) - d = {self.duration[0] : self.duration[1]} - expired = created+timedelta(**d) - if expired < dt.now(): - log.debug('%s expired, deleted', fn) - os.remove(fn) - - def clear(self): - for fn in os.listdir(self.location): - if os.path.isdir(fn): - continue - if fn.startswith(self.fnprefix) and fn.endswith(self.fnsuffix): - log.debug('deleted: %s', fn) - os.remove(os.path.join(self.location, fn)) - - def open(self, fn, *args): - """ - Open the cache file making sure the directory is created. - """ - self.mktmp() - return open(fn, *args) - - def __fn(self, url): - if self.__ignored(url): - raise Exception('URL %s, ignored') - fn = '%s-%s.%s' % (self.fnprefix, abs(hash(url)), self.fnsuffix) - return os.path.join(self.location, fn) - - def __ignored(self, url): - """ ignore urls based on protocol """ - protocol = urlparse(url)[0] - return protocol in ('file',) \ No newline at end of file diff --git a/suds/transport/http.py b/suds/transport/http.py index b2650ba..cf00615 100644 --- a/suds/transport/http.py +++ b/suds/transport/http.py @@ -46,9 +46,6 @@ class HttpTransport(Transport): - B{timeout} - Set the url open timeout (seconds). - type: I{float} - default: 90 - - B{cache} - The http I{transport} cache. May be set (None) for no caching. - - type: L{Cache} - - default: L{NoCache} """ Transport.__init__(self) Unskin(self.options).update(kwargs) @@ -58,16 +55,10 @@ class HttpTransport(Transport): def open(self, request): try: url = request.url - cache = self.options.cache - fp = cache.get(url) - if fp is not None: - log.debug('opening (%s), cached', url) - return fp log.debug('opening (%s)', url) u2request = u2.Request(url) self.setproxy(url, u2request) - fp = self.u2open(u2request) - return cache.put(url, fp) + return self.u2open(u2request) except u2.HTTPError, e: raise TransportError(str(e), e.code, e.fp) diff --git a/suds/transport/https.py b/suds/transport/https.py index 3c3a4a2..52b463f 100644 --- a/suds/transport/https.py +++ b/suds/transport/https.py @@ -45,9 +45,6 @@ class HttpAuthenticated(HttpTransport): - B{timeout} - Set the url open timeout (seconds). - type: I{float} - default: 90 - - B{cache} - The http I{transport} cache. May be set (None) for no caching. - - type: L{Cache} - - default: L{NoCache} - B{username} - The username used for http authentication. - type: I{str} - default: None diff --git a/suds/transport/options.py b/suds/transport/options.py index e920ca3..8b0d194 100644 --- a/suds/transport/options.py +++ b/suds/transport/options.py @@ -26,9 +26,6 @@ from suds.properties import * class Options(Skin): """ Options: - - B{cache} - The http I{transport} cache. May be set (None) for no caching. - - type: L{Cache} - - default: L{NoCache} - B{proxy} - An http proxy to be specified on requests. The proxy is defined as {protocol:proxy,} - type: I{dict} @@ -51,7 +48,6 @@ class Options(Skin): def __init__(self, **kwargs): domain = __name__ definitions = [ - Definition('cache', Cache, NoCache()), Definition('proxy', dict, {}), Definition('timeout', (int,float), 90), Definition('headers', dict, {}), diff --git a/suds/wsdl.py b/suds/wsdl.py index f9708fd..558f8be 100644 --- a/suds/wsdl.py +++ b/suds/wsdl.py @@ -23,8 +23,6 @@ found in the document. from logging import getLogger from suds import * from suds.sax import splitPrefix -from suds.transport import Request -from suds.sax.parser import Parser from suds.sax.element import Element from suds.bindings.document import Document from suds.bindings.rpc import RPC, Encoded @@ -33,6 +31,7 @@ from suds.xsd.schema import Schema, SchemaCollection from suds.xsd.query import ElementQuery from suds.sudsobject import Object from suds.sudsobject import Factory as SFactory +from suds.reader import DocumentReader from urlparse import urljoin import re, soaparray @@ -169,9 +168,8 @@ class Definitions(WObject): @type options: L{options.Options} """ log.debug('reading wsdl at: %s ...', url) - fp = options.transport.open(Request(url)) - p = Parser() - d = p.parse(file=fp) + reader = DocumentReader(options) + d = reader.open(url) root = d.root() WObject.__init__(self, root) self.id = objid(self) diff --git a/suds/xsd/sxbasic.py b/suds/xsd/sxbasic.py index 15ef40e..d58dfff 100644 --- a/suds/xsd/sxbasic.py +++ b/suds/xsd/sxbasic.py @@ -25,8 +25,8 @@ from suds.xsd import * from suds.xsd.sxbase import * from suds.xsd.query import * from suds.sax import splitPrefix, Namespace -from suds.sax.parser import Parser -from suds.transport import Request, TransportError +from suds.transport import TransportError +from suds.reader import DocumentReader from urlparse import urljoin @@ -527,10 +527,8 @@ class Import(SchemaObject): try: if '://' not in url: url = urljoin(self.schema.baseurl, url) - transport = self.schema.options.transport - p = Parser() - fp = transport.open(Request(url)) - d = p.parse(file=fp) + reader = DocumentReader(self.schema.options) + d = reader.open(url) root = d.root() root.set('url', url) return self.schema.instance(root, url) @@ -581,10 +579,8 @@ class Include(SchemaObject): try: if '://' not in url: url = urljoin(self.schema.baseurl, url) - transport = self.schema.options.transport - p = Parser() - fp = transport.open(Request(url)) - d = p.parse(file=fp) + reader = DocumentReader(self.schema.options) + d = reader.open(url) root = d.root() root.set('url', url) self.__applytns(root) diff --git a/tests/axis2.py b/tests/axis2.py index 966cb7e..8ed0bdd 100644 --- a/tests/axis2.py +++ b/tests/axis2.py @@ -38,7 +38,7 @@ print 'url=%s' % url # # create a service client using the wsdl. # -client = Client(url, cache=None) +client = Client(url) # # print the service (introspection) -- cgit v1.2.1