为什么 Python 在打印回溯时从当前目录读取?

Why does Python read from the current directory when printing a traceback?

$ echo "Your code is bad and you should feel bad" > "<stdin>"
$ python
Python 3.6.0 (default, Dec 28 2016, 19:53:26) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-11)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 2 + '2'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
    Your code is bad and you should feel bad
TypeError: unsupported operand type(s) for +: 'int' and 'str'

为什么 Python 会将字符串 "<stdin>" 与匹配该文件名的文件混淆?我不希望 Python 在遇到未处理的异常时尝试从我的磁盘中读取任何文件。

您也可以使用 "<string>" 文件名获取它:

$ echo "pining for the fjords" > "<string>"
$ python -c 'wat'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
    pining for the fjords
NameError: name 'wat' is not defined

有什么方法可以防止这种行为,或者它是否被硬编码到 REPL 中?

Python 不会跟踪哪些源代码对应于任何已编译的字节码。在需要打印回溯之前,它甚至可能不会读取该源代码,例如,如果模块是从 .pyc 文件加载的。

当Python需要打印回溯时,就是它试图找到与所有涉及的堆栈帧对应的源代码的时候。你在堆栈跟踪中看到的文件名和行号都是 Python 必须继续的。如果它使用 traceback 模块,代码路径将通过 linecacheexcludes filenames starting and ending with < and > 的部分,但默认 sys.excepthook 不会通过该路径。

default sys.excepthook goes through the native call PyErr_Display, which eventually winds up using _Py_DisplaySourceLine 显示单独的源代码行。 _Py_DisplaySourceLine 无条件地尝试在当前工作目录中查找文件(出于某种原因 - 错误的优化?),然后调用 _Py_FindSourceFile 搜索 sys.path 匹配该名称的文件如果工作目录没有。通常,它不会找到 <stdin><string> 文件,当它找不到文件时它会跳过打印源代码,但如果找到一个,它会从该文件打印.

我最初认为您可以通过 运行 Python 和 -I flag 将其置于隔离模式来防止这种情况。隔离模式的效果之一是从 sys.path 中删除脚本的目录。实验证明,这并没有改变什么,我意识到 _Py_DisplaySourceLine 无论如何都会尝试工作目录。

通过在本机代码路径中排除 <> 文件名来解决此问题非常简单,就像 linecache 所做的那样。无条件在当前目录搜索文件的代码也要改一下