Given a tracecack or an exception, return a tuple of chained exceptions and current traceback to inspect. This will deal with selecting the right ``__cause__`` or ``__context__`` as well as handling cycles, and return a flattened list of exceptions we
(self, tb_or_exc)
| 435 | if CHAIN_EXCEPTIONS: |
| 436 | |
| 437 | def _get_tb_and_exceptions(self, tb_or_exc): |
| 438 | """ |
| 439 | Given a tracecack or an exception, return a tuple of chained exceptions |
| 440 | and current traceback to inspect. |
| 441 | This will deal with selecting the right ``__cause__`` or ``__context__`` |
| 442 | as well as handling cycles, and return a flattened list of exceptions we |
| 443 | can jump to with do_exceptions. |
| 444 | """ |
| 445 | _exceptions = [] |
| 446 | if isinstance(tb_or_exc, BaseException): |
| 447 | traceback, current = tb_or_exc.__traceback__, tb_or_exc |
| 448 | |
| 449 | while current is not None: |
| 450 | if current in _exceptions: |
| 451 | break |
| 452 | _exceptions.append(current) |
| 453 | if current.__cause__ is not None: |
| 454 | current = current.__cause__ |
| 455 | elif ( |
| 456 | current.__context__ is not None |
| 457 | and not current.__suppress_context__ |
| 458 | ): |
| 459 | current = current.__context__ |
| 460 | |
| 461 | if len(_exceptions) >= self.MAX_CHAINED_EXCEPTION_DEPTH: |
| 462 | self.message( |
| 463 | f"More than {self.MAX_CHAINED_EXCEPTION_DEPTH}" |
| 464 | " chained exceptions found, not all exceptions" |
| 465 | " will be browsable with `exceptions`." |
| 466 | ) |
| 467 | break |
| 468 | else: |
| 469 | traceback = tb_or_exc |
| 470 | return tuple(reversed(_exceptions)), traceback |
| 471 | |
| 472 | @contextmanager |
| 473 | def _hold_exceptions(self, exceptions): |