【问题标题】:Why does Python read from the current directory when printing a traceback?为什么 Python 在打印回溯时从当前目录读取?
【发布时间】:2017-09-21 11:28:13
【问题描述】:
$ 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 会将字符串 "&lt;stdin&gt;" 与匹配该文件名的文件混淆?如果遇到未处理的异常,我不希望 Python 试图从我的磁盘中读取任何文件。

您也可以使用"&lt;string&gt;" 文件名获取它:

$ 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 stdin


【解决方案1】:

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

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

default sys.excepthook 通过本机调用PyErr_Display,最终使用_Py_DisplaySourceLine 显示各个源代码行。 _Py_DisplaySourceLine 无条件地尝试在当前工作目录中查找文件(出于某种原因 - 被误导的优化?),然后调用 _Py_FindSourceFile 来搜索 sys.path 以查找与该名称匹配的文件(如果工作目录没有)。通常,它不会找到&lt;stdin&gt;&lt;string&gt; 文件,当它找不到文件时它会跳过打印源代码,但如果找到,它会从该文件打印。

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

通过在本机代码路径中排除 &lt;&gt; 文件名来解决这个问题相当简单,就像 linecache 一样。无条件在当前目录中搜索文件的代码也应该修改。

【讨论】:

  • 它已经是特殊情况 filename.startswith("&lt;") and filename.endswith("&gt;") 在寻找更新 linecache (linecache.updatecache()) 时,所以添加类似于 checkcache() 的问题并不是不可能的。
  • @pbuck:嗯,确实如此。不过,我认为这个代码路径实际上并没有经过linecache(还有吗?)。当我在 Python 3 上尝试时,'linecache' in sys.modules 在未捕获异常前后报告 False
  • 使用-I 标志运行确实可以防止sys.path[0] 中出现空字符串,但这不会改变从磁盘读取回溯的行为
  • @wim:你似乎是对的。我期待它能够工作,但看起来我误读了_Py_DisplaySourceLine 中的一行,它试图在当前工作目录中查找文件,而不考虑当前目录是否在路径上。
猜你喜欢
  • 2011-07-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多