hints.py 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. from collections import Sequence
  2. from pytest_clarity.output import deleted_text, hint_body_text, hint_text, inserted_text
  3. from pytest_clarity.util import (
  4. direct_type_mismatch,
  5. has_differing_len,
  6. pformat_no_color,
  7. possibly_missing_eq,
  8. )
  9. def hints_for(op, lhs, rhs):
  10. hints = [""] # immediate newline
  11. if op == "==":
  12. if direct_type_mismatch(lhs, rhs):
  13. lhs_type, rhs_type = inserted_text(type(lhs)), deleted_text(type(rhs))
  14. hints += [
  15. hint_text("left and right are different types:"),
  16. hint_body_text(
  17. " type(left) is {}, type(right) is {}".format(lhs_type, rhs_type)
  18. ),
  19. ]
  20. if has_differing_len(lhs, rhs):
  21. lhs_len, rhs_len = inserted_text(len(lhs)), deleted_text(len(rhs))
  22. hints += [
  23. hint_text("left and right have different lengths:"),
  24. hint_body_text(
  25. " len(left) == {}, len(right) == {}".format(lhs_len, rhs_len)
  26. ),
  27. ]
  28. if possibly_missing_eq(lhs, rhs):
  29. hints += [
  30. hint_text("left and right are equal in data and in type: "),
  31. hint_body_text(" perhaps you forgot to implement __eq__ and __ne__?"),
  32. ]
  33. both_sequences = isinstance(lhs, Sequence) and isinstance(rhs, Sequence)
  34. both_dicts = isinstance(lhs, dict) and isinstance(rhs, dict)
  35. if both_dicts:
  36. lhs, rhs = lhs.items(), rhs.items()
  37. if both_sequences or both_dicts:
  38. num_extras, lines = find_extras(lhs, rhs, inserted_text, "+")
  39. hints += [
  40. hint_text("{} items in left, but not right:".format(num_extras))
  41. ] + lines
  42. num_extras, lines = find_extras(rhs, lhs, deleted_text, "-")
  43. hints += [
  44. hint_text("{} items in right, but not left:".format(num_extras))
  45. ] + lines
  46. return hints
  47. def find_extras(lhs, rhs, text_fn, item_marker):
  48. lhs_extras_lines = []
  49. num_extras = 0
  50. for item in lhs:
  51. if item not in rhs:
  52. num_extras += 1
  53. for i, line in enumerate(pformat_no_color(item, 80).splitlines()):
  54. gutter_content = item_marker if i == 0 else " "
  55. coloured_line = "{gutter_content}{spacing}{line}".format(
  56. gutter_content=text_fn(gutter_content),
  57. spacing=" ",
  58. line=text_fn(line),
  59. )
  60. lhs_extras_lines.append(coloured_line)
  61. return num_extras, lhs_extras_lines