Skip to content

Instantly share code, notes, and snippets.

@thatrand0mnpc
Last active August 1, 2022 21:22
Show Gist options
  • Save thatrand0mnpc/3ad7d01b803fc490dfed1766d2400dad to your computer and use it in GitHub Desktop.
Save thatrand0mnpc/3ad7d01b803fc490dfed1766d2400dad 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, Dict, List, 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() -> None:
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()
@jftuga
Copy link

jftuga commented Jul 31, 2022

This is very cool!

Note that it works with Python 3.10, but not 3.8. I did not test with 3.9.

@thatrand0mnpc
Copy link
Author

This is very cool!

Note that it works with Python 3.10, but not 3.8. I did not test with 3.9.

Hey thanks!! I changed the types so that it is compatible for python 3.6+

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment