Computes the fingerprint string for a struct type used in schema versioning. Fingerprint Format: Each field contributes: , , , [ ]; Fields are sorted by tag ID (if >=0) or snake_case field name when no ID is configured. F
(type_resolver, field_names, serializers, nullable_map=None, field_infos_list=None)
| 1069 | |
| 1070 | |
| 1071 | def compute_struct_fingerprint(type_resolver, field_names, serializers, nullable_map=None, field_infos_list=None): |
| 1072 | """ |
| 1073 | Computes the fingerprint string for a struct type used in schema versioning. |
| 1074 | |
| 1075 | Fingerprint Format: |
| 1076 | Each field contributes: <field_id_or_name>,<type_id>,<ref>,<nullable>[<child...>]; |
| 1077 | Fields are sorted by tag ID (if >=0) or snake_case field name when no ID is configured. |
| 1078 | |
| 1079 | Field Components: |
| 1080 | - field_id_or_name: Tag ID as string if id >= 0, otherwise snake_case field name |
| 1081 | - type_id: Fory TypeId as decimal string (e.g., "4" for INT32) |
| 1082 | - ref: "1" if field has ref=True in pyfory.field(), "0" otherwise |
| 1083 | (based on field annotation, NOT runtime config) |
| 1084 | - nullable: "1" if null flag is written, "0" otherwise |
| 1085 | |
| 1086 | Example fingerprints: |
| 1087 | With tag IDs: "0,4,0,0;1,12,0,1;2,0,0,1;" |
| 1088 | With field names: "age,4,0,0;email,12,0,1;name,9,0,0;" |
| 1089 | |
| 1090 | This format is consistent across Go, Java, Rust, C++, and Python implementations. |
| 1091 | """ |
| 1092 | if nullable_map is None: |
| 1093 | nullable_map = {} |
| 1094 | |
| 1095 | # Build field info list for fingerprint: (sort_key, field_id_or_name, type_fingerprint) |
| 1096 | fp_fields = [] |
| 1097 | |
| 1098 | # Build a lookup for field_infos by name if available |
| 1099 | field_info_map = {} |
| 1100 | if field_infos_list: |
| 1101 | field_info_map = {fi.name: fi for fi in field_infos_list} |
| 1102 | |
| 1103 | for i, field_name in enumerate(field_names): |
| 1104 | serializer = serializers[i] |
| 1105 | |
| 1106 | # Get field metadata if available |
| 1107 | fi = field_info_map.get(field_name) |
| 1108 | tag_id = fi.tag_id if fi else -1 |
| 1109 | nullable = fi.nullable if fi else nullable_map.get(field_name, False) |
| 1110 | ref = fi.ref if fi else False |
| 1111 | type_hint = fi.type_hint if fi else (serializer.type_ if serializer is not None else typing.Any) |
| 1112 | type_fingerprint = _build_schema_fingerprint_type( |
| 1113 | type_resolver, |
| 1114 | type_hint, |
| 1115 | nullable=nullable, |
| 1116 | track_ref=ref, |
| 1117 | include_ref=True, |
| 1118 | ) |
| 1119 | |
| 1120 | # Determine field identifier for fingerprint |
| 1121 | if tag_id >= 0: |
| 1122 | field_id_or_name = str(tag_id) |
| 1123 | sort_key = (0, tag_id, "") # 0 = tag ID fields come first |
| 1124 | else: |
| 1125 | field_id_or_name = _to_snake_case(field_name) |
| 1126 | # Sort by snake_case field name for name-based fields. |
| 1127 | sort_key = (1, field_id_or_name, field_name) # 1 = name fields come after |
| 1128 |