Search code examples
pythonsoapspyne

How do you @rpc _returns polymorphic types in spyne?


Edit

Example,

class A(ComplexModel):
    Id = Unicode

class B(ComplexModel):
    __extends__ = A
    Name = Unicode

@rpc(String, _returns=A)
def hello(self, name):
    ret = B()
    B.Id = '123'
    B.Name = name
    return ret

How do you handle this behavior so it doesn't return an object of A?


How would I write the spyne decorators to correctly return more than one type? If, for example, _returns is set to ZObj then returning an XAccount (like in the code) doesn't do anything.

Can I write the XAccount object so that it extends ZObj and is a valid return type?

@rpc(String, _returns=(ZObj, XAccount))
def hello(self, name):
    acc = XAccount(
        user_name = name.upper(),
        first_name = name,
        last_name = 'last ' + name
    )
    return acc

Class examples....

class ZObj(ComplexModel):
    id = Unicode(pattern='[a-zA-Z0-9]{32}|\d+')

class Account(DeclarativeBase):
    __tablename__ = 'account'

    id = Column(Integer, primary_key=True)
    user_name = Column(String(256))
    first_name = Column(String(256))
    last_name = Column(String(256))


class XAccount(TableModel):
    __table__ = Account.__table__

Solution

  • Deleting my previous answer as you apparently need polymorphism, not multiple return types.

    So, There are two ways of doing polymorphism in Spyne: The Python way and the Spyne way.

    Let:

    class A(ComplexModel):
        i = Integer
    
    class B(A):
        s = Unicode
    
    class C(A):
        d = DateTime
    

    The Python way uses duck typing to return values.

    Let's define a generic class:

    class GenericA(ComplexModel):
        i = Integer
        s = Unicode
        d = DateTime
    

    and use it as return value of our sample service:

    class SomeService(ServiceBase):
        @rpc(Unicode(values=['A', 'B', 'C']), _returns=GenericA)
        def get_some_a(self, type_name):
            # (...)
    

    This way, you get your data, but it's tagged as a GenericA object. If you don't care about this, you can create a class that has all types from all objects (assuming attributes with same names have the same type) and just be done with it. This is easy, stable and works today.

    If that's not enough for your needs, you have to do the polymorphism the Spyne way. To do that, first set your return type to the base class:

    class SomeService(ServiceBase):
        @rpc(Unicode(values=['A', 'B', 'C']), _returns=A)
        def get_some_a(self, type_name):
            # (...)
    

    and tag your output protocol to be polymorphic:

    application = Application([SomeService], 'tns',
        in_protocol=Soap11(validator='lxml'),
        out_protocol=Soap11(polymorphic=True)
    )
    

    This requires at least Spyne-2.12.

    Working example: https://github.com/arskom/spyne/blob/a1b3593f3754a9c8a6787c29ff50f591db89fd49/examples/xml/polymorphism.py