SoFunction
Updated on 2024-10-28

Getting line numbers and function names in python using sys templates and the logging module

For python, there are two questions that have been bugging me for the past few days:.
There is no way to get the current line number and function name directly. This is an issue that was brought up in the forums, and a bunch of people just speculated why python doesn't provide __line__ and __func__ like __file__, but didn't end up finding a solution.
2. How can a function call itself recursively if it doesn't know its own name. This was asked by one of my coworkers, actually getting the function name as well, but at the time it was also unanswerable.


But tonight! All the questions were answered.
It all started with my use of python's logging module, where the format in logging has the following options.

Copy Code The code is as follows.

%(name)s            Name of the logger (logging channel)
%(levelno)s         Numeric logging level for the message (DEBUG, INFO,
                    WARNING, ERROR, CRITICAL)
%(levelname)s       Text logging level for the message ("DEBUG", "INFO",
                    "WARNING", "ERROR", "CRITICAL")
%(pathname)s        Full pathname of the source file where the logging
                    call was issued (if available)
%(filename)s        Filename portion of pathname
%(module)s          Module (name portion of filename)
%(lineno)d          Source line number where the logging call was issued
                    (if available)
%(funcName)s        Function name
%(created)f         Time when the LogRecord was created (()
                    return value)
%(asctime)s         Textual time when the LogRecord was created
%(msecs)d           Millisecond portion of the creation time
%(relativeCreated)d Time in milliseconds when the LogRecord was created,
                    relative to the time the logging module was loaded
                    (typically at application startup time)
%(thread)d          Thread ID (if available)
%(threadName)s      Thread name (if available)
%(process)d         Process ID (if available)
%(message)s         The result of (), computed just as
                    the record is emitted

That is, logging is able to get the caller's line number and function name, so will it also be able to get its own line number and function name?
Let's take a look at the source code, the main parts are as follows.

Copy Code The code is as follows.

def currentframe():
    """Return the frame object for the caller's stack frame."""
    try:
        raise Exception
    except:
        return sys.exc_info()[2].tb_frame.f_back
def findCaller(self):
    """
    Find the stack frame of the caller so that we can note the source
    file name, line number and function name.
    """
    f = currentframe()
    #On some versions of IronPython, currentframe() returns None if
    #IronPython isn't run with -X:Frames.
    if f is not None:
        f = f.f_back
    rv = "(unknown file)", 0, "(unknown function)"
    while hasattr(f, "f_code"):
        co = f.f_code
        filename = (co.co_filename)
        if filename == _srcfile:
            f = f.f_back
            continue
        rv = (co.co_filename, f.f_lineno, co.co_name)
        break
    return rv
def _log(self, level, msg, args, exc_info=None, extra=None):
    """
    Low-level logging routine which creates a LogRecord and then calls
    all the handlers of this logger to handle the record.
    """
    if _srcfile:
        #IronPython doesn't track Python frames, so findCaller throws an
        #exception on some versions of IronPython. We trap it here so that
        #IronPython can use logging.
        try:
            fn, lno, func = ()
        except ValueError:
            fn, lno, func = "(unknown file)", 0, "(unknown function)"
    else:
        fn, lno, func = "(unknown file)", 0, "(unknown function)"
    if exc_info:
        if not isinstance(exc_info, tuple):
            exc_info = sys.exc_info()
    record = (, level, fn, lno, msg, args, exc_info, func, extra)
    (record)

I'll briefly explain that it's actually done by throwing an exception in the currentframe function and then finding the information about the call by looking up. where

Copy Code The code is as follows.

rv = (co.co_filename, f.f_lineno, co.co_name)

The three values are file name, line number, and function name. (You can go to /library/ to see the description of several system functions in the code)
OK, if you have read the source code, then get the current position of the line number and function name I believe it is also very clear, the code is as follows.

Copy Code The code is as follows.

#!/usr/bin/python
# -*- coding: utf-8 -*-
'''
#=============================================================================
#  FileName:       
# Description: Get the line number and function name of the current position.
#  Version:         1.0
#=============================================================================
'''
import sys
def get_cur_info():
    """Return the frame object for the caller's stack frame."""
    try:
        raise Exception
    except:
        f = sys.exc_info()[2].tb_frame.f_back
    return (f.f_code.co_name, f.f_lineno)

def callfunc():
    print get_cur_info()

 
if __name__ == '__main__':
    callfunc()

The input result is.
Copy Code The code is as follows.
('callfunc', 24)

Meets expectations~
Now you don't have to complain that you can't get the line number and function name, right?

=============================================================================
It was later realized that there could actually be a simpler way to do this as well, as follows.

Copy Code The code is as follows.
import sys
def get_cur_info():
    print sys._getframe().f_code.co_name
    print sys._getframe().f_back.f_code.co_name
get_cur_info()

The result of the call is.
Copy Code The code is as follows.
get_cur_info
<module>