summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjortel <devnull@localhost>2010-01-26 22:19:21 +0000
committerjortel <devnull@localhost>2010-01-26 22:19:21 +0000
commitd14a6d40f819b0a46c035da9a5ea4350ca13da13 (patch)
tree6ff39465aa2f3fc23106852c7bb5418efaa5c2c4
parentb0ecda65f6d2f31523bb7f75f4c9cfd59d3e05ba (diff)
downloadsuds-d14a6d40f819b0a46c035da9a5ea4350ca13da13.tar.gz
Enable WSDL caching as pickled object; refactor caching; remove /options/ attributes on object within (xsd) package and instead - pass to methods as needed (which was only a few); remove /schema/ & /options/ attributes from objects in (binding) package and get from wsdl instead; replace Object subclasses with Facade in wsdl for better pickling - true subclasses have no value here anyway.
-rw-r--r--suds/bindings/binding.py24
-rw-r--r--suds/bindings/rpc.py4
-rw-r--r--suds/cache.py194
-rw-r--r--suds/client.py12
-rw-r--r--suds/mx/encoded.py2
-rw-r--r--suds/mx/literal.py9
-rw-r--r--suds/reader.py76
-rw-r--r--suds/wsdl.py142
-rw-r--r--suds/xsd/schema.py49
-rw-r--r--suds/xsd/sxbasic.py24
10 files changed, 309 insertions, 227 deletions
diff --git a/suds/bindings/binding.py b/suds/bindings/binding.py
index c2e7810..a6d421e 100644
--- a/suds/bindings/binding.py
+++ b/suds/bindings/binding.py
@@ -61,10 +61,14 @@ class Binding:
@type wsdl: L{wsdl.Definitions}
"""
self.wsdl = wsdl
- self.schema = wsdl.schema
- self.options = wsdl.options
self.multiref = MultiRef()
+ def schema(self):
+ return self.wsdl.schema
+
+ def options(self):
+ return self.wsdl.options
+
def unmarshaller(self, typed=True):
"""
Get the appropriate XML decoder.
@@ -72,7 +76,7 @@ class Binding:
@rtype: L{UmxTyped}
"""
if typed:
- return UmxTyped(self.schema)
+ return UmxTyped(self.schema())
else:
return UmxBasic()
@@ -82,7 +86,7 @@ class Binding:
@return: An L{MxLiteral} marshaller.
@rtype: L{MxLiteral}
"""
- return MxLiteral(self.schema)
+ return MxLiteral(self.schema(), self.options().xstq)
def param_defs(self, method):
"""
@@ -114,7 +118,7 @@ class Binding:
content = self.bodycontent(method, args, kwargs)
body = self.body(content)
env = self.envelope(header, body)
- if self.options.prefixes:
+ if self.options().prefixes:
body.normalizePrefixes()
env.promotePrefixes()
else:
@@ -230,7 +234,7 @@ class Binding:
fault = soapbody.getChild('Fault')
unmarshaller = self.unmarshaller(False)
p = unmarshaller.process(fault)
- if self.options.faults:
+ if self.options().faults:
raise WebFault(p, faultroot)
return (faultroot, p.detail)
@@ -329,10 +333,10 @@ class Binding:
"""
n = 0
content = []
- wsse = self.options.wsse
+ wsse = self.options().wsse
if wsse is not None:
content.append(wsse.xml())
- headers = self.options.soapheaders
+ headers = self.options().soapheaders
if not isinstance(headers, (tuple,list,dict)):
headers = (headers,)
if len(headers) == 0:
@@ -405,7 +409,7 @@ class Binding:
query = ElementQuery(p.element)
else:
query = TypeQuery(p.type)
- pt = query.execute(self.schema)
+ pt = query.execute(self.schema())
if pt is None:
raise TypeNotFound(query.ref)
if p.type is not None:
@@ -441,7 +445,7 @@ class Binding:
query = ElementQuery(part.element)
else:
query = TypeQuery(part.type)
- pt = query.execute(self.schema)
+ pt = query.execute(self.schema())
if pt is None:
raise TypeNotFound(query.ref)
if part.type is not None:
diff --git a/suds/bindings/rpc.py b/suds/bindings/rpc.py
index 9acd3e1..f780aa4 100644
--- a/suds/bindings/rpc.py
+++ b/suds/bindings/rpc.py
@@ -84,7 +84,7 @@ class Encoded(RPC):
"""
def marshaller(self):
- return MxEncoded(self.schema)
+ return MxEncoded(self.schema())
def unmarshaller(self, typed=True):
"""
@@ -93,6 +93,6 @@ class Encoded(RPC):
@rtype: L{UmxTyped}
"""
if typed:
- return UmxEncoded(self.schema)
+ return UmxEncoded(self.schema())
else:
return RPC.unmarshaller(self, typed)
diff --git a/suds/cache.py b/suds/cache.py
index 80764dc..661bf27 100644
--- a/suds/cache.py
+++ b/suds/cache.py
@@ -33,49 +33,85 @@ except:
log = getLogger(__name__)
-class ByteCache:
+class Cache:
"""
- The URL caching object.
+ An object object cache.
"""
+
+ def get(self, id):
+ """
+ Get a object from the cache by ID.
+ @param id: The object ID.
+ @type id: str
+ @return: The object, else None
+ @rtype: any
+ """
+ raise Exception('not-implemented')
- def put(self, id, fp):
+ def getf(self, id):
"""
- Put an item into the cache.
- @param id: A file ID.
+ Get a object from the cache by ID.
+ @param id: The object ID.
@type id: str
- @param fp: A file stream.
- @type fp: stream
- @return: The stream.
- @rtype: stream
+ @return: The object, else None
+ @rtype: any
"""
raise Exception('not-implemented')
- def get(self, id):
+ def put(self, id, object):
+ """
+ Put a object into the cache.
+ @param id: The object ID.
+ @type id: str
+ @param object: The object to add.
+ @type object: any
+ """
+ raise Exception('not-implemented')
+
+ def putf(self, id, fp):
"""
- Get an item from the cache by id.
- @param id: A file ID.
+ Write a fp into the cache.
+ @param id: The object ID.
@type id: str
- @return: A stream when found, else None.
- @rtype: stream
+ @param fp: File pointer.
+ @type fp: file-like object.
"""
raise Exception('not-implemented')
def purge(self, id):
"""
- Purge a file from the cache by id.
- @param id: A file ID.
+ Purge a object from the cache by id.
+ @param id: A object ID.
@type id: str
"""
raise Exception('not-implemented')
def clear(self):
"""
- Clear the cache.
+ Clear all objects from the cache.
"""
raise Exception('not-implemented')
+
+
+class NoCache(Cache):
+ """
+ The passthru object cache.
+ """
+
+ def get(self, id):
+ return None
+
+ def getf(self, id):
+ return None
+
+ def put(self, id, object):
+ pass
+
+ def putf(self, id, fp):
+ pass
-class FileCache(ByteCache):
+class FileCache(Cache):
"""
A file-based URL cache.
@cvar fnprefix: The file name prefix.
@@ -89,6 +125,7 @@ class FileCache(ByteCache):
@type location: str
"""
fnprefix = 'suds'
+ fnsuffix = 'gcf'
units = ('months', 'weeks', 'days', 'hours', 'minutes', 'seconds')
def __init__(self, location=None, **duration):
@@ -100,7 +137,6 @@ class FileCache(ByteCache):
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
@@ -142,25 +178,45 @@ class FileCache(ByteCache):
log.debug(self.location, exc_info=1)
return self
- def put(self, id, fp):
+ def put(self, id, bfr):
+ try:
+ fn = self.__fn(id)
+ f = self.open(fn, 'w')
+ f.write(bfr)
+ f.close()
+ return bfr
+ except:
+ log.debug(id, exc_info=1)
+ return fp
+
+ def putf(self, id, fp):
try:
fn = self.__fn(id)
f = self.open(fn, 'w')
f.write(fp.read())
f.close()
- return open(fn)
+ return fp
except:
log.debug(id, exc_info=1)
return fp
-
+
def get(self, id):
try:
+ f = self.getf(id)
+ bfr = f.read()
+ f.close()
+ return bfr
+ except:
+ pass
+
+ def getf(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}.
@@ -199,91 +255,35 @@ class FileCache(ByteCache):
return open(fn, *args)
def __fn(self, id):
- fn = '%s-%s.%s' % (self.fnprefix, abs(hash(id)), self.fnsuffix)
+ 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)
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):
+class ObjectCache(FileCache):
"""
- The passthru document cache.
+ Provides pickled object caching.
+ @cvar protocol: The pickling protocol.
+ @type protocol: int
"""
-
- 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
+ protocol = 2
def get(self, id):
try:
- fp = self.cache.get(id)
+ fp = FileCache.getf(self, id)
if fp is None:
return None
else:
return pickle.load(fp)
except:
- self.cache.purge(id)
+ FileCache.purge(self, 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
+ def put(self, id, object):
+ bfr = pickle.dumps(object, self.protocol)
+ FileCache.put(self, id, bfr)
+ return object
diff --git a/suds/client.py b/suds/client.py
index 0d7c7f3..b91a7da 100644
--- a/suds/client.py
+++ b/suds/client.py
@@ -23,6 +23,7 @@ import suds
import suds.metrics as metrics
from cookielib import CookieJar
from suds import *
+from suds.reader import DefinitionsReader
from suds.transport import TransportError, Request
from suds.transport.https import HttpAuthenticated
from suds.servicedefinition import ServiceDefinition
@@ -32,7 +33,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.cache import ObjectCache
from suds.sax.document import Document
from suds.sax.parser import Parser
from suds.options import Options
@@ -104,9 +105,10 @@ class Client(object):
options = Options()
options.transport = HttpAuthenticated()
self.options = options
- options.cache = DocumentStore(days=1)
+ options.cache = ObjectCache(days=1)
self.set_options(**kwargs)
- self.wsdl = Definitions(url, options)
+ reader = DefinitionsReader(options, Definitions)
+ self.wsdl = reader.open(url)
self.factory = Factory(self.wsdl)
self.service = ServiceSelector(self, self.wsdl.services)
self.sd = []
@@ -587,7 +589,6 @@ class SoapClient:
timer.start()
result = None
binding = self.method.binding.input
- binding.options = self.options
msg = binding.get_message(self.method, args, kwargs)
timer.stop()
metrics.log.debug(
@@ -750,17 +751,14 @@ class SimClient(SoapClient):
def __reply(self, reply, args, kwargs):
""" simulate the reply """
binding = self.method.binding.input
- binding.options = self.options
msg = binding.get_message(self.method, args, kwargs)
log.debug('inject (simulated) send message:\n%s', msg)
binding = self.method.binding.output
- binding.options = self.options
return self.succeeded(binding, reply)
def __fault(self, reply):
""" simulate the (fault) reply """
binding = self.method.binding.output
- binding.options = self.options
if self.options.faults:
r, p = binding.get_fault(reply)
self.last_received(r)
diff --git a/suds/mx/encoded.py b/suds/mx/encoded.py
index 93fe444..9cbc8c5 100644
--- a/suds/mx/encoded.py
+++ b/suds/mx/encoded.py
@@ -87,7 +87,7 @@ class Encoded(Literal):
return
ns = None
name = content.real.name
- if self.options.xstq:
+ if self.xstq:
ns = content.real.namespace()
Typer.manual(node, name, ns)
diff --git a/suds/mx/literal.py b/suds/mx/literal.py
index 706d14a..937ad8e 100644
--- a/suds/mx/literal.py
+++ b/suds/mx/literal.py
@@ -53,14 +53,17 @@ class Typed(Core):
@type resolver: L{GraphResolver}
"""
- def __init__(self, schema):
+ def __init__(self, schema, xstq=True):
"""
@param schema: A schema object
@type schema: L{xsd.schema.Schema}
+ @param xstq: The B{x}ml B{s}chema B{t}ype B{q}ualified flag indicates
+ that the I{xsi:type} attribute values should be qualified by namespace.
+ @type xstq: bool
"""
Core.__init__(self)
self.schema = schema
- self.options = schema.options
+ self.xstq = xstq
self.resolver = GraphResolver(self.schema)
def reset(self):
@@ -190,7 +193,7 @@ class Typed(Core):
return
ns = None
name = content.real.name
- if self.options.xstq:
+ if self.xstq:
ns = content.real.namespace('ns1')
Typer.manual(node, name, ns)
diff --git a/suds/reader.py b/suds/reader.py
index 16eff1f..70a7074 100644
--- a/suds/reader.py
+++ b/suds/reader.py
@@ -21,13 +21,31 @@ Contains xml document reader classes.
from suds.sax.parser import Parser
from suds.transport import Request
+from logging import getLogger
+
+
+log = getLogger(__name__)
+
+
+class ObjectId(object):
+
+ def __init__(self, name, suffix):
+ self.name = name
+ self.suffix = suffix
+
class DocumentReader:
"""
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
+ @ivar options: An options object.
+ @type options: I{Options}
"""
+ suffix = 'pxd'
+
def __init__(self, options):
"""
@param options: An options object.
@@ -39,18 +57,68 @@ class DocumentReader:
"""
Open an XML document at the specified I{url}.
First, the document attempted to be retrieved from
- the I{document cache}. If not found, it is downloaded and
+ the I{object cache}. If not found, it is downloaded and
parsed using the SAX parser. The result is added to the
- document store for the next open().
+ cache for the next open().
@param url: A document url.
@type url: str.
@return: The specified XML document.
@rtype: I{Document}
"""
- d = self.options.cache.get(url)
+ id = ObjectId(url, self.suffix)
+ cache = self.options.cache
+ d = cache.get(id)
if d is None:
fp = self.options.transport.open(Request(url))
sax = Parser()
d = sax.parse(file=fp)
- self.options.cache.put(url, d)
+ cache.put(id, d)
return d
+
+
+class DefinitionsReader:
+ """
+ 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.
+ @type options: I{Options}
+ @param fn: A factory function (constructor) used to
+ create the object not found in the cache.
+ @type fn: I{Constructor}
+ """
+ self.options = options
+ self.fn = fn
+
+ def open(self, url):
+ """
+ Open a WSDL at the specified I{url}.
+ First, the WSDL attempted to be retrieved from
+ the I{object cache}. If not found, it is downloaded and
+ instantiated using the I{fn} constructor. The result is added
+ to the cache for the next open().
+ @param url: A WSDL url.
+ @type url: str.
+ @return: The WSDL object.
+ @rtype: I{Definitions}
+ """
+ id = ObjectId(url, self.suffix)
+ cache = self.options.cache
+ d = cache.get(id)
+ if d is None:
+ d = self.fn(url, self.options)
+ cache.put(id, d)
+ d.options = self.options
+ return d \ No newline at end of file
diff --git a/suds/wsdl.py b/suds/wsdl.py
index 558f8be..c3e47be 100644
--- a/suds/wsdl.py
+++ b/suds/wsdl.py
@@ -29,9 +29,8 @@ from suds.bindings.rpc import RPC, Encoded
from suds.xsd import qualify, Namespace
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 suds.sudsobject import Object, Facade, Metadata
+from suds.reader import DocumentReader, DefinitionsReader
from urlparse import urljoin
import re, soaparray
@@ -42,41 +41,6 @@ soapns = (None, 'http://schemas.xmlsoap.org/wsdl/soap/')
soap12ns = (None, 'http://schemas.xmlsoap.org/wsdl/soap12/')
-class Factory:
- """
- Simple WSDL object factory.
- @cvar tags: Dictionary of tag->constructor mappings.
- @type tags: dict
- """
-
- tags =\
- {
- 'import' : lambda x,y: Import(x,y),
- 'types' : lambda x,y: Types(x,y),
- 'message' : lambda x,y: Message(x,y),
- 'portType' : lambda x,y: PortType(x,y),
- 'binding' : lambda x,y: Binding(x,y),
- 'service' : lambda x,y: Service(x,y),
- }
-
- @classmethod
- def create(cls, root, definitions):
- """
- Create an object based on the root tag name.
- @param root: An XML root element.
- @type root: L{Element}
- @param definitions: A definitions object.
- @type definitions: L{Definitions}
- @return: The created object.
- @rtype: L{WObject}
- """
- fn = cls.tags.get(root.name)
- if fn is not None:
- return fn(root, definitions)
- else:
- return None
-
-
class WObject(Object):
"""
Base object for wsdl types.
@@ -93,9 +57,9 @@ class WObject(Object):
"""
Object.__init__(self)
self.root = root
- pmd = SFactory.metadata()
+ pmd = Metadata()
pmd.excludes = ['root']
- pmd.wrappers = dict(qname=lambda x: repr(x))
+ pmd.wrappers = dict(qname=repr)
self.__metadata__.__print__ = pmd
def resolve(self, definitions):
@@ -127,7 +91,7 @@ class NamedObject(WObject):
self.name = root.get('name')
self.qname = (self.name, definitions.tns[1])
pmd = self.__metadata__.__print__
- pmd.wrappers['qname'] = lambda x: repr(x)
+ pmd.wrappers['qname'] = repr
class Definitions(WObject):
@@ -189,7 +153,7 @@ class Definitions(WObject):
pmd = self.__metadata__.__print__
pmd.excludes.append('children')
pmd.excludes.append('wsdl')
- pmd.wrappers['schema'] = lambda x: repr(x)
+ pmd.wrappers['schema'] = repr
self.open_imports()
self.resolve()
self.build_schema()
@@ -246,14 +210,14 @@ class Definitions(WObject):
""" Process L{Types} objects and create the schema collection """
container = SchemaCollection(self)
for t in [t for t in self.types if t.local()]:
- for r in t.contents():
- entry = (r, self)
- container.add(entry)
+ for root in t.contents():
+ schema = Schema(root, self.url, self.options, container)
+ container.add(schema)
if not len(container): # empty
- r = Element.buildPath(self.root, 'types/schema')
- entry = (r, self)
- container.add(entry)
- self.schema = container.load()
+ root = Element.buildPath(self.root, 'types/schema')
+ schema = Schema(root, self.url, self.options, container)
+ container.add(schema)
+ self.schema = container.load(self.options)
for s in [t.schema() for t in self.types if t.imported()]:
self.schema.merge(s)
return self.schema
@@ -270,10 +234,10 @@ class Definitions(WObject):
ptype = p.binding.type
operations = p.binding.type.operations.values()
for name in [op.name for op in operations]:
- m = SFactory.object('Method')
+ m = Facade('Method')
m.name = name
m.location = p.location
- m.binding = SFactory.object('binding')
+ m.binding = Facade('binding')
op = binding.operation(name)
m.soap = op.soap
key = '/'.join((op.soap.style, op.soap.input.body.use))
@@ -302,7 +266,11 @@ class Definitions(WObject):
if resolved.builtin():
continue
body.wrapped = True
-
+
+ def __getstate__(self):
+ d = self.__dict__.copy()
+ del d['options']
+ return d
class Import(WObject):
@@ -316,6 +284,9 @@ class Import(WObject):
@type imported: L{Definitions}
"""
+ def getid(x):
+ return x.id
+
def __init__(self, root, definitions):
"""
@param root: An XML root element.
@@ -328,7 +299,7 @@ class Import(WObject):
self.ns = root.get('namespace')
self.imported = None
pmd = self.__metadata__.__print__
- pmd.wrappers['imported'] = ( lambda x: x.id )
+ pmd.wrappers['imported'] = getid
def load(self, definitions):
""" Load the object by opening the URL """
@@ -336,7 +307,8 @@ class Import(WObject):
log.debug('importing (%s)', url)
if '://' not in url:
url = urljoin(definitions.url, url)
- d = Definitions(url, definitions.options)
+ reader = DefinitionsReader(definitions.options, Definitions)
+ d = reader.open(url)
if d.root.match(Definitions.Tag, wsdlns):
self.import_definitions(definitions, d)
return
@@ -424,9 +396,8 @@ class Part(NamedObject):
@type definitions: L{Definitions}
"""
NamedObject.__init__(self, root, definitions)
- pmd = SFactory.metadata()
- pmd.wrappers = \
- dict(element=lambda x: repr(x), type=lambda x: repr(x))
+ pmd = Metadata()
+ pmd.wrappers = dict(element=repr, type=repr)
self.__metadata__.__print__ = pmd
tns = definitions.tns
self.element = self.__getref('element', tns)
@@ -482,7 +453,7 @@ class PortType(NamedObject):
NamedObject.__init__(self, root, definitions)
self.operations = {}
for c in root.getChildren('operation'):
- op = SFactory.object('Operation')
+ op = Facade('Operation')
op.name = c.get('name')
op.tns = definitions.tns
input = c.getChild('input')
@@ -497,7 +468,7 @@ class PortType(NamedObject):
op.output = output.get('message')
faults = []
for fault in c.getChildren('fault'):
- f = SFactory.object('Fault')
+ f = Facade('Fault')
f.name = fault.get('name')
f.message = fault.get('message')
faults.append(f)
@@ -576,7 +547,7 @@ class Binding(NamedObject):
self.soap = None
log.debug('binding: "%s" not a soap binding', self.name)
return
- soap = SFactory.object('soap')
+ soap = Facade('soap')
self.soap = soap
self.soap.style = sr.get('style', default='document')
self.add_operations(self.root, definitions)
@@ -593,17 +564,17 @@ class Binding(NamedObject):
""" Add <operation/> children """
dsop = Element('operation', ns=soapns)
for c in root.getChildren('operation'):
- op = SFactory.object('Operation')
+ op = Facade('Operation')
op.name = c.get('name')
sop = c.getChild('operation', default=dsop)
- soap = SFactory.object('soap')
+ soap = Facade('soap')
soap.action = '"%s"' % sop.get('soapAction', default='')
soap.style = sop.get('style', default=self.soap.style)
- soap.input = SFactory.object('Input')
- soap.input.body = SFactory.object('Body')
+ soap.input = Facade('Input')
+ soap.input.body = Facade('Body')
soap.input.headers = []
- soap.output = SFactory.object('Output')
- soap.output.body = SFactory.object('Body')
+ soap.output = Facade('Output')
+ soap.output.body = Facade('Body')
soap.output.headers = []
op.soap = soap
input = c.getChild('input')
@@ -626,7 +597,7 @@ class Binding(NamedObject):
if sf is None:
continue
fn = fault.get('name')
- f = SFactory.object('Fault')
+ f = Facade('Fault')
f.name = sf.get('name', default=fn)
f.use = sf.get('use', default='literal')
faults.append(f)
@@ -657,7 +628,7 @@ class Binding(NamedObject):
""" add the input/output header properties """
if root is None:
return
- header = SFactory.object('Header')
+ header = Facade('Header')
parent.headers.append(header)
header.use = root.get('use', default='literal')
ns = root.get('namespace')
@@ -911,3 +882,38 @@ class Service(NamedObject):
def __gt__(self, other):
return True
+
+
+class Factory:
+ """
+ Simple WSDL object factory.
+ @cvar tags: Dictionary of tag->constructor mappings.
+ @type tags: dict
+ """
+
+ tags =\
+ {
+ 'import' : Import,
+ 'types' : Types,
+ 'message' : Message,
+ 'portType' : PortType,
+ 'binding' : Binding,
+ 'service' : Service,
+ }
+
+ @classmethod
+ def create(cls, root, definitions):
+ """
+ Create an object based on the root tag name.
+ @param root: An XML root element.
+ @type root: L{Element}
+ @param definitions: A definitions object.
+ @type definitions: L{Definitions}
+ @return: The created object.
+ @rtype: L{WObject}
+ """
+ fn = cls.tags.get(root.name)
+ if fn is not None:
+ return fn(root, definitions)
+ else:
+ return None
diff --git a/suds/xsd/schema.py b/suds/xsd/schema.py
index 2998cc4..a22f1c5 100644
--- a/suds/xsd/schema.py
+++ b/suds/xsd/schema.py
@@ -55,7 +55,6 @@ class SchemaCollection:
@type wsdl: L{suds.wsdl.Definitions}
"""
self.wsdl = wsdl
- self.options = wsdl.options
self.children = []
self.namespaces = {}
@@ -63,34 +62,34 @@ class SchemaCollection:
"""
Add a schema node to the collection. Schema(s) within the same target
namespace are consolidated.
- @param schema: A <schema/> entry.
- @type schema: (L{suds.wsdl.Definitions},L{sax.element.Element})
+ @param schema: A schema object.
+ @type schema: (L{Schema})
"""
- root, wsdl = schema
- child = Schema(root, wsdl.url, self.options, container=self)
- key = child.tns[1]
+ key = schema.tns[1]
existing = self.namespaces.get(key)
if existing is None:
- self.children.append(child)
- self.namespaces[key] = child
+ self.children.append(schema)
+ self.namespaces[key] = schema
else:
- existing.root.children += root.children
- existing.root.nsprefixes.update(root.nsprefixes)
+ existing.root.children += schema.root.children
+ existing.root.nsprefixes.update(schema.root.nsprefixes)
- def load(self):
+ def load(self, options):
"""
Load the schema objects for the root nodes.
- de-references schemas
- merge schemas
+ @param options: An options dictionary.
+ @type options: L{options.Options}
@return: The merged schema.
@rtype: L{Schema}
"""
- if self.options.autoblend:
+ if options.autoblend:
self.autoblend()
for child in self.children:
child.build()
for child in self.children:
- child.open_imports()
+ child.open_imports(options)
for child in self.children:
child.dereference()
log.debug('loaded:\n%s', self)
@@ -165,8 +164,6 @@ class Schema:
@type root: L{sax.element.Element}
@ivar baseurl: The I{base} URL for this schema.
@type baseurl: str
- @ivar options: An options dictionary.
- @type options: L{options.Options}
@ivar container: A schema collection containing this schema.
@type container: L{SchemaCollection}
@ivar types: A schema types cache.
@@ -201,7 +198,6 @@ class Schema:
self.id = objid(self)
self.tns = self.mktns()
self.baseurl = baseurl
- self.options = options
self.container = container
self.children = []
self.all = []
@@ -211,9 +207,8 @@ class Schema:
self.attributes = {}
self.groups = {}
self.agrps = {}
- doctor = self.options.doctor
- if doctor is not None:
- doctor.examine(root)
+ if options.doctor is not None:
+ options.doctor.examine(root)
form = self.root.get('elementFormDefault')
if form is None:
self.form_qualified = False
@@ -221,7 +216,7 @@ class Schema:
self.form_qualified = ( form == 'qualified' )
if container is None:
self.build()
- self.open_imports()
+ self.open_imports(options)
log.debug('built:\n%s', self)
self.dereference()
log.debug('dereferenced:\n%s', self)
@@ -291,17 +286,19 @@ class Schema:
schema.merged = True
return self
- def open_imports(self):
+ def open_imports(self, options):
"""
Instruct all contained L{sxbasic.Import} children to import
the schema's which they reference. The contents of the
imported schema are I{merged} in.
+ @param options: An options dictionary.
+ @type options: L{options.Options}
"""
for imp in self.imports:
- imported = imp.open()
+ imported = imp.open(options)
if imported is None:
continue
- imported.open_imports()
+ imported.open_imports(options)
log.debug('imported:\n%s', imported)
self.merge(imported)
@@ -376,7 +373,7 @@ class Schema:
except:
return False
- def instance(self, root, baseurl):
+ def instance(self, root, baseurl, options):
"""
Create and return an new schema object using the
specified I{root} and I{url}.
@@ -384,11 +381,13 @@ class Schema:
@type root: L{sax.element.Element}
@param baseurl: A base URL.
@type baseurl: str
+ @param options: An options dictionary.
+ @type options: L{options.Options}
@return: The newly created schema object.
@rtype: L{Schema}
@note: This is only used by Import children.
"""
- return Schema(root, baseurl, self.options)
+ return Schema(root, baseurl, options)
def str(self, indent=0):
tab = '%*s'%(indent*3, '')
diff --git a/suds/xsd/sxbasic.py b/suds/xsd/sxbasic.py
index d58dfff..a10e0cd 100644
--- a/suds/xsd/sxbasic.py
+++ b/suds/xsd/sxbasic.py
@@ -495,9 +495,11 @@ class Import(SchemaObject):
self.location = self.locations.get(self.ns[1])
self.opened = False
- def open(self):
+ def open(self, options):
"""
Open and import the refrenced schema.
+ @param options: An options dictionary.
+ @type options: L{options.Options}
@return: The referenced schema.
@rtype: L{Schema}
"""
@@ -510,7 +512,7 @@ class Import(SchemaObject):
if self.location is None:
log.debug('imported schema (%s) not-found', self.ns[1])
else:
- result = self.download()
+ result = self.download(options)
log.debug('imported:\n%s', result)
return result
@@ -521,17 +523,17 @@ class Import(SchemaObject):
else:
return self.schema.locate(self.ns)
- def download(self):
+ def download(self, options):
""" download the schema """
url = self.location
try:
if '://' not in url:
url = urljoin(self.schema.baseurl, url)
- reader = DocumentReader(self.schema.options)
+ reader = DocumentReader(options)
d = reader.open(url)
root = d.root()
root.set('url', url)
- return self.schema.instance(root, url)
+ return self.schema.instance(root, url, options)
except TransportError:
msg = 'imported schema (%s) at (%s), failed' % (self.ns[1], url)
log.error('%s, %s', self.id, msg, exc_info=True)
@@ -559,9 +561,11 @@ class Include(SchemaObject):
self.location = self.locations.get(self.ns[1])
self.opened = False
- def open(self):
+ def open(self, options):
"""
Open and include the refrenced schema.
+ @param options: An options dictionary.
+ @type options: L{options.Options}
@return: The referenced schema.
@rtype: L{Schema}
"""
@@ -569,22 +573,22 @@ class Include(SchemaObject):
return
self.opened = True
log.debug('%s, including location="%s"', self.id, self.location)
- result = self.download()
+ result = self.download(options)
log.debug('included:\n%s', result)
return result
- def download(self):
+ def download(self, options):
""" download the schema """
url = self.location
try:
if '://' not in url:
url = urljoin(self.schema.baseurl, url)
- reader = DocumentReader(self.schema.options)
+ reader = DocumentReader(options)
d = reader.open(url)
root = d.root()
root.set('url', url)
self.__applytns(root)
- return self.schema.instance(root, url)
+ return self.schema.instance(root, url, options)
except TransportError:
msg = 'include schema at (%s), failed' % url
log.error('%s, %s', self.id, msg, exc_info=True)