PyInstaller 生成的 exe 在另一台机器上安静地失败

Exe generated by PyInstaller fails quietly on a different machine

我无法让 PyInstaller 在一台 PC(PC-Good)上运行另一台(PC-Bad)上生成的 exe。

代码见下方:

print('Start')
import matplotlib
matplotlib.use('Agg')

import matplotlib.pyplot as plt
print("Import of matplotlib & pyplot successful")

plt.set_loglevel("debug")

x = [0, 1, 2, 3, 4]
y = [4, 3, 2, 1, 0]
print('list creation successful')

try:
    fig, ax = plt.subplots(1, 1, figsize=(12, 6))
except Exception as exception:
    print(exception.__class__.__name__ + ": ", + exception.message)
finally:
    print('run subplots() successful')

plt.scatter(x, y)
print('plot creation successful')
plt.savefig('saved_plot.png')
print('code complete')

PC 上的输出良好:

Start
C:\tools\miniconda3\envs\bcht\lib\site-packages\PyInstaller\loader\pyimod03_importers.py:623: MatplotlibDeprecationWarning:
The MATPLOTLIBDATA environment variable was deprecated in Matplotlib 3.1 and will be removed in 3.3.
  exec(bytecode, module.__dict__)
Import of matplotlib & pyplot successful
list creation successful
run subplots() successful
plot creation successful
DEBUG:matplotlib.font_manager:findfont: ...
(and many more matplotlib DEBUG messages)
code complete

PC 上的输出错误:

Start
C:\tools\miniconda3\envs\bcht\lib\site-packages\PyInstaller\loader\pyimod03_importers.py:623: MatplotlibDeprecationWarning:
The MATPLOTLIBDATA environment variable was deprecated in Matplotlib 3.1 and will be removed in 3.3.
Import of matplotlib & pyplot successful
list creation successful

由于没有错误输出,我完全不知道下一步该去哪里。 Matplotlib 文档没有提供任何额外的技巧来为绘图或子图等方法获取更精细的调试消息。有没有其他人观察到这样的问题或知道修复方法?或者有人知道是否有办法让 Matplotlib 告诉我更多信息吗?

这可能有多种原因。

  1. Pyinstaller 没有“完全”包含模块:pyinstaller 试图通过仅包含应用程序使用的那些模块部分来减小大小。它可能没有检测到模块的某些重要部分的使用并且没有包含它。尝试在一个目录模式下使用 pyinstaller。然后你就可以看到pynstaller选择的模块了。如果缺少某些内容,那么下次您可以指定包含该模块。

  2. 尝试在您的代码中放置 print("reached some function") 这样的检查点以进行调试。这样可以更容易地找到问题所在。

  3. 也尝试在目标计算机上 运行 正常 python 中的代码。

我找到了这个问题的解决方法,但由于 PyInstaller 和 MatPlotlib 都没有提供任何调试消息来进一步调查根本原因,所以我还不能称之为修复。尽管如此,它还是解决了我看到的问题。

TL;DR

  • 问题是由于使用 PyInstaller 生成带有 Python 代码的可执行文件与 matplotlib 3.1.3 和 numpy 1.18.1 时不兼容。这种不兼容性不会导致 python 脚本本身出错。这种不兼容性不会产生任何指向自身的错误消息或症状,因此很难找出根本原因。
  • 如果有人观察到冻结的可执行文件在没有任何错误消息的情况下静默退出。值得调查这是否是由于库不兼容造成的。如果在执行 mpl 方法期间发生退出,则立即查看 numpy。

现在 TL: 一开始怀疑这个问题是由于虚拟环境或 conda 干扰了冻结过程(它们不是)所以我用另一台计算机安装了干净的 python (3.7.9) 和 matplotlib 3.1.3它自动填充了所有依赖项(让我们将这个新环境称为“精简环境”,将问题陈述中的源 PC 称为“完整环境”)。我在 lean env 上复制了 bundling/freezing 过程,一切正常。此时,我注意到精益环境中的一些库比完整环境更新。所以我在完整的环境中升级了它们,包括:

numpy 从 1.18.1 到 1.20.0 kiwisolver 从 1.3.0 到 1.3.1

在此之后,完整的 env 也能够生成一个包,当 运行 在源 PC 和目标 PC 上都可以正常工作。所以问题很可能是 matplotlib 3.1.3 和这两者之一之间的“无声”不兼容。

为了进一步缩小范围,我在完整环境中将 numpy 恢复为 1.18.1 并重现了我看到的旧问题。就是它了。

令人沮丧的是,numpy 和 matplotlib 都没有产生任何可识别的错误(Pyinstaller 也没有)。此外,matplotlib 本身没有任何方法可以生成任何有用的详细调试消息来进一步调查。 (我确实打开了 matplotlib 的 log_level 进行调试,但只得到了一些指示正在使用的字体的信息。)因此,我不会进一步调查。解决方法足以满足我的使用。如果其他人有兴趣,请随时询问 mpl。我也不会在 pyinstaller、matplotlib 和 numpy 之间进行进一步的互操作。我只想说:

好:matplotlib==3.1.3,numpy==1.20.0,pyinstaller==3.6

不好:matplotlib==3.1.3,numpy==1.18.1,pyinstaller==3.6