我有一个库函数(用C编写),它通过将输出写入/* Library function */
void write_numbers(FILE * f, int arg1, int arg2)
{
fprintf(f, "%d %d\n", arg1, arg2);
}
来生成文本。我想在Python(2.7.x)中使用创建临时文件或管道的代码将其包装,将其传递给函数,从文件中读取结果,并将其作为Python字符串返回。
这是一个简单的例子来说明我之后的事情:
from ctypes import *
mylib = CDLL('mylib.so')
def write_numbers( a, b ):
rd, wr = os.pipe()
write_fp = MAGIC_HERE(wr)
mylib.write_numbers(write_fp, a, b)
os.close(wr)
read_file = os.fdopen(rd)
res = read_file.read()
read_file.close()
return res
#Should result in '1 2\n' being printed.
print write_numbers(1,2)
Python包装器:
MAGIC_HERE()
我想知道ctypes
最好的选择是什么。
我很想使用libc.fdopen()
并创建一个返回Python c_void_t的 editForm.$rollbackViewValue()
包装器,然后将其传递给库函数。我觉得这在理论上应该是安全的 - 只是想知道这种方法是否存在问题,或者现有的Python主题是否存在问题。
此外,这将进入一个长期运行的过程(让我们假设"永远"),因此任何泄露的文件描述符都会有问题。
答案 0 :(得分:4)
首先,请注意FILE*
是特定于stdio的实体。它不存在于系统级别。系统级中存在的东西是UNIX中的描述符(使用file.fileno()
检索)(os.pipe()
已经返回普通描述符)和处理(使用msvcrt.get_osfhandle()
检索)在Windows中。 因此,如果可以有多个C运行时,它作为库间交换格式的选择很差。如果您的库是针对另一个C编译的,那么您将遇到麻烦运行时比你的Python副本:1)结构的二进制布局可能不同(例如,由于对齐或其他成员用于调试目的,甚至不同的类型大小); 2)在Windows中,结构链接到的文件描述符也是特定于C的实体,它们的表由C运行时内部 1 维护。
此外,在Python 3中,对I / O进行了大修,以便从stdio
中解开它。因此,FILE*
与Python风格不同(可能也是大多数非C风格)。
现在,您需要的是
fdopen()
(或等效的)。(Python的一个座右铭是 "让事情变得容易而且错误的事情很难"毕竟)
最干净的方法是使用图书馆链接的精确实例(请祈祷它与动态链接,或者没有导出的符号来调用)
对于第一项,我找不到任何可以分析加载的动态模块的Python模块。元数据,以找出它已链接到哪些DLL(只是一个名称甚至名称+版本是不够的,你知道,由于系统上可能有多个库实例)。虽然它的格式信息广泛可用,但它绝对是可能的。
对于第二项,它是一个微不足道的ctypes.cdll('path').fdopen
(MSVCRT的_fdopen
)。
其次,您可以执行一个小帮助程序模块,该模块将针对与库相同(或保证兼容)的运行时进行编译,并将为您执行上述描述符/句柄的转换。这实际上是正确编辑库的一种解决方法。
最后,通过ctypes.pythonapi
提供的Python C API,使用Python的C运行时实例(所有上述警告全部适用)是最简单(也是最脏)的方法。它利用了
stdio
FILE*
的包装器(Python 3&#39}不是)PyFile_AsFile
返回包装FILE*
的API(请注意it's missing from Python 3)
fd
,您需要首先构造一个类似文件的对象(以便返回FILE*
;)>>> open("test.txt")
<open file 'test.txt', mode 'r' at 0x017F8F40>
>>> f=_
>>> f.fileno()
3
>>> ctypes.pythonapi
<PyDLL 'python dll', handle 1e000000 at 12808b0>
>>> api=_
>>> api.PyFile_AsFile
<_FuncPtr object at 0x018557B0>
>>> api.PyFile_AsFile.restype=ctypes.c_void_p #as per ctypes docs,
# pythonapi assumes all fns
# to return int by default
>>> api.PyFile_AsFile.argtypes=(ctypes.c_void_p,) # as of 2.7.10, long integers are
#silently truncated to ints, see http://bugs.python.org/issue24747
>>> api.PyFile_AsFile(id(f))
2019259400
请记住,使用 fd
和C指针,您需要手动确保适当的对象生命周期!
os.fdopen()
返回的类似文件的对象会关闭.close()
上的描述符
os.dup()
重复描述符PyFile_IncUseCount()
/ PyFile_DecUseCount()
调整相应对象的引用计数。iter(f)
/ for l in f
以来,内部缓存完全独立于stdio
的缓存)