Java调用PyInstaller编译的Python二进制文件

Java调用PyInstaller编译的Python二进制文件

Java调用PyInstaller打包的Python脚本二进制文件 这是一个悲伤的故事,事情的起因是业务需要做一些简单的数据分析,然后业务组有一位数学分析的海归硕士,就提出,分析核心功能他们自己实现,我们这边直接调用就行。实事真是如此吗?

在定方案的时候,我说就,既然你们做数据分析,那么,你们就基于Python实现一套完整逻辑的接口,基于HTTP或者GRPC给我们都可以。然而,数据分析的同事仅是学数据分析的,掌握Python的程度并没有那么深,只是在数据分析相关的Python库有所造诣。为了降低难度,最后选择了我们用Java去执行Python脚本,在Windows上开发的时候,一切也比较顺利,毕竟Python环境一安装,相关依赖安装好,就可以直接使用 Runtime.getRuntime().exec() 进行调用。

但是在我们部署到Docker上的时候,就遇到了一些麻烦。我们的Docker是基于openjdk:8u252-jdk的基础镜像,每次在打包镜像安装Python环境,安装依赖也比较麻烦。后来就想了一个办法,要不就直接将脚本编译为独立的二进制文件,这样依赖,直接复制过去就可以直接执行了。

在整个过程也遇到了一些问题,这里便记录一下。

问题1

选择什么编译Python脚本呢?处于部署环境的原因,我们需要在pyinstallernuitka之间做选择。

PyInstaller

  • 安装简便,可以通过pip轻松安装
  • 提供了丰富的文档和教程,帮助用户快速上手
  • 打包速度较快,特别是在处理小型项目时
  • 生成的可执行文件体积相对较大,这可能会影响应用的性能和加载时间
  • 支持跨平台操作,包括Windows、Linux和macOS等操作系统
  • 自动检测Python脚本的依赖项,并将它们打包到生成的可执行文件中
  • 兼容性较好,能够正确打包许多主要的Python包
  • 生成的可执行文件虽然看起来更加安全,但由于其仍然依赖于解释器,因此存在被逆向工程的风险
  • 支持单文件发布,可以将整个Python应用程序及其所有依赖项打包成一个单独的可执行文件

Nuitka

  • 安装简便,可以通过pip轻松安装
  • 相对于PyInstaller,Nuitka的编译过程可能稍显复杂
  • 打包速度相对较慢,尤其是在处理大型项目时
  • 生成的C代码可执行文件体积较小,且运行效率更高
  • 通过将Python代码编译为C代码,Nuitka显著提高了代码的执行速度,特别是在数值计算和数据处理方面。
  • 可以编译大部分Python代码,并且生成的C代码可以与其他C代码库集成,但由于生成的C代码依赖于目标平台的C编译器,因此其可移植性相对较差
  • 将Python代码编译为C代码后,增加了代码的保护性,降低了被逆向工程的可能性

鉴于目前的情况,我们并不需要在这方面投入太多时间,就选择了较为简单的PyInstallerPyInstaller会将所有必要的文件和资源(包括Python解释器、依赖库文件和程序代码)一起打包到一个二进制文件中。这个二进制文件是独立的可执行文件,可以直接在目标环境中运行,当运行可执行文件时,PyInstaller会模拟一个完整的Python环境,包括加载必要的依赖项和模块。这使得Python程序可以在没有实际Python解释器的环境中运行。

问题2

1
2
3
4
Could not fetch URL https://pypi.org/simple/numpy/: There was a problem confirming the ssl certificate: HTTPSConnectionPool(host='pypi.org', port=443): Max retries exceeded with url: /simple/numpy/ (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1091)'))) - skipping
ERROR: Could not find a version that satisfies the requirement numpy (from versions: none)
ERROR: No matching distribution found for numpy
Could not fetch URL https://pypi.org/simple/pip/: There was a problem confirming the ssl certificate: HTTPSConnectionPool(host='pypi.org', port=443): Max retries exceeded with url: /simple/pip/ (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1091)'))) - skipping

这是默认镜像地址的原因,可修改默认的镜像地址,为了方便,我们直接在下载的时候,带上镜像源

1
pip install seaborn -i http://mirrors.aliyun.com/pypi/simple --trusted-host mirrors.aliyun.com

镜像源地址,大家可以在网上查一下,我这里用的是阿里的镜像源http://mirrors.aliyun.com/pypi/simple

问题3

1
2
findfont: Font family ['sans-serif'] not found. Falling back to DejaVu Sans.
findfont: Generic family 'sans-serif' not found because none of the following families were found: SimHei

我们在程序里面设置了中文字体

1
2
3
4
# 设置中文显示字体
mpl.rcParams["font.sans-serif"] = ["SimHei"]
mpl.rcParams["axes.unicode_minus"] = False
plt.rcParams['font.sans-serif'] = ['SimHei']

但是在Linux中没有这个字体,所以,就需要下载对应的字段。

我们可以在其他地方去找到SimHei.ttf然后复制到linux的/usr/share/fonts/中。

问题4-PyInstaller编译后,依赖不存在

程序里面有引用,但是一直报找不到依赖。通过--hidden-import参数,执行漏掉的依赖。

PyInstaller命令简单介绍

PyInstaller命令的基本语法如下:

1
pyinstaller [options] script[.py]

其中,script是要打包的Python脚本文件的文件名,可以带.py后缀;options是可选的命令行选项,可以用来指定打包的方式、输出文件的位置等。

1
2
3
4
5
6
7
8
9
10
-F或--onefile 将所有文件打包为一个单独的可执行文件。
-D或--onedir 将所有文件打包为一个目录,包含可执行文件和所有依赖的文件。
-c或--console 将程序与命令提示符结合在一起,以便在命令提示符下运行。
-w或--windowed 打包为窗口文件,不显示控制台窗口。这个选项适用于图形用户界面(GUI)应用程序。
-n或--name 指定输出文件的名称。
-i或--icon 为Windows可执行文件指定图标。这个选项允许用户为生成的可执行文件添加自定义图标。
--add-data 添加非Python文件或目录到打包文件中。这个选项允许用户将额外的数据文件或目录包含到打包文件中。
--hidden-import 手动指定需要导入的模块。 这个选项用于解决某些模块在打包时未被自动导入的问题。
-d或--debug 将调试信息打包进可执行文件中。这个选项用于生成包含调试信息的可执行文件,便于调试和排查问题。
-p DIR, --path=DIR 设置导入路径,从而导入需要的模块。

最终,使用pyinstaller -d noarchive -F --hidden-import seaborn DY.py将我们的脚本编译为二进制,编译后的文件是真的大的,接近50MB

最后,我们直接在代码中通过Runtime.getRuntime().exec()来调用编译好的二进制文件。

在Dockerfile中,我们加入相应的文件

1
2
3
4
5
6
7
8
FROM openjdk:8u252-jdk


WORKDIR /home
COPY src/main/resources/python/GT /home
RUN chmod -R 777 GT
COPY target/*.jar /home
COPY /fonts/*.ttf /usr/share/fonts/

这样一来字体加二进制文件,整个镜像又多了60MB+,虽然目的咱们达到了,但是这种方式,我…

Java调用PyInstaller编译的Python二进制文件

https://blogs.52fx.biz/posts/1519181065.html

作者

eyiadmin

发布于

2024-11-26

更新于

2024-11-26

许可协议

评论