returnn.util.better_exchook

https://github.com/albertz/py_better_exchook

This is a simple replacement for the standard Python exception handler (sys.excepthook). In addition to what the standard handler does, it also prints all referenced variables (no matter if local, global or builtin) of the code line of each stack frame. See below for some examples and some example output.

See these functions:

  • better_exchook

  • format_tb / print_tb

  • iter_traceback

  • get_current_frame

  • dump_all_thread_tracebacks

  • install

  • replace_traceback_format_tb

Although there might be a few more useful functions, thus we export all of them.

Also see the demo/tests at the end.

returnn.util.better_exchook.parse_py_statement(line)[source]

Parse Python statement into tokens. Note that this is incomplete. It should be simple and fast and just barely enough for what we need here.

Reference: https://docs.python.org/3/reference/lexical_analysis.html

Parameters:

line (str)

Returns:

yields (type, value)

Return type:

Iterator[Tuple[str,str]]

returnn.util.better_exchook.parse_py_statements(source_code)[source]
Parameters:

source_code (str)

Returns:

via parse_py_statement()

Return type:

Iterator[Tuple[str,str]]

returnn.util.better_exchook.grep_full_py_identifiers(tokens)[source]
Parameters:

tokens (Iterable[(str,str)])

Return type:

Iterator[str]

returnn.util.better_exchook.set_linecache(filename, source)[source]

The linecache module has some cache of the source code for the current source. Sometimes it fails to find the source of some files. We can explicitly set the source for some filename.

Parameters:
  • filename (str)

  • source (str)

Returns:

nothing

returnn.util.better_exchook.simple_debug_shell(globals, locals)[source]
Parameters:
  • globals (dict[str])

  • locals (dict[str])

Returns:

nothing

returnn.util.better_exchook.debug_shell(user_ns, user_global_ns, traceback=None, execWrapper=None)[source]

Spawns some interactive shell. Tries to use IPython if available. Falls back to pdb.post_mortem() or simple_debug_shell().

Parameters:
  • user_ns (dict[str])

  • user_global_ns (dict[str])

  • traceback

  • execWrapper

Returns:

nothing

returnn.util.better_exchook.output_limit()[source]
Returns:

num chars

Return type:

int

returnn.util.better_exchook.fallback_findfile(filename)[source]
Parameters:

filename (str)

Returns:

try to find the full filename, e.g. in modules, etc

Return type:

str|None

returnn.util.better_exchook.is_source_code_missing_brackets(source_code, prioritize_missing_open=False)[source]

We check whether this source code snippet (e.g. one line) is complete/even w.r.t. opening/closing brackets.

Parameters:
  • source_code (str)

  • prioritize_missing_open (bool) – once we found any missing open bracket, directly return -1

Returns:

1 if missing_close, -1 if missing_open, 0 otherwise. I.e. whether there are missing open/close brackets. E.g. this would mean that you might want to include the prev/next source code line as well in the stack trace.

Return type:

int

returnn.util.better_exchook.is_source_code_missing_open_brackets(source_code)[source]

We check whether this source code snippet (e.g. one line) is complete/even w.r.t. opening/closing brackets.

Parameters:

source_code (str)

Returns:

whether there are missing open brackets. E.g. this would mean that you might want to include the previous source code line as well in the stack trace.

Return type:

bool

returnn.util.better_exchook.get_source_code(filename, lineno, module_globals=None)[source]
Parameters:
  • filename (str)

  • lineno (int)

  • module_globals (dict[str]|None)

Returns:

source code of that line (including newline)

Return type:

str

returnn.util.better_exchook.str_visible_len(s)[source]
Parameters:

s (str)

Returns:

len without escape chars

Return type:

int

returnn.util.better_exchook.add_indent_lines(prefix, s)[source]
Parameters:
  • prefix (str)

  • s (str)

Returns:

s with prefix indent added to all lines

Return type:

str

returnn.util.better_exchook.get_indent_prefix(s)[source]
Parameters:

s (str)

Returns:

the indent spaces of s

Return type:

str

returnn.util.better_exchook.get_same_indent_prefix(lines)[source]
Parameters:

lines (list[])

Return type:

str|None

returnn.util.better_exchook.remove_indent_lines(s)[source]
Parameters:

s (str)

Returns:

remove as much indentation as possible

Return type:

str

returnn.util.better_exchook.replace_tab_indent(s, replace='    ')[source]
Parameters:
  • s (str) – string with tabs

  • replace (str) – e.g. 4 spaces

