方法映射表是PyMethodDef结构的数组,
struct PyMethodDef {
char *ml_name;
PyCFunction ml_meth;
int ml_flags;
char *ml_doc;
};
如果ml_name是在解释器中访问的函数的名称(即gc.collect()),则 ml_meth 是上一节中描述的函数的地址,ml_flags表示使用哪个签名,ml_doc是函数的文档字符串。
现在,假设我想从gcmodule.c gc_list_append中访问它,它不是由gc接口公开的。
我试过以下几种方法:
pythonapi.PyImport_ImportModule("gcmodule")
或
pythonapi.PyImport_ImportModule(ctypes.c_wchar("gcmodule"))
希望返回的引用可以用于访问未公开的方法,但在这两种情况下,我都会得到错误:
ImportError: No module named 'g'
或
TypeError: one character unicode string expected
知道如何通过ctype/pythonapi访问未公开的函数或数据结构吗?
发布于 2014-05-13 04:25:49
PyImport_ImportModule
导入一个由const char *
参数命名的模块。它调用PyImport_Import
,然后在PY2中调用__builtin__.__import__
。如果使用C字符串"gc"
调用它,则返回对sys.modules['gc']
的引用,否则解释器通过调用initgc
创建模块,在_PyImport_Inittab
中可以找到该模块。gc模块的init函数调用Py_InitModule4
,它循环在GcMethods
表上向模块dict添加方法。
在gcmodule.c中标记为static
的每个全局定义仅在系统级别的编译单元中可见,即它没有外部链接。通常,扩展模块只导出PyMODINIT_FUNC
的名称,但CPython中的几个内置模块也会导出公共API(如PyObject_GC_Track
)。
在Windows上,按名称或序号从DLL导出符号需要在.def文件中或用修饰符__declspec(dllexport)
声明符号。在CPython中,它通过宏PyAPI_FUNC
(pyport.h)、PyAPI_DATA
和PyMODINIT_FUNC
应用。例如,objind.h声明了PyAPI_FUNC(void) PyObject_GC_Track(void *)
。
声明符号static
是命名空间管理。但是,如果您可以以某种方式获得函数的地址,那么当然可以使用ctype函数指针来调用它。找到地址才是问题所在。ctypes使用标准的POSIX dlsym
和GetProcAddress
函数来查找导出的符号,这在这里帮不上忙。您可以访问CPython对象直接或间接引用的函数指针,因为id
返回对象基地址。
下面的内容与你的问题没有直接关系,但我把它包括在内是为了大家的兴趣。
在用dlopen
加载扩展模块的POSIX系统上,可以指定RTLD_GLOBAL
将动态符号合并到全局命名空间中。例如,_ctypes
扩展模块是从多个对象文件链接的,需要几个extern
符号,例如PyCData_set
。您可以调整导入,使PyCData_set
可以全局访问(例如,通过ctypes.pythonapi
):
>>> import sys, DLFCN
>>> sys.setdlopenflags(DLFCN.RTLD_GLOBAL | DLFCN.RTLD_NOW)
>>> import ctypes
>>> hasattr(ctypes.pythonapi, 'PyCData_set')
True
当然,这在Windows上是行不通的。扩展模块使C可用的正确方法是使用PyCapsule
,例如datetime.datetime_CAPI
(datetimemodule.c)。
https://stackoverflow.com/questions/23621958
复制相似问题