Search code examples
pythonweb-servicessoapspynesoaplib

spyne - use default parameters in services definitions


I'm using Spyne and it works well. I'd like to be able to use default values for my optional parameters. As I understand, each type can be use on its Mandatory form or not: am I right?

Here is my service:

from spyne.model.primitive import Unicode, Boolean

@rpc(Unicode, _returns = Unicode)
def optional_parameter(ctx, input = "default value")
    return input

When I call it with no parameter it returns nothing, exactly as if I had called with '' (empty string).

SOAP INPUT (from SoapUI):

<soapenv:Body>
  <spy:optional_parameter>
     <!--Optional:-->
     <spy:input/>
  </spy:optional_parameter>
</soapenv:Body>

SOAP OUTPUT:

<senv:Body>
   <tns:optional_parameterResponse>
      <tns:optional_parameterResult/>
   </tns:optional_parameterResponse>
</senv:Body>

With a boolean it's worst, here is another service:

@rpc(Boolean, _returns = Boolean)
def optional_parameter_second(ctx, input = False)
    return input

There, when I call my service using SoapUI I get the following error:

INPUT Request:

<soapenv:Body>
  <spy:optional_parameter>
     <!--Optional:-->
     <spy:input/>
  </spy:optional_parameter>
</soapenv:Body>

OUTPUT:

  <senv:Fault>
     <faultcode>senv:Client.SchemaValidationError</faultcode>
     <faultstring>:10:0:ERROR:SCHEMASV:SCHEMAV_CVC_DATATYPE_VALID_1_2_1: Element '{spyne.examples.hello}input': '' is not a valid value of the atomic type 'xs:boolean'.</faultstring>
     <faultactor/>
  </senv:Fault>

Do you have any idea to help me?

I'm using Spyne 2.11.0


Solution

  • In Spyne, all parameters are optional (min_occurs=0 and nillable=True) by default.

    When an argument is omitted from a request, Spyne passes it as None anyway, so Python's default arguments are never used. You should pass the default kwarg to type markers instead.

    Example:

    @rpc(Unicode(default='default value'), _returns=Unicode)
    def optional_parameter(ctx, input)
        return input
    

    This said, consider the following excerpt:

    <soapenv:Body>
      <spy:optional_parameter>
         <spy:input/>
      </spy:optional_parameter>
    </soapenv:Body>
    

    You're not passing a missing argument here, you're passing an empty string (""). If you want to omit that parameter, you must omit the tag. Which means:

    <soapenv:Body>
      <spy:optional_parameter/>
    </soapenv:Body>
    

    Here, the tag content is again an empty string but as the Soap processor is looking for subelements and not for tag content, it's irrelevant here.

    If you want to pass an explicit null, here's how you do it:

    <soapenv:Body>
      <spy:optional_parameter>
         <spy:input xmlns:xsi="http://w3.org/2001/XMLSchema-instance" xmlns:xsi="" xsi:nil="true"/>
      </spy:optional_parameter>
    </soapenv:Body>
    

    Note that Spyne 2.11 will still use the default value if you have one even when an explicit null is passed.

    See: https://github.com/arskom/spyne/blob/79f8120668ec471097f410e63fa48cdf797fae7a/spyne/protocol/xml.py#L378

    I'm not sure whether this is a bug or a feature :)