The ability to use introspection to dynamically determine the current code context and frame/stack can be a powerful tool for a developer.
Introspection can be applied for debugging, logging, metrics collection, or method overloading based on type. I will show a simple example of its usage in this article.
Simple Example
Let’s use a simple HelloWorld function that takes an single optional parameter like below:
def show_hello_world_basic(name="World"): print("Hello {}!".format(name))
Using the standard inspect module, we can pull the current frame and argument values.
def show_hello_world_basic(name="World"): # gets the current frame and examines args myframe = inspect.currentframe() args,_,_,values = inspect.getargvalues(myframe) # prints function name funcname = myframe.f_code.co_name print("function {}()".format(funcname)) # prints simple function arguments for i in (args if not args is None else []): print("\t{}={}".format(i,values[i])) # prints greeting print("Hello {}!".format(name))
Which would show us the argument name and values (name=World), before actually printing the message.
function show_hello_world_basic() name=World Hello World!
Extending this logic, we can pull not only the simple arguments, but also positional varargs and keywords varargs.
Sample Program
I have uploaded inspect_func_test.py to github, which you can use to test the more advanced cases using methods with variable args and kwargs. By placing the following line into a method:
inspect_simple(inspect.currentframe())
You can have it call out to the ‘inspect_simple()’ helper function which shows all the arguments and values passed into a function.
def inspect_simple(frame): # pull tuple from frame args,args_paramname,kwargs_paramname,values = inspect.getargvalues(frame) # stack param could be used to pull function name print("function {}()".format(funcname)) # show static parameters for i in (args if not args is None else []): print("\t{}={}".format(i,values[i])) # show positional varargs if args_paramname is not None: varglist = values[args_paramname] for v in (varglist if not varglist is None else []): print("\t*{}={}".format(args_paramname,v)) # show named varargs if kwargs_paramname is not None: varglist = values[kwargs_paramname] for k in (sorted(varglist) if not varglist is None else []): print("\t*{} {}={}".format(kwargs_paramname,k,varglist[k]))
If you want to take this to the next level, read my article on using the @ “pie” syntax and custom decorators to inspect function arguments.
REFERENCE
stackoverflow, showing function name and param name/value pairs
markmiyashita, basic explanation of *args and **kwargs
stefaanlippens, get current function name from stack
programcreek, usage of inspect.CO_VARARGS
realpython, primer on python decorators capturing args kwargs
stackoverflow, performance tradeoff for inspect
NOTES
generate help from docstring
pydoc inspect_func_test.py
do PEP8 style check
pylint3 inspect_func_test.py
make PEP8 style fixes automatically, inline and allow longer lines
python -m pip install autopep8 autopep8 --max-line-length=120 -i inspect_func_test.py python -m pip install pycodestyle pycodestyle --max-line-length=120 -i inspect_func_test.py