issequence_containinginanyorder.py 3.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. from hamcrest.core.base_matcher import BaseMatcher
  2. from hamcrest.core.helpers.hasmethod import hasmethod
  3. from hamcrest.core.helpers.wrap_matcher import wrap_matcher
  4. __author__ = "Jon Reid"
  5. __copyright__ = "Copyright 2011 hamcrest.org"
  6. __license__ = "BSD, see License.txt"
  7. class MatchInAnyOrder(object):
  8. def __init__(self, matchers, mismatch_description):
  9. self.matchers = matchers[:]
  10. self.mismatch_description = mismatch_description
  11. def matches(self, item):
  12. return self.isnotsurplus(item) and self.ismatched(item)
  13. def isfinished(self, sequence):
  14. if not self.matchers:
  15. return True
  16. if self.mismatch_description:
  17. self.mismatch_description.append_text('no item matches: ') \
  18. .append_list('', ', ', '', self.matchers) \
  19. .append_text(' in ') \
  20. .append_list('[', ', ', ']', sequence)
  21. return False
  22. def isnotsurplus(self, item):
  23. if not self.matchers:
  24. if self.mismatch_description:
  25. self.mismatch_description.append_text('not matched: ') \
  26. .append_description_of(item)
  27. return False
  28. return True
  29. def ismatched(self, item):
  30. for index, matcher in enumerate(self.matchers):
  31. if matcher.matches(item):
  32. del self.matchers[index]
  33. return True
  34. if self.mismatch_description:
  35. self.mismatch_description.append_text('not matched: ') \
  36. .append_description_of(item)
  37. return False
  38. class IsSequenceContainingInAnyOrder(BaseMatcher):
  39. def __init__(self, matchers):
  40. self.matchers = matchers
  41. def matches(self, sequence, mismatch_description=None):
  42. try:
  43. sequence = list(sequence)
  44. matchsequence = MatchInAnyOrder(self.matchers, mismatch_description)
  45. for item in sequence:
  46. if not matchsequence.matches(item):
  47. return False
  48. return matchsequence.isfinished(sequence)
  49. except TypeError:
  50. if mismatch_description:
  51. super(IsSequenceContainingInAnyOrder, self) \
  52. .describe_mismatch(sequence, mismatch_description)
  53. return False
  54. def describe_mismatch(self, item, mismatch_description):
  55. self.matches(item, mismatch_description)
  56. def describe_to(self, description):
  57. description.append_text('a sequence over ') \
  58. .append_list('[', ', ', ']', self.matchers) \
  59. .append_text(' in any order')
  60. def contains_inanyorder(*items):
  61. """Matches if sequences's elements, in any order, satisfy a given list of
  62. matchers.
  63. :param match1,...: A comma-separated list of matchers.
  64. This matcher iterates the evaluated sequence, seeing if each element
  65. satisfies any of the given matchers. The matchers are tried from left to
  66. right, and when a satisfied matcher is found, it is no longer a candidate
  67. for the remaining elements. If a one-to-one correspondence is established
  68. between elements and matchers, ``contains_inanyorder`` is satisfied.
  69. Any argument that is not a matcher is implicitly wrapped in an
  70. :py:func:`~hamcrest.core.core.isequal.equal_to` matcher to check for
  71. equality.
  72. """
  73. matchers = []
  74. for item in items:
  75. matchers.append(wrap_matcher(item))
  76. return IsSequenceContainingInAnyOrder(matchers)