如何解决pyinstaller 可执行文件的差异更新修改嵌入式 PYZ-00.pyz
我打算创建一个巨大的可执行目录并将其安装在某些设备上。
想象一下,后来我在我的一个 Python 模块中发现了一个错误。 有没有什么办法只传输/复制修改后的字节码,然后用新的字节码替换原来的字节码。
我想这样做的原因是,在我的上下文中带宽非常昂贵,我想远程修补代码。
示例:我有一个包含两个文件的项目:
prog.py
:(以下三行)
import mod1
if __name__ == "__main__":
mod1.hello()
mod1.py
:(以下两行)
def hello():
print("hello old world")
现在我使用 PYTHONHASHSEED=2 pyinstaller prog.py
创建我的目录并将其复制到我的设备
现在我修改mod1.py
:
def hello():
print("hello new world")
然后我用 PYTHONHASHSEED=2 pyinstaller prog.py
重新编译
完整目录(去皮和压缩)大小约为 10M
文件 dist/prog/prog
的大小约为 1M
使用 pyi-archive_viewer
我可以从我的可执行文件 PYZ-00.pyz
中提取 dist/prog/prog
在 PYZ-00.pyz
中,我可以找到并提取仅使用 133 个字节的 mod1
。
现在,如果我将该文件复制到我的设备上,我该如何更新旧的
dist/prog/prog
这样,它具有新的 PYZ-00.pyz:mod1
字节码。
我可以用什么代码来分解,在替换一个特定的文件(模块)后我可以用什么代码来重新组装?
解决方法
这是一个相当复杂的问题,但我认为这可能至少是您正在寻找的一部分。
根据您的示例,我更改了 prog.py
,因此它在从源代码运行时可以正常导入,但在使用 pyinstaller 冻结时直接从 pyc
文件运行。
import sys
def import_pyc(name):
import py_compile
import types
import marshal
pyversion = f"{sys.version_info.major}{sys.version_info.minor}"
filename = f"{name}.cpython-{pyversion}.pyc"
with open(filename,"rb") as pyc_file:
# pyc files have 16 bytes reserved at the start in python 3.7+
# due to https://www.python.org/dev/peps/pep-0552/
# may change again in the future
pyc_file.seek(16)
code_obj = marshal.load(pyc_file)
module = types.ModuleType(name)
exec(code_obj,module.__dict__)
globals()[name] = module
def import_py(name):
import importlib
globals()[name] = importlib.import_module("mod1")
def import2(name):
if getattr(sys,"frozen",False):
import_pyc(name)
else:
import_py(name)
import2("mod1")
if __name__ == "__main__":
mod1.hello()
这很大程度上基于精彩的答案here。
这意味着 mod.py
不是由 PyInstaller 打包的,您必须将 mod1.cpython-38.pyc
作为数据文件包含在内。
一种方便的方法是使用命令 PyInstaller --add-data "__pycache__/*;." prog.py
(尽管如果您不是在 Windows 上,请将分号切换为冒号)。这会将 __pycache__
文件夹中的所有内容(所有导入的模块)放入您的结尾 dist/prog
文件夹中。请注意,如果您多次运行此程序,PyInstaller 会将主 python 文件夹的 pyc 放在 __pycache__
中,以便在后续运行时将其捆绑。
根据您捆绑和运行项目的方式,您可能会遇到当前工作目录关闭的问题,这将导致在您尝试加载 FileNotFound
时出现 pyc
。我不能给你一个灵丹妙药来找到你想要的路径,因为这取决于你最终如何做事,但我通常用来找到应该是当前工作目录的绝对路径的方法是 os.path.dirname(sys.executable)
和os.path.dirname(os.path.abspath(__file__))
。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。