def _StackSummary_extract(frame_gen, limit=None, lookup_lines=True, capture_locals=False): """Create a StackSummary from a traceback or stack object. Very simplified copy of the original StackSummary.extract(). We want always to capture locals, that is why we overwrite it. Additionally, we also capture the frame. This is a bit hacky and also not like this is originally intended (to not keep refs). :param frame_gen: A generator that yields (frame, lineno) tuples to include in the stack. :param limit: None to include all frames or the number of frames to include. :param lookup_lines: If True, lookup lines for each frame immediately, otherwise lookup is deferred until the frame is rendered. :param capture_locals: If True, the local variables from each frame will be captured as object representations into the FrameSummary. """ result = StackSummary() for f, lineno in frame_gen: co = f.f_code filename = co.co_filename name = co.co_name result.append( ExtendedFrameSummary(frame=f, filename=filename, lineno=lineno, name=name, lookup_line=False)) return result
def _remove_frame_from_stack(tbss: StackSummary, framename: str) -> StackSummary: filtered_stack_list: List[traceback.FrameSummary] = \ list(filter(lambda frame: getattr(frame, 'filename') != framename, tbss)) filtered_stack: StackSummary = StackSummary.from_list(filtered_stack_list) return filtered_stack
def get_exception_details(): from traceback import walk_tb, StackSummary, FrameSummary from time import strftime cla, exc, exc_traceback = sys.exc_info() exc_args = exc.__dict__["args"] if "args" in exc.__dict__ else "<no args>" ex_title = cla.__name__ + ": Exception:" + str(exc) + " - args:" + str( exc_args) msgs = [ ex_title, ] except_location = "" tb: list[FrameSummary] = StackSummary.extract(walk_tb(exc_traceback), limit=None, capture_locals=True) for frame in tb: local_vars_info = [] if frame.locals: for name, value in frame.locals.items(): if name == "self": continue local_vars_info.append(f'\t{name} = {value}') except_location += "\n" + frame.filename + ":" + str(frame.lineno) + " \n" + str(frame.name) + \ "\n<Args>:" + "\n".join(local_vars_info) msgs.insert(1, except_location) time = strftime("%Y-%m-%d %H:%M:%S") return time + " - " + ("".join(msgs))
def iter_traceback(tb=None, enforce_most_recent_call_first=False): """ Iterates a traceback of various formats: - traceback (types.TracebackType) - frame object (types.FrameType) - stack summary (traceback.StackSummary) :param types.TracebackType|types.FrameType|StackSummary|None tb: traceback. if None, will use sys._getframe :param bool enforce_most_recent_call_first: Frame or stack summery: most recent call first (top of the stack is the first entry in the result) Traceback: most recent call last If True, and we get traceback, will unroll and reverse, such that we have always the most recent call first. :return: yields the frames (types.FrameType) :rtype: list[types.FrameType|DummyFrame] """ if tb is None: tb = get_current_frame() def is_stack_summary(_tb): return isinstance(_tb, StackSummary) is_frame = inspect.isframe is_traceback = inspect.istraceback assert is_traceback(tb) or is_frame(tb) or is_stack_summary(tb) # Frame or stack summery: most recent call first # Traceback: most recent call last if is_traceback(tb) and enforce_most_recent_call_first: frames = list(iter_traceback(tb)) for frame in frames[::-1]: yield frame return _tb = tb while _tb is not None: if is_frame(_tb): frame = _tb elif is_stack_summary(_tb): if isinstance(_tb[0], ExtendedFrameSummary): frame = _tb[0].tb_frame else: frame = DummyFrame.from_frame_summary(_tb[0]) else: frame = _tb.tb_frame yield frame if is_frame(_tb): _tb = _tb.f_back elif is_stack_summary(_tb): _tb = StackSummary.from_list(_tb[1:]) if not _tb: _tb = None else: _tb = _tb.tb_next
def except_hook( exc_type: Type[BaseException], exc_value: BaseException, tb: TracebackType ) -> None: exception_config: Union[DeveloperExceptionConfig, None] = getattr( exc_value, _typer_developer_exception_attr_name, None ) standard_traceback = os.getenv("_TYPER_STANDARD_TRACEBACK") if ( standard_traceback or not exception_config or not exception_config.pretty_exceptions_enable ): _original_except_hook(exc_type, exc_value, tb) return typer_path = os.path.dirname(__file__) click_path = os.path.dirname(click.__file__) supress_internal_dir_names = [typer_path, click_path] exc = exc_value if rich: rich_tb = Traceback.from_exception( type(exc), exc, exc.__traceback__, show_locals=exception_config.pretty_exceptions_show_locals, suppress=supress_internal_dir_names, ) console_stderr.print(rich_tb) return tb_exc = traceback.TracebackException.from_exception(exc) stack: List[FrameSummary] = [] for frame in tb_exc.stack: if any( [frame.filename.startswith(path) for path in supress_internal_dir_names] ): if not exception_config.pretty_exceptions_short: # Hide the line for internal libraries, Typer and Click stack.append( traceback.FrameSummary( filename=frame.filename, lineno=frame.lineno, name=frame.name, line="", ) ) else: stack.append(frame) # Type ignore ref: https://github.com/python/typeshed/pull/8244 final_stack_summary = StackSummary.from_list(stack) # type: ignore tb_exc.stack = final_stack_summary for line in tb_exc.format(): print(line, file=sys.stderr) return
def __post_init__(self, function, test_id): file_frame = FrameSummary( inspect.getfile(function), docs_start_lineno(function) + self.path.line, function.__name__, lookup_line=False, ) dynamic_frame = FrameSummary(test_id, 1, function.__name__, lookup_line=False, line=self.path) self.stack = StackSummary.from_list([file_frame, dynamic_frame])
def _StackSummary_extract(frame_gen, limit=None, lookup_lines=True, capture_locals=False): """Create a StackSummary from a traceback or stack object. Very simplified copy of the original StackSummary.extract(). We want always to capture locals, that is why we overwrite it. Additionally, we also capture the frame. This is a bit hacky and also not like this is originally intended (to not keep refs). :param frame_gen: A generator that yields (frame, lineno) tuples to include in the stack. :param limit: None to include all frames or the number of frames to include. :param lookup_lines: If True, lookup lines for each frame immediately, otherwise lookup is deferred until the frame is rendered. :param capture_locals: If True, the local variables from each frame will be captured as object representations into the FrameSummary. """ result = StackSummary() for f, lineno in frame_gen: co = f.f_code filename = co.co_filename name = co.co_name result.append(ExtendedFrameSummary( frame=f, filename=filename, lineno=lineno, name=name, lookup_line=False)) return result
def print_task_stack(task: asyncio.Task, *, file: IO = sys.stderr, limit: int = DEFAULT_MAX_FRAMES, capture_locals: bool = False) -> None: """Print the stack trace for an :class:`asyncio.Task`.""" print(f'Stack for {task!r} (most recent call last):', file=file) tb = Traceback.from_task(task, limit=limit) print_list( StackSummary.extract( cast(Generator, walk_tb(cast(TracebackType, tb))), limit=limit, capture_locals=capture_locals, ), file=file, )
def print_coro_stack(coro: Coroutine, *, file: IO = sys.stderr, limit: int = DEFAULT_MAX_FRAMES, capture_locals: bool = False) -> None: """Print the stack trace for a currently running coroutine.""" print(f'Stack for {coro!r} (most recent call last):', file=file) tb = Traceback.from_coroutine(coro, limit=limit) print_list( StackSummary.extract( cast(Generator, walk_tb(cast(TracebackType, tb))), limit=limit, capture_locals=capture_locals, ), file=file, )
def print_agen_stack(agen: AsyncGenerator, *, file: IO = sys.stderr, limit: int = DEFAULT_MAX_FRAMES, capture_locals: bool = False) -> None: """Print the stack trace for a currently running async generator.""" print(f'Stack for {agen!r} (most recent call last):', file=file) tb = Traceback.from_agen(agen, limit=limit) print_list( StackSummary.extract( cast(Generator, walk_tb(cast(TracebackType, tb))), limit=limit, capture_locals=capture_locals, ), file=file, )
def generate_move_process(generate_move: cu.GenMove, moves_q: mp.Queue): from traceback import StackSummary from random import seed as random_seed import io from time import time import pickle from contextlib import redirect_stderr, redirect_stdout logger = logging.getLogger(__name__) f_stderr, f_stdout = io.StringIO(), io.StringIO() gma: GenMoveArgs = moves_q.get() np.random.seed(gma.seed) random_seed(gma.seed) try: with redirect_stdout(f_stdout), redirect_stderr(f_stderr): t0 = time() returned = generate_move(gma.board, gma.player, gma.state) saved_state = None if isinstance(returned, tuple): action = returned[0] if len(returned) > 1: saved_state = returned[1] else: action = returned move_time = time() - t0 stdout, stderr = f_stdout.getvalue(), f_stderr.getvalue() result = GenMoveSuccess(stdout, stderr, move_time, action, saved_state) except Exception as e: logger.exception("An exception was thrown by the agent.") error_msg = repr(e) + "\n" extracted_list = traceback.extract_tb(e.__traceback__) for item in StackSummary.from_list(extracted_list).format(): error_msg += str(item) stdout, stderr = f_stdout.getvalue(), f_stderr.getvalue() result = GenMoveFailure(stdout, stderr, error_msg) try: moves_q.put(result) except pickle.PickleError: logger.exception( "Internal error in trying to send the result, probably caused by saved_state" ) moves_q.put(GenMoveSuccess(stdout, stderr, move_time, action, None))
def _exception_handler(loop, context): ''' Last resort exception handler This will only catch exceptions that happen in the main thread. Others very well may go entirely unnoticed and unlogged. Some exceptions are unexpected, so we end up here. For these we kill ourselves after logging about the exception. Other exceptions are impossible to catch before we get here. For example, a client failing the TLS handshake with us. (ugh what the f**k). For these we notify the state machine so it can react. ''' # Check for exceptions that should not be fatal and we should tell other # parts of the code about so they can react intelligently if 'exception' in context: exception_type = type(context['exception']) # Check for recoverable TLS errors if exception_type == ssl.SSLError: if 'transport' in context: machine.notif_sslerror(context['exception'], context['transport']) return else: log.warn( 'SSLError caught without a transport too. Cannot pass ' + 'to state machine to handle gracefully.') # Additional recoverable errors would continue here # All other exceptions. These are fatal log.error('%s', context['message']) if 'exception' in context: log.error('%s %s', type(context['exception']), context['exception']) if 'handle' in context: log.error(context['handle']) if 'source_traceback' in context: log.error('Traceback:') summary = StackSummary.from_list(context['source_traceback']) for line_super in summary.format(): # The above line has multiple lines in it for line in line_super.split('\n'): if len(line): log.error(' %s', line) else: log.error( 'Traceback not available. Maybe run with PYTHONASYNCIODEBUG=1') machine.change_state_fatal_error()
def _exception_handler(loop, context): log.error('%s', context['message']) if 'exception' in context: log.error(context['exception']) if 'handle' in context: log.error(context['handle']) if 'source_traceback' in context: log.error('Traceback:') summary = StackSummary.from_list(context['source_traceback']) for line_super in summary.format(): # The above line has multiple lines in it for line in line_super.split('\n'): if len(line): log.error(' %s', line) else: log.error('Traceback not available. Run with PYTHONASYNCIODEBUG=1') machine.change_state_fatal_error()
def format(self, record) -> str: # set for different levels format_orig = self._style._fmt self._style._fmt = self.msg_fmt[record.levelname] # preprocess \n line_count = 0 for s in record.msg: if s == '\n': line_count += 1 record.msg = record.msg[1:] else: break if record.msg: self._style._fmt = line_count * '\n' + self._style._fmt else: self._style._fmt = line_count * '\n' if record.levelno >= self.stack_level and record.levelno != logging.MAIL and record.msg[: 9] != 'Traceback': record.stack_info = ''.join( StackSummary.from_list( extract_stack() [:self._stack_prune]).format()) # self._stack_prune # make it colorful levelname = record.levelname if self.use_color and levelname in COLORS: levelname_color = COLOR_SEQ % ( 30 + COLORS[levelname]) + record.levelname + RESET_SEQ record.levelname = levelname_color self.datefmt = '%m/%d/%Y %I:%M:%S %p' result = logging.Formatter.format(self, record) self._style._fmt = format_orig record.levelname = levelname return result
def format_exc(name='mlchain', tb=None, exception=None): if exception is None: formatted_lines = traceback.format_exc().splitlines() else: formatted_lines = [] if tb is not None: for item in StackSummary.from_list(extract_tb(tb)).format(): str_item = str(item) if str_item.endswith("\n"): formatted_lines.append(str_item[:-1]) else: formatted_lines.append(str_item) formatted_lines += [ x for x in re.split('(\\\\n)|(\\n)', str(exception)) if x not in ["\\n", "\n", "", None] ] output = [] kt = True last_mlchain_append = -1 for x in formatted_lines: output.append(x) # if x.strip().startswith("File"): # if ('site-packages/mlchain' in x or 'site-packages/trio' in x) and not ("mlchain.base.exceptions" in x or "AssertionError" in x): # kt = False # else: # kt = True # if kt or 'AssertionError' in x or 'mlchain.base.exceptions' in x: # if x.startswith("'), AssertionError"): # output.append("\n" + x[4:]) # else: # output.append(x) # elif last_mlchain_append != len(output): # output.append(' File "{}" collapsed errors'.format(name)) # last_mlchain_append = len(output) return "\n".join(output) + "\n"
def format_exc(name='mlchain', tb=None, exception=None, return_str=True): if exception is None: formatted_lines = traceback.format_exc().splitlines() else: formatted_lines = [] if tb is not None: for item in StackSummary.from_list(extract_tb(tb)).format(): str_item = str(item) if str_item.endswith("\n"): formatted_lines.append(str_item[:-1]) else: formatted_lines.append(str_item) formatted_lines += [ x for x in re.split('(\\\\n)|(\\n)', str(exception)) if x not in ["\\n", "\n", "", None] ] output = [] for x in formatted_lines: output.append(x) if return_str: return "\n".join(output) return output
def analyze_frame(trace_back, full_context: int = False) -> str: """ Read out variables' content surrounding the error line of code :param trace_back: A traceback object when exception occur :param full_context: Also export local variables that is not in the error line full_context == 0: export only variables that appear on the error line full_context == 1: export variables within the error function's scope full_context >= 2: export variables along the function's call stack up to `full_context` level :return: string of analyzed frame """ result = [] full_context = max(0, int(full_context)) # todo: add color bullet_1 = '|->' bullet_2 = '=>' multi_line_indent1 = 8 + len(bullet_1) multi_line_indent2 = 8 + len(bullet_2) stack_depth = get_traceback_depth(trace_back) with logging_disabled(): for idx, obj in enumerate(walk_tb(trace_back)): frame, _ = obj global_var = frame.f_globals local_var = frame.f_locals summary = StackSummary.extract([obj], capture_locals=True)[0] line = summary.line if not is_full_statement(summary.line): line = get_full_statement(summary.filename, summary.lineno) line = ' '.join(line) txt = [ f' File "{summary.filename}", line {summary.lineno}, in {summary.name}', f' {line}' ] # todo: dump all level to different file? parse_level = max(full_context - 1, 0) if idx + 1 < (stack_depth - parse_level): # don't parse variables for top levels txt.append('') result.append('\n'.join(txt)) continue # get value of variables on the error line identifiers = ID_PATTERN.findall(line) seen = set() outer = "(outer) " if idx else "" # ground level variables are not outer for sure for i in identifiers: if i in seen or i.endswith('.'): continue seen.add(i) spaces = multi_line_indent1 + len(i) if i in local_var: value = get_repr(local_var[i], spaces) txt.append(f' {bullet_1} {i} = {value}') elif i in global_var: spaces += len(outer) value = get_repr(global_var[i], spaces) txt.append(f' {bullet_1} {outer}{i} = {value}') elif '.' in i: # class attribute access spaces += len(outer) instance = i.split('.')[0] obj = local_var.get(instance, global_var.get(instance)) attribute = get_recur_attr(obj, i[len(instance) + 1:]) value = get_repr(attribute, spaces) scope = outer if instance in global_var else '' txt.append(f' {bullet_1} {scope}{i} = {value}') else: # reserved Keyword or non-identifier, eg. word inside the string pass # get value of other variables within local scope if full_context: other_local_var = set(local_var) - set(identifiers) if other_local_var: spaces = multi_line_indent2 txt.extend([ f' {bullet_2} {k} = {get_repr(v, spaces + len(k))}' for k, v in local_var.items() if k in other_local_var ]) txt.append('') result.append('\n'.join(txt)) return '\n'.join(result)
def dump_stack(frames): stack = StackSummary.from_list(extract_stack(frames)) return [{"filename": f.filename, "lineno": f.lineno, "name": f.name, "line": f.line, "locals": {name: repr(value) for name, value in sorted(f.locals.items())} if f.locals else None} for f in stack]
from contextlib import suppress # External from async_tools import Loopable from async_tools.abstract import Loopable as AbstractLoopable # Project from ._helper import reject, fulfill, resolve # Generic types K = T.TypeVar("K") L = T.TypeVar("L") # Fake stacktrace information for use when no stack can be recovered from promise _FAKE_STACK = list( StackSummary.from_list([("unknown", 0, "unknown", "invalid")])) class ChainLink(T.Awaitable[K], Loopable): @staticmethod def log_unhandled_exception(promise: "ChainLink[T.Any]", fut: "Future[T.Any]") -> None: assert fut.done() get_loop: T.Callable[[], AbstractEventLoop] = getattr( fut, "get_loop", None) loop: AbstractEventLoop = get_loop() if callable( get_loop) else getattr(fut, "_loop", None) assert loop is not None
def extract_log_tb(exc=None): tb = ''.join(StackSummary.extract(walk_tb(sys.exc_info()[-1])).format()) if exc.__cause__ is not None and isinstance(exc.__cause__, Traceback): tb = exc.__cause__.tb + tb return tb
def handle_exceptions(err: Exception): msg = '\n'.join(['', ''.join(StackSummary.from_list(extract_tb(err.__traceback__)).format()), repr(err)]) current_app.logger.error(msg) return jsonify(code=500, error=repr(err), data=None), 200
def format_tb(tb=None, limit=None, allLocals=None, allGlobals=None, withTitle=False, with_color=None, with_vars=None): """ :param types.TracebackType|types.FrameType|StackSummary tb: traceback. if None, will use sys._getframe :param int|None limit: limit the traceback to this number of frames. by default, will look at sys.tracebacklimit :param dict[str]|None allLocals: if set, will update it with all locals from all frames :param dict[str]|None allGlobals: if set, will update it with all globals from all frames :param bool withTitle: :param bool|None with_color: output with ANSI escape codes for color :param bool with_vars: will print var content which are referenced in the source code line. by default enabled. :return: list of strings (line-based) :rtype: list[str] """ if with_vars is None and is_at_exit(): # Better to not show __repr__ of some vars, as this might lead to crashes # when native extensions are involved. with_vars = False if with_vars is None: if any([f.f_code.co_name == "__del__" for f in iter_traceback()]): # __del__ is usually called via the Python garbage collector (GC). # This can happen and very random / non-deterministic places. # There are cases where it is not safe to access some of the vars on the stack # because they might be in a non-well-defined state, thus calling their __repr__ is not safe. # See e.g. this bug: # https://github.com/tensorflow/tensorflow/issues/22770 with_vars = False if with_vars is None: with_vars = True color = Color(enable=with_color) out = [] def output(s1, s2=None, **kwargs): if kwargs: s1 = color(s1, **kwargs) if s2 is not None: s1 = add_indent_lines(s1, s2) out.append(s1 + "\n") def format_filename(s): base = os.path.basename(s) return ( color('"' + s[:-len(base)], "cyan") + color(base, "cyan", bold=True) + color('"', "cyan")) def format_py_obj(obj): return color.py_syntax_highlight(pretty_print(obj)) if tb is None: try: tb = get_current_frame() assert tb except Exception: output(color("format_tb: tb is None and sys._getframe() failed", "red", bold=True)) return out def isstacksummary(_tb): return isinstance(_tb, StackSummary) isframe = inspect.isframe if withTitle: if isframe(tb) or isstacksummary(tb): output(color('Traceback (most recent call first):', "blue")) else: # expect traceback-object (or compatible) output(color('Traceback (most recent call last):', "blue")) try: if limit is None: if hasattr(sys, 'tracebacklimit'): limit = sys.tracebacklimit n = 0 _tb = tb class NotFound(Exception): pass def _resolve_identifier(namespace, id): if id[0] not in namespace: raise NotFound() obj = namespace[id[0]] for part in id[1:]: obj = getattr(obj, part) return obj def _try_set(old, prefix, func): if old is not None: return old try: return add_indent_lines(prefix, func()) except NotFound: return old except Exception as e: return prefix + "!" + e.__class__.__name__ + ": " + str(e) while _tb is not None and (limit is None or n < limit): if isframe(_tb): f = _tb elif isstacksummary(_tb): if isinstance(_tb[0], ExtendedFrameSummary): f = _tb[0].tb_frame else: f = DummyFrame.from_frame_summary(_tb[0]) else: f = _tb.tb_frame if allLocals is not None: allLocals.update(f.f_locals) if allGlobals is not None: allGlobals.update(f.f_globals) if hasattr(_tb, "tb_lineno"): lineno = _tb.tb_lineno elif isstacksummary(_tb): lineno = _tb[0].lineno else: lineno = f.f_lineno co = f.f_code filename = co.co_filename name = co.co_name output("".join([ ' ', color("File ", "blue", bold=True), format_filename(filename), ", ", color("line ", "blue"), color("%d" % lineno, "magenta"), ", ", color("in ", "blue"), name])) if not os.path.isfile(filename): altfn = fallback_findfile(filename) if altfn: output(color(" -- couldn't find file, trying this instead: ", "blue") + format_filename(altfn)) filename = altfn source_code = get_source_code(filename, lineno, f.f_globals) if source_code: source_code = remove_indent_lines(replace_tab_indents(source_code)).rstrip() output(" line: ", color.py_syntax_highlight(source_code), color="blue") if not with_vars: pass elif isinstance(f, DummyFrame) and not f.have_vars_available: pass else: output(color(' locals:', "blue")) alreadyPrintedLocals = set() for tokenstr in grep_full_py_identifiers(parse_py_statement(source_code)): splittedtoken = tuple(tokenstr.split(".")) for token in [splittedtoken[0:i] for i in range(1, len(splittedtoken) + 1)]: if token in alreadyPrintedLocals: continue tokenvalue = None tokenvalue = _try_set(tokenvalue, color("<local> ", "blue"), lambda: format_py_obj(_resolve_identifier(f.f_locals, token))) tokenvalue = _try_set(tokenvalue, color("<global> ", "blue"), lambda: format_py_obj(_resolve_identifier(f.f_globals, token))) tokenvalue = _try_set(tokenvalue, color("<builtin> ", "blue"), lambda: format_py_obj(_resolve_identifier(f.f_builtins, token))) tokenvalue = tokenvalue or color("<not found>", "blue") prefix = ' %s ' % color(".", "blue", bold=True).join(token) + color("= ", "blue", bold=True) output(prefix, tokenvalue) alreadyPrintedLocals.add(token) if len(alreadyPrintedLocals) == 0: output(color(" no locals", "blue")) else: output(color(' -- code not available --', "blue")) if isframe(_tb): _tb = _tb.f_back elif isstacksummary(_tb): _tb = StackSummary.from_list(_tb[1:]) if not _tb: _tb = None else: _tb = _tb.tb_next n += 1 except Exception as e: output(color("ERROR: cannot get more detailed exception info because:", "red", bold=True)) import traceback for l in traceback.format_exc().split("\n"): output(" " + l) return out