When the Python interpreter reports an error/exception (I'm just going to say "error" to refer to both of these from now on), it prints the line number and contents of the line that caused the error.
Interestingly, if you have a long-running Python script which causes an error and change the .py
file while the script is running, then the interpreter can report an incorrect line as raising the error, based on the changed contents of the .py
file.
sample.py
from time import sleep
for i in range(10):
print(i)
sleep(1)
raise Exception("foo", "bar")
This script runs for 10 seconds, then raises an exception.
sample2.py
from time import sleep
for i in range(10):
print(i)
sleep(1)
"""
This
is
just
some
filler
to
demonstrate
the
behavior
"""
raise Exception("foo", "bar")
This file is identical to sample.py
except that it has some junk between the end of the loop and the line raises the following exception:
Traceback (most recent call last):
File "sample.py", line 7, in <module>
Exception: ('foo', 'bar')
python3 sample.py
mv sample.py sample.py.bak && cp sample2.py sample.py
before sample.py
finishes executionThe interpreter reports the following:
Traceback (most recent call last):
File "sample.py", line 7, in <module>
Exception: ('foo', 'bar')
Here, the interpreter reports that there was an exception on line 7 of sample.py
and prints the Exception.
The interpreter reports the following:
Traceback (most recent call last):
File "sample.py", line 7, in <module>
"""
Exception: ('foo', 'bar')
Here, the interpreter also reports """
when it reports the exception.
It seems to be looking in the file on disk to find this information, rather than the file loaded into memory to run the program.
The following is my mental model for what happens when I run python3 sample.py
:
sample.py
into memoryClearly, there is a flaw in my mental model.
As per the answer linked by @b_c,
Python doesn't keep track of what source code corresponds to any compiled bytecode. It might not even read that source code until it needs to print a traceback.
[...]
When Python needs to print a traceback, that's when it tries to find source code corresponding to all the stack frames involved. The file name and line number you see in the stack trace are all Python has to go on
[...]
The default
sys.excepthook
goes through the native callPyErr_Display
, which eventually winds up using_Py_DisplaySourceLine
to display individual source lines._Py_DisplaySourceLine
unconditionally tries to find the file in the current working directory (for some reason - misguided optimization?), then calls_Py_FindSourceFile
to searchsys.path
for a file matching that name if the working directory didn't have it.