Context manager catching unraisable exception using sys.unraisablehook. Storing the exception value (cm.unraisable.exc_value) creates a reference cycle. The reference cycle is broken explicitly when the context manager exits. Storing the object (cm.unraisable.object) can resurrect
| 13 | |
| 14 | # Copied from cpython/Lib/test/support/__init__.py, with modifications. |
| 15 | class catch_unraisable_exception: |
| 16 | """Context manager catching unraisable exception using sys.unraisablehook. |
| 17 | |
| 18 | Storing the exception value (cm.unraisable.exc_value) creates a reference |
| 19 | cycle. The reference cycle is broken explicitly when the context manager |
| 20 | exits. |
| 21 | |
| 22 | Storing the object (cm.unraisable.object) can resurrect it if it is set to |
| 23 | an object which is being finalized. Exiting the context manager clears the |
| 24 | stored object. |
| 25 | |
| 26 | Usage: |
| 27 | with catch_unraisable_exception() as cm: |
| 28 | # code creating an "unraisable exception" |
| 29 | ... |
| 30 | # check the unraisable exception: use cm.unraisable |
| 31 | ... |
| 32 | # cm.unraisable attribute no longer exists at this point |
| 33 | # (to break a reference cycle) |
| 34 | """ |
| 35 | |
| 36 | def __init__(self) -> None: |
| 37 | self.unraisable: Optional["sys.UnraisableHookArgs"] = None |
| 38 | self._old_hook: Optional[Callable[["sys.UnraisableHookArgs"], Any]] = None |
| 39 | |
| 40 | def _hook(self, unraisable: "sys.UnraisableHookArgs") -> None: |
| 41 | # Storing unraisable.object can resurrect an object which is being |
| 42 | # finalized. Storing unraisable.exc_value creates a reference cycle. |
| 43 | self.unraisable = unraisable |
| 44 | |
| 45 | def __enter__(self) -> "catch_unraisable_exception": |
| 46 | self._old_hook = sys.unraisablehook |
| 47 | sys.unraisablehook = self._hook |
| 48 | return self |
| 49 | |
| 50 | def __exit__( |
| 51 | self, |
| 52 | exc_type: Optional[Type[BaseException]], |
| 53 | exc_val: Optional[BaseException], |
| 54 | exc_tb: Optional[TracebackType], |
| 55 | ) -> None: |
| 56 | assert self._old_hook is not None |
| 57 | sys.unraisablehook = self._old_hook |
| 58 | self._old_hook = None |
| 59 | del self.unraisable |
| 60 | |
| 61 | |
| 62 | def unraisable_exception_runtest_hook() -> Generator[None, None, None]: |
no outgoing calls
no test coverage detected