@@ -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,18 @@ 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 incompatible type. "
516
+ f"{ new_field .type .fullname } vs { old_field .type .fullname } ."
517
+ )
518
+ parsed_fields [new_field .name ] = field
511
519
else :
512
520
raise SchemaParseException (f"Not a valid field: { field } " )
513
521
field_objects .append (new_field )
@@ -655,3 +663,27 @@ def make_avsc_object(json_data: JsonDataType, names: Optional[Names] = None) ->
655
663
# not for us!
656
664
fail_msg = f"Could not make an Avro Schema object from { json_data } ."
657
665
raise SchemaParseException (fail_msg )
666
+
667
+
668
+ def is_subtype (existing : PropType , new : PropType ) -> bool :
669
+ """Checks if a new type specification is compatible with an existing type spec."""
670
+ if existing == new :
671
+ return True
672
+ if isinstance (existing , list ) and (new in existing ):
673
+ return True
674
+ if (
675
+ isinstance (existing , dict )
676
+ and "type" in existing
677
+ and existing ["type" ] == "array"
678
+ and isinstance (new , dict )
679
+ and "type" in new
680
+ and new ["type" ] == "array"
681
+ ):
682
+ return is_subtype (existing ["items" ], new ["items" ])
683
+ if isinstance (existing , list ) and isinstance (new , list ):
684
+ missing = False
685
+ for _type in new :
686
+ if _type not in existing and (not is_subtype (existing , _type )):
687
+ missing = True
688
+ return not missing
689
+ return False
0 commit comments