之前写了一个小工具,将 excel 配置表转换为 json、xml、lua 等配置文件。最近在学习 egret,正好需要转换配置文件,刚好就用上了。然而当我想把工具拷到工作目录时,就发愁了。之前我为了方便扩展,把程序拆分得太细:
- xzc@xzc - HP - ProBook - 4446s: ~ / Documents / code / github / py_exceltools$ ls - lh总用量80K drwxrwxr - x 2 xzc xzc 4.0K 7月27 23 : 03 bin drwxrwxr - x 2 xzc xzc 4.0K 7月27 23 : 03 client - rw - rw - r--1 xzc xzc 7.7K 7月27 23 : 03 decoder.py - rw - rw - r--1 xzc xzc 893 7月27 23 : 03 error.py - rw - rw - r--1 xzc xzc 16K 7月27 23 : 03 example.xlsx - rw - rw - r--1 xzc xzc 131 7月27 23 : 03 lancher.bat - rw - rw - r--1 xzc xzc 127 7月27 23 : 03 lancher.sh - rw - rw - r--1 xzc xzc 3.9K 7月27 23 : 03 loader.py - rw - rw - r--1 xzc xzc 705 7月27 23 : 03 loader.spec - rw - rw - r--1 xzc xzc 2.4K 7月27 23 : 03 README.md drwxrwxr - x 2 xzc xzc 4.0K 7月27 23 : 03 server - rw - rw - r--1 xzc xzc 4.0K 7月27 23 : 03 writer_json.py - rw - rw - r--1 xzc xzc 7.2K 7月27 23 : 03 writer_lua.py - rw - rw - r--1 xzc xzc 5.6K 7月27 23 : 03 writer_xml.py
如此多的文件,放到工作目录不太好组织,也容易与项目的源代码混在一起。毕竟我用的 vs code 分不清哪些才是工程内文件。何况以后还要给策划用,还得装 python 和 openpyxl 库,部署比较麻烦。于是想尝试一下把 python 脚本打包为一个 exe 文件。
google 了一下,常用的工具不外乎 pyinstaller 和 py2exe。两者的功能都差不多,但是发现 pyinstaller 有一个参数 --onefile,即脚本都打包成单个可运行文件,这不正是我要的么。于是下载安装来尝试一下:
- py_exceltools$pip install pyinstaller py_exceltools$pyinstaller - F loader.py
- ...tuple index out of range
安装过程很顺利,但是打包时却报了个错 ("tuple index out of range")。google 一下 "pyinstaller tuple index out of range",在 github 中发现是 pyinstaller3.2.1 不兼容 python3.6.1。但是看看 issue 的回复,在 dev 版本是修复了。于是想试一下开发版本,不过看了一眼 README,发现 OS X、Linux、Win 三个平台的 CI 都是 failing:
想想还是算了吧,免得折腾半天又不能用。直接把本机的 python 从 3.6.1 降为 3.5,再从新安装 pyinstaller,运行 "pyinstaller -F loader.py" 打包,一切顺利,在 dist 目录下生成了一个 loader.exe。试运行下 loader.exe,结果却是这样的:
- Traceback(most recent call last) : File "loader.py",
- line 96,
- inoptions.timeout,
- options.suffix,
- options.srv_writer,
- options.clt_writer) File "loader.py",
- line 25,
- in__init__ self.srv_writer = importlib.import_module("writer_" + srv_writer) File "importlib\__init__.py",
- line 126,
- inimport_module File "<frozen importlib._bootstrap>",
- line 986,
- in_gcd_import File "<frozen importlib._bootstrap>",
- line 969,
- in_find_and_load File "<frozen importlib._bootstrap>",
- line 956,
- in_find_and_load_unlocked ImportError: No module named 'writer_lua'Failed to execute script loader请按任意键继续...
缺失了模块 writer_lua,这是我自己写的一个转换为 Lua 配置的模块。pyinstaller 本来有分析脚本依赖的模块的,但是我这个程序是根据运行时传入的参数动态加载模块的,因为我并不能预先知道用户要把 excel 转换为什么类型的文件。全部加载所有模块,是一个解决方案,但不太合适,因为我本来的写法是:规定了模块的接口,新增模块时,不需要修改我原有的代码,会自动加载新模块。再次搜索了一下,居然没有找到相同的案例。阅读了下 pyinstaller 的手册 (https://pythonhosted.org/PyInstaller/spec-files.html),发现可以用 spec 配置文件来打包各种数据的,比如程序的 icon,甚至自定义的一些二进制文件。在 http://pythonhosted.org/PyInstaller/when-things-go-wrong.html#listing-hidden-imports 和 http://pythonhosted.org/PyInstaller/hooks.html#understanding-pyinstaller-hooks 中提到可以使用 hiddenimports 选项来导入隐藏的模块。
查看了下 pyinstaller 打包的过程,运行 "pyinstaller -F loader.py" 时确实在当前目录下生成了一个 loader.spec 文件:
- # - *-mode: python - *-
- block_cipher = None
- a = Analysis(['loader.py'], pathex = ['E:\\linux_share\\github\\py_exceltools'], binaries = [], datas = [], hiddenimports = [], hookspath = [], runtime_hooks = [], excludes = [], win_no_prefer_redirects = False, win_private_assemblies = False, cipher = block_cipher) pyz = PYZ(a.pure, a.zipped_data, cipher = block_cipher) exe = EXE(pyz, a.scripts, a.binaries, a.zipfiles, a.datas, name = 'loader', debug = False, strip = False, upx = True, console = True)
在 hiddenimports 中加入自己动态加载的模块,变成 hiddenimports=['writer_lua','writer_xml','writer_json'],重新打包。注意,重新打包时不要再运行 "pyinstaller -F loader.py" 了,因为这个指令会重新生成 spec 文件,把你修改的覆盖了。直接用 "pyinstaller loader.spec" 来打包。
加入动态加载的模块后,整个 exe 有 7M 多,运行正常。但是在 64bit 系统打包出来的程序,是 64bit 的,不能在 32bit 下运行。整个工具放在 github 上:https://github.com/changnet/py_exceltools
来源: http://www.cnblogs.com/coding-my-life/p/7248072.html