How can I get the file name and line number in a Python script?
Exactly the file information we get from an exception traceback. In this case without raising an exception.
Thanks to mcandre, the answer is:
#python3 from inspect import currentframe, getframeinfo frameinfo = getframeinfo(currentframe()) print(frameinfo.filename, frameinfo.lineno) import inspect inspect.getframeinfo(inspect.currentframe()).linenoframeinfo.lineno give you the line numer when you evaluate it, or when you created it with getframeinfo(currentframe())?currentframe() is called, which means you can't simplify this any more than getframeinfo(currentframe()).lineno (if you only care about the line number and not the file name). See docs.python.org/2/library/inspect.html#inspect.currentframeWhether you use currentframe().f_back depends on whether you are using a function or not.
Calling inspect directly:
from inspect import currentframe, getframeinfo cf = currentframe() # <--- Python saves the line here filename = getframeinfo(cf).filename print(f'This is line 6, but Python says line {cf.f_lineno}') # <--- And we write it here print(f'The filename is "{filename}"') This is line 6, but Python says line 3 The filename is "getlineno.py" Calling a function that does it for you:
from inspect import currentframe def get_linenumber(): cf = currentframe() return cf.f_back.f_lineno # <--- Line of caller print(f'This is line 7, python says line {get_linenumber()}') This is line 7, python says line 7 def log(*args, **kwargs): cf = inspect.currentframe() print(f"{inspect.stack()[1][1]}:{cf.f_back.f_lineno}", *args, **kwargs) line = lambda : currentframe().f_back.f_lineno with your solution, very nicesys equivalent in other answers is faster if performance matters (e.g. you're calling it a lot); on my newish windows pc this takes ~800ns while the sys version takes 500ns.Better to use sys also-
import sys print(dir(sys._getframe())) print(dir(sys._getframe().f_lineno) print(sys._getframe().f_lineno) The output is:
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'f_back', 'f_builtins', 'f_code', 'f_exc_traceback', 'f_exc_type', 'f_exc_value', 'f_globals', 'f_lasti', 'f_lineno', 'f_locals', 'f_restricted', 'f_trace'] ['__abs__', '__add__', '__and__', '__class__', '__cmp__', '__coerce__', '__delattr__', '__div__', '__divmod__', '__doc__', '__float__', '__floordiv__', '__format__', '__getattribute__', '__getnewargs__', '__hash__', '__hex__', '__index__', '__init__', '__int__', '__invert__', '__long__', '__lshift__', '__mod__', '__mul__', '__neg__', '__new__', '__nonzero__', '__oct__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdiv__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'bit_length', 'conjugate', 'denominator', 'imag', 'numerator', 'real'] 14 sys._getframe().f_back.f_lineno, especialy when used in a function, otherwise it will print the line where it's being called in the function.In Python 3 you can use a variation on (This has been improved after a comment by Claudio):
def Deb(msg=""): print(f"Debug {sys._getframe().f_back.f_lineno}: {msg}") In code, you can then use:
Deb("Some useful information") Deb() To produce:
123: Some useful information 124: Where the 123 and 124 are the lines that the calls are made from.
sys is already imported. inspect from other answers is overkill for my use case.Filename:
__file__ # or sys.argv[0] Line:
inspect.currentframe().f_lineno (not inspect.currentframe().f_back.f_lineno as mentioned above)
NameError: global name '__file__' is not defined on my Python interpreter: Python 2.7.6 (default, Sep 26 2014, 15:59:23). See stackoverflow.com/questions/9271464/…def __LINE__() -> int: return inspect.currentframe().f_back.f_lineno def __FILE__() -> str: return inspect.currentframe().f_back.f_code.co_filenamesys.argv[0] gives the command used to invokeHere's a short function that prints the file name and line number.
from inspect import currentframe, getframeinfo def HERE(do_print=True): ''' Get the current file and line number in Python script. The line number is taken from the caller, i.e. where this function is called. Parameters ---------- do_print : boolean If True, print the file name and line number to stdout. Returns ------- String with file name and line number if do_print is False. Examples -------- >>> HERE() # Prints to stdout >>> print(HERE(do_print=False)) ''' frameinfo = getframeinfo(currentframe().f_back) filename = frameinfo.filename.split('/')[-1] linenumber = frameinfo.lineno loc_str = 'File: %s, line: %d' % (filename, linenumber) if do_print: print('HERE AT %s' % (loc_str)) else: return loc_str Usage:
HERE() # Prints to stdout # Output: HERE AT File: model.py, line: 275 print(HERE(False)) # Retrieves string and prints it. # Output: File: model.py, line: 276 Just to contribute,
there is a linecache module in python, here is two links that can help.
linecache module documentation
linecache source code
In a sense, you can "dump" a whole file into its cache , and read it with linecache.cache data from class.
import linecache as allLines ## have in mind that fileName in linecache behaves as any other open statement, you will need a path to a file if file is not in the same directory as script linesList = allLines.updatechache( fileName ,None) for i,x in enumerate(lineslist): print(i,x) #prints the line number and content #or for more info print(line.cache) #or you need a specific line specLine = allLines.getline(fileName,numbOfLine) #returns a textual line from that number of line For additional info, for error handling, you can simply use
from sys import exc_info try: raise YourError # or some other error except Exception: print(exc_info() ) import inspect file_name = __FILE__ current_line_no = inspect.stack()[0][2] current_function_name = inspect.stack()[0][3] #Try printing inspect.stack() you can see current stack and pick whatever you want __file__: See stackoverflow.com/questions/3056048/…Here's what works for me to get the line number in Python 3.7.3 in VSCode 1.39.2 (dmsg is my mnemonic for debug message):
import inspect def dmsg(text_s): print (str(inspect.currentframe().f_back.f_lineno) + '| ' + text_s) To call showing a variable name_s and its value:
name_s = put_code_here dmsg('name_s: ' + name_s) Output looks like this:
37| name_s: value_of_variable_at_line_37 Golang style
import inspect import sys import atexit ERR_FILE = open('errors.log', 'w+', encoding='utf-8') LOG_FILE = open('log.log', 'w+', encoding='utf-8') def exit_handler(): # ctrl + C works as well log("Exiting") ERR_FILE.close() LOG_FILE.close() # close files before exit atexit.register(exit_handler) def log(*args, files=[sys.stdout, LOG_FILE]): # can also add timestamps etc. cf = inspect.currentframe() for f in files: print("DEBUG", f"{inspect.stack()[1][1]}:{cf.f_back.f_lineno}", *args, file=f) f.flush() def log_err(*args, files=[ERR_FILE, sys.stderr]): cf = inspect.currentframe() for f in files: print("ERROR", f"{inspect.stack()[1][1]}:{cf.f_back.f_lineno}", *args, file=f) f.flush() log("Hello World!") log_err("error") Output
DEBUG sample.py:29 Hello World! ERROR sample.py:30 error DEBUG sample.py:9 Exiting Inspired by many of the above answers and the console.log() function of JavaScript, I have deployed a pip package to serve this purpose. Please check this out here.
Just install the package using the command:
pip3 install print_position A simple example is (test.py):
from printPosition.printPosition import printPosition as print print("Test on line 2 from test.py") print("Test on line 3 from test.py") print("Test on line 7 from test.py") The output of the above code is:
@/home/pranav/Desktop/GitHub/PrintPosition-pip/print-log/test.py: 2 Test on line 2 from test.py @/home/pranav/Desktop/GitHub/PrintPosition-pip/print-log/test.py: 3 Test on line 3 from test.py @/home/pranav/Desktop/GitHub/PrintPosition-pip/print-log/test.py: 7 Test on line 7 from test.py Please let me know if it helped you! Also feel free to raise any issues/requests, everything is mentioned on the pip page here.
People familiar with PHP's __FILE__ and __LINE__ might appreciate
import inspect def __FILE__() -> str: # ptyhon has a native __file__ return inspect.currentframe().f_back.f_code.co_filename def __LINE__() -> int: # python has no native __line__, the closest thing I could find was: sys._getframe().f_lineno return inspect.currentframe().f_back.f_lineno unfortunately they must be used with (), sample usage:
print("file: " + __FILE__() + " line: " + str(__LINE__()) ); (both are part of my collection of php-apis-ported-to-python, https://github.com/divinity76/phppy/blob/main/php.py )
sys._getframe().f_lineno is BY FAR the most performant out of the 3 below execution line retrieval methods according to timeit() results.
# Prints the line # the call was executed from print(sys._getframe().f_lineno) # METHOD #1 print(getframeinfo(currentframe()).lineno) # METHOD #2 print(inspect.stack()[0][2]) # METHOD #3 # Number used for timeit() iterations num = 10000 # Below Line: METHOD #1 - Fastest - 0.0011057000083383173 (~2000x faster than below) print(timeit.timeit('sys._getframe().f_lineno', setup = 'from __main__ import sys', number = num)) # Below Line: METHOD #2 - 2nd Fastest - 2.3906398999970406 (~2.5x faster than below) print(timeit.timeit('(getframeinfo(currentframe()).lineno)', setup = 'from __main__ import currentframe, getframeinfo', number = num)) # Below Line: METHOD #3 - Slowest - 6.436119699996198 (~6,000x slower than fastest) print(timeit.timeit('inspect.stack()[0][2]', setup = 'from __main__ import inspect', number = num)) I'm not super familiar with these methods so please comment if there are reasons that this test scenario doesn't reflect real-world performance, although based on the fact that if you change the number arg from 10,000 to 1,000 the results are also divided by 10 it does seem that these are accurate.
The sys._getframe().f_back.f_lineno expression above does the job nicely. I’ve wrapped it into a simple logging function:
def dbug(*args, **kwargs): print(f'{sys._getframe().f_back.f_lineno}: ', *args, **kwargs) I use the dbug() function as a replacement for the print() function to print out values at strategic points in the code I’m trying to trouble-shoot, and it’s handy to be able to include the line number.