Skip to content

Commit

Permalink
Merge pull request arskom#194 from plq/master
Browse files Browse the repository at this point in the history
fix _body_style='bare' for soap
  • Loading branch information
plq committed Oct 26, 2012
2 parents 4f7a03a + 1a43a0e commit f4ea16b
Show file tree
Hide file tree
Showing 14 changed files with 432 additions and 257 deletions.
13 changes: 0 additions & 13 deletions examples/helloworld_soap.py
Expand Up @@ -77,19 +77,6 @@ def say_hello(name, times):
for i in range(times):
yield u'Hello, %s' % name

@srpc(Unicode, Integer, _returns=Iterable(Unicode), _soap_body_style='document',_body_style='bare')
def say_hello_2(name, times):
'''
Docstrings for service methods appear as documentation in the wsdl
<b>what fun</b>
@param name the name to say hello to
@param the number of times to say hello
@return the completed array
'''

for i in range(times):
yield u'Hello, %s' % name


if __name__=='__main__':
from wsgiref.simple_server import make_server
Expand Down
3 changes: 1 addition & 2 deletions examples/helloworld_soap_suds_client.py
Expand Up @@ -31,7 +31,6 @@

from suds.client import Client

client = Client('http://localhost:7789/?wsdl')
client = Client('http://localhost:7789/?wsdl', cache=None)

