Simple Tracing in PythonCustom tracing for debugging in Python |
Date | ||
---|---|---|---|
Author | @asyncze |
In python, print()
is the quick–and-dirty always-available debug tool that everyone uses but no one is talking about. A few thousand lines and a couple of weeks or months into a project and there's very likely print statements nested into just about everything (most which needs to be removed before releasing the software).
I do this a lot, but have lately started to do something similar but more informative than a simple print()
.
import inspect
def trace(*args):
frame = inspect.currentframe()
caller_frame = frame.f_back
instance = caller_frame.f_locals.get('self', None)
class_name = instance.__class__.__name__ if instance else "Global"
instance_id = id(instance) if instance else 0
function_name = caller_frame.f_code.co_name
# _, _, _, values = inspect.getargvalues(caller_frame)
# for arg in args: print(f"{ arg } = { values[arg] }")
print(f"{ class_name } @ { instance_id } : { function_name } : { args[0:] }")
del frame
del caller_frame
So, the inspect
module is a great way to look into what's going on under the hood of Python programs, but also the automate some parts of tracing. In the above function, trace()
, we're getting class name, instance id (in case of multiple instances of same class), function name, and any arguments passed to function. This is all from just placing a single call to trace()
.
And that's it. Just include a call to trace(arguments)
, with whatever arguments interested in from anywhere in the source code.
trace(event)
All traces can later be disabled by replacing function with pass
or just adding debug toggle to trace function, or avoiding the call completely by adding debug toggle to line of call.
def trace(*args): pass
if DEBUG: trace(event)
Here's an example trace from another project:
class CodeEditor(QsciScintilla):
...
def keyPressEvent(self, event):
if DEBUG: trace(f"modifiers : { int(event.modifiers()) }", f"key : { event.key() }")
...
CodeEditor @ 4335861440 : keyPressEvent : ('modifiers : 67108864', 'key : 16777249')
This is a trace originating in the keyPressEvent
function with parts of event
in arguments (showing which keys are pressed) from class CodeEditor
with id 4335861440
.
Here's another:
def load_config():
...
if DEBUG: trace(user_config_path)
...
Global @ 0 : load_config : ('/Users/michael/Library/Application Support/Hackerman Text/.hackerman',)
This is a trace from Global
, as seen in class_name = instance.__class__.__name__ if instance else "Global"
, which is functions outside classes, so global functions.