我最近才开始使用 py2exe,我想将 py2exe 与 MINGW64 Python3 程序(和相应的库)一起使用。但是,我尝试的第一个示例未能构建。
之后,我找到了https://www.py2exe.org/index.cgi/TroubleshootingImportErrors页面,其中注明:
检查 zipextimporter 是否在您的系统上运行
zipextimporter 是 py2exe 的启动组件,可能会导致问题。要调试它,您将需要 _memimporter.pyd 二进制模块。这些模块可以在您的 Python 版本的二进制 py2exe 发行版中找到(我使用 7Zip 解压 .exe 发行版)。
还有一个测试脚本,但它是Python 2(该页面的最后更新时间是2011-01-07);所以我将其转换为 Python 3 语法:
import zipextimporter
zipextimporter.install()
import sys
sys.path.insert(0, "lib.zip")
import _socket
print(_socket)
# <module '_socket' from 'lib.zip\_socket.pyd'>
print(_socket.__file__)
# 'lib.zip\\_socket.pyd'
print(_socket.__loader__)
# <ZipExtensionImporter object 'lib.zip'>
# Reloading also works correctly:
print(_socket is reload(_socket))
# True
但是当我运行它时,我得到:
$ python3 test_zipextimporter.py
Traceback (most recent call last):
File "D:/msys64/tmp/test_zipextimporter.py", line 1, in <module>
import zipextimporter
File "D:/msys64/mingw64/lib/python3.11/site-packages/zipextimporter.py", line 51, in <module>
import _memimporter
ModuleNotFoundError: No module named '_memimporter'
所以,正如上面的注释所说,我需要一个
_memimporter.pyd
二进制模块;直接包里没有这个文件:
$ pacman -Ql mingw-w64-x86_64-python-py2exe | grep _mem
$
但是,注释还指出“这些模块可以在您的 Python 版本的二进制 py2exe 发行版中找到(我使用 7Zip 解压 .exe 发行版)。”目前包中有这些二进制文件:
$ pacman -Ql mingw-w64-x86_64-python-py2exe | grep '\.exe\|\.dll'
mingw-w64-x86_64-python-py2exe /mingw64/lib/python3.11/site-packages/py2exe/resources.dll
mingw-w64-x86_64-python-py2exe /mingw64/lib/python3.11/site-packages/py2exe/run-py311-mingw_x86_64.exe
mingw-w64-x86_64-python-py2exe /mingw64/lib/python3.11/site-packages/py2exe/run_ctypes_dll-py311-mingw_x86_64.dll
mingw-w64-x86_64-python-py2exe /mingw64/lib/python3.11/site-packages/py2exe/run_w-py311-mingw_x86_64.exe
这些都不能用
unzip -l
来列出;但它们可以用 7z l
列出 - 不幸的是,我没有看到任何类似 _memimporter.pyd
的东西 - 两个 .exe 都显示了这种文件结构:
$ 7z l /mingw64/lib/python3.11/site-packages/py2exe/run_w-py311-mingw_x86_64.exe
...
Date Time Attr Size Compressed Name
------------------- ----- ------------ ------------ ------------------------
2023-12-27 10:55:20 ..... 24064 24064 .text
2023-12-27 10:55:20 ..... 1024 1024 .data
2023-12-27 10:55:20 ..... 6656 6656 .rdata
2023-12-27 10:55:20 ..... 2048 2048 .pdata
2023-12-27 10:55:20 ..... 2048 2048 .xdata
2023-12-27 10:55:20 ..... 0 0 .bss
2023-12-27 10:55:20 ..... 2048 2048 .edata
2023-12-27 10:55:20 ..... 4096 4096 .idata
2023-12-27 10:55:20 ..... 512 512 .CRT
2023-12-27 10:55:20 ..... 512 512 .tls
..... 766 744 .rsrc/1033/ICON/1.ico
..... 20 20 .rsrc/1033/GROUP_ICON/1
..... 1167 1167 .rsrc/0/MANIFEST/1
2023-12-27 10:55:20 ..... 512 512 .reloc
------------------- ----- ------------ ------------ ------------------------
2023-12-27 10:55:20 45473 45451 14 files
我尝试与普通的 py2exe 下载进行比较,结果相似:
$ wget https://files.pythonhosted.org/packages/b1/07/f45b201eb8c3fea1af6a9bd9f733479aa9d009139ce2396e06db7aa778c8/py2exe-0.13.0.1-cp311-cp311-win_amd64.whl
# ...
$ mkdir py2exe_0.13.0.1
$ (cd py2exe_0.13.0.1; unzip ../py2exe-0.13.0.1-cp311-cp311-win_amd64.whl)
# ...
$ 7z l py2exe_0.13.0.1/py2exe/run-py3.11-win-amd64.exe
# ...
Date Time Attr Size Compressed Name
------------------- ----- ------------ ------------ ------------------------
2023-10-07 18:15:11 ..... 20480 20480 .text
2023-10-07 18:15:11 ..... 11264 11264 .rdata
2023-10-07 18:15:11 ..... 512 512 .data
2023-10-07 18:15:11 ..... 2048 2048 .pdata
..... 766 744 .rsrc/ICON/1.ico
..... 20 20 .rsrc/GROUP_ICON/1
..... 381 381 .rsrc/MANIFEST/1
2023-10-07 18:15:11 ..... 512 512 .reloc
------------------- ----- ------------ ------------ ------------------------
2023-10-07 18:15:11 35983 35961 8 files
我想这
_memimporter
仍然是一件事,因为毕竟我的测试脚本失败了“没有名为'_memimporter'的模块”;并且该包的 Python 代码中仍然有引用:
$ grep -rI _memimporter /mingw64/lib/python3.11/site-packages/py2exe
/mingw64/lib/python3.11/site-packages/py2exe/distutils_buildexe.py:## self.excludes.append("_memimporter") # builtin in run_*.exe and run_*.dll
/mingw64/lib/python3.11/site-packages/py2exe/hooks.py:# _memimporter can be excluded because it is built into the run-stub.
/mingw64/lib/python3.11/site-packages/py2exe/hooks.py:_memimporter
...但是,我必须问 - 这
_memimporter.pyd
仍然是一个东西吗 - 如果是,我在哪里可以找到它?
对,这很痛苦......无论如何,至于问题的第一部分 -
_memimporter
显然仍然是一件事,因为最近有讨论:
至于获得
_memimporter
,这又很棘手,因为你必须从源代码构建。有一个历史版本 https://github.com/mitre/caldera-py2exe - 但由于上面最近的补丁,最好直接从主 py2exe git 存储库直接构建:
$ git clone https://github.com/py2exe/py2exe py2exe_git
现在的问题是使用哪个命令来构建 - 不幸的是,主存储库 README.md 没有指定这一点(它只是提到
pip install py2exe
)。在历史仓库中,提到了一个命令 python setup.py bdist
,但它似乎已被弃用,因为它提出了:
D:/msys64/mingw64/lib/python3.11/site-packages/setuptools/__init__.py:80: _DeprecatedInstaller: setuptools.ins
taller and fetch_build_eggs are deprecated.
!!
********************************************************************************
Requirements should be satisfied by a PEP 517 installer.
If you are using pip, you can try `pip install --use-pep517`.
********************************************************************************
!!
dist.fetch_build_eggs(dist.setup_requires)
我终于在相应的MINGW-packages/mingw-w64-python-py2exe/PKGBUILD文件中找到了MINGW64包用来构建的命令。
另外,请注意,我必须安装:
pacman -S mingw-w64-x86_64-python-build
pacman -S mingw-w64-x86_64-python-wheel
然后,直接的 py2exe 源代码将无法编译,因此还必须应用 PKGBUILD 文件中提到的补丁。所以我们有:
$ cd py2exe_git
$ wget https://raw.githubusercontent.com/msys2/MINGW-packages/master/mingw-w64-python-py2exe/001-setup-fix.patch
# ...
$ wget https://raw.githubusercontent.com/msys2/MINGW-packages/master/mingw-w64-python-py2exe/002-mingw-fix.patch
# ...
$ patch -p1 -i 001-setup-fix.patch
patching file py2exe_setuptools.py
Hunk #5 succeeded at 194 with fuzz 1.
patching file setup.py
Hunk #1 succeeded at 23 (offset 2 lines).
Hunk #2 succeeded at 46 (offset 1 line).
Hunk #3 succeeded at 73 (offset 1 line).
Hunk #4 succeeded at 91 (offset 1 line).
Hunk #5 succeeded at 109 (offset 1 line).
Hunk #6 succeeded at 125 (offset 1 line).
$ patch -p1 -i 002-mingw-fix.patch
patching file py2exe/hooks.py
patching file py2exe/runtime.py
Hunk #1 succeeded at 307 (offset 5 lines).
patching file source/_memimporter.c
patching file source/python-dynload.c
patching file source/start.c
好的,我们现在可以构建了 - 仍然有弃用警告,但构建将会成功:
$ python3 -m build --wheel --skip-dependency-check --no-isolation
* Building wheel...
D:/msys64/tmp/py2exe_git/py2exe_setuptools.py:10: SetuptoolsDeprecationWarning: dep_util is Deprecated. Use fu
nctions from setuptools.modified instead.
!!
********************************************************************************
Please use `setuptools.modified` instead of `setuptools.dep_util`.
By 2024-May-21, you need to update your project and remove deprecated calls
or your builds will no longer be supported.
See https://github.com/pypa/setuptools/pull/4069 for details.
********************************************************************************
!!
from setuptools.dep_util import newer_group
running bdist_wheel
...
adding 'py2exe-0.13.0.1.dist-info/RECORD'
removing build/bdist.mingw_x86_64/wheel
Successfully built py2exe-0.13.0.1-cp311-cp311-mingw_x86_64.whl
对 - 所以这里的技巧是,一个
_memimporter.pyd
文件没有构建,只有一个 .o
文件:
$ find . -name '_mem*'
./build/temp.mingw_x86_64-cpython-311/source/_memimporter.o
./source/_memimporter.c
如果我们检查构建日志,我们会看到这个
.o
文件直接构建到 run-py311-mingw_x86_64.exe
:
gcc -s ... build/temp.mingw_x86_64-cpython-311/source/_memimporter.o ... -lpython3.11 -o build/lib.mingw_x86_64-cpython-311/py2exe/run-py311-mingw_x86_64.exe -m64 -mconsole -municode
...这可能就是为什么无法从
_memimporter.pyd
中提取 .exe
的原因。
所以,现在我们有了
.o
文件,但我们需要 .pyd
- 我在 如何创建 .pyd 文件? 中找到了提示(值得庆幸的是,还引用了 MSYS2/MINGW64) - 所以最终,命令是:
gcc -shared -L/mingw64/bin \
./build/temp.mingw_x86_64-cpython-311/source/_memimporter.o \
build/temp.mingw_x86_64-cpython-311/source/memorymodule.o \
build/temp.mingw_x86_64-cpython-311/source/myloadlibrary.o \
build/temp.mingw_x86_64-cpython-311/source/actctx.o \
build/temp.mingw_x86_64-cpython-311/source/python-dynload.o \
-lpython3.11 -o _memimporter.pyd
所以,现在我们有一个
_memimporter.pyd
;为了使其可用以便测试脚本能够工作,我们将其复制到 /mingw64/lib/python3.11/site-packages/
(由于某种原因,将其复制到 /mingw64/lib/python3.11/site-packages/py2exe
不起作用):
$ cp -av /tmp/py2exe_git/_memimporter.pyd /mingw64/lib/python3.11/site-packages/
'/tmp/py2exe_git/_memimporter.pyd' -> '/mingw64/lib/python3.11/site-packages/_memimporter.pyd'
然后我们终于可以测试导入并没有看到错误:
$ python3 -c 'import _memimporter'
$
现在我们可以从OP运行测试脚本 - 但是,由于某种原因,我没有得到
_socket
的打印输出:
$ python3 test_zipextimporter.py
$
...但至少没有与
_memimporter
相关的错误。