| from __future__ import annotations | |
| import functools | |
| import operator | |
| # from jaraco.context 6.1 | |
| class ExceptionTrap: | |
| """ | |
| A context manager that will catch certain exceptions and provide an | |
| indication they occurred. | |
| >>> with ExceptionTrap() as trap: | |
| ... raise Exception() | |
| >>> bool(trap) | |
| True | |
| >>> with ExceptionTrap() as trap: | |
| ... pass | |
| >>> bool(trap) | |
| False | |
| >>> with ExceptionTrap(ValueError) as trap: | |
| ... raise ValueError("1 + 1 is not 3") | |
| >>> bool(trap) | |
| True | |
| >>> trap.value | |
| ValueError('1 + 1 is not 3') | |
| >>> trap.tb | |
| <traceback object at ...> | |
| >>> with ExceptionTrap(ValueError) as trap: | |
| ... raise Exception() | |
| Traceback (most recent call last): | |
| ... | |
| Exception | |
| >>> bool(trap) | |
| False | |
| """ | |
| exc_info = None, None, None | |
| def __init__(self, exceptions=(Exception,)): | |
| self.exceptions = exceptions | |
| def __enter__(self): | |
| return self | |
| @property | |
| def type(self): | |
| return self.exc_info[0] | |
| @property | |
| def value(self): | |
| return self.exc_info[1] | |
| @property | |
| def tb(self): | |
| return self.exc_info[2] | |
| def __exit__(self, *exc_info): | |
| type = exc_info[0] | |
| matches = type and issubclass(type, self.exceptions) | |
| if matches: | |
| self.exc_info = exc_info | |
| return matches | |
| def __bool__(self): | |
| return bool(self.type) | |
| def raises(self, func, *, _test=bool): | |
| """ | |
| Wrap func and replace the result with the truth | |
| value of the trap (True if an exception occurred). | |
| First, give the decorator an alias to support Python 3.8 | |
| Syntax. | |
| >>> raises = ExceptionTrap(ValueError).raises | |
| Now decorate a function that always fails. | |
| >>> @raises | |
| ... def fail(): | |
| ... raise ValueError('failed') | |
| >>> fail() | |
| True | |
| """ | |
| @functools.wraps(func) | |
| def wrapper(*args, **kwargs): | |
| with ExceptionTrap(self.exceptions) as trap: | |
| func(*args, **kwargs) | |
| return _test(trap) | |
| return wrapper | |
| def passes(self, func): | |
| """ | |
| Wrap func and replace the result with the truth | |
| value of the trap (True if no exception). | |
| First, give the decorator an alias to support Python 3.8 | |
| Syntax. | |
| >>> passes = ExceptionTrap(ValueError).passes | |
| Now decorate a function that always fails. | |
| >>> @passes | |
| ... def fail(): | |
| ... raise ValueError('failed') | |
| >>> fail() | |
| False | |
| """ | |
| return self.raises(func, _test=operator.not_) |