Return type:

str

returnn.util.better_exchook.replace_tab_indents(s, replace='    ')[source]
Parameters:
  • s (str) – multi-line string with tabs

  • replace (str) – e.g. 4 spaces

Return type:

str

returnn.util.better_exchook.to_bool(s, fallback=None)[source]
Parameters:
  • s (str) – str to be converted to bool, e.g. “1”, “0”, “true”, “false”

  • fallback (T) – if s is not recognized as a bool

Returns:

boolean value, or fallback

Return type:

bool|T

class returnn.util.better_exchook.Color(enable=None)[source]

Helper functions provided to perform terminal coloring.

Parameters:

enable (bool|None)

ColorIdxTable = {'black': 0, 'blue': 4, 'cyan': 6, 'green': 2, 'magenta': 5, 'red': 1, 'white': 7, 'yellow': 3}[source]
classmethod get_global_color_enabled()[source]
Return type:

bool

classmethod is_dark_terminal_background()[source]
Returns:

Whether we have a dark Terminal background color, or None if unknown. We currently just check the env var COLORFGBG, which some terminals define like “<foreground-color>:<background-color>”, and if <background-color> in {0,1,2,3,4,5,6,8}, then we have some dark background. There are many other complex heuristics we could do here, which work in some cases but not in others. See e.g. here. But instead of adding more heuristics, we think that explicitly setting COLORFGBG would be the best thing, in case it’s not like you want it.

Return type:

bool|None

color(s, color=None, bold=False)[source]
Parameters:
  • s (str)

  • color (str|None) – sth in self.ColorIdxTable

  • bold (bool)

Returns:

s optionally wrapped with ansi escape codes

Return type:

str

py_syntax_highlight(s)[source]
Parameters:

s (str)

Return type:

str

class returnn.util.better_exchook.DomTerm[source]

