1

I want to call a web service method that has a spyne binary type as argument. But I'm not able to find any python soap client supporting it.

To debug my problem, I made a simple web service method that should print a file:



    #     @srpc(Attachment, _returns=Unicode)
        @srpc(ByteArray, _returns=Unicode)
    #     @srpc(File, _returns=Unicode)
    #     @srpc(Unicode, _returns=Unicode)
        def print_file(file_content):
            logger.info(u"print file:\n{}\ntype:{}".format(file_content, file_content.__class__))

            return u''

As you can see, I tried with 3 spyne binary types. For debug, I also tried with Unicode, and passing the file content in base64, and in this case there is no problem. Thus the web service is operational.

The server side is a Django application and a spyne application. My problem is on client side. With suds, the obtained error is very obscur, and no solution exists according forums.

I tried all SOAP clients described on https://wiki.python.org/moin/WebServices#SOAP, with python 2.7 and 3.3. They all fail when building the request, when serializing the spyne binary object.

My last try is with zeep. I instantiate the zeep client with wsdl local url. Sorry, wsdl is not public.

I call this method with an empty ByteArray:

param = ByteArray()
client.service.print_file(param)

The catched exception is:



        File "/usr/lib/python2.7/site-packages/zeep/client.py", line 41, in __call__
            self._op_name, args, kwargs)
        File "/usr/lib/python2.7/site-packages/zeep/wsdl/bindings/soap.py", line 107, in send
            options=options)
        File "/usr/lib/python2.7/site-packages/zeep/wsdl/bindings/soap.py", line 65, in _create
            serialized = operation_obj.create(*args, **kwargs)
        File "/usr/lib/python2.7/site-packages/zeep/wsdl/definitions.py", line 165, in create
            return self.input.serialize(*args, **kwargs)
        File "/usr/lib/python2.7/site-packages/zeep/wsdl/messages/soap.py", line 48, in serialize
            self.body.render(body, body_value)
        File "/usr/lib/python2.7/site-packages/zeep/xsd/elements.py", line 333, in render
            self._render_value_item(parent, value)
        File "/usr/lib/python2.7/site-packages/zeep/xsd/elements.py", line 354, in _render_value_item
            return self.type.render(node, value)
        File "/usr/lib/python2.7/site-packages/zeep/xsd/types.py", line 356, in render
            element.render(parent, element_value)
        File "/usr/lib/python2.7/site-packages/zeep/xsd/indicators.py", line 189, in render
            element.render(parent, element_value)
        File "/usr/lib/python2.7/site-packages/zeep/xsd/elements.py", line 333, in render
            self._render_value_item(parent, value)
        File "/usr/lib/python2.7/site-packages/zeep/xsd/elements.py", line 354, in _render_value_item
            return self.type.render(node, value)
        File "/usr/lib/python2.7/site-packages/zeep/xsd/types.py", line 180, in render
            parent.text = self.xmlvalue(value)
        File "/usr/lib/python2.7/site-packages/zeep/xsd/builtins.py", line 83, in _wrapper
            return func(self, value)
        File "/usr/lib/python2.7/site-packages/zeep/xsd/builtins.py", line 357, in xmlvalue
            return base64.b64encode(value)
        File "/usr/lib64/python2.7/base64.py", line 53, in b64encode
            encoded = binascii.b2a_base64(s)[:-1]
    TypeError: must be convertible to a buffer, not ModelBaseMeta


Does anyone know if there is a solution with zeep ?
Perhaps with a dedicated zeep plugin ?

Or is there another solution ?
Perhaps in C/C++, compiled as a python package ?

Eric

============================================================
== Solution

As I lost a lot of time on this problem, here is the solution for me.

Finally I needed also the filename, not only file data. The type spyne.model.binary.File would have been perfect, but it is not serialisable into SOAP format.

Thank to discussion with Burak, the final solution is to create a custom ComplexType like:



class File(ComplexModel):
    filename = Unicode
    data = ByteArray

On client side with suds, replace the ByteArray field directly with the data encoded in base64:



    f = File()
    data = open({FILENAME}, "rb").read()
    f.data = base64.b64encode(data)

On server side, f.data will directly contain the data decoded.

2 Answers2

0

Complete answer is here: http://lists.spyne.io/archives/people/2016-December/000187.html

For completeness; you can use suds if you do base64 operations manually. See https://github.com/arskom/spyne/blob/be222c041837c9f7cd1e6e6e455e6704b5069837/spyne/test/interop/test_suds.py#L111

Burak Arslan
  • 7,671
  • 2
  • 15
  • 24
  • Thank you very much. I haven't tried to bypass the ByteArray type and replace it by the result of b64encode. – Eric Klumpp Dec 14 '16 at 17:15
  • ByteArray is not a value, it's just a type marker. The native value that is compatible with ByteArray is a sequence of bytes. eg. let `class A(ComplexModel): a=ByteArray;` you should do `A(a=[b"\x00\x01 etc.."])` to assign binary data to a class correctly. – Burak Arslan Dec 15 '16 at 20:57
0

Zeep should handle bytes fine, I've just tested this against a spyne server.

E.g. service.client.echo_bytearray(b'\x00\x01\x02\x03\x04') works without issue. Do you have more information?

Cheers, Michael (author of zeep)

mvantellingen
  • 1,229
  • 10
  • 6
  • Thanks Michael for your time. As Burak, you directly call the webservice with bytes, not with a ByteArray object. If you try to pass a ByteArray object, it won't work. It was my mistake. – Eric Klumpp Dec 14 '16 at 17:19