s3/packaging: Fix rpm build issues on RHEL4.
[Samba/gebeck_regimport.git] / lib / testtools / testtools / matchers.py
blob4725265f9854eb181a724aa3fc1253326a22b0f6
1 # Copyright (c) 2009-2011 testtools developers. See LICENSE for details.
3 """Matchers, a way to express complex assertions outside the testcase.
5 Inspired by 'hamcrest'.
7 Matcher provides the abstract API that all matchers need to implement.
9 Bundled matchers are listed in __all__: a list can be obtained by running
10 $ python -c 'import testtools.matchers; print testtools.matchers.__all__'
11 """
13 __metaclass__ = type
14 __all__ = [
15 'AfterPreprocessing',
16 'AllMatch',
17 'Annotate',
18 'Contains',
19 'DocTestMatches',
20 'EndsWith',
21 'Equals',
22 'GreaterThan',
23 'Is',
24 'IsInstance',
25 'KeysEqual',
26 'LessThan',
27 'MatchesAll',
28 'MatchesAny',
29 'MatchesException',
30 'MatchesListwise',
31 'MatchesRegex',
32 'MatchesSetwise',
33 'MatchesStructure',
34 'NotEquals',
35 'Not',
36 'Raises',
37 'raises',
38 'StartsWith',
41 import doctest
42 import operator
43 from pprint import pformat
44 import re
45 import sys
46 import types
48 from testtools.compat import (
49 classtypes,
50 _error_repr,
51 isbaseexception,
52 _isbytes,
53 istext,
54 str_is_unicode,
55 text_repr
59 class Matcher(object):
60 """A pattern matcher.
62 A Matcher must implement match and __str__ to be used by
63 testtools.TestCase.assertThat. Matcher.match(thing) returns None when
64 thing is completely matched, and a Mismatch object otherwise.
66 Matchers can be useful outside of test cases, as they are simply a
67 pattern matching language expressed as objects.
69 testtools.matchers is inspired by hamcrest, but is pythonic rather than
70 a Java transcription.
71 """
73 def match(self, something):
74 """Return None if this matcher matches something, a Mismatch otherwise.
75 """
76 raise NotImplementedError(self.match)
78 def __str__(self):
79 """Get a sensible human representation of the matcher.
81 This should include the parameters given to the matcher and any
82 state that would affect the matches operation.
83 """
84 raise NotImplementedError(self.__str__)
87 class Mismatch(object):
88 """An object describing a mismatch detected by a Matcher."""
90 def __init__(self, description=None, details=None):
91 """Construct a `Mismatch`.
93 :param description: A description to use. If not provided,
94 `Mismatch.describe` must be implemented.
95 :param details: Extra details about the mismatch. Defaults
96 to the empty dict.
97 """
98 if description:
99 self._description = description
100 if details is None:
101 details = {}
102 self._details = details
104 def describe(self):
105 """Describe the mismatch.
107 This should be either a human-readable string or castable to a string.
108 In particular, is should either be plain ascii or unicode on Python 2,
109 and care should be taken to escape control characters.
111 try:
112 return self._description
113 except AttributeError:
114 raise NotImplementedError(self.describe)
116 def get_details(self):
117 """Get extra details about the mismatch.
119 This allows the mismatch to provide extra information beyond the basic
120 description, including large text or binary files, or debugging internals
121 without having to force it to fit in the output of 'describe'.
123 The testtools assertion assertThat will query get_details and attach
124 all its values to the test, permitting them to be reported in whatever
125 manner the test environment chooses.
127 :return: a dict mapping names to Content objects. name is a string to
128 name the detail, and the Content object is the detail to add
129 to the result. For more information see the API to which items from
130 this dict are passed testtools.TestCase.addDetail.
132 return getattr(self, '_details', {})
134 def __repr__(self):
135 return "<testtools.matchers.Mismatch object at %x attributes=%r>" % (
136 id(self), self.__dict__)
139 class MismatchError(AssertionError):
140 """Raised when a mismatch occurs."""
142 # This class exists to work around
143 # <https://bugs.launchpad.net/testtools/+bug/804127>. It provides a
144 # guaranteed way of getting a readable exception, no matter what crazy
145 # characters are in the matchee, matcher or mismatch.
147 def __init__(self, matchee, matcher, mismatch, verbose=False):
148 # Have to use old-style upcalling for Python 2.4 and 2.5
149 # compatibility.
150 AssertionError.__init__(self)
151 self.matchee = matchee
152 self.matcher = matcher
153 self.mismatch = mismatch
154 self.verbose = verbose
156 def __str__(self):
157 difference = self.mismatch.describe()
158 if self.verbose:
159 # GZ 2011-08-24: Smelly API? Better to take any object and special
160 # case text inside?
161 if istext(self.matchee) or _isbytes(self.matchee):
162 matchee = text_repr(self.matchee, multiline=False)
163 else:
164 matchee = repr(self.matchee)
165 return (
166 'Match failed. Matchee: %s\nMatcher: %s\nDifference: %s\n'
167 % (matchee, self.matcher, difference))
168 else:
169 return difference
171 if not str_is_unicode:
173 __unicode__ = __str__
175 def __str__(self):
176 return self.__unicode__().encode("ascii", "backslashreplace")
179 class MismatchDecorator(object):
180 """Decorate a ``Mismatch``.
182 Forwards all messages to the original mismatch object. Probably the best
183 way to use this is inherit from this class and then provide your own
184 custom decoration logic.
187 def __init__(self, original):
188 """Construct a `MismatchDecorator`.
190 :param original: A `Mismatch` object to decorate.
192 self.original = original
194 def __repr__(self):
195 return '<testtools.matchers.MismatchDecorator(%r)>' % (self.original,)
197 def describe(self):
198 return self.original.describe()
200 def get_details(self):
201 return self.original.get_details()
204 class _NonManglingOutputChecker(doctest.OutputChecker):
205 """Doctest checker that works with unicode rather than mangling strings
207 This is needed because current Python versions have tried to fix string
208 encoding related problems, but regressed the default behaviour with unicode
209 inputs in the process.
211 In Python 2.6 and 2.7 `OutputChecker.output_difference` is was changed to
212 return a bytestring encoded as per `sys.stdout.encoding`, or utf-8 if that
213 can't be determined. Worse, that encoding process happens in the innocent
214 looking `_indent` global function. Because the `DocTestMismatch.describe`
215 result may well not be destined for printing to stdout, this is no good
216 for us. To get a unicode return as before, the method is monkey patched if
217 `doctest._encoding` exists.
219 Python 3 has a different problem. For some reason both inputs are encoded
220 to ascii with 'backslashreplace', making an escaped string matches its
221 unescaped form. Overriding the offending `OutputChecker._toAscii` method
222 is sufficient to revert this.
225 def _toAscii(self, s):
226 """Return `s` unchanged rather than mangling it to ascii"""
227 return s
229 # Only do this overriding hackery if doctest has a broken _input function
230 if getattr(doctest, "_encoding", None) is not None:
231 from types import FunctionType as __F
232 __f = doctest.OutputChecker.output_difference.im_func
233 __g = dict(__f.func_globals)
234 def _indent(s, indent=4, _pattern=re.compile("^(?!$)", re.MULTILINE)):
235 """Prepend non-empty lines in `s` with `indent` number of spaces"""
236 return _pattern.sub(indent*" ", s)
237 __g["_indent"] = _indent
238 output_difference = __F(__f.func_code, __g, "output_difference")
239 del __F, __f, __g, _indent
242 class DocTestMatches(object):
243 """See if a string matches a doctest example."""
245 def __init__(self, example, flags=0):
246 """Create a DocTestMatches to match example.
248 :param example: The example to match e.g. 'foo bar baz'
249 :param flags: doctest comparison flags to match on. e.g.
250 doctest.ELLIPSIS.
252 if not example.endswith('\n'):
253 example += '\n'
254 self.want = example # required variable name by doctest.
255 self.flags = flags
256 self._checker = _NonManglingOutputChecker()
258 def __str__(self):
259 if self.flags:
260 flagstr = ", flags=%d" % self.flags
261 else:
262 flagstr = ""
263 return 'DocTestMatches(%r%s)' % (self.want, flagstr)
265 def _with_nl(self, actual):
266 result = self.want.__class__(actual)
267 if not result.endswith('\n'):
268 result += '\n'
269 return result
271 def match(self, actual):
272 with_nl = self._with_nl(actual)
273 if self._checker.check_output(self.want, with_nl, self.flags):
274 return None
275 return DocTestMismatch(self, with_nl)
277 def _describe_difference(self, with_nl):
278 return self._checker.output_difference(self, with_nl, self.flags)
281 class DocTestMismatch(Mismatch):
282 """Mismatch object for DocTestMatches."""
284 def __init__(self, matcher, with_nl):
285 self.matcher = matcher
286 self.with_nl = with_nl
288 def describe(self):
289 s = self.matcher._describe_difference(self.with_nl)
290 if str_is_unicode or isinstance(s, unicode):
291 return s
292 # GZ 2011-08-24: This is actually pretty bogus, most C0 codes should
293 # be escaped, in addition to non-ascii bytes.
294 return s.decode("latin1").encode("ascii", "backslashreplace")
297 class DoesNotContain(Mismatch):
299 def __init__(self, matchee, needle):
300 """Create a DoesNotContain Mismatch.
302 :param matchee: the object that did not contain needle.
303 :param needle: the needle that 'matchee' was expected to contain.
305 self.matchee = matchee
306 self.needle = needle
308 def describe(self):
309 return "%r not in %r" % (self.needle, self.matchee)
312 class DoesNotStartWith(Mismatch):
314 def __init__(self, matchee, expected):
315 """Create a DoesNotStartWith Mismatch.
317 :param matchee: the string that did not match.
318 :param expected: the string that 'matchee' was expected to start with.
320 self.matchee = matchee
321 self.expected = expected
323 def describe(self):
324 return "%s does not start with %s." % (
325 text_repr(self.matchee), text_repr(self.expected))
328 class DoesNotEndWith(Mismatch):
330 def __init__(self, matchee, expected):
331 """Create a DoesNotEndWith Mismatch.
333 :param matchee: the string that did not match.
334 :param expected: the string that 'matchee' was expected to end with.
336 self.matchee = matchee
337 self.expected = expected
339 def describe(self):
340 return "%s does not end with %s." % (
341 text_repr(self.matchee), text_repr(self.expected))
344 class _BinaryComparison(object):
345 """Matcher that compares an object to another object."""
347 def __init__(self, expected):
348 self.expected = expected
350 def __str__(self):
351 return "%s(%r)" % (self.__class__.__name__, self.expected)
353 def match(self, other):
354 if self.comparator(other, self.expected):
355 return None
356 return _BinaryMismatch(self.expected, self.mismatch_string, other)
358 def comparator(self, expected, other):
359 raise NotImplementedError(self.comparator)
362 class _BinaryMismatch(Mismatch):
363 """Two things did not match."""
365 def __init__(self, expected, mismatch_string, other):
366 self.expected = expected
367 self._mismatch_string = mismatch_string
368 self.other = other
370 def _format(self, thing):
371 # Blocks of text with newlines are formatted as triple-quote
372 # strings. Everything else is pretty-printed.
373 if istext(thing) or _isbytes(thing):
374 return text_repr(thing)
375 return pformat(thing)
377 def describe(self):
378 left = repr(self.expected)
379 right = repr(self.other)
380 if len(left) + len(right) > 70:
381 return "%s:\nreference = %s\nactual = %s\n" % (
382 self._mismatch_string, self._format(self.expected),
383 self._format(self.other))
384 else:
385 return "%s %s %s" % (left, self._mismatch_string, right)
388 class Equals(_BinaryComparison):
389 """Matches if the items are equal."""
391 comparator = operator.eq
392 mismatch_string = '!='
395 class NotEquals(_BinaryComparison):
396 """Matches if the items are not equal.
398 In most cases, this is equivalent to ``Not(Equals(foo))``. The difference
399 only matters when testing ``__ne__`` implementations.
402 comparator = operator.ne
403 mismatch_string = '=='
406 class Is(_BinaryComparison):
407 """Matches if the items are identical."""
409 comparator = operator.is_
410 mismatch_string = 'is not'
413 class IsInstance(object):
414 """Matcher that wraps isinstance."""
416 def __init__(self, *types):
417 self.types = tuple(types)
419 def __str__(self):
420 return "%s(%s)" % (self.__class__.__name__,
421 ', '.join(type.__name__ for type in self.types))
423 def match(self, other):
424 if isinstance(other, self.types):
425 return None
426 return NotAnInstance(other, self.types)
429 class NotAnInstance(Mismatch):
431 def __init__(self, matchee, types):
432 """Create a NotAnInstance Mismatch.
434 :param matchee: the thing which is not an instance of any of types.
435 :param types: A tuple of the types which were expected.
437 self.matchee = matchee
438 self.types = types
440 def describe(self):
441 if len(self.types) == 1:
442 typestr = self.types[0].__name__
443 else:
444 typestr = 'any of (%s)' % ', '.join(type.__name__ for type in
445 self.types)
446 return "'%s' is not an instance of %s" % (self.matchee, typestr)
449 class LessThan(_BinaryComparison):
450 """Matches if the item is less than the matchers reference object."""
452 comparator = operator.__lt__
453 mismatch_string = 'is not >'
456 class GreaterThan(_BinaryComparison):
457 """Matches if the item is greater than the matchers reference object."""
459 comparator = operator.__gt__
460 mismatch_string = 'is not <'
463 class MatchesAny(object):
464 """Matches if any of the matchers it is created with match."""
466 def __init__(self, *matchers):
467 self.matchers = matchers
469 def match(self, matchee):
470 results = []
471 for matcher in self.matchers:
472 mismatch = matcher.match(matchee)
473 if mismatch is None:
474 return None
475 results.append(mismatch)
476 return MismatchesAll(results)
478 def __str__(self):
479 return "MatchesAny(%s)" % ', '.join([
480 str(matcher) for matcher in self.matchers])
483 class MatchesAll(object):
484 """Matches if all of the matchers it is created with match."""
486 def __init__(self, *matchers):
487 self.matchers = matchers
489 def __str__(self):
490 return 'MatchesAll(%s)' % ', '.join(map(str, self.matchers))
492 def match(self, matchee):
493 results = []
494 for matcher in self.matchers:
495 mismatch = matcher.match(matchee)
496 if mismatch is not None:
497 results.append(mismatch)
498 if results:
499 return MismatchesAll(results)
500 else:
501 return None
504 class MismatchesAll(Mismatch):
505 """A mismatch with many child mismatches."""
507 def __init__(self, mismatches):
508 self.mismatches = mismatches
510 def describe(self):
511 descriptions = ["Differences: ["]
512 for mismatch in self.mismatches:
513 descriptions.append(mismatch.describe())
514 descriptions.append("]")
515 return '\n'.join(descriptions)
518 class Not(object):
519 """Inverts a matcher."""
521 def __init__(self, matcher):
522 self.matcher = matcher
524 def __str__(self):
525 return 'Not(%s)' % (self.matcher,)
527 def match(self, other):
528 mismatch = self.matcher.match(other)
529 if mismatch is None:
530 return MatchedUnexpectedly(self.matcher, other)
531 else:
532 return None
535 class MatchedUnexpectedly(Mismatch):
536 """A thing matched when it wasn't supposed to."""
538 def __init__(self, matcher, other):
539 self.matcher = matcher
540 self.other = other
542 def describe(self):
543 return "%r matches %s" % (self.other, self.matcher)
546 class MatchesException(Matcher):
547 """Match an exc_info tuple against an exception instance or type."""
549 def __init__(self, exception, value_re=None):
550 """Create a MatchesException that will match exc_info's for exception.
552 :param exception: Either an exception instance or type.
553 If an instance is given, the type and arguments of the exception
554 are checked. If a type is given only the type of the exception is
555 checked. If a tuple is given, then as with isinstance, any of the
556 types in the tuple matching is sufficient to match.
557 :param value_re: If 'exception' is a type, and the matchee exception
558 is of the right type, then match against this. If value_re is a
559 string, then assume value_re is a regular expression and match
560 the str() of the exception against it. Otherwise, assume value_re
561 is a matcher, and match the exception against it.
563 Matcher.__init__(self)
564 self.expected = exception
565 if istext(value_re):
566 value_re = AfterPreproccessing(str, MatchesRegex(value_re), False)
567 self.value_re = value_re
568 self._is_instance = type(self.expected) not in classtypes() + (tuple,)
570 def match(self, other):
571 if type(other) != tuple:
572 return Mismatch('%r is not an exc_info tuple' % other)
573 expected_class = self.expected
574 if self._is_instance:
575 expected_class = expected_class.__class__
576 if not issubclass(other[0], expected_class):
577 return Mismatch('%r is not a %r' % (other[0], expected_class))
578 if self._is_instance:
579 if other[1].args != self.expected.args:
580 return Mismatch('%s has different arguments to %s.' % (
581 _error_repr(other[1]), _error_repr(self.expected)))
582 elif self.value_re is not None:
583 return self.value_re.match(other[1])
585 def __str__(self):
586 if self._is_instance:
587 return "MatchesException(%s)" % _error_repr(self.expected)
588 return "MatchesException(%s)" % repr(self.expected)
591 class Contains(Matcher):
592 """Checks whether something is container in another thing."""
594 def __init__(self, needle):
595 """Create a Contains Matcher.
597 :param needle: the thing that needs to be contained by matchees.
599 self.needle = needle
601 def __str__(self):
602 return "Contains(%r)" % (self.needle,)
604 def match(self, matchee):
605 try:
606 if self.needle not in matchee:
607 return DoesNotContain(matchee, self.needle)
608 except TypeError:
609 # e.g. 1 in 2 will raise TypeError
610 return DoesNotContain(matchee, self.needle)
611 return None
614 class StartsWith(Matcher):
615 """Checks whether one string starts with another."""
617 def __init__(self, expected):
618 """Create a StartsWith Matcher.
620 :param expected: the string that matchees should start with.
622 self.expected = expected
624 def __str__(self):
625 return "StartsWith(%r)" % (self.expected,)
627 def match(self, matchee):
628 if not matchee.startswith(self.expected):
629 return DoesNotStartWith(matchee, self.expected)
630 return None
633 class EndsWith(Matcher):
634 """Checks whether one string starts with another."""
636 def __init__(self, expected):
637 """Create a EndsWith Matcher.
639 :param expected: the string that matchees should end with.
641 self.expected = expected
643 def __str__(self):
644 return "EndsWith(%r)" % (self.expected,)
646 def match(self, matchee):
647 if not matchee.endswith(self.expected):
648 return DoesNotEndWith(matchee, self.expected)
649 return None
652 class KeysEqual(Matcher):
653 """Checks whether a dict has particular keys."""
655 def __init__(self, *expected):
656 """Create a `KeysEqual` Matcher.
658 :param expected: The keys the dict is expected to have. If a dict,
659 then we use the keys of that dict, if a collection, we assume it
660 is a collection of expected keys.
662 try:
663 self.expected = expected.keys()
664 except AttributeError:
665 self.expected = list(expected)
667 def __str__(self):
668 return "KeysEqual(%s)" % ', '.join(map(repr, self.expected))
670 def match(self, matchee):
671 expected = sorted(self.expected)
672 matched = Equals(expected).match(sorted(matchee.keys()))
673 if matched:
674 return AnnotatedMismatch(
675 'Keys not equal',
676 _BinaryMismatch(expected, 'does not match', matchee))
677 return None
680 class Annotate(object):
681 """Annotates a matcher with a descriptive string.
683 Mismatches are then described as '<mismatch>: <annotation>'.
686 def __init__(self, annotation, matcher):
687 self.annotation = annotation
688 self.matcher = matcher
690 @classmethod
691 def if_message(cls, annotation, matcher):
692 """Annotate ``matcher`` only if ``annotation`` is non-empty."""
693 if not annotation:
694 return matcher
695 return cls(annotation, matcher)
697 def __str__(self):
698 return 'Annotate(%r, %s)' % (self.annotation, self.matcher)
700 def match(self, other):
701 mismatch = self.matcher.match(other)
702 if mismatch is not None:
703 return AnnotatedMismatch(self.annotation, mismatch)
706 class AnnotatedMismatch(MismatchDecorator):
707 """A mismatch annotated with a descriptive string."""
709 def __init__(self, annotation, mismatch):
710 super(AnnotatedMismatch, self).__init__(mismatch)
711 self.annotation = annotation
712 self.mismatch = mismatch
714 def describe(self):
715 return '%s: %s' % (self.original.describe(), self.annotation)
718 class Raises(Matcher):
719 """Match if the matchee raises an exception when called.
721 Exceptions which are not subclasses of Exception propogate out of the
722 Raises.match call unless they are explicitly matched.
725 def __init__(self, exception_matcher=None):
726 """Create a Raises matcher.
728 :param exception_matcher: Optional validator for the exception raised
729 by matchee. If supplied the exc_info tuple for the exception raised
730 is passed into that matcher. If no exception_matcher is supplied
731 then the simple fact of raising an exception is considered enough
732 to match on.
734 self.exception_matcher = exception_matcher
736 def match(self, matchee):
737 try:
738 result = matchee()
739 return Mismatch('%r returned %r' % (matchee, result))
740 # Catch all exceptions: Raises() should be able to match a
741 # KeyboardInterrupt or SystemExit.
742 except:
743 exc_info = sys.exc_info()
744 if self.exception_matcher:
745 mismatch = self.exception_matcher.match(exc_info)
746 if not mismatch:
747 del exc_info
748 return
749 else:
750 mismatch = None
751 # The exception did not match, or no explicit matching logic was
752 # performed. If the exception is a non-user exception (that is, not
753 # a subclass of Exception on Python 2.5+) then propogate it.
754 if isbaseexception(exc_info[1]):
755 del exc_info
756 raise
757 return mismatch
759 def __str__(self):
760 return 'Raises()'
763 def raises(exception):
764 """Make a matcher that checks that a callable raises an exception.
766 This is a convenience function, exactly equivalent to::
768 return Raises(MatchesException(exception))
770 See `Raises` and `MatchesException` for more information.
772 return Raises(MatchesException(exception))
775 class MatchesListwise(object):
776 """Matches if each matcher matches the corresponding value.
778 More easily explained by example than in words:
780 >>> MatchesListwise([Equals(1)]).match([1])
781 >>> MatchesListwise([Equals(1), Equals(2)]).match([1, 2])
782 >>> print (MatchesListwise([Equals(1), Equals(2)]).match([2, 1]).describe())
783 Differences: [
784 1 != 2
785 2 != 1
789 def __init__(self, matchers):
790 self.matchers = matchers
792 def match(self, values):
793 mismatches = []
794 length_mismatch = Annotate(
795 "Length mismatch", Equals(len(self.matchers))).match(len(values))
796 if length_mismatch:
797 mismatches.append(length_mismatch)
798 for matcher, value in zip(self.matchers, values):
799 mismatch = matcher.match(value)
800 if mismatch:
801 mismatches.append(mismatch)
802 if mismatches:
803 return MismatchesAll(mismatches)
806 class MatchesStructure(object):
807 """Matcher that matches an object structurally.
809 'Structurally' here means that attributes of the object being matched are
810 compared against given matchers.
812 `fromExample` allows the creation of a matcher from a prototype object and
813 then modified versions can be created with `update`.
815 `byEquality` creates a matcher in much the same way as the constructor,
816 except that the matcher for each of the attributes is assumed to be
817 `Equals`.
819 `byMatcher` creates a similar matcher to `byEquality`, but you get to pick
820 the matcher, rather than just using `Equals`.
823 def __init__(self, **kwargs):
824 """Construct a `MatchesStructure`.
826 :param kwargs: A mapping of attributes to matchers.
828 self.kws = kwargs
830 @classmethod
831 def byEquality(cls, **kwargs):
832 """Matches an object where the attributes equal the keyword values.
834 Similar to the constructor, except that the matcher is assumed to be
835 Equals.
837 return cls.byMatcher(Equals, **kwargs)
839 @classmethod
840 def byMatcher(cls, matcher, **kwargs):
841 """Matches an object where the attributes match the keyword values.
843 Similar to the constructor, except that the provided matcher is used
844 to match all of the values.
846 return cls(
847 **dict((name, matcher(value)) for name, value in kwargs.items()))
849 @classmethod
850 def fromExample(cls, example, *attributes):
851 kwargs = {}
852 for attr in attributes:
853 kwargs[attr] = Equals(getattr(example, attr))
854 return cls(**kwargs)
856 def update(self, **kws):
857 new_kws = self.kws.copy()
858 for attr, matcher in kws.items():
859 if matcher is None:
860 new_kws.pop(attr, None)
861 else:
862 new_kws[attr] = matcher
863 return type(self)(**new_kws)
865 def __str__(self):
866 kws = []
867 for attr, matcher in sorted(self.kws.items()):
868 kws.append("%s=%s" % (attr, matcher))
869 return "%s(%s)" % (self.__class__.__name__, ', '.join(kws))
871 def match(self, value):
872 matchers = []
873 values = []
874 for attr, matcher in sorted(self.kws.items()):
875 matchers.append(Annotate(attr, matcher))
876 values.append(getattr(value, attr))
877 return MatchesListwise(matchers).match(values)
880 class MatchesRegex(object):
881 """Matches if the matchee is matched by a regular expression."""
883 def __init__(self, pattern, flags=0):
884 self.pattern = pattern
885 self.flags = flags
887 def __str__(self):
888 args = ['%r' % self.pattern]
889 flag_arg = []
890 # dir() sorts the attributes for us, so we don't need to do it again.
891 for flag in dir(re):
892 if len(flag) == 1:
893 if self.flags & getattr(re, flag):
894 flag_arg.append('re.%s' % flag)
895 if flag_arg:
896 args.append('|'.join(flag_arg))
897 return '%s(%s)' % (self.__class__.__name__, ', '.join(args))
899 def match(self, value):
900 if not re.match(self.pattern, value, self.flags):
901 pattern = self.pattern
902 if not isinstance(pattern, str_is_unicode and str or unicode):
903 pattern = pattern.decode("latin1")
904 pattern = pattern.encode("unicode_escape").decode("ascii")
905 return Mismatch("%r does not match /%s/" % (
906 value, pattern.replace("\\\\", "\\")))
909 class MatchesSetwise(object):
910 """Matches if all the matchers match elements of the value being matched.
912 That is, each element in the 'observed' set must match exactly one matcher
913 from the set of matchers, with no matchers left over.
915 The difference compared to `MatchesListwise` is that the order of the
916 matchings does not matter.
919 def __init__(self, *matchers):
920 self.matchers = matchers
922 def match(self, observed):
923 remaining_matchers = set(self.matchers)
924 not_matched = []
925 for value in observed:
926 for matcher in remaining_matchers:
927 if matcher.match(value) is None:
928 remaining_matchers.remove(matcher)
929 break
930 else:
931 not_matched.append(value)
932 if not_matched or remaining_matchers:
933 remaining_matchers = list(remaining_matchers)
934 # There are various cases that all should be reported somewhat
935 # differently.
937 # There are two trivial cases:
938 # 1) There are just some matchers left over.
939 # 2) There are just some values left over.
941 # Then there are three more interesting cases:
942 # 3) There are the same number of matchers and values left over.
943 # 4) There are more matchers left over than values.
944 # 5) There are more values left over than matchers.
946 if len(not_matched) == 0:
947 if len(remaining_matchers) > 1:
948 msg = "There were %s matchers left over: " % (
949 len(remaining_matchers),)
950 else:
951 msg = "There was 1 matcher left over: "
952 msg += ', '.join(map(str, remaining_matchers))
953 return Mismatch(msg)
954 elif len(remaining_matchers) == 0:
955 if len(not_matched) > 1:
956 return Mismatch(
957 "There were %s values left over: %s" % (
958 len(not_matched), not_matched))
959 else:
960 return Mismatch(
961 "There was 1 value left over: %s" % (
962 not_matched, ))
963 else:
964 common_length = min(len(remaining_matchers), len(not_matched))
965 if common_length == 0:
966 raise AssertionError("common_length can't be 0 here")
967 if common_length > 1:
968 msg = "There were %s mismatches" % (common_length,)
969 else:
970 msg = "There was 1 mismatch"
971 if len(remaining_matchers) > len(not_matched):
972 extra_matchers = remaining_matchers[common_length:]
973 msg += " and %s extra matcher" % (len(extra_matchers), )
974 if len(extra_matchers) > 1:
975 msg += "s"
976 msg += ': ' + ', '.join(map(str, extra_matchers))
977 elif len(not_matched) > len(remaining_matchers):
978 extra_values = not_matched[common_length:]
979 msg += " and %s extra value" % (len(extra_values), )
980 if len(extra_values) > 1:
981 msg += "s"
982 msg += ': ' + str(extra_values)
983 return Annotate(
984 msg, MatchesListwise(remaining_matchers[:common_length])
985 ).match(not_matched[:common_length])
988 class AfterPreprocessing(object):
989 """Matches if the value matches after passing through a function.
991 This can be used to aid in creating trivial matchers as functions, for
992 example::
994 def PathHasFileContent(content):
995 def _read(path):
996 return open(path).read()
997 return AfterPreprocessing(_read, Equals(content))
1000 def __init__(self, preprocessor, matcher, annotate=True):
1001 """Create an AfterPreprocessing matcher.
1003 :param preprocessor: A function called with the matchee before
1004 matching.
1005 :param matcher: What to match the preprocessed matchee against.
1006 :param annotate: Whether or not to annotate the matcher with
1007 something explaining how we transformed the matchee. Defaults
1008 to True.
1010 self.preprocessor = preprocessor
1011 self.matcher = matcher
1012 self.annotate = annotate
1014 def _str_preprocessor(self):
1015 if isinstance(self.preprocessor, types.FunctionType):
1016 return '<function %s>' % self.preprocessor.__name__
1017 return str(self.preprocessor)
1019 def __str__(self):
1020 return "AfterPreprocessing(%s, %s)" % (
1021 self._str_preprocessor(), self.matcher)
1023 def match(self, value):
1024 after = self.preprocessor(value)
1025 if self.annotate:
1026 matcher = Annotate(
1027 "after %s on %r" % (self._str_preprocessor(), value),
1028 self.matcher)
1029 else:
1030 matcher = self.matcher
1031 return matcher.match(after)
1033 # This is the old, deprecated. spelling of the name, kept for backwards
1034 # compatibility.
1035 AfterPreproccessing = AfterPreprocessing
1038 class AllMatch(object):
1039 """Matches if all provided values match the given matcher."""
1041 def __init__(self, matcher):
1042 self.matcher = matcher
1044 def __str__(self):
1045 return 'AllMatch(%s)' % (self.matcher,)
1047 def match(self, values):
1048 mismatches = []
1049 for value in values:
1050 mismatch = self.matcher.match(value)
1051 if mismatch:
1052 mismatches.append(mismatch)
1053 if mismatches:
1054 return MismatchesAll(mismatches)
1057 # Signal that this is part of the testing framework, and that code from this
1058 # should not normally appear in tracebacks.
1059 __unittest = True