来自 Python BufferedIO 对象的 C `FILE` 流
C `FILE` stream from Python BufferedIO object
我正在为需要 FILE *
句柄作为输入的 C 库函数编写 Python 绑定。
我希望 Python 调用者将一个打开的 io.BufferedReader
对象传递给函数,以保留对句柄的控制,例如:
with open(fname, 'rb') as fh:
my_c_function(fh)
因此,我不想在C函数内部传递文件名和打开句柄。
我的 C 包装器大致如下所示:
PyObject *my_c_function (PyObject *self, PyObject *args)
{
FILE *fh;
if (! PyArgs_ParseTuple (args, "?", &fh)) return NULL;
my_c_lib_function (fh);
// [...]
}
显然我无法弄清楚我应该为 "?"
使用什么符号,或者我是否应该使用与 PyArgs_ParseTuple
不同的方法。
Python C API 文档似乎没有提供任何关于如何处理缓冲 IO 对象的示例(据我了解,Buffer 协议适用于字节对象和 co.... 对吗?)
似乎我可以在我的 C 包装器中查看 Python 句柄对象的文件描述符(就像调用 fileno()
)并使用 [=18 从中创建一个 C 文件句柄=].
几个问题:
- 这是最方便的方式吗?还是Python C API中有我没有看到的内置方法?
fileno()
文档提到:“Return 流的底层文件描述符(整数)(如果存在)。如果 IO 对象不使用文件描述符,则会引发 OSError。 “在什么情况下会发生这种情况?如果我传递由 open()
以外的人在 Python 中创建的文件句柄怎么办?
- 在Python打开的只读fd上打开一个只读的C句柄似乎很安全,应该保证在C函数之后关闭句柄;但是,有人能想到这种方法有什么缺陷吗?
不确定这是否是最合理的方式,但我在 Linux 中通过以下方式解决了它:
static PyObject *
get_fh_from_python_fh (PyObject *self, PyObject *args)
{
PyObject *buf, *fileno_fn, *fileno_obj, *fileno_args;
if (! PyArg_ParseTuple (args, "O", &buf)) return NULL;
// Get the file descriptor from the Python BufferedIO object.
// FIXME This is not sure to be reliable. See
// https://docs.python.org/3/library/io.html#io.IOBase.fileno
if (! (fileno_fn = PyObject_GetAttrString (buf, "fileno"))) {
PyErr_SetString (PyExc_TypeError, "Object has no fileno function.");
return NULL;
}
fileno_args = PyTuple_New(0);
if (! (fileno_obj = PyObject_CallObject (fileno_fn, fileno_args))) {
PyErr_SetString (PyExc_SystemError, "Error calling fileno function.");
return NULL;
}
int fd = PyLong_AsSize_t (fileno_obj);
/*
* From the Linux man page:
*
* > The file descriptor is not dup'ed, and will be closed when the stream
* > created by fdopen() is closed. The result of applying fdopen() to a
* > shared memory object is undefined.
*
* This handle must not be closed. Leave open for the Python caller to
* handle it.
*/
FILE *fh = fdopen (fd, "r");
// rest of the code...
}
这只考虑了 Linux,但到目前为止它做了它需要做的事情。更好的方法是深入了解 BufferedReader 对象,甚至可能在其中找到 FILE *
;但如果这不是 Python API 的一部分,它可能会在未来的版本中被破坏。
我正在为需要 FILE *
句柄作为输入的 C 库函数编写 Python 绑定。
我希望 Python 调用者将一个打开的 io.BufferedReader
对象传递给函数,以保留对句柄的控制,例如:
with open(fname, 'rb') as fh:
my_c_function(fh)
因此,我不想在C函数内部传递文件名和打开句柄。
我的 C 包装器大致如下所示:
PyObject *my_c_function (PyObject *self, PyObject *args)
{
FILE *fh;
if (! PyArgs_ParseTuple (args, "?", &fh)) return NULL;
my_c_lib_function (fh);
// [...]
}
显然我无法弄清楚我应该为 "?"
使用什么符号,或者我是否应该使用与 PyArgs_ParseTuple
不同的方法。
Python C API 文档似乎没有提供任何关于如何处理缓冲 IO 对象的示例(据我了解,Buffer 协议适用于字节对象和 co.... 对吗?)
似乎我可以在我的 C 包装器中查看 Python 句柄对象的文件描述符(就像调用 fileno()
)并使用 [=18 从中创建一个 C 文件句柄=].
几个问题:
- 这是最方便的方式吗?还是Python C API中有我没有看到的内置方法?
fileno()
文档提到:“Return 流的底层文件描述符(整数)(如果存在)。如果 IO 对象不使用文件描述符,则会引发 OSError。 “在什么情况下会发生这种情况?如果我传递由open()
以外的人在 Python 中创建的文件句柄怎么办?- 在Python打开的只读fd上打开一个只读的C句柄似乎很安全,应该保证在C函数之后关闭句柄;但是,有人能想到这种方法有什么缺陷吗?
不确定这是否是最合理的方式,但我在 Linux 中通过以下方式解决了它:
static PyObject *
get_fh_from_python_fh (PyObject *self, PyObject *args)
{
PyObject *buf, *fileno_fn, *fileno_obj, *fileno_args;
if (! PyArg_ParseTuple (args, "O", &buf)) return NULL;
// Get the file descriptor from the Python BufferedIO object.
// FIXME This is not sure to be reliable. See
// https://docs.python.org/3/library/io.html#io.IOBase.fileno
if (! (fileno_fn = PyObject_GetAttrString (buf, "fileno"))) {
PyErr_SetString (PyExc_TypeError, "Object has no fileno function.");
return NULL;
}
fileno_args = PyTuple_New(0);
if (! (fileno_obj = PyObject_CallObject (fileno_fn, fileno_args))) {
PyErr_SetString (PyExc_SystemError, "Error calling fileno function.");
return NULL;
}
int fd = PyLong_AsSize_t (fileno_obj);
/*
* From the Linux man page:
*
* > The file descriptor is not dup'ed, and will be closed when the stream
* > created by fdopen() is closed. The result of applying fdopen() to a
* > shared memory object is undefined.
*
* This handle must not be closed. Leave open for the Python caller to
* handle it.
*/
FILE *fh = fdopen (fd, "r");
// rest of the code...
}
这只考虑了 Linux,但到目前为止它做了它需要做的事情。更好的方法是深入了解 BufferedReader 对象,甚至可能在其中找到 FILE *
;但如果这不是 Python API 的一部分,它可能会在未来的版本中被破坏。