grongierisc / interoperability-embedded-python

Hack of PEX Python but for Embedded Python
MIT License
13 stars 6 forks source link

Support for list messages #25

Open Antoine-dh opened 1 month ago

Antoine-dh commented 1 month ago

I wanted to return a json message that is directly a list at its root element For context, my use case is for making a REST client that returns the response body as a IOP message that can be deserialized into a dataclass. This works well for singular objects, however I have some troubles when the json response's root element is a list, because of course:

The message must be an instance of a class that is a subclass of Message or IRISObject %Persistent class. -

To work around this issue, I tried to make a list/message hybrid class such as:

@dataclass(init=False)
class PetList(list[Pet], Message):
    pass

This works well as the list is correctly serialized into a IOP message, until the sender tries to deserialize it

Traceback (most recent call last):
File "/irisdev/app/src/python/demo/bs.py", line 31, in on_process_input
response: PetList = self.send_request_sync('PetstoreOperation', RESTRequest.create('find_pets_by_status'))
File "/home/irisowner/.local/lib/python3.10/site-packages/iop/_business_host.py", line 70, in dispatch_serializer
return fonction(self, *serialized, **param2)
File "/home/irisowner/.local/lib/python3.10/site-packages/iop/_business_host.py", line 84, in dispatch_deserializer
return self._dispatch_deserializer(fonction(self,*params, **param2))
File "/home/irisowner/.local/lib/python3.10/site-packages/iop/_business_host.py", line 302, in _dispatch_deserializer
return self._deserialize_message(serial)
File "/home/irisowner/.local/lib/python3.10/site-packages/iop/_business_host.py", line 340, in _deserialize_message
msg = self._dataclass_from_dict(msg,jdict)
File "/home/irisowner/.local/lib/python3.10/site-packages/iop/_business_host.py", line 358, in _dataclass_from_dict
for key,val in dikt.items():
AttributeError: 'list' object has no attribute 'items'

So I thought I could just alternatively wrap every response into a generic class such as:

from iop import Message
from dataclasses import dataclass
from pydantic import BaseModel
from typing import TypeVar, Generic, Optional

_T = TypeVar('T')

@dataclass(init=False)
class RESTResponse(BaseModel, Generic[_T], Message):
    data: Optional[_T] = None

And this almost worked, but unfortunately the generic type is interpreted as a dict and is not deserialized correctly. So in order to make this work I would have to define a wrapper class for every different list type my API returns which is not ideal.

Maybe a solution would be for IOP to implement a ListMessage class, or detect if the response class is a list and deserialize objects individually.

Let me know what you think about this, Thank you