我想从我的python源文件中创建一个unix可执行文件.
我有两个文件,p1.py和p2.py
p1.py: –
from p2 import test_func
print (test_func())
p2.py: –
def test_func():
return ('Test')
现在,我们可以看到p1.py依赖于p2.py.我想通过将两个文件组合在一起来制作可执行文件.我正在使用cython.
我将文件名分别更改为p1.pyx和p2.pyx.
现在,我可以使用cython使文件可执行,
cython p1.pyx --embed
它将生成一个名为p1.c的C源文件.接下来我们可以使用gcc使其可执行,
gcc -Os -I /usr/include/python3.5m -o test p1.c -lpython3.5m -lpthread -lm -lutil -ldl
但是如何将两个文件合并为一个可执行文件?
解决方法:
有一些循环你必须跳过才能使它工作.
首先,您必须意识到生成的可执行文件是一个非常纤薄的层,它将整个工作委托给(即调用函数)pythonX.Ym.so.调用时可以看到这种依赖关系
ldd test
...
libpythonX.Ym.so.1.0 => not found
...
因此,要运行该程序,您需要将LD_LIBRARY_PATH显示到libpythonX.Ym.so的位置,或者使用–rpath选项构建exe,否则在测试动态加载程序启动时将抛出类似于
/test: error while loading shared libraries: libpythonX.Ym.so.1.0: cannot open shared object file: No such file or directory
通用构建命令如下所示:
gcc -fPIC -o test p1.c -I -L -Wl,-rpath= -lpython3.6m
生成的可执行测试的行为与它是python-interpreter的行为完全相同.这意味着现在,测试将失败,因为它将找不到模块p2.
一个简单的解决方案是将p2模块进行cython化(cythonize p2.pyx -i),你将获得所需的行为 – 但是,你必须分发生成的共享对象p2.so以及test.
将两个扩展名捆绑成一个可执行文件很容易 – 只需将两个cythonized c文件传递给gcc即可:
# creates p1.c:
cython --empbed p1.pyx
# creates p2.c:
cython p2.pyx
gcc ... -o test p1.c p2.c ...
但现在出现了一个新的(或旧的)问题:生成的测试可执行文件不能再次找到模块p2,因为python-path上没有p2.py且没有p2.so.
关于这个问题,有两个类似的SO问题,here和here.在你的情况下,建议的解决方案有点矫枉过正,这里足以初始化p2模块,然后在p1.pyx文件中导入它以使其工作:
# making init-function from other modules accessible:
cdef extern object PyInit_p2();
#init/load p2-module manually
PyInit_p2() #Cython handles error, i.e. if NULL returned
# actually using already cached imported module
# no search in python path needed
from p2 import test_func
print(test_func())
在导入模块之前调用模块的init函数(实际上模块不会第二次真正导入,只在缓存中查找),如果模块之间存在循环依赖关系,也可以工作.例如,如果模块p2导入模块p3,它将轮流导入p2.
由于Cython 0.29,Cython使用默认的Python> = 3.5进行多阶段初始化,因此调用PyInit_p2是不够的(参见例如this SO-post).要关闭此多阶段初始化-DCYTHON_PEP489_MULTI_PHASE_INIT = 0应该传递给gcc或类似于其他编译器.
一句警告:如果我们将PyInit_p2()声明为
from cpython cimport PyObject
cdef extern PyObject *PyInit_p2();
PyInit_p2(); # TODO: error handling if NULL is returned
Cython将不再处理错误和我们的责任.代替
PyObject *__pyx_t_1 = NULL;
__pyx_t_1 = PyInit_p2(); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 4, __pyx_L1_error)
__Pyx_GOTREF(__pyx_t_1);
__Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
为object-version生成,生成的代码变为:
(void)(PyInit_p2());
即没有错误检查!
另一方面使用
cdef extern from *:
"""
PyObject *PyInit_p2(void);
"""
object PyInit_p2()
将无法使用g – 必须将extern C添加到声明中.
标签:python,cython,cythonize