| 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_) |