spyne - use default parameters in services definitions

1.4k Views Asked by At

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

2

There are 2 best solutions below

0
On BEST ANSWER

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 :)

0
On

I found out one way which seems clean:

Service implementation with default Boolean:

def optional_parameter_second(ctx, input):
    if input is not None:
        return my_method(input)
    else:
        return my_method()

def my_method(param = True):
    return param

And the call from soapUI must not contain the tag

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

It works like a charm

<senv:Body>
  <tns:optional_booleanResponse>
     <tns:optional_booleanResult>true</tns:optional_booleanResult>
  </tns:optional_booleanResponse>
</senv:Body>