使用 cython 从多个 pyx 文件创建可执行文件

2025-03-04 08:24:00
admin
原创
82
摘要:问题描述:我正在尝试从我的 python 源文件创建一个 unix 可执行文件。我有两个文件p1.py,p2.pyp1.py:from p2 import test_func print (test_func()) p2.py:def test_func(): return ('Test') 现在,...

问题描述:

我正在尝试从我的 python 源文件创建一个 unix 可执行文件。

我有两个文件p1.pyp2.py

p1.py:

from p2 import test_func 
print (test_func())

p2.py:

def test_func():
    return ('Test')

现在,我们可以看到p1.py依赖于p2.py。我想通过将两个文件合并在一起来制作一个可执行文件。我正在使用 cython。

我将文件名分别改为p1.pyxp2.pyx

现在,我可以使用 cython 使文件可执行,

cython p1.pyx --embed

它将生成一个名为的 C 源文件p1.c。接下来我们可以使用 gcc 使其可执行,

gcc -Os -I /usr/include/python3.5m -o test p1.c -lpython3.5m -lpthread -lm -lutil -ldl 

但是如何将两个文件合并为一个可执行文件?


解决方案 1:

您必须跳过一些循环才能使其工作。

首先,你必须意识到,生成的可执行文件是一个非常薄的层,它只是将整个工作委托给(即调用函数)pythonX.Ym.so。你可以在调用时看到这种依赖关系

ldd test
...
libpythonX.Ym.so.1.0 => not found
...

因此,要运行该程序,您要么需要显示LD_LIBRARY_PATH到的位置,libpythonX.Ym.so要么使用选项构建 exe --rpath,否则在动态加载程序启动时test将引发类似的错误

/test:加载共享库时出错:libpythonX.Ym.so.1.0:无法打开共享对象文件:没有此文件或目录

通用构建命令如下所示:

gcc -fPIC <other flags> -o test p1.c -I<path_python_include> -L<path_python_lib> -Wl,-rpath=<path_python_lib> -lpython3.6m <other_needed_libs>

还可以针对 python 库的静态版本进行构建,从而消除对 libpythonX.Ym 的运行时依赖,例如参见此SO-post。


生成的可执行文件的test行为与 Python 解释器完全相同。这意味着现在test将失败,因为它找不到模块p2

一个简单的解决方案是就地对 p2 模块进行 cythonize(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 ...

但是现在出现了一个新的(或旧的)问题:生成的test可执行文件无法再次找到模块,因为在 python-path 上p2没有p2.py和 没有。p2.so

关于这个问题有两个类似的 SO 问题,这里和这里。 在你的情况下,提出的解决方案有点过头了,这里在将 p2 模块导入到p1.pyx-file 之前初始化它就足以使其工作:

# 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是不够的(例如,参见此 SO-post)。要关闭此多阶段初始化,-DCYTHON_PEP489_MULTI_PHASE_INIT=0应传递给 gcc 或类似于其他编译器。


注意:但是,即使完成了上述所有操作,嵌入式解释器仍需要其标准库(例如,参见此SO-post)——要使其真正独立,还有很多工作要做!所以也许应该听取@DavidW 的建议:

“不要这样做”可能是绝大多数人最好的解决办法。


警告:如果我们声明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声明。

解决方案 2:

人们倾向于这样做,因为对于最简单的情况(一个模块,没有依赖关系)来说,这相当容易做到。@ead 的答案很好,但老实说相当繁琐,它正在处理下一个最简单的情况(两个你完全控制的模块,没有依赖关系)。

一般来说,Python 程序将依赖于一系列外部模块。Python 附带一个大型标准库,大多数程序都会在一定程度上使用它。有各种各样的第三方数学库、GUI 库和 Web 框架库。即使通过这些库跟踪这些依赖关系并找出需要构建的内容也很复杂,PyInstaller 等工具尝试了这一点,但并不是 100% 可靠。

在编译所有这些 Python 模块时,您可能会遇到一些 Cython 不兼容/错误。它通常相当不错,但在自省等功能方面存在困难,因此大型项目不太可能干净完整地编译。

最重要的是,许多模块都是用 C 语言或 SWIG、F2Py、Cython、boost-python 等工具编写的编译模块。这些编译模块可能有自己独特的特性,因此很难将它们链接在一起形成一个大块。

总而言之,这可能是可行的,但对于非平凡的程序来说,无论它看起来多么有吸引力,这都不是一个好主意。像 PyInstaller、Py2Exe 和 PyOxidizer 这样的工具使用更简单的方法(将所有内容捆绑到一个巨大的 zip 文件中),更适合这项任务(即使这样,它们也很难真正强大)。


请注意,发布此答案的目的是使该问题成为此问题的典型副本。虽然显示如何完成的答案很有用,但“不要这样做”可能是绝大多数人的最佳解决方案。

解决方案 3:

这不可能直接实现,但是您可以通过简单的破解来实现,只需根据需要创建多个文件,管理您的代码,然后在您的 setup.py 中将它们全部合并在一起并进行编译。

import os

# Check if file exists then remove it
if os.path.exists("merged.pyx"):
    os.remove("merged.pyx")
else:
    print("The file does not exist")

# merge.py
filenames = ['part1.pyx', 'part2.pyx', 'part3.pyx']
with open('merged.pyx', 'w') as outfile:
    # This will clear the file before writing to it
    outfile.truncate(0)
    for fname in filenames:
        with open(fname) as infile:
            outfile.write(infile.read())
            outfile.write('
')  # Add a newline character at the end of each file

# Rest of your merge script...

非常简单,而且非常有效。

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   3892  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   2717  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Freshdesk、ClickUp、nTask、Hubstaff、Plutio、Productive、Targa、Bonsai、Wrike。在当今快速变化的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多企业在项目管理过程中面临着诸多痛点,如任务分配不...
项目管理系统   52  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Monday、TeamGantt、Filestage、Chanty、Visor、Smartsheet、Productive、Quire、Planview。在当今快速变化的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多项目经理和团队在管理复杂项目时,常...
开源项目管理工具   54  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Smartsheet、GanttPRO、Backlog、Visor、ResourceGuru、Productive、Xebrio、Hive、Quire。在当今快节奏的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多企业在选择项目管理工具时常常面临困惑:...
项目管理系统   49  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用