_deltas.py 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
  1. from __future__ import annotations
  2. from ..._utils import is_dict, is_list
  3. def accumulate_delta(acc: dict[object, object], delta: dict[object, object]) -> dict[object, object]:
  4. for key, delta_value in delta.items():
  5. if key not in acc:
  6. acc[key] = delta_value
  7. continue
  8. acc_value = acc[key]
  9. if acc_value is None:
  10. acc[key] = delta_value
  11. continue
  12. # the `index` property is used in arrays of objects so it should
  13. # not be accumulated like other values e.g.
  14. # [{'foo': 'bar', 'index': 0}]
  15. #
  16. # the same applies to `type` properties as they're used for
  17. # discriminated unions
  18. if key == "index" or key == "type":
  19. acc[key] = delta_value
  20. continue
  21. if isinstance(acc_value, str) and isinstance(delta_value, str):
  22. acc_value += delta_value
  23. elif isinstance(acc_value, (int, float)) and isinstance(delta_value, (int, float)):
  24. acc_value += delta_value
  25. elif is_dict(acc_value) and is_dict(delta_value):
  26. acc_value = accumulate_delta(acc_value, delta_value)
  27. elif is_list(acc_value) and is_list(delta_value):
  28. # for lists of non-dictionary items we'll only ever get new entries
  29. # in the array, existing entries will never be changed
  30. if all(isinstance(x, (str, int, float)) for x in acc_value):
  31. acc_value.extend(delta_value)
  32. continue
  33. for delta_entry in delta_value:
  34. if not is_dict(delta_entry):
  35. raise TypeError(f"Unexpected list delta entry is not a dictionary: {delta_entry}")
  36. try:
  37. index = delta_entry["index"]
  38. except KeyError as exc:
  39. raise RuntimeError(f"Expected list delta entry to have an `index` key; {delta_entry}") from exc
  40. if not isinstance(index, int):
  41. raise TypeError(f"Unexpected, list delta entry `index` value is not an integer; {index}")
  42. try:
  43. acc_entry = acc_value[index]
  44. except IndexError:
  45. acc_value.insert(index, delta_entry)
  46. else:
  47. if not is_dict(acc_entry):
  48. raise TypeError("not handled yet")
  49. acc_value[index] = accumulate_delta(acc_entry, delta_entry)
  50. acc[key] = acc_value
  51. return acc