in pyiceberg/avro/resolver.py [0:0]
def struct(self, struct: StructType, expected_struct: Optional[IcebergType], field_readers: List[Reader]) -> Reader:
read_struct_id = self.context[STRUCT_ROOT] if len(self.context) > 0 else STRUCT_ROOT
struct_callable = self.read_types.get(read_struct_id, Record)
if not expected_struct:
return StructReader(tuple(enumerate(field_readers)), struct_callable, struct)
if not isinstance(expected_struct, StructType):
raise ResolveError(f"File/read schema are not aligned for struct, got {expected_struct}")
expected_positions: Dict[int, int] = {field.field_id: pos for pos, field in enumerate(expected_struct.fields)}
# first, add readers for the file fields that must be in order
results: List[Tuple[Optional[int], Reader]] = [
(
expected_positions.get(field.field_id),
# Check if we need to convert it to an Enum
result_reader if not (enum_type := self.read_enums.get(field.field_id)) else EnumReader(enum_type, result_reader),
)
for field, result_reader in zip(struct.fields, field_readers)
]
file_fields = {field.field_id for field in struct.fields}
for pos, read_field in enumerate(expected_struct.fields):
if read_field.field_id not in file_fields:
if isinstance(read_field, NestedField) and read_field.initial_default is not None:
# The field is not in the file, but there is a default value
# and that one can be required
results.append((pos, DefaultReader(read_field.initial_default)))
elif read_field.required:
raise ResolveError(f"{read_field} is non-optional, and not part of the file schema")
else:
# Just set the new field to None
results.append((pos, NoneReader()))
return StructReader(tuple(results), struct_callable, expected_struct)