import sys import traceback def walk_exception_chain(exc, ignore_first=False, reverse=False): l = _inner_walk_exception_chain(exc, ignore_first) if reverse: l = reversed(list(l)) return l def _inner_walk_exception_chain(exc, ignore_first): if not ignore_first: yield exc exc = getattr(exc, "__cause__", None) while exc is not None: yield exc exc = getattr(exc, "__cause__", None) def dump_error(raw_exc, msg=None, handle=sys.stderr, tb=None): # force default output for exceptions if getattr(handle, "reset", False): handle.write(handle.reset) prefix = "" if msg: prefix = " " handle.write(msg.rstrip("\n") + ":\n") if tb: handle.write("Traceback follows:\n") traceback.print_tb(tb, file=handle) exc_strings = [] if raw_exc is not None: for exc in walk_exception_chain(raw_exc): exc_strings.extend( prefix + x.strip() for x in (x for x in str(exc).split("\n") if x) ) if exc_strings: if msg and tb: handle.write(f"\n{raw_exc.__class__.__name__}:\n") handle.write("\n".join(exc_strings)) handle.write("\n")