Source code for visidata.errors

import traceback
import sys
import re

from visidata import vd, VisiData

vd.option('debug', False, 'exit on error and display stacktrace')


[docs]class ExpectedException(Exception): 'Controlled Exception from fail() or confirm(). Status or other interface update is done by raiser.' pass
def stacktrace(e=None, exclude_caller=False): '''Return a list of strings for the stack trace, without newlines at the end. If an exception handler is executing, and *e* is none, the stack trace includes extra levels of callers beyond the level where the exception was caught. If *exclude_caller* is True, the trace will exclude the function that called stacktrace(). The trace will exclude several uninformative levels that are run in interactive visidata.''' if e: return traceback.format_exception_only(type(e), e) #in Python 3.11 we can replace sys.exc_info() with sys.exception() handling = (sys.exc_info() != (None, None, None)) stack = ''.join(traceback.format_stack()).strip().splitlines() if handling: trim_levels = 2 # remove levels for stacktrace() -> format_stack() if exclude_caller: trim_levels += 1 trace_above = stack[:-2*trim_levels] else: trace_above = stack if trace_above: trace_above[0] = ' ' + trace_above[0] #fix indent level of first line try: # remove several levels of uninformative stacktrace in typical interactive vd idx = trace_above.index(' ret = vd.mainloop(scr)') trace_above = trace_above[idx+1:] except ValueError: pass if not handling: return trace_above # remove lines that mark error columns with carets and sometimes tildes trace_below = [ line for line in traceback.format_exc().strip().splitlines() if not re.match('^ *~*\\^+$', line) ] # move the "Traceback (most recent call last) header to the top of the output return [trace_below[0]] + trace_above + trace_below[1:] @VisiData.api def exceptionCaught(vd, exc=None, status=True, **kwargs): 'Add *exc* to list of last errors and add to status history. Show on left status bar if *status* is True. Reraise exception if options.debug is True.' if isinstance(exc, ExpectedException): # already reported, don't log return # save a stack trace that does not include this function vd.lastErrors.append(stacktrace(exclude_caller=True)) if status: vd.status(f'{type(exc).__name__}: {exc}', priority=2) else: vd.addToStatusHistory(vd.lastErrors[-1][-1]) if vd.options.debug: raise vd.addGlobals(stacktrace=stacktrace, ExpectedException=ExpectedException) # see textsheet.py for ErrorSheet and associated commands