2017-04-24 84 views
14
$ 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试图从我的磁盘读取任何文件,如果它遇到未处理的异常。为什么打印回溯时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?

+8

哦,这是一个可爱的bug。你应该报告它。 – kindall

回答

7

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

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

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

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

通过在本地代码路径中排除<>文件名来修复此问题将非常简单,例如linecache。无条件地搜索文件当前目录的代码也应该更改。

+0

当想要更新linecache('linecache.updatecache()')时,它已经是特殊情况了'filename.startswith(“<”)和filename.endswith(“>”)',所以添加类似checkcache()。 – pbuck

+0

@pbuck:呵呵,它确实如此。不过,我认为这段代码路径实际上并不通过'linecache'(更多?)。 sys.modules'中的''linecache'在Python 3上尝试它时在未捕获的异常之前和之后报告'False'。 – user2357112

+0

使用'-I'标志运行会阻止'sys.path [0]'中的空字符串,但这不会改变从磁盘回溯读取的行为 – wim