Skip to content
Snippets Groups Projects
_exception.py 3.76 KiB
Newer Older
  • Learn to ignore specific revisions
  • # Copyright (c) 2009-2012 testtools developers.
    #
    # Copyright 2019 PrivateStorage.io, LLC
    #
    # Licensed under the Apache License, Version 2.0 (the "License");
    # you may not use this file except in compliance with the License.
    # You may obtain a copy of the License at
    #
    #     http://www.apache.org/licenses/LICENSE-2.0
    #
    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS,
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    # See the License for the specific language governing permissions and
    # limitations under the License.
    
    __all__ = [
        'MatchesExceptionType',
        'Raises',
        'raises',
        ]
    
    import sys
    
    from testtools.matchers import (
        Matcher,
        Mismatch,
    )
    from testtools.content import (
        TracebackContent,
    )
    
    
    def _is_exception(exc):
        return isinstance(exc, BaseException)
    
    
    def _is_user_exception(exc):
        return isinstance(exc, Exception)
    
    
    class MatchesExceptionType(Matcher):
        """
        Match an exc_info tuple against an exception type.
        """
    
        def __init__(self, exception_type):
            """
            Create a MatchesException that will match exc_info's for exception.
    
            :param exception: An exception type.
            """
            Matcher.__init__(self)
            self.expected = exception_type
    
        def match(self, other):
            if type(other) != tuple:
                return Mismatch('{!r} is not an exc_info tuple'.format(other))
            expected_class = self.expected
            etype, evalue, etb = other
            if not issubclass(etype, expected_class):
                return Mismatch(
                    "{} is an instance of {}, expected an instance of {}.".format(
                        evalue,
                        etype,
                        expected_class,
                    ),
                    dict(
                        traceback=TracebackContent(other, None),
                    ),
                )
    
        def __str__(self):
            return "MatchesExceptionType({!r})".format(self.expected)
    
    
    class Raises(Matcher):
        """Match if the matchee raises an exception when called.
    
        Exceptions which are not subclasses of Exception propogate out of the
        Raises.match call unless they are explicitly matched.
        """
    
        def __init__(self, exception_matcher):
            """
            Create a Raises matcher.
    
            :param exception_matcher: Validator for the exception raised by
                matchee. The exc_info tuple for the exception raised is passed
                into that matcher.
            """
            self.exception_matcher = exception_matcher
    
        def match(self, matchee):
            try:
                result = matchee()
                return Mismatch('%r returned %r' % (matchee, result))
            # Catch all exceptions: Raises() should be able to match a
            # KeyboardInterrupt or SystemExit.
            except:
                exc_info = sys.exc_info()
                mismatch = self.exception_matcher.match(exc_info)
                exc_type = exc_info[1]
                # It's safer not to keep the traceback around.
                del exc_info
                if mismatch:
                    # The exception did not match, or no explicit matching logic was
                    # performed. If the exception is a non-user exception then
                    # propagate it.
                    if _is_exception(exc_type) and not _is_user_exception(exc_type):
                        raise
                    return mismatch
            return None
    
        def __str__(self):
            return 'Raises()'
    
    
    def raises(exception_type):
        """Make a matcher that checks that a callable raises an exception.
    
        This is a convenience function, exactly equivalent to::
    
            return Raises(MatchesExceptionType(exception_type))
    
        See `Raises` and `MatchesExceptionType` for more information.
        """
        return Raises(MatchesExceptionType(exception_type))