4

Background

Consider the following minimal example:

When I save the following script and run it from terminal,

import time

time.sleep(5)
raise Exception

the code will raise an error after sleeping five seconds, leaving the following traceback.

Traceback (most recent call last):
  File "test/minimal_error.py", line 4, in <module>
    raise Exception
Exception

Now, say, I run the script, and during the 5-second-sleep, I add a line in the middle.

import time

time.sleep(5)
a = 1
raise Exception

After the python interpreter wakes up from the sleep and reaches the next line, raise Exception, it will raise the error, but it leaves the following traceback.

Traceback (most recent call last):
  File "test/minimal_error.py", line 4, in <module>
    a = 1
Exception

So the obvious problem is that it doesn't print the actual code that caused the error. Although it gives the correct line number (correctly reflecting the version of the script that is running, while understandably useless) and a proper error message, I can't really know what piece of code actually caused the error.

In real practice, I implement one part of a program, run it to see if that part is doing fine, and while it is still running, I move on to the next thing I have to implement. And when the script throws an error, I have to find which actual line of code caused the error. I usually just read the error message and try to deduce the original code that caused it. Sometimes it isn't easy to guess, so I copy the script to clipboard and rollback the code by undoing what I've written after running the script, check the line that caused error, and paste back from clipboard.

Question

Is there any understandable reason why the interpreter shows a = 1, which is line 4 of the "current" version of the code, instead of raise Exception, which is line 4 of the "running" version of the code? If the interpreter knows "line 4" caused the error and the error message is "Exception", why can't it say the command raise Exception raised it?

I'm not really sure if this question is on-topic here, but I don't think I can conclude it off-topic from what the help center says. It is about "[a] software [tool] commonly used by programmers" (the Python interpreter) and is "a practical, answerable problem that is unique to software development," I think. I don't think it's opinion-based, because there should be a reason for this choice of implementation.

(Observed the same in Python 2.7.16, 3.6.8, 3.7.2, and 3.7.3, so it doesn't seem to be version-specific, but a thing that just happens in Python.)

Ignatius
  • 2,745
  • 2
  • 20
  • 32
  • https://stackoverflow.com/questions/5296977/what-will-happen-if-i-modify-a-python-script-while-its-running – CristiFati Apr 03 '19 at 10:29
  • 1
    CristiFati, thanks for the link! What it says is exactly what I expected, that the script is loaded to memory when I run it so that further editing on the script file wouldn't affect *any* behavior. Unfortunately that's not the case with error tracebacks, as an answer from the link also points out (without explaining any rationales.) – Ignatius Apr 03 '19 at 10:35

2 Answers2

6

The immediate reason is that Python re-opens the file and reads the specified line again to print it in error messages. So why would it need to do that when it already read the file in the beginning? Because it doesn't keep the source code in memory, only the generated byte code.

In fact, Python will never hold the entire contents of the source file in memory at one time. Instead the lexer will read from the file and produce one token at a time, which the parser then parses and turns into byte code. Once the parser is done with a token, it's gone.

So the only way to get back at the original source code is to open the source file again.

sepp2k
  • 363,768
  • 54
  • 674
  • 675
0

I think it a classic problem which is described here.

Sleep use os system call to pause execution of that thread.