@@ -477,7 +477,7 @@ class RecordSchema(NamedSchema):
477
477
def make_field_objects (field_data : List [PropsType ], names : Names ) -> List [Field ]:
478
478
"""We're going to need to make message parameters too."""
479
479
field_objects = [] # type: List[Field]
480
- field_names = [] # type: List [str]
480
+ parsed_fields : Dict [str , PropsType ] = {}
481
481
for field in field_data :
482
482
if hasattr (field , "get" ) and callable (field .get ):
483
483
atype = field .get ("type" )
@@ -504,10 +504,19 @@ def make_field_objects(field_data: List[PropsType], names: Names) -> List[Field]
504
504
atype , name , has_default , default , order , names , doc , other_props
505
505
)
506
506
# make sure field name has not been used yet
507
- if new_field .name in field_names :
508
- fail_msg = f"Field name { new_field .name } already in use."
509
- raise SchemaParseException (fail_msg )
510
- field_names .append (new_field .name )
507
+ if new_field .name in parsed_fields :
508
+ old_field = parsed_fields [new_field .name ]
509
+ if "inherited_from" not in old_field :
510
+ raise SchemaParseException (
511
+ f"Field name { new_field .name } already in use."
512
+ )
513
+ if not is_subtype (old_field ["type" ], field ["type" ]):
514
+ raise SchemaParseException (
515
+ f"Field name { new_field .name } already in use with "
516
+ "incompatible type. "
517
+ f"{ field ['type' ]} vs { old_field ['type' ]} ."
518
+ )
519
+ parsed_fields [new_field .name ] = field
511
520
else :
512
521
raise SchemaParseException (f"Not a valid field: { field } " )
513
522
field_objects .append (new_field )
@@ -655,3 +664,29 @@ def make_avsc_object(json_data: JsonDataType, names: Optional[Names] = None) ->
655
664
# not for us!
656
665
fail_msg = f"Could not make an Avro Schema object from { json_data } ."
657
666
raise SchemaParseException (fail_msg )
667
+
668
+
669
+ def is_subtype (existing : PropType , new : PropType ) -> bool :
670
+ """Checks if a new type specification is compatible with an existing type spec."""
671
+ if existing == new :
672
+ return True
673
+ if isinstance (existing , list ) and (new in existing ):
674
+ return True
675
+ if (
676
+ isinstance (existing , dict )
677
+ and "type" in existing
678
+ and existing ["type" ] == "array"
679
+ and isinstance (new , dict )
680
+ and "type" in new
681
+ and new ["type" ] == "array"
682
+ ):
683
+ return is_subtype (existing ["items" ], new ["items" ])
684
+ if isinstance (existing , list ) and isinstance (new , list ):
685
+ missing = False
686
+ for _type in new :
687
+ if _type not in existing and (
688
+ not is_subtype (existing , cast (PropType , _type ))
689
+ ):
690
+ missing = True
691
+ return not missing
692
+ return False
0 commit comments