diff --git a/setup.py b/setup.py
index 659e123..51f8236 100644
--- a/setup.py
+++ b/setup.py
@@ -21,9 +21,9 @@
from setuptools import setup, find_packages
setup(
- name="suds",
+ name="suds-transbank-chile",
version=suds.__version__,
- description="Lightweight SOAP client",
+ description="Lightweight SOAP client (Adapted For working with Transbank, Chile)",
author="Jeff Ortel",
author_email="jortel@redhat.com",
maintainer="Jeff Ortel",
diff --git a/suds/__init__.py b/suds/__init__.py
index c1d249a..166a206 100644
--- a/suds/__init__.py
+++ b/suds/__init__.py
@@ -27,7 +27,7 @@
#
__version__ = '0.4'
-__build__="(beta) R663-20100303"
+__build__="GA R699-20100913"
#
# Exceptions
diff --git a/suds/bindings/binding.py b/suds/bindings/binding.py
index a6d421e..4a7a996 100644
--- a/suds/bindings/binding.py
+++ b/suds/bindings/binding.py
@@ -22,6 +22,7 @@
from suds import *
from suds.sax import Namespace
from suds.sax.parser import Parser
+from suds.sax.document import Document
from suds.sax.element import Element
from suds.sudsobject import Factory, Object
from suds.mx import Content
@@ -32,6 +33,7 @@
from suds.xsd.query import TypeQuery, ElementQuery
from suds.xsd.sxbasic import Element as SchemaElement
from suds.options import Options
+from suds.plugin import PluginContainer
from copy import deepcopy
log = getLogger(__name__)
@@ -109,8 +111,8 @@ def get_message(self, method, args, kwargs):
@type args: list
@param kwargs: Named (keyword) args for the method invoked.
@type kwargs: dict
- @return: The soap message.
- @rtype: str
+ @return: The soap envelope.
+ @rtype: L{Document}
"""
content = self.headercontent(method)
@@ -123,7 +125,7 @@ def get_message(self, method, args, kwargs):
env.promotePrefixes()
else:
env.refitPrefixes()
- return env
+ return Document(env)
def get_reply(self, method, reply):
"""
@@ -141,9 +143,12 @@ def get_reply(self, method, reply):
reply = self.replyfilter(reply)
sax = Parser()
replyroot = sax.parse(string=reply)
+ plugins = PluginContainer(self.options().plugins)
+ plugins.message.parsed(reply=replyroot)
soapenv = replyroot.getChild('Envelope')
soapenv.promotePrefixes()
soapbody = soapenv.getChild('Body')
+ self.detect_fault(soapbody)
soapbody = self.multiref.process(soapbody)
nodes = self.replycontent(method, soapbody)
rtypes = self.returned_types(method)
@@ -161,6 +166,23 @@ def get_reply(self, method, reply):
return (replyroot, result)
return (replyroot, None)
+ def detect_fault(self, body):
+ """
+ Detect I{hidden} soapenv:Fault element in the soap body.
+ @param body: The soap envelope body.
+ @type body: L{Element}
+ @raise WebFault: When found.
+ """
+ fault = body.getChild('Fault', envns)
+ if fault is None:
+ return
+ unmarshaller = self.unmarshaller(False)
+ p = unmarshaller.process(fault)
+ if self.options().faults:
+ raise WebFault(p, fault)
+ return self
+
+
def replylist(self, rt, nodes):
"""
Construct a I{list} reply. This mehod is called when it has been detected
@@ -206,14 +228,19 @@ def replycomposite(self, rtypes, nodes):
continue
resolved = rt.resolve(nobuiltin=True)
sobject = unmarshaller.process(node, resolved)
- if rt.unbounded():
- value = getattr(composite, tag, None)
- if value is None:
+ value = getattr(composite, tag, None)
+ if value is None:
+ if rt.unbounded():
value = []
setattr(composite, tag, value)
- value.append(sobject)
+ value.append(sobject)
+ else:
+ setattr(composite, tag, sobject)
else:
- setattr(composite, tag, sobject)
+ if not isinstance(value, list):
+ value = [value,]
+ setattr(composite, tag, value)
+ value.append(sobject)
return composite
def get_fault(self, reply):
diff --git a/suds/builder.py b/suds/builder.py
index 95f81c4..c2aad98 100644
--- a/suds/builder.py
+++ b/suds/builder.py
@@ -44,10 +44,10 @@ def build(self, name):
else:
type = name
cls = type.name
- if len(type):
- data = Factory.object(cls)
- else:
+ if type.mixed():
data = Factory.property(cls)
+ else:
+ data = Factory.object(cls)
resolved = type.resolve()
md = data.__metadata__
md.sxtype = resolved
@@ -73,10 +73,15 @@ def process(self, data, type, history):
value = []
else:
if len(resolved) > 0:
- value = Factory.object(resolved.name)
- md = value.__metadata__
- md.sxtype = resolved
- md.ordering = self.ordering(resolved)
+ if resolved.mixed():
+ value = Factory.property(resolved.name)
+ md = value.__metadata__
+ md.sxtype = resolved
+ else:
+ value = Factory.object(resolved.name)
+ md = value.__metadata__
+ md.sxtype = resolved
+ md.ordering = self.ordering(resolved)
setattr(data, type.name, value)
if value is not None:
data = value
diff --git a/suds/cache.py b/suds/cache.py
index 0e2b314..801c23c 100644
--- a/suds/cache.py
+++ b/suds/cache.py
@@ -19,8 +19,11 @@
"""
import os
+import suds
from tempfile import gettempdir as tmp
from suds.transport import *
+from suds.sax.parser import Parser
+from suds.sax.element import Element
from datetime import datetime as dt
from datetime import timedelta
from cStringIO import StringIO
@@ -115,8 +118,6 @@ class FileCache(Cache):
"""
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.
@@ -125,7 +126,6 @@ class FileCache(Cache):
@type location: str
"""
fnprefix = 'suds'
- fnsuffix = 'gcf'
units = ('months', 'weeks', 'days', 'hours', 'minutes', 'seconds')
def __init__(self, location=None, **duration):
@@ -142,6 +142,15 @@ def __init__(self, location=None, **duration):
self.location = location
self.duration = (None, 0)
self.setduration(**duration)
+ self.checkversion()
+
+ def fnsuffix(self):
+ """
+ Get the file name suffix
+ @return: The suffix
+ @rtype: str
+ """
+ return 'gcf'
def setduration(self, **duration):
"""
@@ -194,8 +203,9 @@ def putf(self, id, fp):
fn = self.__fn(id)
f = self.open(fn, 'w')
f.write(fp.read())
+ fp.close()
f.close()
- return fp
+ return open(fn)
except:
log.debug(id, exc_info=1)
return fp
@@ -226,7 +236,7 @@ def validate(self, fn):
if self.duration[1] < 1:
return
created = dt.fromtimestamp(os.path.getctime(fn))
- d = {self.duration[0] : self.duration[1]}
+ d = { self.duration[0]:self.duration[1] }
expired = created+timedelta(**d)
if expired < dt.now():
log.debug('%s expired, deleted', fn)
@@ -254,15 +264,50 @@ def open(self, fn, *args):
self.mktmp()
return open(fn, *args)
+ def checkversion(self):
+ path = os.path.join(self.location, 'version')
+ try:
+
+ f = self.open(path)
+ version = f.read()
+ f.close()
+ if version != suds.__version__:
+ raise Exception()
+ except:
+ self.clear()
+ f = self.open(path, 'w')
+ f.write(suds.__version__)
+ f.close()
+
def __fn(self, id):
- if hasattr(id, 'name') and hasattr(id, 'suffix'):
- name = id.name
- suffix = id.suffix
- else:
- name = id
- suffix = self.fnsuffix
- fn = '%s-%s.%s' % (self.fnprefix, abs(hash(name)), suffix)
+ name = id
+ suffix = self.fnsuffix()
+ fn = '%s-%s.%s' % (self.fnprefix, name, suffix)
return os.path.join(self.location, fn)
+
+
+class DocumentCache(FileCache):
+ """
+ Provides xml document caching.
+ """
+
+ def fnsuffix(self):
+ return 'xml'
+
+ def get(self, id):
+ try:
+ fp = FileCache.getf(self, id)
+ if fp is None:
+ return None
+ p = Parser()
+ return p.parse(fp)
+ except:
+ FileCache.purge(self, id)
+
+ def put(self, id, object):
+ if isinstance(object, Element):
+ FileCache.put(self, id, str(object))
+ return object
class ObjectCache(FileCache):
@@ -273,6 +318,9 @@ class ObjectCache(FileCache):
"""
protocol = 2
+ def fnsuffix(self):
+ return 'px'
+
def get(self, id):
try:
fp = FileCache.getf(self, id)
diff --git a/suds/client.py b/suds/client.py
index b91a7da..a000b01 100644
--- a/suds/client.py
+++ b/suds/client.py
@@ -40,6 +40,7 @@
from suds.properties import Unskin
from urlparse import urlparse
from copy import deepcopy
+from suds.plugin import PluginContainer
from logging import getLogger
log = getLogger(__name__)
@@ -109,6 +110,8 @@ def __init__(self, url, **kwargs):
self.set_options(**kwargs)
reader = DefinitionsReader(options, Definitions)
self.wsdl = reader.open(url)
+ plugins = PluginContainer(options.plugins)
+ plugins.init.initialized(wsdl=self.wsdl)
self.factory = Factory(self.wsdl)
self.service = ServiceSelector(self, self.wsdl.services)
self.sd = []
@@ -589,24 +592,26 @@ def invoke(self, args, kwargs):
timer.start()
result = None
binding = self.method.binding.input
- msg = binding.get_message(self.method, args, kwargs)
+ soapenv = binding.get_message(self.method, args, kwargs)
timer.stop()
metrics.log.debug(
"message for '%s' created: %s",
- self.method.name, timer)
+ self.method.name,
+ timer)
timer.start()
- result = self.send(msg)
+ result = self.send(soapenv)
timer.stop()
metrics.log.debug(
"method '%s' invoked: %s",
- self.method.name, timer)
+ self.method.name,
+ timer)
return result
- def send(self, msg):
+ def send(self, soapenv):
"""
Send soap message.
- @param msg: A soap message to send.
- @type msg: basestring
+ @param soapenv: A soap envelope to send.
+ @type soapenv: L{Document}
@return: The reply to the sent message.
@rtype: I{builtin} or I{subclass of} L{Object}
"""
@@ -615,12 +620,29 @@ def send(self, msg):
binding = self.method.binding.input
transport = self.options.transport
retxml = self.options.retxml
- log.debug('sending to (%s)\nmessage:\n%s', location, msg)
+ prettyxml = self.options.prettyxml
+ log.debug('sending to (%s)\nmessage:\n%s', location, soapenv)
try:
- self.last_sent(Document(msg))
- request = Request(location, str(msg))
+ self.last_sent(soapenv)
+ plugins = PluginContainer(self.options.plugins)
+ plugins.message.marshalled(envelope=soapenv.root())
+ if prettyxml:
+ soapenv = soapenv.str()
+ else:
+ soapenv = soapenv.plain()
+ soapenv = soapenv.encode('utf-8')
+ result = plugins.message.sending(envelope=soapenv)
+ if (result):
+ #print("result:" + str(result))
+ soapenv = result.envelope
+ #print("SENDING PLUGIN")
+ #print(soapenv)
+ #print(soapenv)
+ request = Request(location, soapenv)
request.headers = self.headers()
reply = transport.send(request)
+ ctx = plugins.message.received(reply=reply.message)
+ reply.message = ctx.reply
if retxml:
result = reply.message
else:
@@ -640,7 +662,7 @@ def headers(self):
@rtype: dict
"""
action = self.method.soap.action
- stock = { 'Content-Type' : 'text/xml', 'SOAPAction': action }
+ stock = { 'Content-Type' : 'text/xml; charset=utf-8', 'SOAPAction': action }
result = dict(stock, **self.options.headers)
log.debug('headers = %s', result)
return result
@@ -650,23 +672,25 @@ def succeeded(self, binding, reply):
Request succeeded, process the reply
@param binding: The binding to be used to process the reply.
@type binding: L{bindings.binding.Binding}
+ @param reply: The raw reply text.
+ @type reply: str
@return: The method result.
@rtype: I{builtin}, L{Object}
@raise WebFault: On server.
"""
log.debug('http succeeded:\n%s', reply)
+ plugins = PluginContainer(self.options.plugins)
if len(reply) > 0:
- r, p = binding.get_reply(self.method, reply)
- self.last_received(r)
- if self.options.faults:
- return p
- else:
- return (200, p)
+ reply, result = binding.get_reply(self.method, reply)
+ self.last_received(reply)
else:
- if self.options.faults:
- return None
- else:
- return (200, None)
+ result = None
+ ctx = plugins.message.unmarshalled(reply=result)
+ result = ctx.reply
+ if self.options.faults:
+ return result
+ else:
+ return (200, result)
def failed(self, binding, error):
"""
diff --git a/suds/mx/appender.py b/suds/mx/appender.py
index 0501415..206abc0 100644
--- a/suds/mx/appender.py
+++ b/suds/mx/appender.py
@@ -191,7 +191,7 @@ def append(self, parent, content):
if content.tag.startswith('_'):
attr = content.tag[1:]
value = tostr(content.value)
- if value is not None and len(value):
+ if value:
parent.set(attr, value)
else:
child = self.node(content)
@@ -305,6 +305,12 @@ class TextAppender(Appender):
"""
def append(self, parent, content):
- child = self.node(content)
- child.setText(content.value)
- parent.append(child)
+ if content.tag.startswith('_'):
+ attr = content.tag[1:]
+ value = tostr(content.value)
+ if value:
+ parent.set(attr, value)
+ else:
+ child = self.node(content)
+ child.setText(content.value)
+ parent.append(child)
diff --git a/suds/options.py b/suds/options.py
index 7e5c069..86ea245 100644
--- a/suds/options.py
+++ b/suds/options.py
@@ -84,10 +84,21 @@ class Options(Skin):
of the python object graph.
- type: I{bool}
- default: False
+ - B{prettyxml} - Flag that causes I{pretty} xml to be rendered when generating
+ the outbound soap envelope.
+ - type: I{bool}
+ - default: False
- B{autoblend} - Flag that ensures that the schema(s) defined within the
- WSDL import each other. B{**Experimental**}.
+ WSDL import each other.
- type: I{bool}
- default: False
+ - B{cachingpolicy} - The caching policy.
+ - type: I{int}
+ - 0 = Cache XML documents.
+ - 1 = Cache WSDL (pickled) object.
+ - default: 0
+ - B{plugins} - A plugin container.
+ - type: I{list}
"""
def __init__(self, **kwargs):
domain = __name__
@@ -104,6 +115,9 @@ def __init__(self, **kwargs):
Definition('xstq', bool, True),
Definition('prefixes', bool, True),
Definition('retxml', bool, False),
+ Definition('prettyxml', bool, False),
Definition('autoblend', bool, False),
+ Definition('cachingpolicy', int, 0),
+ Definition('plugins', (list, tuple), []),
]
Skin.__init__(self, domain, definitions, kwargs)
diff --git a/suds/plugin.py b/suds/plugin.py
new file mode 100644
index 0000000..11fbcad
--- /dev/null
+++ b/suds/plugin.py
@@ -0,0 +1,258 @@
+# 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 )
+
+"""
+The plugin module provides classes for implementation
+of suds plugins.
+"""
+
+from suds import *
+from logging import getLogger
+
+log = getLogger(__name__)
+
+
+class Context(object):
+ """
+ Plugin context.
+ """
+ pass
+
+
+class InitContext(Context):
+ """
+ Init Context.
+ @ivar wsdl: The wsdl.
+ @type wsdl: L{wsdl.Definitions}
+ """
+ pass
+
+
+class DocumentContext(Context):
+ """
+ The XML document load context.
+ @ivar url: The URL.
+ @type url: str
+ @ivar document: Either the XML text or the B{parsed} document root.
+ @type document: (str|L{sax.element.Element})
+ """
+ pass
+
+
+class MessageContext(Context):
+ """
+ The context for sending the soap envelope.
+ @ivar envelope: The soap envelope to be sent.
+ @type envelope: (str|L{sax.element.Element})
+ @ivar reply: The reply.
+ @type reply: (str|L{sax.element.Element}|object)
+ """
+ pass
+
+
+class Plugin:
+ """
+ Plugin base.
+ """
+ pass
+
+
+class InitPlugin(Plugin):
+ """
+ The base class for suds I{init} plugins.
+ """
+
+ def initialized(self, context):
+ """
+ Suds client initialization.
+ Called after wsdl the has been loaded. Provides the plugin
+ with the opportunity to inspect/modify the WSDL.
+ @param context: The init context.
+ @type context: L{InitContext}
+ """
+ pass
+
+
+class DocumentPlugin(Plugin):
+ """
+ The base class for suds I{document} plugins.
+ """
+
+ def loaded(self, context):
+ """
+ Suds has loaded a WSDL/XSD document. Provides the plugin
+ with an opportunity to inspect/modify the unparsed document.
+ Called after each WSDL/XSD document is loaded.
+ @param context: The document context.
+ @type context: L{DocumentContext}
+ """
+ pass
+
+ def parsed(self, context):
+ """
+ Suds has parsed a WSDL/XSD document. Provides the plugin
+ with an opportunity to inspect/modify the parsed document.
+ Called after each WSDL/XSD document is parsed.
+ @param context: The document context.
+ @type context: L{DocumentContext}
+ """
+ pass
+
+
+class MessagePlugin(Plugin):
+ """
+ The base class for suds I{soap message} plugins.
+ """
+
+ def marshalled(self, context):
+ """
+ Suds will send the specified soap envelope.
+ Provides the plugin with the opportunity to inspect/modify
+ the envelope Document before it is sent.
+ @param context: The send context.
+ The I{envelope} is the envelope docuemnt.
+ @type context: L{MessageContext}
+ """
+ pass
+
+ def sending(self, context):
+ """
+ Suds will send the specified soap envelope.
+ Provides the plugin with the opportunity to inspect/modify
+ the message text it is sent.
+ @param context: The send context.
+ The I{envelope} is the envelope text.
+ @type context: L{MessageContext}
+ """
+ pass
+
+ def received(self, context):
+ """
+ Suds has received the specified reply.
+ Provides the plugin with the opportunity to inspect/modify
+ the received XML text before it is SAX parsed.
+ @param context: The reply context.
+ The I{reply} is the raw text.
+ @type context: L{MessageContext}
+ """
+ pass
+
+ def parsed(self, context):
+ """
+ Suds has sax parsed the received reply.
+ Provides the plugin with the opportunity to inspect/modify
+ the sax parsed DOM tree for the reply before it is unmarshalled.
+ @param context: The reply context.
+ The I{reply} is DOM tree.
+ @type context: L{MessageContext}
+ """
+ pass
+
+ def unmarshalled(self, context):
+ """
+ Suds has unmarshalled the received reply.
+ Provides the plugin with the opportunity to inspect/modify
+ the unmarshalled reply object before it is returned.
+ @param context: The reply context.
+ The I{reply} is unmarshalled suds object.
+ @type context: L{MessageContext}
+ """
+ pass
+
+
+class PluginContainer:
+ """
+ Plugin container provides easy method invocation.
+ @ivar plugins: A list of plugin objects.
+ @type plugins: [L{Plugin},]
+ @cvar ctxclass: A dict of plugin method / context classes.
+ @type ctxclass: dict
+ """
+
+ domains = {\
+ 'init': (InitContext, InitPlugin),
+ 'document': (DocumentContext, DocumentPlugin),
+ 'message': (MessageContext, MessagePlugin ),
+ }
+
+ def __init__(self, plugins):
+ """
+ @param plugins: A list of plugin objects.
+ @type plugins: [L{Plugin},]
+ """
+ self.plugins = plugins
+
+ def __getattr__(self, name):
+ domain = self.domains.get(name)
+ if domain:
+ plugins = []
+ ctx, pclass = domain
+ for p in self.plugins:
+ if isinstance(p, pclass):
+ plugins.append(p)
+ return PluginDomain(ctx, plugins)
+ else:
+ raise Exception, 'plugin domain (%s), invalid' % name
+
+
+class PluginDomain:
+ """
+ The plugin domain.
+ @ivar ctx: A context.
+ @type ctx: L{Context}
+ @ivar plugins: A list of plugins (targets).
+ @type plugins: list
+ """
+
+ def __init__(self, ctx, plugins):
+ self.ctx = ctx
+ self.plugins = plugins
+
+ def __getattr__(self, name):
+ return Method(name, self)
+
+
+class Method:
+ """
+ Plugin method.
+ @ivar name: The method name.
+ @type name: str
+ @ivar domain: The plugin domain.
+ @type domain: L{PluginDomain}
+ """
+
+ def __init__(self, name, domain):
+ """
+ @param name: The method name.
+ @type name: str
+ @param domain: A plugin domain.
+ @type domain: L{PluginDomain}
+ """
+ self.name = name
+ self.domain = domain
+
+ def __call__(self, **kwargs):
+ ctx = self.domain.ctx()
+ ctx.__dict__.update(kwargs)
+ for plugin in self.domain.plugins:
+ try:
+ method = getattr(plugin, self.name, None)
+ if method and callable(method):
+ method(ctx)
+ except Exception, pe:
+ log.exception(pe)
+ raise
+ return ctx
diff --git a/suds/reader.py b/suds/reader.py
index 84e6822..1184f12 100644
--- a/suds/reader.py
+++ b/suds/reader.py
@@ -21,38 +21,44 @@
from suds.sax.parser import Parser
from suds.transport import Request
+from suds.cache import Cache, NoCache
from suds.store import DocumentStore
+from suds.plugin import PluginContainer
from logging import getLogger
log = getLogger(__name__)
-class ObjectId(object):
-
- def __init__(self, name, suffix):
- self.name = name
- self.suffix = suffix
-
-
-class DocumentReader:
+class Reader:
"""
- The XML document reader provides an integration
- between the SAX L{Parser} and the document cache.
- @cvar suffix: The cache file suffix.
- @type suffix: str
+ The reader provides integration with cache.
@ivar options: An options object.
@type options: I{Options}
"""
-
- suffix = 'pxd'
-
+
def __init__(self, options):
"""
@param options: An options object.
@type options: I{Options}
"""
self.options = options
+ self.plugins = PluginContainer(options.plugins)
+
+ def mangle(self, name, x):
+ """
+ Mangle the name by hashing the I{name} and appending I{x}.
+ @return: the mangled name.
+ """
+ h = abs(hash(name))
+ return '%s-%s' % (h, x)
+
+
+class DocumentReader(Reader):
+ """
+ The XML document reader provides an integration
+ between the SAX L{Parser} and the document cache.
+ """
def open(self, url):
"""
@@ -66,12 +72,13 @@ def open(self, url):
@return: The specified XML document.
@rtype: I{Document}
"""
- id = ObjectId(url, self.suffix)
- cache = self.options.cache
+ cache = self.cache()
+ id = self.mangle(url, 'document')
d = cache.get(id)
if d is None:
d = self.download(url)
cache.put(id, d)
+ self.plugins.document.parsed(url=url, document=d.root())
return d
def download(self, url):
@@ -86,25 +93,34 @@ def download(self, url):
fp = store.open(url)
if fp is None:
fp = self.options.transport.open(Request(url))
+ content = fp.read()
+ fp.close()
+ ctx = self.plugins.document.loaded(url=url, document=content)
+ content = ctx.document
sax = Parser()
- return sax.parse(file=fp)
+ return sax.parse(string=content)
+
+ def cache(self):
+ """
+ Get the cache.
+ @return: The I{options} when I{cachingpolicy} = B{0}.
+ @rtype: L{Cache}
+ """
+ if self.options.cachingpolicy == 0:
+ return self.options.cache
+ else:
+ return NoCache()
-class DefinitionsReader:
+class DefinitionsReader(Reader):
"""
The WSDL definitions reader provides an integration
between the Definitions and the object cache.
- @cvar suffix: The cache file suffix.
- @type suffix: str
- @ivar options: An options object.
- @type options: I{Options}
@ivar fn: A factory function (constructor) used to
create the object not found in the cache.
@type fn: I{Constructor}
"""
- suffix = 'pw'
-
def __init__(self, options, fn):
"""
@param options: An options object.
@@ -113,7 +129,7 @@ def __init__(self, options, fn):
create the object not found in the cache.
@type fn: I{Constructor}
"""
- self.options = options
+ Reader.__init__(self, options)
self.fn = fn
def open(self, url):
@@ -129,8 +145,8 @@ def open(self, url):
@return: The WSDL object.
@rtype: I{Definitions}
"""
- id = ObjectId(url, self.suffix)
- cache = self.options.cache
+ cache = self.cache()
+ id = self.mangle(url, 'wsdl')
d = cache.get(id)
if d is None:
d = self.fn(url, self.options)
@@ -140,3 +156,14 @@ def open(self, url):
for imp in d.imports:
imp.imported.options = self.options
return d
+
+ def cache(self):
+ """
+ Get the cache.
+ @return: The I{options} when I{cachingpolicy} = B{1}.
+ @rtype: L{Cache}
+ """
+ if self.options.cachingpolicy == 1:
+ return self.options.cache
+ else:
+ return NoCache()
\ No newline at end of file
diff --git a/suds/sax/__init__.py b/suds/sax/__init__.py
index b4d343e..3d71432 100644
--- a/suds/sax/__init__.py
+++ b/suds/sax/__init__.py
@@ -68,6 +68,10 @@ class Namespace:
def create(cls, p=None, u=None):
return (p, u)
+ @classmethod
+ def none(cls, ns):
+ return ( ns == cls.default )
+
@classmethod
def xsd(cls, ns):
try:
diff --git a/suds/sax/date.py b/suds/sax/date.py
index 81a7a10..6e31c4c 100644
--- a/suds/sax/date.py
+++ b/suds/sax/date.py
@@ -120,6 +120,8 @@ class Time:
- HH:MI:SS.ms(z|Z)
- HH:MI:SS(+|-)06:00
- HH:MI:SS.ms(+|-)06:00
+ @ivar tz: The timezone
+ @type tz: L{Timezone}
@ivar date: The object value.
@type date: B{datetime}.I{time}
"""
@@ -181,8 +183,7 @@ def __adjust(self):
"""
if hasattr(self, 'offset'):
today = dt.date.today()
- tz = Timezone()
- delta = Timezone.adjustment(self.offset)
+ delta = self.tz.adjustment(self.offset)
d = dt.datetime.combine(today, self.time)
d = ( d + delta )
self.time = d.time()
@@ -303,8 +304,7 @@ def __adjust(self):
"""
if not hasattr(self, 'offset'):
return
- tz = Timezone()
- delta = Timezone.adjustment(self.offset)
+ delta = self.tz.adjustment(self.offset)
try:
d = ( self.datetime + delta )
self.datetime = d
@@ -341,13 +341,17 @@ class Timezone:
@cvar local: The (A) local TZ offset.
@type local: int
@cvar patten: The regex patten to match TZ.
- @type patten: L{re.RegexObject}
+ @type patten: re.Pattern
"""
pattern = re.compile('([zZ])|([\-\+][0-9]{2}:[0-9]{2})')
+
+ LOCAL = ( 0-time.timezone/60/60 )
- def __init__(self):
- self.local = ( 0-time.timezone/60/60 )
+ def __init__(self, offset=None):
+ if offset is None:
+ offset = self.LOCAL
+ self.local = offset
@classmethod
def split(cls, s):
@@ -363,13 +367,12 @@ def split(cls, s):
return (s,)
x = m.start(0)
return (s[:x], s[x:])
-
- @classmethod
- def adjustment(cls, offset):
+
+ def adjustment(self, offset):
"""
Get the adjustment to the I{local} TZ.
@return: The delta between I{offset} and local TZ.
@rtype: B{datetime}.I{timedelta}
"""
- delta = ( cls.local - offset )
+ delta = ( self.local - offset )
return dt.timedelta(hours=delta)
diff --git a/suds/sax/document.py b/suds/sax/document.py
index c7129cb..5a004eb 100644
--- a/suds/sax/document.py
+++ b/suds/sax/document.py
@@ -27,6 +27,8 @@
class Document(Element):
""" simple document """
+
+ DECL = ''
def __init__(self, root=None):
Element.__init__(self, 'document')
@@ -34,18 +36,26 @@ def __init__(self, root=None):
self.append(root)
def root(self):
- if len(self.children) > 0:
+ if len(self.children):
return self.children[0]
else:
return None
+ def str(self):
+ s = []
+ s.append(self.DECL)
+ s.append('\n')
+ s.append(self.root().str())
+ return ''.join(s)
+
+ def plain(self):
+ s = []
+ s.append(self.DECL)
+ s.append(self.root().plain())
+ return ''.join(s)
+
def __str__(self):
return unicode(self).encode('utf-8')
def __unicode__(self):
- result = ''
- root = self.root()
- if root is not None:
- result += '\n'
- result += root.str()
- return unicode(result)
+ return self.str()
\ No newline at end of file
diff --git a/suds/sax/element.py b/suds/sax/element.py
index ca44801..9dec1f9 100644
--- a/suds/sax/element.py
+++ b/suds/sax/element.py
@@ -767,6 +767,29 @@ def str(self, indent=0):
result.append('%s>' % self.qname())
result = ''.join(result)
return result
+
+ def plain(self):
+ """
+ Get a string representation of this XML fragment.
+ @return: A I{plain} string.
+ @rtype: basestring
+ """
+ result = []
+ result.append('<%s' % self.qname())
+ result.append(self.nsdeclarations())
+ for a in [unicode(a) for a in self.attributes]:
+ result.append(' %s' % a)
+ if self.isempty():
+ result.append('/>')
+ return ''.join(result)
+ result.append('>')
+ if self.hasText():
+ result.append(self.text.escape())
+ for c in self.children:
+ result.append(c.plain())
+ result.append('%s>' % self.qname())
+ result = ''.join(result)
+ return result
def nsdeclarations(self):
"""
@@ -923,6 +946,41 @@ def __str__(self):
def __unicode__(self):
return self.str()
+
+ def __iter__(self):
+ return NodeIterator(self)
+
+
+class NodeIterator:
+ """
+ The L{Element} child node iterator.
+ @ivar pos: The current position
+ @type pos: int
+ @ivar children: A list of a child nodes.
+ @type children: [L{Element},..]
+ """
+
+ def __init__(self, parent):
+ """
+ @param parent: An element to iterate.
+ @type parent: L{Element}
+ """
+ self.pos = 0
+ self.children = parent.children
+
+ def next(self):
+ """
+ Get the next child.
+ @return: The next child.
+ @rtype: L{Element}
+ @raise StopIterator: At the end.
+ """
+ try:
+ child = self.children[self.pos]
+ self.pos += 1
+ return child
+ except:
+ raise StopIteration()
class PrefixNormalizer:
diff --git a/suds/soaparray.py b/suds/soaparray.py
index c7b85e5..04847d5 100644
--- a/suds/soaparray.py
+++ b/suds/soaparray.py
@@ -39,7 +39,10 @@ def __init__(self, schema, root, aty):
@type aty: The value of wsdl:arrayType.
"""
SXAttribute.__init__(self, schema, root)
- self.aty = aty[:-2]
+ if aty.endswith('[]'):
+ self.aty = aty[:-2]
+ else:
+ self.aty = aty
def autoqualified(self):
aqs = SXAttribute.autoqualified(self)
diff --git a/suds/umx/core.py b/suds/umx/core.py
index 7fb4ac4..07d33c4 100644
--- a/suds/umx/core.py
+++ b/suds/umx/core.py
@@ -135,7 +135,7 @@ def append_children(self, content):
@param content: The current content being unmarshalled.
@type content: L{Content}
"""
- for child in content.node.children:
+ for child in content.node:
cont = Content(child)
cval = self.append(cont)
key = reserved.get(child.name, child.name)
diff --git a/suds/xsd/doctor.py b/suds/xsd/doctor.py
index 84c9151..d7bbc14 100644
--- a/suds/xsd/doctor.py
+++ b/suds/xsd/doctor.py
@@ -22,6 +22,7 @@
from logging import getLogger
from suds.sax import splitPrefix, Namespace
from suds.sax.element import Element
+from suds.plugin import DocumentPlugin, DocumentContext
log = getLogger(__name__)
@@ -186,7 +187,7 @@ def exists(self, root):
return 0
-class ImportDoctor(Doctor):
+class ImportDoctor(Doctor, DocumentPlugin):
"""
Doctor used to fix missing imports.
@ivar imports: A list of imports to apply.
@@ -207,6 +208,19 @@ def add(self, *imports):
"""
self.imports += imports
- def examine(self, root):
+ def examine(self, node):
for imp in self.imports:
- imp.apply(root)
+ imp.apply(node)
+
+ def parsed(self, context):
+ node = context.document
+ # xsd root
+ if node.name == 'schema' and Namespace.xsd(node.namespace()):
+ self.examine(node)
+ return
+ # look deeper
+ context = DocumentContext()
+ for child in node:
+ context.document = child
+ self.parsed(context)
+
\ No newline at end of file
diff --git a/suds/xsd/schema.py b/suds/xsd/schema.py
index a22f1c5..cb7d678 100644
--- a/suds/xsd/schema.py
+++ b/suds/xsd/schema.py
@@ -22,7 +22,7 @@
tranparent referenced type resolution and targeted denormalization.
"""
-from logging import getLogger
+
import suds.metrics
from suds import *
from suds.xsd import *
@@ -33,6 +33,7 @@
from suds.xsd.deplist import DepList
from suds.sax.element import Element
from suds.sax import splitPrefix, Namespace
+from logging import getLogger
log = getLogger(__name__)
@@ -166,16 +167,22 @@ class Schema:
@type baseurl: str
@ivar container: A schema collection containing this schema.
@type container: L{SchemaCollection}
- @ivar types: A schema types cache.
- @type types: {name:L{SchemaObject}}
- @ivar groups: A schema groups cache.
- @type groups: {name:L{SchemaObject}}
@ivar children: A list of direct top level children.
@type children: [L{SchemaObject},...]
@ivar all: A list of all (includes imported) top level children.
@type all: [L{SchemaObject},...]
+ @ivar types: A schema types cache.
+ @type types: {name:L{SchemaObject}}
@ivar imports: A list of import objects.
@type imports: [L{SchemaObject},...]
+ @ivar elements: A list of objects.
+ @type elements: [L{SchemaObject},...]
+ @ivar attributes: A list of objects.
+ @type attributes: [L{SchemaObject},...]
+ @ivar groups: A list of group objects.
+ @type groups: [L{SchemaObject},...]
+ @ivar agrps: A list of attribute group objects.
+ @type agrps: [L{SchemaObject},...]
@ivar form_qualified: The flag indicating:
(@elementFormDefault).
@type form_qualified: bool
diff --git a/suds/xsd/sxbase.py b/suds/xsd/sxbase.py
index 9c2cb1f..2577ffd 100644
--- a/suds/xsd/sxbase.py
+++ b/suds/xsd/sxbase.py
@@ -23,6 +23,7 @@
from suds import *
from suds.xsd import *
from suds.sax.element import Element
+from suds.sax import Namespace
log = getLogger(__name__)
@@ -220,7 +221,7 @@ def resolve(self, nobuiltin=False):
def sequence(self):
"""
Get whether this is an
- @return: True if any, else False
+ @return: True if , else False
@rtype: boolean
"""
return False
@@ -296,6 +297,12 @@ def restriction(self):
@rtype: boolean
"""
return False
+
+ def mixed(self):
+ """
+ Get whether this I{mixed} content.
+ """
+ return False
def find(self, qref, classes=()):
"""
@@ -354,9 +361,14 @@ def autoqualified(self):
def qualify(self):
"""
Convert attribute values, that are references to other
- objects, into I{qref}.
+ objects, into I{qref}. Qualfied using default document namespace.
+ Since many wsdls are written improperly: when the document does
+ not define a default namespace, the schema target namespace is used
+ to qualify references.
"""
defns = self.root.defaultNamespace()
+ if Namespace.none(defns):
+ defns = self.schema.tns
for a in self.autoqualified():
ref = getattr(self, a)
if ref is None:
diff --git a/suds/xsd/sxbasic.py b/suds/xsd/sxbasic.py
index 0b536d2..2506e04 100644
--- a/suds/xsd/sxbasic.py
+++ b/suds/xsd/sxbasic.py
@@ -118,6 +118,12 @@ def extension(self):
if c.extension():
return True
return False
+
+ def mixed(self):
+ for c in self.rawchildren:
+ if isinstance(c, SimpleContent) and c.mixed():
+ return True
+ return False
class Group(SchemaObject):
@@ -195,6 +201,9 @@ def enum(self):
if isinstance(child, Enumeration):
return True
return False
+
+ def mixed(self):
+ return len(self)
def description(self):
return ('name',)
@@ -340,6 +349,9 @@ def restriction(self):
if c.restriction():
return True
return False
+
+ def mixed(self):
+ return len(self)
class Enumeration(Content):
@@ -470,7 +482,7 @@ def extension(self):
def description(self):
return ('ref',)
-
+
class Import(SchemaObject):
"""
diff --git a/suds_transbank_chile.egg-info/PKG-INFO b/suds_transbank_chile.egg-info/PKG-INFO
new file mode 100644
index 0000000..848a962
--- /dev/null
+++ b/suds_transbank_chile.egg-info/PKG-INFO
@@ -0,0 +1,10 @@
+Metadata-Version: 1.0
+Name: suds-transbank-chile
+Version: 0.4
+Summary: Lightweight SOAP client (Adapted For working with Transbank, Chile)
+Home-page: https://fedorahosted.org/suds
+Author: Jeff Ortel
+Author-email: jortel@redhat.com
+License: UNKNOWN
+Description: UNKNOWN
+Platform: UNKNOWN
diff --git a/suds_transbank_chile.egg-info/SOURCES.txt b/suds_transbank_chile.egg-info/SOURCES.txt
new file mode 100644
index 0000000..bdb2d32
--- /dev/null
+++ b/suds_transbank_chile.egg-info/SOURCES.txt
@@ -0,0 +1,61 @@
+README
+setup.cfg
+suds/__init__.py
+suds/builder.py
+suds/cache.py
+suds/client.py
+suds/metrics.py
+suds/options.py
+suds/plugin.py
+suds/properties.py
+suds/reader.py
+suds/resolver.py
+suds/servicedefinition.py
+suds/serviceproxy.py
+suds/soaparray.py
+suds/store.py
+suds/sudsobject.py
+suds/wsdl.py
+suds/wsse.py
+suds/bindings/__init__.py
+suds/bindings/binding.py
+suds/bindings/document.py
+suds/bindings/multiref.py
+suds/bindings/rpc.py
+suds/mx/__init__.py
+suds/mx/appender.py
+suds/mx/basic.py
+suds/mx/core.py
+suds/mx/encoded.py
+suds/mx/literal.py
+suds/mx/typer.py
+suds/sax/__init__.py
+suds/sax/attribute.py
+suds/sax/date.py
+suds/sax/document.py
+suds/sax/element.py
+suds/sax/enc.py
+suds/sax/parser.py
+suds/sax/text.py
+suds/transport/__init__.py
+suds/transport/http.py
+suds/transport/https.py
+suds/transport/options.py
+suds/umx/__init__.py
+suds/umx/attrlist.py
+suds/umx/basic.py
+suds/umx/core.py
+suds/umx/encoded.py
+suds/umx/typed.py
+suds/xsd/__init__.py
+suds/xsd/deplist.py
+suds/xsd/doctor.py
+suds/xsd/query.py
+suds/xsd/schema.py
+suds/xsd/sxbase.py
+suds/xsd/sxbasic.py
+suds/xsd/sxbuiltin.py
+suds_transbank_chile.egg-info/PKG-INFO
+suds_transbank_chile.egg-info/SOURCES.txt
+suds_transbank_chile.egg-info/dependency_links.txt
+suds_transbank_chile.egg-info/top_level.txt
\ No newline at end of file
diff --git a/suds_transbank_chile.egg-info/dependency_links.txt b/suds_transbank_chile.egg-info/dependency_links.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/suds_transbank_chile.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/suds_transbank_chile.egg-info/top_level.txt b/suds_transbank_chile.egg-info/top_level.txt
new file mode 100644
index 0000000..55a2df3
--- /dev/null
+++ b/suds_transbank_chile.egg-info/top_level.txt
@@ -0,0 +1 @@
+suds