How to print function arguments in sys.settrace?
You can use the combination of Code Objects and Frame Objects.
See for the descriptions of these in the Python Data-Model Reference.
import sys
def fn(frame, msg, arg):
if msg != 'call': return
# Filter as appropriate
if frame.f_code.co_filename.startswith("/usr"): return
print("Called", frame.f_code.co_name)
for i in range(frame.f_code.co_argcount):
name = frame.f_code.co_varnames[i]
print(" Argument", name, "is", frame.f_locals[name])
sys.settrace(fn)
def hai(a, b, c):
print(a, b, c)
hai("Hallo", "Welt", "!")
The crucial thing to realize is that
- we can see all local variables in the frame as
f_locals
. - We can extract the names of the variables in the parameter list from
f_code.co_varnames
.
I turned Marcs answer into a script which can be used for inspecting other scripts:
print_func_calls.py:
#!/usr/bin/env python
import sys
# opt-out file names which start with one of these prefixes
FILENAME_FILTER = {"/usr", "<"}
# opt-in file names again which match one of these prefixes
FILENAME_UNFILTER = {"/lib/python/some-important-module"}
# opt-out function names
FN_NAME_FILTER = {"<module>", "__new__", "__setattr__", "<lambda>"}
def to_str(exp):
"""Turn an argument value into a string without dying on exceptions"""
try:
return repr(exp)[:100]
except Exception as exc:
return "n/a"
def fn(frame, msg, arg):
if msg != 'call':
return
filename, fn_name = frame.f_code.co_filename, frame.f_code.co_name
if (not all(not filename.startswith(p) for p in FILENAME_FILTER) and
all(not filename.startswith(p) for p in FILENAME_UNFILTER) or
fn_name in FN_NAME_FILTER):
return
argstr = ", ".join("%s=%s" % (
frame.f_code.co_varnames[i], to_str(frame.f_locals[frame.f_code.co_varnames[i]]))
for i in range(frame.f_code.co_argcount))
print(">>> %s::\033[37m%s\033[0m(%s)" % (filename, fn_name, argstr))
sys.settrace(fn)
sys.argv = sys.argv[1:]
exec(open(sys.argv[0]).read())
Use it like this:
print_func_calls.py my-script.py arg1..argN