os.fdopen() 的语义是什么?

4

我曾认为os.fdopen()要么接受文件描述符并返回文件io对象,要么引发异常。

例如:

fd = os.open("/etc/passwd", os.O_RDONLY)
try: os.fdopen(fd, "w")
except: os.close(fd)  # prevent memory leak

然而,这些语义似乎并不总是成立。 下面是在OSX上的一个例子:
In [1]: import os

In [2]: os.open("/", os.O_RDONLY, 0660)
Out[2]: 5

In [3]: os.fdopen(5, "rb")
---------------------------------------------------------------------------
IOError                                   Traceback (most recent call last)
<ipython-input-3-3ca4d619250e> in <module>()
----> 1 os.fdopen(5, "rb")

IOError: [Errno 21] Is a directory: '<fdopen>'

In [4]: os.close(5)
---------------------------------------------------------------------------
OSError                                   Traceback (most recent call last)
<ipython-input-4-76713e571514> in <module>()
----> 1 os.close(5)

OSError: [Errno 9] Bad file descriptor

看起来os.fdopen()既吃掉了我的文件描述符5,又引发了异常...

有没有安全的方法使用os.fdopen()

我错过了什么吗?

我找到了一个 bug 吗?

P.S. Python 版本字符串为Python 2.7.6 (v2.7.6:3a1db0d2747e, Nov 10 2013, 00:42:54),以防有人无法复现。

P.P.S. 在 Py2.7 Linux 上也存在同样的问题。但是在 Py3.3 中不会出现此问题。


嗯,我真的不明白有什么不清楚的。在操作系统级别上,您可以像打开文件一样打开目录并尝试读取它(虽然opendirreaddir做得更好)。虽然Python文件确实是文件,但当您尝试在文件目录描述符上创建文件对象时,它会因异常而中断。就是这样。 - user3159253
2
问题是 os.fdopen 是否允许在不返回文件对象的情况下使用文件描述符。 - Dima Tisnek
1个回答

2
Python在创建文件对象并将其存储在Python对象中后,会检查生成的FILE*是否指向目录。由于目录检查出现错误,文件对象被取消引用(因为不会返回),这会导致析构函数被调用以关闭文件。
我认为如果文档能展示fdopen对传入的文件描述符所产生的影响会更好。我不确定您想要什么样的“安全”使用fdopen。如果您打算在失败时关闭文件描述符,那么Python关闭该文件有何关系?只需使用即可。
try: os.close(fd)
except: pass

为了消除次要异常,需要进行翻译。

fill_file_fields函数被PyFile_FromFile调用,以填充文件对象的成员,并在字段被填充后调用dircheck函数。这导致fill_file_fields返回NULL,因此PyFile_FromFile执行Py_DECREF(f);,其中f是文件对象。由于这是最后一个引用,因此调用解分配器file_dealloc,该解分配器调用close_the_file,该函数(惊喜吧)关闭文件。

在3.4分支中,fileio_init执行dircheck,并使用标志变量fd_is_own来确定是否应在错误条件下关闭文件。


1
好的解释。然而,你的 try/except 并不安全。如果 fdopen 静默关闭了 fd,并且另一个操作(例如线程或日志记录)创建了一个文件描述符,则该其他文件描述符将具有相同的编号,您会无意中关闭它。 - Dima Tisnek
你能告诉我在CPython源代码中实际的位置,文件对象被取消引用吗? - Dima Tisnek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接