Search code examples
pythonpython-3.xintrospection

Determine function name from within an aliased function


How can I determine whether a function was called using the function's name or by the name of an alias of that function?

I can inspect a function to get its name from within the body of a function by doing:

import inspect

def foo():
   print(inspect.stack()[0][3])

foo() # prints 'foo'

source: Determine function name from within that function (without using traceback)

However, if I alias the function and try the same thing I get the original function name (not the alias)

bar = foo
bar() # prints 'foo'

I would like to be able to be able to do the following:

def foo():
    print(... some code goes here ...)

bar = foo

foo() # prints 'foo'
bar() # prints 'bar'

Solution

  • I have a (somewhat hacky) solution that relies on a regex to parse the function name out of a string. There might be a cleaner solution, but at least using inspect only this is the best I could find.

    import inspect
    import re
    
    
    function_from_call = re.compile("\w+(?=\(\))")
    
    
    def foo():
        _, call_frame, *_ = inspect.stack()
        _, _, _, _, call, *_ = call_frame
        print(re.search(function_from_call, str(call)).group())
    
    bar = foo
    bar()  # prints bar
    foo()  # prints foo
    

    Short explanation: First, I am grabbing the inspect frame of the call that resulted in a call to this function. Then, I am extracting the actual call string from this frame and I apply a regex to this call string that gives us the function name only.

    Note: From an interpreter shell, inspect behaves differently and my code above produces an error because my regex cannot match an actual function name. An additional caveat is pointed out in a comment to this question by @user2357112: It is not obvious that a call is directly tied to a name, as in

    l = [foo]; l[0]()
    

    When run from a script, my solution will handle simple renaming cases properly (as the one given in this question) but I do not advocate using it since corner cases as the one above will result in confusing errors.