DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

FlΓ‘via Bastos
FlΓ‘via Bastos

Posted on • Originally published at flaviabastos.ca on

A caller id for your python function

Most of you might be too young to know this but there was time that the phone in your house – not in your pocket! – would ring and gasp! you had no idea who were calling! You had to ANSWER the phone to find out. πŸ€¦β€β™€οΈ Now we have caller ID and know exactly who is calling – and can completely ignore the call decide if we are answering the call or not.

When working with large applications, for debugging purposes, I find very helpful to know which function is calling what function. Breakpoints and print statements are useful tools but sometimes you just need more information. In a large codebase it’s fairly easy to get lost with breakpoints so I’ve been using the chunk of code below to print extra info and learn a little bit more about my application.

import inspect
func = inspect.currentframe().f_back.f_code
print(f"in function 'my_function' called from NAME: {func.co_name} in FILENAME: {func.co_filename} on LINE#: {func.co_firstlineno}")
Enter fullscreen mode Exit fullscreen mode

How to use

Consider a *very* simple program (which honestly doesn’t even need our helper function, but for the sake of example, stay with me here):


def helper_function(value):
    return value.isnumeric()

def adder(num1, num2):
    return int(num1) + int(num2)

def main():
    print("The most useless calculator!")
    first_number = input("Please enter a number: ")
    second_number = input("Please enter another number: ")
    print(f"Let's add {first_number} and {second_number}")

    if helper_function(first_number) and helper_function(second_number):
        total = adder(first_number, second_number)
        print(f"The total is {total}")
    else:
        print("Sorry, we cannot add these values...")

main()

Enter fullscreen mode Exit fullscreen mode

The function above takes two pieces of input form the command line (lines 11 and 12), calls a helper function to check if the values entered are numbers (line 15) and if so, calls the function to add the numbers (line 16).

Now, just for a moment let’s pretend that each of these functions are way more complicated than this and live in separate files. Add some more functionality, a framework, 2 cups of all-purpose flour and a webserver. VoilΓ ! You have a web application. What if you still want to know which function called what? Let’s add our chunk of code to adder:


def helper_function(value):
    return value.isnumeric()

def adder(num1, num2):
    import inspect
    func = inspect.currentframe().f_back.f_code
    print(
        f" **** in function 'adder' called from NAME: {func.co_name} in FILENAME: {func.co_filename} on LINE#: {func.co_firstlineno}"
    )

    return int(num1) + int(num2)

def main():
    print("The most useless calculator!")
    first_number = input("Please enter a number: ")
    second_number = input("Please enter another number: ")
    print(f"Let's add {first_number} and {second_number}")

    if helper_function(first_number) and helper_function(second_number):
        total = adder(first_number, second_number)
        print(f"The total is {total}")
    else:
        print("Sorry, we cannot add these values...")

main()

Enter fullscreen mode Exit fullscreen mode

When you run this script now (let’s save it as β€œwatcher.py”), it will output the information about the function that called adder, in this case, main:

>>> python3 watcher.py 
The most useless calculator!
Please enter a number: 4
Please enter another number: 2
Let's add 4 and 2
**** in function 'adder' called from NAME: main in FILENAME: watcher.py on LINE#: 15
The total is 6
Enter fullscreen mode Exit fullscreen mode

Note that the line number is the line where the parent function, in this case β€œmain”, starts, not where our function was called from!

This has been very helpful and I’ve used a lot lately. I hope it helps you too!

Fancypants πŸ‘–

You will need to copy this chunk of code into every function you would like to debug but that’s a bit tedious. Instead, you can turn it into a decorator and decorate the functions you want to inspect. Start by creating your decorator:


def caller_id(my_func):
    def wrapper(*args, **kwargs):
        import inspect

        my_func(*args, **kwargs)

        func = inspect.currentframe().f_back.f_code
        print(
            f"in function {my_func. __name__ } called from NAME: {func.co_name} in FILENAME: {func.co_filename} on LINE#: {func.co_firstlineno}"
        )

    return wrapper

Enter fullscreen mode Exit fullscreen mode

Then, you can decorate your function with it. Let’s add it to adder again. Now adder looks like:

@caller_id
def adder(num1, num2):
    return int(num1) + int(num2)
Enter fullscreen mode Exit fullscreen mode

That’s it.


If you found this helpful, let me know on Twitter!

The post A caller id for your python function _was originally published at _flaviabastos.ca

Top comments (0)

Another day as a dev

Stop by this week's meme thread!