Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SOAP Fault detail does not accept ComplexModel/Raw lxml element may be required #582

Open
mario-kr opened this issue Aug 28, 2018 · 6 comments

Comments

@mario-kr
Copy link

Hello,
First of all: working with spyne has been pretty straightforward; it seems to be the most advanced framework for python to write soap (and other) services with!

Problem description / My PoV

For my current project, a requirement specifies that I must send a list of complex objects in the detail-field of the Fault.
I can actually do with only one element, so the limitation of only one element in the detail-field is not that much of a problem;
but: the complex object I'm supposed to send contains attributes; this is not possible to do with a plain dict.
Unfortunately, I have absolutely no influence on these requirements :-(
I worked around this by using a deprecated, not-documented feature, namely manually building the lxml tree and using that for the detail-field.

Actual request:

The "accept raw lxml elements as detail" feature is commented as deprecated (https://github.com/arskom/spyne/blob/master/spyne/protocol/xml.py#L877) - can you please keep it until another solution/option is in place?

Of course I'm open for other suggestions/approaches on how to solve this issue on my side.

@plq
Copy link
Member

plq commented Aug 31, 2018

The "accept raw lxml elements as detail" feature is commented as deprecated (https://github.com/arskom/spyne/blob/master/spyne/protocol/xml.py#L877) - can you please keep it until another solution/option is in place?

It'll be there until Spyne 3. I don't know when if ever there will be a Spyne 3

@mario-kr
Copy link
Author

Thank you for the information, I guess I'll just need to be on the lookout for a major release then.

@plq
Copy link
Member

plq commented Aug 31, 2018

Of course I'm open for other suggestions/approaches on how to solve this issue on my side.

I'm aware of this issue but I did not have time to think about a an api or method about this. At worst you can patch the outgoing document via ctx.out_body_doc

@mario-kr
Copy link
Author

I'll look into that when the need arises; for now, I'm happy assembling an lxml.etree.

I have to admit though, that I wondered why it doesn't accept a ComplexModel? My best guess is, that this is not trivially implemented

@plq
Copy link
Member

plq commented Aug 31, 2018

You can always use get_object_as_xml if your want to create the lxml Elements automatically.

I seem to remember that subclassing fault object used to work. Maybe you can do:

class SomeException(Fault):
    detail = SomeComplexObject

though I'm not sure whether that'd work.

@mario-kr
Copy link
Author

mario-kr commented Sep 3, 2018

I tried to do the subclassing, but that didn't work out; Here's the minified example:

#!/usr/bin/env python3

from spyne.model.fault import Fault
from spyne.model.primitive import Unicode
from spyne.model.complex import ComplexModel, XmlAttribute, XmlData

from spyne.protocol.soap import Soap11
from spyne import Application, rpc, ServiceBase
from spyne.server.wsgi import WsgiApplication

class TComplex(ComplexModel):

    attr = XmlAttribute(Unicode)
    data = XmlData(Unicode)


class TFault(Fault):

    detail = TComplex


class TService(ServiceBase):

    @rpc(_returns=Unicode)
    def fail_to_say_hello(ctx):

        tcompl = TComplex()
        tcompl.attr = 'attr-val'
        tcompl.data = 'data-val'

        raise TFault('Server', detail=tcompl)

app = Application([TService], 'https://example.namespace.com', in_protocol=Soap11(), out_protocol=Soap11())

wsgi_app = WsgiApplication(app)

import logging
from wsgiref.simple_server import make_server

logging.basicConfig(level=logging.INFO)
logging.getLogger('spyne.protocol.xml').setLevel(logging.INFO)

logging.info("listening to http://127.0.0.1:8000")
logging.info("wsdl is at: http://localhost:8000/?wsdl")

server = make_server('127.0.0.1', 8000, wsgi_app)
server.serve_forever()

curl-command:

-> curl 'http://127.0.0.1:8000' -d'<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="https://example.namespace.com">
<soapenv:Header/>
<soapenv:Body>
<ser:fail_to_say_hello/>
</soapenv:Body>
</soapenv:Envelope>'
A server error occurred.  Please contact the administrator.

For completeness the stacktrace (the home-path has been shortened and a project name redacted):

-> ./fault_test.py
INFO:root:listening to http://127.0.0.1:8000
INFO:root:wsdl is at: http://localhost:8000/?wsdl
ERROR:spyne.application:Fault(Server: 'TFault')
Traceback (most recent call last):
  File "~/pyenvs/<redacted>/lib/python3.6/site-packages/spyne/application.py", line 151, in process_request
    ctx.out_object = self.call_wrapper(ctx)
  File "~/pyenvs/<redacted>/lib/python3.6/site-packages/spyne/application.py", line 235, in call_wrapper
    retval = ctx.descriptor.service_class.call_wrapper(ctx)
  File "~/pyenvs/<redacted>/lib/python3.6/site-packages/spyne/service.py", line 209, in call_wrapper
    return ctx.function(ctx, *args)
  File "./fault_test.py", line 31, in fail_to_say_hello
    raise TFault('Server', detail=tcompl)
TFault: Fault(Server: 'TFault')
Traceback (most recent call last):
  File "~/pyenvs/<redacted>/lib/python3.6/wsgiref/handlers.py", line 137, in run
    self.result = application(self.environ, self.start_response)
  File "~/pyenvs/<redacted>/lib/python3.6/site-packages/spyne/server/wsgi.py", line 244, in __call__
    return self.handle_rpc(req_env, start_response)
  File "~/pyenvs/<redacted>/lib/python3.6/site-packages/spyne/server/wsgi.py", line 365, in handle_rpc
    start_response)
  File "~/pyenvs/<redacted>/lib/python3.6/site-packages/spyne/server/wsgi.py", line 323, in handle_error
    self.get_out_string(p_ctx)
  File "~/pyenvs/<redacted>/lib/python3.6/site-packages/spyne/server/_base.py", line 122, in get_out_string_pull
    ret = ctx.out_protocol.serialize(ctx, message=ProtocolBase.RESPONSE)
  File "~/pyenvs/<redacted>/lib/python3.6/site-packages/spyne/protocol/soap/soap11.py", line 288, in serialize
    out_body_doc, self.app.interface.get_tns())
  File "~/pyenvs/<redacted>/lib/python3.6/site-packages/spyne/protocol/xml.py", line 463, in to_parent
    return handler(ctx, cls, inst, parent, ns, *args, **kwargs)
  File "~/pyenvs/<redacted>/lib/python3.6/site-packages/spyne/protocol/xml.py", line 845, in fault_to_parent
    return self._fault_to_parent_impl(ctx, cls, inst, parent, ns, subelts)
  File "~/pyenvs/<redacted>/lib/python3.6/site-packages/spyne/protocol/xml.py", line 832, in _fault_to_parent_impl
    raise TypeError('Fault detail Must be dict, got', type(inst.detail))
TypeError: ('Fault detail Must be dict, got', <class '__main__.TComplex'>)
127.0.0.1 - - [03/Sep/2018 10:04:14] "POST / HTTP/1.1" 500 59
^CTraceback (most recent call last):
  File "./fault_test.py", line 47, in <module>
    server.serve_forever()
  File "~/pyenvs/<redacted>/lib/python3.6/socketserver.py", line 236, in serve_forever
    ready = selector.select(poll_interval)
  File "~/pyenvs/<redacted>/lib/python3.6/selectors.py", line 376, in select
    fd_event_list = self._poll.poll(timeout)
KeyboardInterrupt

Using get_object_as_xml seems to work kind of within this minified example, but not within my project; I guess I'll need to double-check some things there.

However, there is some small anomaly in the resulting XML-Doc:

<?xml version='1.0' encoding='UTF-8'?>
<soap11env:Envelope xmlns:soap11env="http://schemas.xmlsoap.org/soap/envelope/">
  <soap11env:Body>
    <soap11env:Fault>
      <faultcode>
        soap11env:Server
      </faultcode>
      <faultstring>
        TFault
      </faultstring>
      <faultactor></faultactor>
      <detail>
        <TComplex attr="attr-val">
          data-val
        </TComplex>
      </detail>
      <detail/> <==== ## double detail element
    </soap11env:Fault>
  </soap11env:Body>
</soap11env:Envelope>

Looking back at my terminal history, the second, empty </detail> element is also there when I constructed the lxml.etree manually

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants