36

I want to log all the names of functions where my code is going. It does not matter who is calling the function.

import inspect

def whoami():
    return inspect.stack()[1][3]

def foo():
    print(whoami())

Currently it prints foo. I want it to print whoami.

Asclepius
  • 57,944
  • 17
  • 167
  • 143
John Kaff
  • 1,905
  • 6
  • 18
  • 30
  • Do you want to call and execute the function, or just call the name? Because you're doing the latter now, without parentheses after `whoami` in the last line. –  Oct 16 '15 at 04:06
  • "currently it prints `foo`": your current sample code prints nothing, or at least it won't print `foo`. Please edit it and provide a valid example. – starrify Oct 16 '15 at 04:06
  • 2
    inspect.stack()[0][3] ? – lsbbo Oct 16 '15 at 04:08
  • 1
    You probably meant to do: `print(whoami())` . Further, @hero is correct, you should access `inspect.stack()[0][3]` to get the name – Nir Alfasi Oct 16 '15 at 04:08

7 Answers7

56

You probably want inspect.getframeinfo(frame).function:

import inspect

def whoami(): 
    frame = inspect.currentframe()
    return inspect.getframeinfo(frame).function

def foo():
    print(whoami())

foo()

prints

whoami
  • 4
    This worked for me: `return inspect.getouterframes(inspect.currentframe())[1].function` – coder.in.me Jun 27 '17 at 05:47
  • 1
    Also using `import inspect`, while setting `frame = inspect.currentframe()` you can follow with either `print(frame.f_code.co_name)` or `return frame.f_code.co_name` which provides the current function's name. – Android Control Feb 04 '23 at 22:36
  • @AndroidControl Indeed. One can also use `currentframe().f_back.f_code.co_name` as is noted in my [answer](https://stackoverflow.com/a/76741643/) to get the calling function's name. – Asclepius Jul 21 '23 at 22:56
30

Actually, Eric's answer points the way if this is about logging:

For my logging purpose i want to log all the names of functions where my code is going

You can adjust the formatter to log the function name:

import logging               

def whoami():
    logging.info("Now I'm there")

def foo():
    logging.info("I'm here")
    whoami()
    logging.info("I'm back here again")

logging.basicConfig(
    format="%(asctime)-15s [%(levelname)s] %(funcName)s: %(message)s",
    level=logging.INFO)
foo()

prints

2015-10-16 16:29:34,227 [INFO] foo: I'm here
2015-10-16 16:29:34,227 [INFO] whoami: Now I'm there
2015-10-16 16:29:34,227 [INFO] foo: I'm back here again
19

For my logging purpose i want to log all the names of functions where my code is going

Have you considered decorators?

import functools
def logme(f):
    @functools.wraps(f)
    def wrapped(*args, **kwargs):
        print(f.__name__)
        return f(*args, **kwargs)
    return wrapped


@logme
def myfunction():
    print("Doing some stuff")
Giovanni Cappellotto
  • 4,597
  • 1
  • 30
  • 33
Eric
  • 95,302
  • 53
  • 242
  • 374
  • 1
    Thanks Eric , i will try that. Is there any way to apply that decorator to all function of class – John Kaff Oct 16 '15 at 04:56
  • @JohnKaff: Yes, you can apply decorators to classes too. You'll want that decorator to iterate over all methods of the class object, and wrap them in this decorator. – Eric Oct 16 '15 at 15:36
11

Call sys._getframe() to get a frame class instance. The f_code.co_name member holds the function name.

sys._getframe(0).f_code.co_name

Add a simple helper function func_name() to wrap the call

import sys

def func_name(): 
    return sys._getframe(1).f_code.co_name

def func1():
    print(func_name())

func1()  # prints 'func1'
JamesThomasMoon
  • 6,169
  • 7
  • 37
  • 63
7

This simple reusable method returns a name of the caller/parent function:

import inspect

def current_method_name():
    # [0] is this method's frame, [1] is the parent's frame - which we want
    return inspect.stack()[1].function  

# Example:
def whoami():
    print(current_method_name())

whoami()

-> output is whoami

Asclepius
  • 57,944
  • 17
  • 167
  • 143
Filip Happy
  • 575
  • 5
  • 17
  • 1
    Although this works for getting the caller's name, note that [`inspect.stack` is thought to be slow](https://stackoverflow.com/questions/17407119/python-inspect-stack-is-slow). An alternative is to use `inspect.currentframe().f_back.f_code.co_name` as in [this answer](https://stackoverflow.com/a/76741643/) – Asclepius Jul 21 '23 at 23:05
1

Adding an answer here as it can be useful to include the class name as well as the function.

This checks for self and cls convention, to include the class name.

def name_of_caller(frame=1):
    """
    Return "class.function_name" of the caller or just "function_name".
    """
    frame = sys._getframe(frame)
    fn_name = frame.f_code.co_name
    var_names = frame.f_code.co_varnames
    if var_names:
        if var_names[0] == "self":
            self_obj = frame.f_locals.get("self")
            if self_obj is not None:
                return type(self_obj).__name__ + "." + fn_name
        if var_names[0] == "cls":
            cls_obj = frame.f_locals.get("cls")
            if cls_obj is not None:
                return cls_obj.__name__ + "." + fn_name
    return fn_name

ideasman42
  • 42,413
  • 44
  • 197
  • 320
1

This is probably the fastest implementation. It avoids the use of additional inspect methods which can be slow or unnecessary.

Implementation:

from inspect import currentframe

def get_self_name() -> str:
    return currentframe().f_code.co_name

def get_caller_name() -> str:
    return currentframe().f_back.f_code.co_name

def get_parent_caller_name() -> str:
    return currentframe().f_back.f_back.f_code.co_name

Usage:

def actual_function_1():
    print('In actual_function_1:', get_self_name())

def actual_function_2():
    print('In actual_function_2:', get_caller_name())

def actual_function_3() -> None:
    print('In actual_function_3:', get_parent_caller_name())

actual_function_1()
actual_function_2()
actual_function_3()

Output:

In actual_function_1: get_self_name
In actual_function_2: actual_function_2
In actual_function_3: <module>
Asclepius
  • 57,944
  • 17
  • 167
  • 143