I want to make a debug_print()
which would output the callers variable and value, and later on I would extend this to only partially print lists and dicts and so on. This post focuses only on the first part of printing callers variables and values.
This post has the following parts:
- Current version of
debug_print
- Constructed test cases
- Output of test cases
- My wanted output, and problem areas
- List of somewhat related questions
Sorry, for a somewhat lengthy post, but I just want to show I've done some research, and really would like some help to get some assistance on how to solve my issues (as listed in second to last section).
Current version of debug_print
import inspect
def debug_print(*args):
try: # find code_context
# First try to use currentframe() (maybe not available in all implementations)
frame = inspect.currentframe()
if frame:
# Found a frame, so get the info, and strip space from the code_context
code_context = inspect.getframeinfo(frame.f_back).code_context[0].strip()
else:
# No frame, so use stack one level above us, and strip space around
# the 4th element, code_context
code_context = inspect.stack()[1][4][0].strip()
finally:
# Deterministic free references to the frame, to be on the safe side
del frame
print('Code context : {}'.format(code_context))
print('Value of args: {}\n'.format(args))
Constructed test cases
# Test with plain variables
a = 0.2
b = 1.2
c = a + 1
debug_print(a, b, c, b+2)
# Test with list, list of lists, tuples and dict's
debug_print([4.1, 4.2], [[4.00, 4.01], ["4.1.0", '4.1.1']])
debug_print((5.1, 5.2), {6.1: 6.2})
# Test with func's or func aliases
def my_func():
return 7
my_alias_func = my_func
debug_print(my_func, my_alias_func, my_alias_func())
# Test with nested func's and list slices
my_list = [1, 2, 3, 4, 5]
def first_level():
def second_level():
debug_print(my_list[:2], my_list[3:])
second_level()
# Execute
first_level()
# Test with multi-line call
debug_print(a, b,
'multi-line call', c)
Output of test cases
Code context : debug_print(a, b, c, b+2)
Value of args: (0.2, 1.2, 1.2, 3.2)
Code context : debug_print([4.1, 4.2], [[4.00, 4.01], ["4.1.0", '4.1.1']])
Value of args: ([4.1, 4.2], [[4.0, 4.01], ['4.1.0', '4.1.1']])
Code context : debug_print((5.1, 5.2), {6.1: 6.2})
Value of args: ((5.1, 5.2), {6.1: 6.2})
Code context : debug_print(my_func, my_alias_func, my_alias_func())
Value of args: (<function my_func at 0x110393668>, <function my_func at 0x110393668>, 7)
Code context : debug_print(my_list[:2], my_list[3:])
Value of args: ([1, 2], [4, 5])
Code context : 'multi-line call', c)
Value of args: (0.2, 1.2, 'multi-line call', 1.2)
Wanted output, and problem areas
I would love for something like the following to be output:
a: 0.2; b: 1.2; c: 1.2; 3.2
<list>: [4.1, 4.2]; <list of lists>: [[4.0, 4.01], ['4.1.0', '4.1.1']]
<tuple>: (5.1, 5.2); <dict>: {6.1: 6.2}
func: my_func; func: my_alias_func -> my_func; my_func(): 7
my_list[:2]: [1, 2]; my_list[3:]: [4, 5]
I do however see from related issues that this is maybe setting the bar a little high. But the closer I get, the better it would be.
Ideally I could loop through some dict which the original argument code as key, but I would also be most satisfied with a way to extract the true argument code from the code_context and then combine these with the actual args.
However I can not simply split the code context on ,
as that might also be a part of lists, dict's or tuples. If some python parser could be used to split the code context, this might be an alternate route to explore. But I would like to avoid using eval
.
So far my search has not revealed any place where I can get the functions actually argument list with both code and values. (Have seen references to f_globals
or func_globals
but they list everything available to the module, and I found no way to reconnect these to a variable arguemnt list).
On a sidenote: I know it is possible to use variations over debug_print(**kwargs)
and then having statements like debug_print(a=a, b=b; c=c; sum=b+2)
, but it would be even better to avoid that duplication when writing the debug statements.
List of somewhat related issues
There has been questions in the past related to similar issues, but whilst most of them deal with fixed arguments, or just displaying names, I would like to show both name and value, if available. Or alternatively a guide to parsing the code context to match the arguments given. But here goes some of the related questions:
I'm not sure that any syntax can be better than what you already got, but:
Gives:
Still, I prefer the
repr
builtin over mypprint
function:pprint([1, 'a'])
Gives
list<int>(1, 'a')
which is obviously wrong, and we can't do better, do you really wantlist<int or string>
?repr
gives[1, 'a']
which is readable and correct.pprint(1.3)
Gives
<float>(1.3)
, like I can't see that1.3
is a float ?repr
gives1.3
, which is clearly enough.