Python 3.5 introduced the traceback.StackSummary.extract
constructor with the awesome capture_locals
option so that format
and the underlying format_frame_summary
methods would include values of all local variables at each frame in the output, immensely useful for a DEBUG
-level logging output as demonstrated here:
However, glaringly missing from the output are the attribute values of objects that don’t have a meaningful/detailed enough __repr__
defined. Something like b = <__main__.Foo object at 0x152cfce8dcd0>
just isn’t very helpful.
Currently my workaround is to patch repr
in a closure that specifies types that need better details:
import sys
import traceback
from unittest.mock import patch
def dump_stack(detailed_types=(), file=sys.stdout):
def detailed_repr(obj, _repr=repr):
def repr_attributes(obj, indent_level=2):
if isinstance(obj, detailed_types):
seen.add(id(obj))
indent = ' ' * indent_level
for name, value in sorted(vars(obj).items()):
yield f'{indent}.{name} = {value}'
if id(value) not in seen:
yield from repr_attributes(value, indent_level + 1)
seen = set()
return '\n'.join([_repr(obj), *repr_attributes(obj)])
with patch('builtins.repr', detailed_repr):
print(
*traceback.StackSummary.extract(
traceback.walk_stack(sys._getframe(1)), capture_locals=True
).format(), sep='\n', file=file
)
so that:
class Foo:
def __init__(self, x, y):
self.bar = Bar(x, y)
self.foo = self
class Bar:
def __init__(self, x, y):
self.x = x
self.y = y
def foo(c):
d = c + 1
dump_stack(detailed_types=(Foo, Bar))
a = 1
b = Foo(2, 3)
foo(a)
would output something like (note the output of attribute values of b
):
File "./prog.py", line 37, in foo
c = 1
d = 2
File "./prog.py", line 41, in <module>
Bar = <class '__main__.Bar'>
Foo = <class '__main__.Foo'>
__annotations__ = {}
__builtins__ = <module 'builtins' (built-in)>
__cached__ = None
__doc__ = None
__file__ = '/home/wT8L4N/./prog'
__loader__ = <_frozen_importlib_external.SourcelessFileLoader object at 0x1546bffd5c10>
__name__ = '__main__'
__package__ = None
__spec__ = None
a = 1
b = <__main__.Foo object at 0x1546bfba8cd0>
.bar = <__main__.Bar object at 0x1546bfba8550>
.x = 2
.y = 3
.foo = <__main__.Foo object at 0x1546bfba8cd0>
dump_stack = <function dump_stack at 0x1546bfab7160>
foo = <function foo at 0x1546bf9c5a60>
patch = <function patch at 0x1546bf31dd30>
sys = <module 'sys' (built-in)>
traceback = <module 'traceback' from '/usr/lib/python3.9/traceback.py'>
Demo: uGGI44 - Online Python3 Interpreter & Debugging Tool - Ideone.com
But it surely would be helpful to almost everyone to have this capability built-in as an optional detailed_types
argument to StackSummary.format
, StackSummary.format_frame_summary
and TracebackException.format
, wouldn’t it?