Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jftuga/52a61d189adfcd2db72d61cd2ff07548 to your computer and use it in GitHub Desktop.
Save jftuga/52a61d189adfcd2db72d61cd2ff07548 to your computer and use it in GitHub Desktop.
Python custom traceback
import linecache
import re
import sys
from types import TracebackType
from typing import Any, Optional
PACKAGE_PATH_PATTERN = r'.*/lib/python.*/site-packages/.*'
class TracebackLogger:
@staticmethod
def _print_variables(variables: dict[str, Any]) -> None:
"""Prints scoped variables contents without dunder keys."""
for k, v in variables.items():
if not (k.startswith('__') and k.endswith('__')):
print(k, ':', v)
@staticmethod
def _print_exception(
exception_type: Optional[type],
exception_value: Optional[BaseException],
traceback: Optional[TracebackType]
) -> None:
"""Format and print exceptions."""
local_vars = {}
global_vars = {}
function_name_local = ''
if exception_type is not None:
print('\n--- exception ----\n')
print(f'{exception_type.__name__}: {exception_value}')
else:
print(f'{exception_type}: {exception_value}')
print('\n--- traceback ----\n')
while traceback:
filename = traceback.tb_frame.f_code.co_filename
function_name = traceback.tb_frame.f_code.co_name
line_no = traceback.tb_lineno
line = linecache.getline(filename, line_no).strip()
print(f'File: "{filename}", line {line_no}, in {function_name}')
print('\t', line)
if not re.match(PACKAGE_PATH_PATTERN, filename):
function_name_local = function_name
local_vars = traceback.tb_frame.f_locals
global_vars = traceback.tb_frame.f_globals
traceback = traceback.tb_next
print(f'\n--- local variables for {function_name_local} ----\n')
TracebackLogger._print_variables(local_vars)
print(f'\n--- global varables for {function_name_local} ----\n')
TracebackLogger._print_variables(global_vars)
@staticmethod
def traceback_hook(
exception_type: Optional[type],
exception_value: Optional[BaseException],
traceback: Optional[TracebackType]
) -> None:
"""Replacement for system exception hook."""
if traceback:
TracebackLogger._print_exception(
exception_type, exception_value, traceback)
@staticmethod
def init_custom_traceback():
sys.excepthook = TracebackLogger.traceback_hook
# exception code ends here
def failing_function(data: list[Any], num: int) -> None:
"""Failing function."""
local_var = 100 # <-- locals
for n in range(num):
data[n]
def main() -> None:
failing_function(DATA, NUM)
if __name__ == '__main__':
TracebackLogger.init_custom_traceback() # <-- initialize hook
# globals
NUM = 12
DATA = list(range(5))
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment