In a Python function, Is there a way to tell the argument's variable name on the caller side?

Question:

I am trying to create a logger function:

def logger(someVariable): 
    # some code that prints the name of someVariable 

So when I call the function:

logger(myvarA)        # should output the string 'myvarA'
logger(someobj.varB)  # should output the string 'someobj.varB'
logger(myarr[3])      # should (probably) output 'myarr[3]'

Not sure if all or even any of these cases are possible? The use case for this is to have a simple function in iPython Notebook to log any variables for their values without having to type something like below:

logger(myVarA,'myVarA')
logger(myarr[i],'myarr[{}]'.format(i))
Asked By: Pan Yan

||

Answers:

The inspect module (https://docs.python.org/3/library/inspect.html#inspect.getframeinfo) will also give you the code context. You simply need to parse that to get the text of what was passed. With only a single variable, it’s pretty easy to do. Here’s a sample.

import inspect
import re

def handle_nested_parens(v):
   parens = 0
   output = ""
   for c in v:
       if c == ")":
           parens = parens - 1
           if parens < 0:
               return output
       output += c
       if c == "(":
           parens = parens + 1
   return output

def logger(v):
   currentframe = inspect.currentframe()
   code_context = inspect.getouterframes(currentframe,1)[1][4][0]
   variable = handle_nested_parens(re.sub(r".*logger((.*)",r"1", code_context))
   print("{0} => {1}".format(variable, v))


def testing(a):
   return(a)

def submain(a):
   ab = (1, 2, a)
   logger(testing(ab))
   return 15

def main():
   xyz = "hello"
   logger(xyz)
   logger(submain(xyz))


if __name__ == '__main__':
    a = 5
    logger(a)
    main()

It outputs

a => 5
xyz => hello
testing(ab) => (1, 2, 'hello')
submain(xyz) => 15
Answered By: clockwatcher

For anyone looking for something like this today I have a get_variable_name() function in my argel1200.utilties that does this. You can do a pip install argel1200 to get it.

Documentation:

Called by dumps()

Pulls the variable names from the function that called this function

This function traces back through the call stack, so we have to subtract -1
for every intermediate function, including this function.

Subtract -1 for every intermediate step in the call stack.
So we have: -1 for this function -1 for whoever called it = -2, which is the default.

If there are more functions in the middle then subtract -1 for each of them. For example:
-1 for this function -1 for dumps(), and -1 for whoever called dumps = -3.

:param stack_back: How far back we need to go in the stack (see above description)
:return: Returns the variable name(s)

And here’s the function:

import re
import traceback    
def get_variable_name(stack_back=-2):
    stack = traceback.extract_stack()
    caller_name = stack[-2].name
    caller_len = len(caller_name)
    line = stack[stack_back].line
    # Example line: print('fu', 'bar/', argel1200.utilities.dumps(header), '/foobar')
    my_line = re.sub(r'(s|u180B|u200B|u200C|u200D|u2060|uFEFF)+', '', line)  # Remove all whitespace
    caller_start = my_line.find(caller_name + '(')  # Find where the caller string is (e.g. where "dumps(" starts)
    caller_end = caller_start + caller_len  # And where it ends (the index of the '(' in "dumps("  )
    my_line_substr = my_line[caller_end:]  # Get a substr of everything past the caller (e.g. "dumps").

    # Now let's find all the variable names passed in
    vars_passed_in = []
    parens = 0
    str_start = None
    for idx, char in enumerate(my_line_substr):
        if char == '(':
            parens += 1
            str_start = idx + 1
        elif char == ',' or char == ')':
            vars_passed_in.append(my_line_substr[str_start:idx])
            str_start = idx + 1
            if char == ')':
                parens -= 1
                if parens == 0:
                    break
    return vars_passed_in

You can see the code on GitHub: https://github.com/argel1200/argel1200-python/blob/master/argel1200/utilities.py

The function dumps() just below it calls it, so you can see how that works.

Answered By: argel1200
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.