SRserves85 / avro-to-python

Light tool for compiling avro schema files (.avsc) to python classes
MIT License
25 stars 19 forks source link

Union of enum is not supported #34

Closed fmiguelez closed 3 months ago

fmiguelez commented 4 months ago

Union of enum is not supported, as it is parsed as String, instead of Enum from JSON.

If we modify RecordWithUnion.avsc to add a union with enum:

{
  "type" : "record",
  "name" : "RecordWithUnion",
  "namespace" : "records",
  "fields" : [ {
    "name" : "optionalString",
    "type" : [ "string", "null" ]
  }, {
    "name" : "intOrThing",
    "type" : [ "int", {
      "type" : "record",
      "name" : "Thing",
      "fields" : [ {
        "name" : "id",
        "type" : "int"
      } ]
    } ]
  }, {
     "name" : "nullOrThingArray",
     "type" : [ "null", {
        "type" : "array",
        "items" : "Thing"
     } ]
  }, {
     "name" : "nullOrMap",
     "type" : [ "null", {
        "type" : "map",
        "values": "float"
     } ]
  }, {
     "name" : "nullOrEnum",
     "type" : [ "null", {
         "type" : "enum",
         "name" : "Flavor",
         "namespace" : "records.nested",
         "symbols" : [ "VANILLA", "CHOCOLATE", "STRAWBERRY" ]
     } ]
  }
  ]
}

And the corresponding test in test_compiled_files.py:

    def test_record_with_union(self):
        """ tests records with union types """

        from records import RecordWithUnion
        from records import Thing
        from records.nested import Flavor

        data1 = {'optionalString': 'hello', 'intOrThing': Thing({'id': 2}), 'nullOrThingArray': None,
                 'nullOrEnum': Flavor('CHOCOLATE')}
        data2 = {'optionalString': 'hello', 'intOrThing': {'id': 2}, 'nullOrEnum': 'CHOCOLATE'}
        data3 = {'optionalString': None, 'intOrThing': 10, 'nullOrThingArray': [{'id': 2}], 'nullOrMap': {'value': 0.1}}
        data4 = {'optionalString': 'hello', 'intOrThing': 'not int or thing'}

        record1 = RecordWithUnion(data1)
        record2 = RecordWithUnion(data2)
        record3 = RecordWithUnion(data3)

        self.assertEqual(
            '{"optionalString": "hello", "intOrThing": {"id": 2}, "nullOrThingArray": null, "nullOrMap": null, '
            '"nullOrEnum": "CHOCOLATE"}',
            record1.serialize()
        )

        self.assertEqual(
            record1.serialize(),
            record2.serialize()
        )

        self.assertEqual(
            record3.nullOrThingArray[0].id,
            2
        )

        self.assertEqual(
            record3.optionalString,
            None
        )

        self.assertEqual(
            record3.intOrThing,
            10
        )

        self.assertEqual(
            record3.serialize(),
            '{"optionalString": null, "intOrThing": 10, "nullOrThingArray": [{"id": 2}], '
            '"nullOrMap": {"value": 0.1}, "nullOrEnum": null}'
        )

        with self.assertRaises(TypeError):
            RecordWithUnion(data4)

It fails with following error:

FAILED  [ 95%]
tests\writer\test_compiled_files.py:252 (PathTests.test_record_with_union)
self = <test_compiled_files.PathTests testMethod=test_record_with_union>

    def test_record_with_union(self):
        """ tests records with union types """

        from records import RecordWithUnion
        from records import Thing
        from records.nested import Flavor

        data1 = {'optionalString': 'hello', 'intOrThing': Thing({'id': 2}), 'nullOrThingArray': None,
                 'nullOrEnum': Flavor('CHOCOLATE')}
        data2 = {'optionalString': 'hello', 'intOrThing': {'id': 2}, 'nullOrEnum': 'CHOCOLATE'}
        data3 = {'optionalString': None, 'intOrThing': 10, 'nullOrThingArray': [{'id': 2}], 'nullOrMap': {'value': 0.1}}
        data4 = {'optionalString': 'hello', 'intOrThing': 'not int or thing'}

        record1 = RecordWithUnion(data1)
>       record2 = RecordWithUnion(data2)

O:\ws\mdp\avro-to-python-bis\tests\writer\test_compiled_files.py:267: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
O:\ws\mdp\avro-to-python-bis\tests\avsc\records\records\RecordWithUnion.py:109: in __init__
    ???
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <records.RecordWithUnion.RecordWithUnion object at 0x000001C4EB25B1F0>
value = 'CHOCOLATE'

>   ???
E   TypeError: field 'nullOrEnum' should be in (None, Flavor)

O:\ws\mdp\avro-to-python-bis\tests\avsc\records\records\RecordWithUnion.py:171: TypeError

Union of enum