1

I want to find the name of the function as it was called ... i.e. the name of the variable that called the function. Using the basic recipes i.e. with __name__, func_name, or inspecting the basic stack does not work for me. For example

def somefunc():
    print "My name is: %s" % inspect.stack()[1][3]

a = somefunc
a()
# would output: out: "My name is: somefunc"
# whereas I want it to output: "My name is: a"

My gut says I can do this, but I can't figure it out. Any python guru's out there?

mgilson
  • 300,191
  • 65
  • 633
  • 696
Pykler
  • 14,565
  • 9
  • 41
  • 50
  • 2
    I doubt this is possible. Any particular reason you want to do this? – mgilson Jul 18 '12 at 17:34
  • 8
    Usually when people ask how to do something like this, they are doing something really awful. Can you clarify what purpose this will serve? – cdhowie Jul 18 '12 at 17:35
  • You can change the __name__ of the a part, but I don't understand why you want to do this. Also, look at the decorator docs. – sean Jul 18 '12 at 17:35
  • 1
    `func_name` is defined when you create the function. `a.func_name` is `somefunc`. – Michael Hoffman Jul 18 '12 at 17:40
  • 1
    It's not possible. A function has one and only one canonical name, the name used to construct it (as specified in the `def` statement and stored in its `func_name` and `__name__` attributes). You can have any number of other identifiers pointing to it, but there is no way to tell from inside the function which is being used to call it. – kindall Jul 18 '12 at 17:41
  • regarding questions about its usefulness, its merely something cool and showing off how dynamic python is. The selected answer shows off how one can bring up the calling code, parse it and further investigate it ... isn't python cool! What I am using it for, is an attempt to a very obfuscated Hello World app :) – Pykler Jul 19 '12 at 18:47

2 Answers2

1

The problem here is indirection. You could probably do something complicated like inspect the stack, get the code for the module that called the function, parse the line number from the stack to find the label used to call the function in the local context, and then use that, but that won't necessarily give you what you want anyway. Consider:

def func(x):
    print get_label_function_called_with()

def func_wrapper(func_in_func_wrapper):
    return func_in_func_wrapper

func_label = func
func_from_func_wrapper = func_wrapper(func_label)
func_from_func_wrapper()

Should this print func, func_in_func_wrapper, func_label, or func_from_func_wrapper? It might seem like an obvious answer at first, but given that you never really know what sort of indirection is going on inside code you are calling, you really can't know for sure.

Silas Ray
  • 25,682
  • 5
  • 48
  • 63
  • this is a complex situation, in my case I am not concerned with wrappers, but others could be. – Pykler Jul 19 '12 at 17:42
  • The point was this is why there is no easy or even really feasible way to do this. – Silas Ray Jul 19 '12 at 18:06
  • Might not be fast/feasible, but my question was regarding if it was possible ... and I am sure there is a way even more robust than the selected answer in python, since python is truly dynamic. – Pykler Jul 19 '12 at 18:50
1

It's probably impossible to do this 100% correctly, but you could give the following a try:

import inspect
import parser

# this flatten function is by mike c fletcher
def flatten(l, ltypes=(list, tuple)):
    ltype = type(l)
    l = list(l)
    i = 0
    while i < len(l):
        while isinstance(l[i], ltypes):
            if not l[i]:
                l.pop(i)
                i -= 1
                break
            else:
                l[i:i + 1] = l[i]
        i += 1
    return ltype(l)

# function we're interested in
def a():
    current_func = eval(inspect.stack()[0][3])
    last_frame = inspect.stack()[1]
    calling_code = last_frame[4][0]
    syntax_tree = parser.expr(calling_code)
    syntax_tree_tuple = parser.st2tuple(syntax_tree)
    flat_syntax_tree_tuple = flatten(syntax_tree_tuple)
    list_of_strings = filter(lambda s: type(s)==str,flat_syntax_tree_tuple)
    list_of_valid_strings = []
    for string in list_of_strings:
        try:
            st = parser.expr(string)
            list_of_valid_strings.append(string)
        except:
            pass
    list_of_candidates = filter(lambda s: eval(s)==current_func, list_of_valid_strings)
    print list_of_candidates

# other function
def c():
    pass

a()
b=a
a(),b(),c()
a(),c()
c(),b()

This will print:

['a']
['a', 'b']
['a', 'b']
['a']
['b']

It's pretty ugly and complicated, but might work for what you need. It works by finding all variables used in the line that called this function and comparing them to the current function.

Tar
  • 276
  • 2
  • 10
  • I like this, it is the essence of what I was trying to write ... as you said it is complex. And I cannot really assign the return value so I can use it of the a() function since the parser gives a syntax error. – Pykler Jul 19 '12 at 17:53
  • To use the return value, I made the list_of_candidates global. Although a simpler solution would be the next challenge, your answer works for what I wanted to do! Thanks! – Pykler Jul 19 '12 at 18:12