pydantic-avro icon indicating copy to clipboard operation
pydantic-avro copied to clipboard

Support for List with Tuple and Union type defined in Pydantic class

Open sushilkjaiswar opened this issue 1 year ago • 1 comments

The pydantic avro is unable to handle module below type mentioned for vertices. It seems inside the list, Tuple and Union is not handled. Can you verify the same if it is supported by the module?

e.g.

class PolygonType(Enum):
    triangle = 3
    tetragon = 4
    pentagon = 5
    hexagon = 6

class PolygonModel(AvroBase):
    polygon_type : PolygonType
    vertices: List[Tuple[Union[int, float], Union[int, float]]]

PolygonModel.avro_schema()

error

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[13], line 1
----> 1 PolygonModel.avro_schema()

File ~/.pyenv/versions/3.11.0/envs/jupyter/lib/python3.11/site-packages/pydantic_avro/base.py:29, in AvroBase.avro_schema(cls, by_alias, namespace)
     25 if namespace is None:
     26     # default namespace will be based on title
     27     namespace = schema["title"]
---> 29 return cls._avro_schema(schema, namespace)

File ~/.pyenv/versions/3.11.0/envs/jupyter/lib/python3.11/site-packages/pydantic_avro/base.py:186, in AvroBase._avro_schema(schema, namespace)
    183         fields.append(avro_type_dict)
    184     return fields
--> 186 fields = get_fields(schema)
    187 print(fields)
    188 return {"type": "record", "namespace": namespace, "name": schema["title"], "fields": fields}

File ~/.pyenv/versions/3.11.0/envs/jupyter/lib/python3.11/site-packages/pydantic_avro/base.py:167, in AvroBase._avro_schema.<locals>.get_fields(s)
    165 required = s.get("required", [])
    166 for key, value in s.get("properties", {}).items():
--> 167     avro_type_dict = get_type(value)
    168     avro_type_dict["name"] = key
    170     if key not in required:

File ~/.pyenv/versions/3.11.0/envs/jupyter/lib/python3.11/site-packages/pydantic_avro/base.py:90, in AvroBase._avro_schema.<locals>.get_type(value)
     88 elif t == "array":
     89     items = value.get("items")
---> 90     tn = get_type(items)
     91     # If items in array are a object:
     92     if "$ref" in items:

File ~/.pyenv/versions/3.11.0/envs/jupyter/lib/python3.11/site-packages/pydantic_avro/base.py:90, in AvroBase._avro_schema.<locals>.get_type(value)
     88 elif t == "array":
     89     items = value.get("items")
---> 90     tn = get_type(items)
     91     # If items in array are a object:
     92     if "$ref" in items:

File ~/.pyenv/versions/3.11.0/envs/jupyter/lib/python3.11/site-packages/pydantic_avro/base.py:48, in AvroBase._avro_schema.<locals>.get_type(value)
     46 """Returns a type of a single field"""
     47 print(value)
---> 48 t = value.get("type")
     49 f = value.get("format")
     50 r = value.get("$ref")

AttributeError: 'NoneType' object has no attribute 'get'

sushilkjaiswar avatar Nov 29 '23 09:11 sushilkjaiswar

There are multiple issues here. I think the main one is that the pydantic-avro library is going to try and turn PolygonType into an enum in the avro schema. Unfortunately enums in the avro schema have a requirement for the values of the enums (link to docs). Just a number is not going to work. Probably the easiest workaround is to either turn your enum to have matching string values like what is below or just turn the field into an int.

With python 3.11 you can use StrEnum (python docs):

class PolygonType(StrEnum):
    triangle = auto()
    tetragon = auto()
    pentagon = auto()
    hexagon = auto()

I have a PR open to support unions within the list. However there is another bug in which tuple is not supported either. I'll see if I can put that into my existing PR as well.

ppease avatar Feb 27 '24 00:02 ppease