Before loading a partner's feed you compare a sample of records with the schema you agreed on:
expected = {field: {"type": "int" | "float" | "str" | "bool", "nullable": True | False}}
Write drift_report(expected, records) returning a dict with exactly these keys:
- •
"added" — fields that appear in any record but not in expected, as a sorted list (each field once). - •
"missing" — {field: n}: for each expected field, how many records don't have the key at all. Only fields with n > 0. - •
"type_changes" — {field: {type_name: n}}: how many values of each unexpected type an expected field holds, by type(value).__name__. None values are not type changes. - •
"null_violations" — {field: n}: how many records have None in a field that isn't nullable.
Type rules: an "int" field holds Python ints; a "float" field holds int or float; a "str" field holds str; a "bool" field holds bool. A `bool` is never an int or a float here, even though Python makes True an int. A field that is absent is *missing*, not None.
Python 3.13 in your browser — the standard library plus pandas and numpy; no pip installs.