微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

来自 Python BufferedIO 对象的 C `FILE` 流

如何解决来自 Python BufferedIO 对象的 C `FILE` 流

我正在为需要 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())并使用 fdopen() 从中创建一个 C 文件句柄。

几个问题:

  1. 这是最方便的方式吗?还是 Python C API 中有我没有看到的内置方法
  2. fileno() 文档提到:“如果存在,则返回流的底层文件描述符(整数)。如果 IO 对象不使用文件描述符,则会引发 OSError。”在什么情况下会发生这种情况?如果我传递一个不是 open() 在 Python 中创建的文件句柄怎么办?
  3. 在 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 的一部分,它可能会在未来的版本中被破坏。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。