Source code for wolframclient.language.traceback

# -*- coding: utf-8 -*-

from __future__ import absolute_import, print_function, unicode_literals

import re

from wolframclient.language import wl
from wolframclient.utils import six
from wolframclient.utils.decorators import to_tuple
from wolframclient.utils.encoding import force_text, safe_force_text
from wolframclient.utils.functional import iterate


[docs]def serialize_traceback(exc_type, exc_value, tb, **opts): return wl.OpenerView([ wl.Row([ safe_force_text(exc_type.__name__), " ", safe_force_text(exc_value) ]), wl.Style( wl.Column(_serialize_traceback(exc_type, exc_value, tb, **opts)), FontFamily="Courier") ], True)
def _serialize_traceback(exc_type, exc_value, tb, **opts): frames = _get_traceback_frames(tb, exc_value, **opts) for i, frame in enumerate(frames): for sub in _serialize_frames( is_opened=i + 1 > len(frames) - 2, **frame): yield sub def _serialize_variables(variables): hidden = variables.get('__traceback_hidden_variables__', ()) if hidden is True: return if not isinstance(hidden, (tuple, list)): hidden = () variables = tuple((safe_force_text(key), safe_force_text(value)) for key, value in variables.items() if not key in hidden) if variables: yield wl.OpenerView([ "Local variables", wl.Grid( iterate( (("Key", "Value"), ), variables, ), Background=[None, [wl.LightGray]], Alignment=wl.Left, Frame=wl.LightGray) ]) else: yield "No local variables" def _paginate(i, line): return '%s. %s' % (force_text(i).rjust(4), line) def _serialize_frames(filename, function, pre_context, post_context, context_line, variables, lineno, pre_context_lineno, is_opened=False, **opts): if filename: description = wl.Row([ wl.Button( wl.Style( filename, wl.RGBColor(0.25, 0.48, 1), wl.Small, FontFamily="Courier"), wl.If(wl.FileExistsQ(filename), wl.SystemOpen(filename)), Appearance="Frameless"), ' in ', function ]) else: description = function yield wl.OpenerView([ description, wl.Column( iterate((wl.Column( iterate( (_paginate(pre_context_lineno + i, l) for i, l in enumerate(pre_context)), [ wl.Item( _paginate(lineno, context_line), Background=wl.LightYellow) ], (_paginate(lineno + i + 1, l) for i, l in enumerate(post_context)), ), Background=[[wl.GrayLevel(0.95), wl.GrayLevel(1)]], Frame=wl.LightGray), ), _serialize_variables(variables))) ], is_opened) @to_tuple def _get_traceback_frames(traceback, exc_value, context_lines=7): def explicit_or_implicit_cause(exc_value): explicit = getattr(exc_value, '__cause__', None) implicit = getattr(exc_value, '__context__', None) return explicit or implicit # Get the exception and all its causes exceptions = [] while exc_value: exceptions.append(exc_value) exc_value = explicit_or_implicit_cause(exc_value) # No exceptions were supplied to ExceptionReporter if exceptions: # In case there's just one exception, take the traceback from self.tb exc_value = exceptions.pop() tb = traceback if not exceptions else exc_value.__traceback__ while tb is not None: # Support for __traceback_hide__ which is used by a few libraries # to hide internal frames. if tb.tb_frame.f_locals.get('__traceback_hide__'): tb = tb.tb_next continue filename = tb.tb_frame.f_code.co_filename function = tb.tb_frame.f_code.co_name lineno = tb.tb_lineno - 1 loader = tb.tb_frame.f_locals.get( '__loader__') or tb.tb_frame.f_globals.get('__loader__') module_name = tb.tb_frame.f_globals.get('__name__') or '' pre_context_lineno, pre_context, context_line, post_context = _get_lines_from_file( filename, lineno, context_lines, loader, module_name, ) if pre_context_lineno is None: pre_context_lineno = lineno pre_context = [] context_line = '<source code not available>' post_context = [] yield { 'filename': filename and force_text(filename) or None, 'function': function and force_text(function) or None, 'lineno': lineno + 1, 'variables': tb.tb_frame.f_locals, 'pre_context': pre_context, 'context_line': context_line, 'post_context': post_context, 'pre_context_lineno': pre_context_lineno + 1, } # If the traceback for current exception is consumed, try the # other exception. if not tb.tb_next and exceptions: exc_value = exceptions.pop() tb = exc_value.__traceback__ else: tb = tb.tb_next def _get_lines_from_file(filename, lineno, context_lines, loader=None, module_name=None): """ Return context_lines before and after lineno from file. Return (pre_context_lineno, pre_context, context_line, post_context). """ source = None if loader is not None and hasattr(loader, "get_source"): try: source = loader.get_source(module_name) except ImportError: pass if source is not None: source = source.splitlines() if source is None: try: with open(filename, 'rb') as fp: source = fp.read().splitlines() except (OSError, IOError): pass if source is None: return None, [], None, [] # If we just read the source from a file, or if the loader did not # apply tokenize.detect_encoding to decode the source into a # string, then we should do that ourselves. if isinstance(source[0], six.binary_type): encoding = 'ascii' for line in source[:2]: # File coding may be specified. Match pattern from PEP-263 # (http://www.python.org/dev/peps/pep-0263/) match = re.search(br'coding[:=]\s*([-\w.]+)', line) if match: encoding = match.group(1).decode('ascii') break source = [force_text(sline, encoding, 'replace') for sline in source] lower_bound = max(0, lineno - context_lines) upper_bound = lineno + context_lines pre_context = source[lower_bound:lineno] context_line = source[lineno] post_context = source[lineno + 1:upper_bound] return lower_bound, pre_context, context_line, post_context