print client.service.say_hello(u'Jérôme', 5)
print client.service.say_hello2(u'Jérôme', 5)
8 changes: 4 additions & 4 deletions spyne/decorator.py
Expand Up @@ -59,7 +59,7 @@ def _produce_input_message(f, params, _in_message_name, _in_variable_names, no_c
if _in_message_name.startswith("{"):
ns = _in_message_name[1:].partition("}")[0]

message=ComplexModel.produce(type_name=_in_message_name, namespace=ns,
message = ComplexModel.produce(type_name=_in_message_name, namespace=ns,
members=in_params)
message.__namespace__ = ns

Expand Down Expand Up @@ -122,14 +122,14 @@ def _produce_output_message(f, func_name, kparams):
if _out_message_name.startswith("{"):
ns = _out_message_name[1:].partition("}")[0]

if _body_style == 'wrapped':
if _body_style == 'bare' and _returns is not None:
message = ComplexModel.alias(_out_message_name, ns, _returns)
else:
message = ComplexModel.produce(type_name=_out_message_name,
namespace=ns,
members=out_params)
message.__namespace__ = ns # FIXME: is this necessary?

else:
message = ComplexModel.alias(_out_message_name, ns, _returns)

return message

Expand Down
5 changes: 0 additions & 5 deletions spyne/interface/_base.py
Expand Up @@ -278,11 +278,6 @@ def get_namespace_prefix(self, ns):
return pref

def add_class(self, cls):
if issubclass(cls, Alias):
if issubclass(cls._target, ComplexModelBase):
self.add_class(cls._target)
return

if self.has_class(cls):
return

Expand Down
3 changes: 2 additions & 1 deletion spyne/interface/xml_schema/model.py
Expand Up @@ -162,9 +162,10 @@ def complex_add(document, cls):


def alias_add(document, cls):
t, = cls._type_info.values()
element = etree.Element('{%s}element' % _ns_xsd)
element.set('name', cls.get_type_name())
element.set('type', cls._target.get_type_name_ns(document.interface))
element.set('type', t.get_type_name_ns(document.interface))

document.add_element(cls, element)

Expand Down
36 changes: 17 additions & 19 deletions spyne/model/complex.py
Expand Up @@ -37,6 +37,7 @@
from spyne.const import xml_ns as namespace
from spyne.const import MAX_STRING_FIELD_LENGTH
from spyne.const import MAX_ARRAY_ELEMENT_NUM
from spyne.const.suffix import ARRAY_SUFFIX
from spyne.const.suffix import TYPE_SUFFIX

from spyne.util import sanitize_args
Expand Down Expand Up @@ -574,13 +575,11 @@ def resolve_namespace(cls, default_ns):
def produce(namespace, type_name, members):
"""Lets you create a class programmatically."""

cls_dict = {}

cls_dict['__namespace__'] = namespace
cls_dict['__type_name__'] = type_name
cls_dict['_type_info'] = TypeInfo(members)

return ComplexModelMeta(type_name, (ComplexModel,), cls_dict)
return ComplexModelMeta(type_name, (ComplexModel,), {
'__namespace__': namespace,
'__type_name__': type_name,
'_type_info': TypeInfo(members),
})

@staticmethod
def alias(type_name, namespace, target):
Expand All @@ -590,17 +589,13 @@ def alias(type_name, namespace, target):
borrow the target's _type_info.
"""

cls_dict = {}

cls_dict['__namespace__'] = namespace
cls_dict['__type_name__'] = type_name
cls_dict['_target'] = target
retval = Alias.customize()

ti = getattr(target, '_type_info', None)
if ti is not None:
cls_dict['_type_info'] = ti
retval.__type_name__ = type_name
retval.__namespace__ = namespace
retval._type_info = {"_target": target}

return ComplexModelMeta(type_name, (Alias,), cls_dict)
return retval

@classmethod
def customize(cls, **kwargs):
Expand Down Expand Up @@ -648,10 +643,11 @@ class ComplexModel(ComplexModelBase):
__metaclass__ = ComplexModelMeta


class Array(ComplexModel):
class Array(ComplexModelBase):
"""This class generates a ComplexModel child that has one attribute that has
the same name as the serialized class. It's contained in a Python list.
"""
__metaclass__ = ComplexModelMeta

def __new__(cls, serializer, **kwargs):
retval = cls.customize(**kwargs)
Expand All @@ -673,7 +669,8 @@ def __new__(cls, serializer, **kwargs):
else:
member_name = serializer.get_type_name()
if cls.__type_name__ is None:
cls.__type_name__ = '%sArray' % serializer.get_type_name()
cls.__type_name__ = '%s%s' % (serializer.get_type_name(),
ARRAY_SUFFIX)

retval.__type_name__ = '%sArray' % member_name
retval._type_info = {member_name: serializer}
Expand Down Expand Up @@ -718,9 +715,10 @@ class Iterable(Array):
"""


class Alias(ComplexModel):
class Alias(ComplexModelBase):
"""Different type_name, same _type_info."""

__metaclass__ = ComplexModelMeta

def log_repr(obj, cls=None):
"""Use this function if you want to echo a ComplexModel subclass. It will
Expand Down
14 changes: 13 additions & 1 deletion spyne/protocol/xml/model.py
Expand Up @@ -174,6 +174,7 @@ def get_members_etree(prot, cls, inst, parent):
attr_parent = parent.find("{%s}%s"%(cls.__namespace__,a_of))
v.marshall(k,subvalue,attr_parent)


@nillable_value
def complex_to_parent_element(prot, cls, value, tns, parent_elt, name=None):
if name is None:
Expand All @@ -188,7 +189,18 @@ def alias_to_parent_element(prot, cls, value, tns, parent_elt, name=None):
if name is None:
name = cls.get_type_name()

prot.to_parent_element(cls._target, value._target, tns, parent_elt, name)
(k,t), = cls._type_info.items()
if t is not None:
subvalue = getattr(value, k, None)
mo = t.Attributes.max_occurs

if subvalue is not None and mo > 1:
for sv in subvalue:
prot.to_parent_element(t, sv, tns, parent_elt, name)

# Don't include empty values for non-nillable optional attributes.
elif subvalue is not None or t.Attributes.min_occurs > 0:
prot.to_parent_element(t, subvalue, tns, parent_elt, name)


@nillable_element
Expand Down
File renamed without changes.
61 changes: 48 additions & 13 deletions spyne/test/interface/wsdl/test_default_wsdl.py
Expand Up @@ -23,11 +23,16 @@

import unittest

from spyne.application import Application

from spyne.test.interface.wsdl import AppTestWrapper
from spyne.test.interface.wsdl import build_app
from spyne.test.interface.wsdl.defult_services import TDefaultPortService
from spyne.test.interface.wsdl.defult_services import TDefaultPortServiceMultipleMethods

from spyne.const.suffix import RESPONSE_SUFFIX
from spyne.const.suffix import ARRAY_SUFFIX


class TestDefaultWSDLBehavior(unittest.TestCase):
def _default_service(self, app_wrapper, service_name):
Expand Down Expand Up @@ -152,20 +157,50 @@ def test_default_binding_methods(self):
['echo_default_port_service']
)

def test_default_binding_methods_multiple(self):
app = build_app(
[TDefaultPortServiceMultipleMethods()],
'DefaultBindingMethodsTns',
'MultipleDefaultBindMethodsApp'
)
def test_bare(self):
ns = {
'wsdl':'http://schemas.xmlsoap.org/wsdl/',
'xs':'http://www.w3.org/2001/XMLSchema',
}

from spyne.model.complex import Array
from spyne.model.primitive import String
from spyne.protocol.soap import Soap11
from spyne.service import ServiceBase
from spyne.decorator import srpc
from spyne.interface.wsdl import Wsdl11

from lxml import etree

class InteropBare(ServiceBase):
@srpc(String, _returns=String, _body_style='bare')
def echo_simple_bare(ss):
return ss

@srpc(Array(String), _returns=Array(String), _body_style='bare')
def echo_complex_bare(ss):
return ss

app = Application([InteropBare], tns='tns',
in_protocol=Soap11(), out_protocol=Soap11())
app.transport = 'None'

wsdl = Wsdl11(app.interface)
wsdl.build_interface_document('url')
wsdl = etree.fromstring(wsdl.get_interface_document())

schema = wsdl.xpath(
'/wsdl:definitions/wsdl:types/xs:schema[@targetNamespace]',
namespaces=ns
)
assert len(schema[0].xpath(
'xs:complexType[@name="string%s"]' % ARRAY_SUFFIX, namespaces=ns)) > 0
elts = schema[0].xpath(
'xs:element[@name="echo_complex_bare%s"]' % RESPONSE_SUFFIX, namespaces=ns)

assert len(elts) > 0
assert elts[0].attrib['type'] == 'tns:stringArray'

wrapper = AppTestWrapper(app)

self._default_binding_methods(
wrapper,
3,
['echo_one', 'echo_two', 'echo_three']
)

if __name__ == '__main__':
unittest.main()
14 changes: 14 additions & 0 deletions spyne/test/interop/server/_service.py
Expand Up @@ -294,6 +294,19 @@ def echo_attachment(a):
def echo_attachment_array(aa):
return aa

class InteropBare(ServiceBase):
@srpc(String, _returns=String, _body_style='bare')
def echo_simple_bare(ss):
return ss

@srpc(Array(String), _returns=Array(String), _body_style='bare')
def echo_complex_bare(ss):
return ss

@srpc(String(max_occurs='unbounded'), _returns=String(max_occurs='unbounded'), _body_style='bare')
def echo_complex_bare_unwrapped_array(ss):
return ss

class InteropException(ServiceBase):
@srpc()
def python_exception():
Expand Down Expand Up @@ -379,4 +392,5 @@ def custom_messages(s):
InteropServiceWithHeader,
InteropServiceWithComplexHeader,
InteropException,
InteropBare,
]
32 changes: 32 additions & 0 deletions spyne/test/interop/test_suds.py
Expand Up @@ -358,5 +358,37 @@ def test_custom_messages(self):

assert ret == 'test'

def test_echo_simple_bare(self):
ret = self.client.service.echo_simple_bare("test")

assert ret == 'test'

def test_echo_complex_bare(self):
val = ['abc','def']
ia = self.client.factory.create('stringArray')
ia.string.extend(val)
ret = self.client.service.echo_complex_bare(ia)

assert ret == val

def test_echo_complex_bare_unwrapped_array(self):
val = ['abc','def']
ret = self.client.service.echo_complex_bare_unwrapped_array(val)

#
# Here's the soap response to this request.
#
# <senv:Envelope xmlns:tns="spyne.test.interop.server"
# xmlns:senv="http://schemas.xmlsoap.org/soap/envelope/">
# <senv:Body>
# <tns:echo_complex_bare_unwrapped_arrayResponse>abc</tns:echo_complex_bare_unwrapped_arrayResponse>
# <tns:echo_complex_bare_unwrapped_arrayResponse>def</tns:echo_complex_bare_unwrapped_arrayResponse>
# </senv:Body>
# </senv:Envelope>

# FIXME: I'm not sure whether this is this the right behavior given the
# above output.
assert ret == val[0]

if __name__ == '__main__':
unittest.main()

0 comments on commit f4ea16b

Please sign in to comment.