pythonfaust

Faust deserializing model when model field is another model


I have a model that is a little complicated where one field can be one of several other models which looks similar to the following:

'''These are the options'''
class ChatterFilter(Record, serializer='json'):
  counts:int
  time:int
  init_state:str
  new_state:str
  stale_time:int
  comment:str


class LimitFilter(Record, serializer='json'):
  lolo:float
  low:float
  high:float
  hihi:float
  comment:str


class DitherFilter(Record, serializer='json'):
  stale_time:int
  init_state:str
  new_state:str
  comment:str

class FitlerOptions(FieldDescriptor[type]):
  ''' This class defines the allowable filter types and validates these on initialisation '''
  def __init__(self, choices: List[type], **kwargs: Any) -> None:
    self.choices = choices
    super().__init__(choices=choices, **kwargs)

  def validate(self, value: type) -> Iterable[ValidationError]:
    if type(value) not in self.choices:
      yield self.validation_error(
        f'{self.field} must be one of {self.choices}, received:\t{value}=>{type(value)}')

class Filter(Record, serializer='json', validation=True):
  filter: type = FitlerOptions([ChatterFilter, LimitFilter, DitherFilter, type(None)])
  initiated:str=None
  end:str=None
  duration:float=None
  initiator:str=None

The idea was that we wanted to ensure that the filter was one of the 3 types (or None if there is no filter), so we used this method to create a model to validate the type supplied.

This appears to work really well... until we try to send it to a topic and then read the incoming event and it fails to deserialize the filter from the serialised dictionary and complains that it's a dictionary instead of one of the allowed options with the error log being received: {'counts': 0, 'time': 0, 'init_state': 'NO_ALARM', 'new_state': 'LOLO', 'stale_time': 60, 'comment': 'comment string', '__faust': {'ns': 'models.models.ChatterFilter'}}=><class 'dict'>

It's clear that it's a model and that faust should recognise it, but it doesn't seem to do it by default. I could add a check in the validation and yield a converted version and error on an unsuccessful attempt, but it seems like I shouldn't have to.


Solution

  • So it does indeed work. It's just a matter of setting it up differently so that you specify it to be a list:

    class Filter(Record): masks:List[Union[ChatterFilter, LimitFilter, DitherFilter]]=[]

    This now works as expected.