1 # Copyright (c) 2009-2012 testtools developers. See LICENSE for details.
11 from testtools.compat import (
17 from ._basic import MatchesRegex
18 from ._higherorder import AfterPreproccessing
25 class MatchesException(Matcher):
26 """Match an exc_info tuple against an exception instance or type."""
28 def __init__(self, exception, value_re=None):
29 """Create a MatchesException that will match exc_info's for exception.
31 :param exception: Either an exception instance or type.
32 If an instance is given, the type and arguments of the exception
33 are checked. If a type is given only the type of the exception is
34 checked. If a tuple is given, then as with isinstance, any of the
35 types in the tuple matching is sufficient to match.
36 :param value_re: If 'exception' is a type, and the matchee exception
37 is of the right type, then match against this. If value_re is a
38 string, then assume value_re is a regular expression and match
39 the str() of the exception against it. Otherwise, assume value_re
40 is a matcher, and match the exception against it.
42 Matcher.__init__(self)
43 self.expected = exception
45 value_re = AfterPreproccessing(str, MatchesRegex(value_re), False)
46 self.value_re = value_re
47 self._is_instance = type(self.expected) not in classtypes() + (tuple,)
49 def match(self, other):
50 if type(other) != tuple:
51 return Mismatch('%r is not an exc_info tuple' % other)
52 expected_class = self.expected
54 expected_class = expected_class.__class__
55 if not issubclass(other[0], expected_class):
56 return Mismatch('%r is not a %r' % (other[0], expected_class))
58 if other[1].args != self.expected.args:
59 return Mismatch('%s has different arguments to %s.' % (
60 _error_repr(other[1]), _error_repr(self.expected)))
61 elif self.value_re is not None:
62 return self.value_re.match(other[1])
66 return "MatchesException(%s)" % _error_repr(self.expected)
67 return "MatchesException(%s)" % repr(self.expected)
70 class Raises(Matcher):
71 """Match if the matchee raises an exception when called.
73 Exceptions which are not subclasses of Exception propogate out of the
74 Raises.match call unless they are explicitly matched.
77 def __init__(self, exception_matcher=None):
78 """Create a Raises matcher.
80 :param exception_matcher: Optional validator for the exception raised
81 by matchee. If supplied the exc_info tuple for the exception raised
82 is passed into that matcher. If no exception_matcher is supplied
83 then the simple fact of raising an exception is considered enough
86 self.exception_matcher = exception_matcher
88 def match(self, matchee):
91 return Mismatch('%r returned %r' % (matchee, result))
92 # Catch all exceptions: Raises() should be able to match a
93 # KeyboardInterrupt or SystemExit.
95 exc_info = sys.exc_info()
96 if self.exception_matcher:
97 mismatch = self.exception_matcher.match(exc_info)
103 # The exception did not match, or no explicit matching logic was
104 # performed. If the exception is a non-user exception (that is, not
105 # a subclass of Exception on Python 2.5+) then propogate it.
106 if isbaseexception(exc_info[1]):
115 def raises(exception):
116 """Make a matcher that checks that a callable raises an exception.
118 This is a convenience function, exactly equivalent to::
120 return Raises(MatchesException(exception))
122 See `Raises` and `MatchesException` for more information.
124 return Raises(MatchesException(exception))