DomTerm (https://github.com/PerBothner/DomTerm/) is a terminal emulator with many extended escape codes, such as folding text away, or even generic HTML. We can make use of some of these features (currently just folding text).

classmethod is_domterm()[source]
Returns:

whether we are inside DomTerm

Return type:

bool

logical_block(file=<_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>)[source]
Parameters:

file (io.TextIOBase|io.StringIO)

hide_button_span(mode, file=<_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>)[source]
Parameters:
  • mode (int) – 1 or 2

  • file (io.TextIOBase|io.StringIO)

indentation(file=<_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>)[source]
Parameters:

file (io.TextIOBase|io.StringIO)

hide_button(file=<_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>)[source]
Parameters:

file (io.TextIOBase|io.StringIO)

fold_text_stream(prefix, postfix='', hidden_stream=None, **kwargs)[source]
Parameters:
  • prefix (str) – always visible

  • postfix (str) – always visible, right after.

  • hidden_stream (io.TextIOBase|io.StringIO) – sys.stdout by default. If this is sys.stdout, it will replace that stream, and collect the data during the context (in the with block).

fold_text(prefix, hidden, postfix='', file=None, align=0)[source]
Parameters:
  • prefix (str) – always visible

  • hidden (str) – hidden If this is sys.stdout, it will replace that stream, and collect the data during the context (in the with block).

  • postfix (str) – always visible, right after. “” by default.

  • file (io.TextIOBase|io.StringIO) – sys.stdout by default.

  • align (int) – remove this number of initial chars from hidden

fold_text_string(prefix, hidden, **kwargs)[source]
Parameters:
  • prefix (str)

  • hidden (str)

  • kwargs – passed to fold_text()

Return type:

str

returnn.util.better_exchook.is_at_exit()[source]

Some heuristics to figure out whether this is called at a stage where the Python interpreter is shutting down.

Returns:

whether the Python interpreter is currently in the process of shutting down

Return type:

bool

returnn.util.better_exchook.format_tb(tb=None, limit=None, allLocals=None, allGlobals=None, withTitle=False, with_color=None, with_vars=None)[source]
Parameters:
  • tb (types.TracebackType|types.FrameType|StackSummary) – traceback. if None, will use sys._getframe

  • limit (int|None) – limit the traceback to this number of frames. by default, will look at sys.tracebacklimit

  • allLocals (dict[str]|None) – if set, will update it with all locals from all frames

  • allGlobals (dict[str]|None) – if set, will update it with all globals from all frames

  • withTitle (bool)

  • with_color (bool|None) – output with ANSI escape codes for color

  • with_vars (bool) – will print var content which are referenced in the source code line. by default enabled.

Returns:

list of strings (line-based)

Return type:

list[str]

returnn.util.better_exchook.print_tb(tb, file=None, **kwargs)[source]
Parameters:
  • tb (types.TracebackType|types.FrameType|StackSummary)

  • file (io.TextIOBase|io.StringIO|TextIO|None) – stderr by default

Returns:

nothing, prints to file

returnn.util.better_exchook.better_exchook(etype, value, tb, debugshell=False, autodebugshell=True, file=None, with_color=None, with_preamble=True)[source]

Replacement for sys.excepthook.

Parameters:
  • etype – exception type

  • value – exception value

  • tb – traceback

  • debugshell (bool) – spawn a debug shell at the context of the exception

  • autodebugshell (bool) – if env DEBUG is an integer != 0, it will spawn a debug shell

  • file (io.TextIOBase|io.StringIO|TextIO|None) – output stream where we will print the traceback and exception information. stderr by default.

  • with_color (bool|None) – whether to use ANSI escape codes for colored output

  • with_preamble (bool) – print a short preamble for the exception

returnn.util.better_exchook.dump_all_thread_tracebacks(exclude_thread_ids=None, file=None)[source]

Prints the traceback of all threads.

Parameters:
  • exclude_thread_ids (set[int]|list[int]|None) – threads to exclude

  • file (io.TextIOBase|io.StringIO|TextIO|None) – output stream

returnn.util.better_exchook.get_current_frame()[source]
Returns:

current frame object (excluding this function call)

Return type:

types.FrameType

Uses sys._getframe if available, otherwise some trickery with sys.exc_info and a dummy exception.

returnn.util.better_exchook.get_func_str_from_code_object(co, frame=None)[source]
Parameters:
  • co (types.CodeType)

  • frame (types.FrameType|DummyFrame|None) – if given, might provide a faster way to get the function name

Returns:

co.co_name as fallback, but maybe sth better like the full func name if possible

Return type:

str

returnn.util.better_exchook.get_func_from_code_object(co, frame=None)[source]
Parameters:
  • co (types.CodeType)

  • frame (types.FrameType|DummyFrame|None) – if given, might provide a faster way to get the function name

Returns:

function, such that func.__code__ is co, or None

Return type:

types.FunctionType

This is CPython specific (to some degree; it uses the gc module to find references). Inspired from: https://stackoverflow.com/questions/12787108/getting-the-python-function-for-a-code-object https://stackoverflow.com/questions/54656758/get-function-object-from-stack-frame-object

returnn.util.better_exchook.iter_traceback(tb=None, enforce_most_recent_call_first=False)[source]
Iterates a traceback of various formats:
  • traceback (types.TracebackType)

  • frame object (types.FrameType)

  • stack summary (traceback.StackSummary)

Parameters:
  • tb (types.TracebackType|types.FrameType|StackSummary|None) – traceback. if None, will use sys._getframe

  • enforce_most_recent_call_first (bool) – 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.

Returns:

yields the frames (types.FrameType)

Return type:

list[types.FrameType|DummyFrame]

class returnn.util.better_exchook.ExtendedFrameSummary(frame, **kwargs)[source]

Extends FrameSummary by self.tb_frame.

Construct a FrameSummary.

Parameters:
  • lookup_line – If True, linecache is consulted for the source code line. Otherwise, the line will be looked up when first needed.

  • locals – If supplied the frame locals, which will be captured as object representations.

  • line – If provided, use this instead of looking up the line in the linecache.

filename[source]
lineno[source]
name[source]
locals[source]
class returnn.util.better_exchook.DummyFrame(filename, lineno, name, f_locals=None, f_globals=None, f_builtins=None)[source]

This class has the same attributes as a code and a frame object and is intended to be used as a dummy replacement.

classmethod from_frame_summary(f)[source]
Parameters:

f (FrameSummary)

Return type:

DummyFrame

clear()[source]
returnn.util.better_exchook.install()[source]

Replaces sys.excepthook by our better_exchook.

returnn.util.better_exchook.replace_traceback_format_tb()[source]

Replaces these functions from the traceback module by our own:

  • traceback.format_tb

  • traceback.StackSummary.format

  • traceback.StackSummary.extract

Note that this kind of monkey patching might not be safe under all circumstances and is not officially supported by Python.