此解决方案既不能“修补”.PYZ 文件,也不能将所有 .pyc 文件放入 zip 文件中。
但到目前为止,这是我发现的唯一可行的解决方案,适用于具有大量第三方依赖项的大型项目。
想法是从 .PYZ 文件中删除所有(或大部分文件)并将相应的 .pyc 文件复制到工作目录中。
随着时间的推移,我会加强和阐述这个答案。我还在尝试:
我通过修改规范文件来实现这一点:
- 确定spec文件所在的目录
MYDIR
- 创建一个目录
MYDIR/src,将a.pure中的所有文件复制到该目录
- 将所有文件从 a.pure 复制到
MYDIR/src。 (具有与模块名称对应的子目录。模块mypackage.mod.common 将例如存储在MYDIR/src/mypackage/mod/common.py 中)
- 遍历文件并将它们编译为
.pyc 文件,然后删除.py 文件。
- 创建一个
PYZ 文件,其中仅包含未复制的文件。 (在我的测试用例中,不要在PYZ 中保留.pyc 文件)
- 用修改后的
PYZ创建exe
- 收集所有应该收集的文件以及来自
MYDIR/src的所有文件(例如a.datas + Tree("src")
规格文件更改:
一开始
import os
MYDIR = os.path.realpath(SPECPATH)
sys.path.append(MYDIR)
import mypyinsthelpers # allows to reuse the code in multiple projects
然后在我添加的(未修改的)a = Analysis(... 部分之后。
to_rmv_from_pyc = mypyinsthelpers.mk_copy_n_compile(a.pure, MYDIR)
# modified creation of pyz`
pyz = PYZ(a.pure - to_rmv_from_pyc, a.zipped_data,
cipher=block_cipher)
下面我会详细介绍函数mypyinsthelpers.mk_copy_n_compile
改变收集阶段:
代替
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
...
我写:
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas + Tree("src"),
...
这里是mypyinsthelpers.mk_copy_n_compile()的声明
import compileall
import os
import shutil
from pathlib import Path
def mk_copy_n_compile(toc, src_tree):
"""
- copy source files to a destination directory
- compile them as pyc
- delete source
"""
dst_base_path = os.path.join(src_tree, "src")
to_rm = []
# copy files to destination tree
for entry in toc:
modname, src, typ = entry
assert typ == "PYMODULE"
assert src.endswith(".py") or src.endswith(".pyw")
# TODO: might add logic to skip some files (keep them in PYC)
to_rm.append(entry)
if src.endswith("__init__.py"):
modname += ".__init__"
m_split = modname.split(".")
m_split[-1] += ".py"
dst_dir = os.path.join(dst_base_path, *m_split[:-1])
dst_path = os.path.join(dst_dir, m_split[-1])
if not os.path.isdir(dst_dir):
os.makedirs(dst_dir)
print(entry[:2], dst_path)
shutil.copy(src, dst_path)
# now compile all files and rmv src
top_tree = src_tree
src_tree = os.path.join(src_tree, "src")
curdir = os.getcwd()
os.chdir(dst_base_path)
for path in Path(dst_base_path).glob("**/*.py"):
# TODO: might add code to keep some files as source
compileall.compile_file(
str(path.relative_to(dst_base_path)), quiet=1, legacy=True)
path.unlink()
os.chdir(curdir)
return to_rm