summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjortel <devnull@localhost>2009-12-02 19:37:59 +0000
committerjortel <devnull@localhost>2009-12-02 19:37:59 +0000
commitf56d82f650365cf160a53026dfce099c24162a62 (patch)
tree4a8f808926d0e20dea433220ebb09b6df138a9bd
parent75b540025a5c5a21d5c0f2ece8df0f8845cbf5fa (diff)
downloadsuds-f56d82f650365cf160a53026dfce099c24162a62.tar.gz
Rename mx.Literal to mx.Typed and create mx.Literal. Update Typed.start() to always sort and translate objects. Also, update mx.Typed.translate() to wrap python dict values as suds objects so they can be sorted. Passing dict for objects should be completely tested. Add 'real', and 'ancestry' content extension in umx package to be used by (Typed, Literal & Encoded) marshallers instead of accessing the resolver stack top() frame. Add comments and update epydocs.
-rw-r--r--suds/mx/encoded.py17
-rw-r--r--suds/mx/literal.py147
-rw-r--r--tests/axis1.py63
3 files changed, 183 insertions, 44 deletions
diff --git a/suds/mx/encoded.py b/suds/mx/encoded.py
index eaad327..04ec077 100644
--- a/suds/mx/encoded.py
+++ b/suds/mx/encoded.py
@@ -79,16 +79,13 @@ class Encoded(Literal):
if content.type.any():
Typer.auto(node, content.value)
return
- resolved = self.resolver.top().resolved
- if resolved is None:
- resolved = content.type.resolve()
- if resolved.any():
+ if content.real.any():
Typer.auto(node, content.value)
return
ns = None
- name = resolved.name
+ name = content.real.name
if self.options.xstq:
- ns = resolved.namespace()
+ ns = content.real.namespace()
Typer.manual(node, name, ns)
def cast(self, content):
@@ -111,13 +108,7 @@ class Encoded(Literal):
if ref is None:
raise TypeNotFound(qref)
for x in content.value:
- if isinstance(x, Object):
- array.item.append(x)
- continue
- if isinstance(x, dict):
- x = Factory.object(ref.name, x)
- md = x.__metadata__
- md.sxtype = ref
+ if isinstance(x, (list, tuple, Object)):
array.item.append(x)
continue
x = Factory.property(ref.name, x)
diff --git a/suds/mx/literal.py b/suds/mx/literal.py
index c9e018d..9417571 100644
--- a/suds/mx/literal.py
+++ b/suds/mx/literal.py
@@ -25,18 +25,25 @@ from suds.mx.core import Core
from suds.mx.typer import Typer
from suds.resolver import GraphResolver, Frame
from suds.sax.element import Element
+from suds.sudsobject import Factory
log = getLogger(__name__)
+#
+# Add typed extensions
+#
Content.extensions.append('type')
+Content.extensions.append('real')
+Content.extensions.append('ancestry')
+
-class Literal(Core):
+class Typed(Core):
"""
- A I{literal} marshaller.
+ A I{typed} marshaller.
This marshaller is semi-typed as needed to support both
- document/literal and rpc/literal soap styles.
+ I{document/literal} and I{rpc/literal} soap message styles.
@ivar schema: An xsd schema.
@type schema: L{xsd.schema.Schema}
@ivar resolver: A schema type resolver.
@@ -57,6 +64,13 @@ class Literal(Core):
self.resolver.reset()
def start(self, content):
+ #
+ # Start marshalling the 'content' by ensuring that both the
+ # 'content' _and_ the resolver are primed with the XSD type
+ # information. The 'content' value is both translated and
+ # sorted based on the XSD type. Only values that are objects
+ # have their attributes sorted.
+ #
log.debug('starting content:\n%s', content)
if content.type is None:
name = content.tag
@@ -72,11 +86,13 @@ class Literal(Core):
if known is None:
log.debug('object has no type information', content.value)
known = content.type
- self.sort(content.value, known)
frame = Frame(content.type, resolved=known)
self.resolver.push(frame)
- resolved = self.resolver.top().resolved
- content.value = self.translated(content.value, resolved)
+ frame = self.resolver.top()
+ content.real = frame.resolved
+ content.ancestry = frame.ancestry
+ self.translate(content)
+ self.sort(content)
if self.skip(content):
log.debug('skipping (optional) content:\n%s', content)
self.resolver.pop()
@@ -85,22 +101,41 @@ class Literal(Core):
return True
def suspend(self, content):
+ #
+ # Suspend to process a list content. Primarily, this
+ # involves popping the 'list' content off the resolver's
+ # stack so the list items can be marshalled.
+ #
self.resolver.pop()
def resume(self, content):
+ #
+ # Resume processing a list content. To do this, we
+ # really need to simply push the 'list' content
+ # back onto the resolver stack.
+ #
self.resolver.push(Frame(content.type))
def end(self, parent, content):
+ #
+ # End processing the content. Make sure the content
+ # ending matches the top of the resolver stack since for
+ # list processing we play games with the resolver stack.
+ #
log.debug('ending content:\n%s', content)
current = self.resolver.top().type
if current == content.type:
self.resolver.pop()
else:
- raise Exception(
+ raise Exception, \
'content (end) mismatch: top=(%s) cont=(%s)' % \
- (current, content))
+ (current, content)
def node(self, content):
+ #
+ # Create an XML node and namespace qualify as defined
+ # by the schema (elementFormDefault).
+ #
ns = content.type.namespace()
if content.type.form_qualified:
node = Element(content.tag, ns=ns)
@@ -112,10 +147,18 @@ class Literal(Core):
return node
def setnil(self, node, content):
+ #
+ # Set the 'node' nil only if the XSD type
+ # specifies that it is permitted.
+ #
if content.type.nillable:
node.setnil()
def setdefault(self, node, content):
+ #
+ # Set the node to the default value specified
+ # by the XSD type.
+ #
default = content.type.default
if default is None:
pass
@@ -126,9 +169,7 @@ class Literal(Core):
def optional(self, content):
if content.type.optional():
return True
- resolver = self.resolver
- ancestry = resolver.top().ancestry
- for a in ancestry:
+ for a in content.ancestry:
if a.optional():
return True
return False
@@ -140,19 +181,24 @@ class Literal(Core):
# referenced type are in different namespaces.
if content.type.any():
return
- resolved = self.resolver.top().resolved
- if resolved is None:
- resolved = content.type.resolve()
- if not resolved.extension():
+ if not content.real.extension():
return
ns = None
- name = resolved.name
+ name = content.real.name
if self.options.xstq:
- ns = resolved.namespace('ns1')
+ ns = content.real.namespace('ns1')
Typer.manual(node, name, ns)
def skip(self, content):
- """ skip this content """
+ """
+ Get whether to skip this I{content}.
+ Should be skipped when the content is optional
+ and either the value=None or the value is an empty list.
+ @param content: The content to skip.
+ @type content: L{Object}
+ @return: True if content is to be skipped.
+ @rtype: bool
+ """
if self.optional(content):
v = content.value
if v is None:
@@ -164,26 +210,57 @@ class Literal(Core):
def optional(self, content):
if content.type.optional():
return True
- ancestry = self.resolver.top().ancestry
- for a in ancestry:
+ for a in content.ancestry:
if a.optional():
return True
return False
- def translated(self, value, resolved):
- """ translate using the schema type """
- if value is not None:
- return resolved.translate(value, False)
- else:
- return None
+ def translate(self, content):
+ """
+ Translate using the XSD type information.
+ Python I{dict} is translated to a suds object. Most
+ importantly, primative values are translated from python
+ types to XML types using the XSD type.
+ @param content: The content to translate.
+ @type content: L{Object}
+ @return: self
+ @rtype: L{Typed}
+ """
+ v = content.value
+ if v is None:
+ return
+ if isinstance(v, dict):
+ cls = content.real.name
+ content.value = Factory.object(cls, v)
+ return
+ v = content.real.translate(v, False)
+ content.value = v
+ return self
- def sort(self, sobject, resolved):
- """ sort attributes using the schema type """
- md = sobject.__metadata__
- md.ordering = self.ordering(resolved)
+ def sort(self, content):
+ """
+ Sort suds object attributes based on ordering defined
+ in the XSD type information.
+ @param content: The content to sort.
+ @type content: L{Object}
+ @return: self
+ @rtype: L{Typed}
+ """
+ v = content.value
+ if isinstance(v, Object):
+ md = v.__metadata__
+ md.ordering = self.ordering(content.real)
+ return self
def ordering(self, type):
- """ get the ordering """
+ """
+ Get the attribute ordering defined in the specified
+ XSD type information.
+ @param type: An XSD type object.
+ @type type: SchemaObject
+ @return: An ordered list of attribute names.
+ @rtype: list
+ """
result = []
for child, ancestry in type.resolve():
name = child.name
@@ -194,3 +271,11 @@ class Literal(Core):
result.append(name)
return result
+
+class Literal(Typed):
+ """
+ A I{literal} marshaller.
+ This marshaller is semi-typed as needed to support both
+ I{document/literal} and I{rpc/literal} soap message styles.
+ """
+ pass \ No newline at end of file
diff --git a/tests/axis1.py b/tests/axis1.py
index f01a24c..9d9be01 100644
--- a/tests/axis1.py
+++ b/tests/axis1.py
@@ -130,6 +130,69 @@ except Exception, e:
tb.print_exc()
try:
+ url = 'http://localhost:8081/axis/services/basic-rpc-encoded?wsdl'
+ start(url)
+ t = HttpAuthenticated(**credentials)
+ client = Client(url, transport=t, cache=None)
+ print client
+ #
+ # create a name object as dict
+ #
+ print 'create name'
+ name = {}
+ name['first'] = 'Elmer'
+ name['last'] = 'Fudd'
+ print name
+ #
+ # create a phone as dict
+ #
+ print 'create phone'
+ phoneA = {}
+ phoneA['npa'] = 410
+ phoneA['nxx'] = 555
+ phoneA['number'] = 5138
+ phoneB = {}
+ phoneB['npa'] = 919
+ phoneB['nxx'] = 555
+ phoneB['number'] = 4406
+ phoneC = {
+ 'npa':205,
+ 'nxx':777,
+ 'number':1212
+ }
+ #
+ # create a dog
+ #
+ dog = {
+ 'name':'Chance',
+ 'trained':True,
+ }
+ #
+ # create a person as dict
+ #
+ person = {}
+ print '{empty} person=\n%s' % person
+ person['name'] = name
+ person['age'] = 43
+ person['phone'] = [phoneA,phoneB, phoneC]
+ person['pets'] = [dog]
+ print 'person=\n%s' % person
+ #
+ # add the person (using the webservice)
+ #
+ print 'addPersion()'
+ result = client.service.addPerson(person)
+ print '\nreply(\n%s\n)\n' % str(result)
+except WebFault, f:
+ errors += 1
+ print f
+ print f.fault
+except Exception, e:
+ errors += 1
+ print e
+ tb.print_exc()
+
+try:
print "echo(' this is cool ')"
result = client.service.echo('this is cool')
print '\nreply( "%s" )\n' % str(result)