Debugging: Get filename and line number from which a function is called?
The function inspect.stack()
returns a list of frame records, starting with the caller and moving out, which you can use to get the information you want:
from inspect import getframeinfo, stack
def debuginfo(message):
caller = getframeinfo(stack()[1][0])
print("%s:%d - %s" % (caller.filename, caller.lineno, message)) # python3 syntax print
def grr(arg):
debuginfo(arg) # <-- stack()[1][0] for this line
grr("aargh") # <-- stack()[2][0] for this line
Output:example.py:8 - aargh
How to log source file name and line number in Python
Sure, check formatters in logging docs. Specifically the lineno and pathname variables.
%(pathname)s Full pathname of the source file where the logging call was issued(if available).Looks something like this:%(filename)s Filename portion of pathname.
%(module)s Module (name portion of filename).
%(funcName)s Name of function containing the logging call.
%(lineno)d Source line number where the logging call was issued (if available).
formatter = logging.Formatter('[%(asctime)s] p%(process)s {%(pathname)s:%(lineno)d} %(levelname)s - %(message)s','%m-%d %H:%M:%S')
C++/embedded Python: Can I retrieve Python file name and line number when a C++ function is called from Python
You will need PyTraceBack_Here
.
You can take a look at a traceback object's implementation here
Here is an example printig the traceback created by PyTraceBack_Here
#include <Python.h>
PyObject * mymodule_meth_test(PyObject * self) {
PyTraceBack_Here(PyEval_GetFrame());
PyObject * exc;
PyObject * val;
PyObject * tb;
PyErr_Fetch(&exc, &val, &tb);
PyTraceBack_Print(tb, PySys_GetObject("stderr"));
Py_RETURN_NONE;
}
PyMethodDef module_methods[] = {
{"test", (PyCFunction)mymodule_meth_test, METH_NOARGS, NULL},
{},
};
PyModuleDef module_def = {PyModuleDef_HEAD_INIT, "mymodule", NULL, -1, module_methods};
extern "C" PyObject * PyInit_mymodule() {
PyObject * module = PyModule_Create(&module_def);
return module;
}
From the tb
object you should be able to extract the filename and line number.It is an ordinary PyObject you can pass it to a python script or inspect it.
Here is how to extract the values without taking care of the refcounts:
int line = PyLong_AsLong(PyObject_GetAttrString(PyObject_GetAttrString(tb, "tb_frame"), "f_lineno"));
const char * filename = PyUnicode_AsUTF8(PyObject_GetAttrString(PyObject_GetAttrString(PyObject_GetAttrString(tb, "tb_frame"), "f_code"), "co_filename"));
python logging using filename and line number
Yes it is possible, althrough I'm quite sure the implementation I'm posting here is not 100% safe and sure there is a better / more elegant implementation, take this as a hint.
To get the filename and line# of the caller you can use the inspect
module, to add those custom infos to your logs you can add a custom Filter
:
log.py
import logging
from logging.handlers import RotatingFileHandler
from inspect import getframeinfo, stack
class CallerFilter(logging.Filter):
""" This class adds some context to the log record instance """
file = ''
line_n = ''
def filter(self, record):
record.file = self.file
record.line_n = self.line_n
return True
def caller_reader(f):
"""This wrapper updates the context with the callor infos"""
def wrapper(self, *args):
caller = getframeinfo(stack()[1][0])
self._filter.file = caller.filename
self._filter.line_n = caller.lineno
return f(self, *args)
return wrapper
class Log:
def __init__(self, path):
self.LOGGER = logging.getLogger('Open-Capture')
if self.LOGGER.hasHandlers():
self.LOGGER.handlers.clear() # Clear the handlers to avoid double logs
logFile = RotatingFileHandler(path, mode='a', maxBytes=5 * 1024 * 1024,
backupCount=2, encoding=None, delay=0)
formatter = logging.Formatter('[%(threadName)-14s] [%(file)s:%(line_n)-15s] %(asctime)s %(levelname)s %(message)s', datefmt='%d-%m-%Y %H:%M:%S')
logFile.setFormatter(formatter)
self.LOGGER.addHandler(logFile)
# Here we add the Filter, think of it as a context
self._filter = CallerFilter()
self.LOGGER.addFilter(self._filter)
self.LOGGER.setLevel(logging.DEBUG)
@caller_reader
def info(self, msg):
self.LOGGER.info(msg)
@caller_reader
def error(self, msg):
self.LOGGER.error(msg)
script_file.py
from log import Log
log = Log('l.log')
log.info('LOG MESSAGE')
log.error('LOG MESSAGE 2 ')
log.info('LOG MESSAGE 2 ')
output:[MainThread ] [script_file.py:4 ] 17-01-2020 16:34:36 INFO LOG MESSAGE
[MainThread ] [script_file.py:5 ] 17-01-2020 16:34:36 ERROR LOG MESSAGE 2
[MainThread ] [script_file.py:6 ] 17-01-2020 16:34:36 INFO LOG MESSAGE 2
How to determine file, function and line number?
There is a module named inspect
which provides these information.
Example usage:
import inspect
def PrintFrame():
callerframerecord = inspect.stack()[1] # 0 represents this line
# 1 represents line at caller
frame = callerframerecord[0]
info = inspect.getframeinfo(frame)
print(info.filename) # __FILE__ -> Test.py
print(info.function) # __FUNCTION__ -> Main
print(info.lineno) # __LINE__ -> 13
def Main():
PrintFrame() # for this line
Main()
However, please remember that there is an easier way to obtain the name of the currently executing file:print(__file__)
Related Topics
Numpy 1.21.2 May Not Yet Support Python 3.10
How to Have Shared Log Files Under Windows
Loading Initial Data with Django 1.7 and Data Migrations
How to Use Asyncio with Existing Blocking Library
How to Set Opacity of Background Colour of Graph with Matplotlib
Detect 64Bit Os (Windows) in Python
Why Isn't Assigning to an Empty List (E.G. [] = "") an Error
Joining Pairs of Elements of a List
Scale Matplotlib.Pyplot.Axes.Scatter Markersize by X-Scale
Python Pandas, Write Dataframe to Fixed-Width File (To_Fwf)
Running Jupyter with Multiple Python and Ipython Paths
How to Pipe Input to Python Line by Line from Linux Program
In Python, What Happens When You Import Inside of a Function
Filtering a Pyspark Dataframe with SQL-Like in Clause
Should All Python Classes Extend Object
Python Ignore Certificate Validation Urllib2