Python 打包与分发
打包与分发
Setuptools
目录结构
与 setup.py 同级的包目录都会被默认的 find_packages()
检索,所以最后安装的包名是 setup.py 的同级包名。
1 | $ tree |
那么 pip install
执行后,使用 pip list
查到的包为 pkg1 和 pkg2. 子包 pkg3 不会被找到。为规范起见,可在 setup.py 同级目录只设顶层包。
开发
以下问题在
pip install
时不存在。顶层包可以找得到,直接导。
快速迭代时,为避免频繁 pip install
,可使用 setup.py 的 develop 选项 ( 见快速迭代一章 ),此时只能在根目录下找到测试包,根目录下与包同级的其他目录中也不可以。
e.g. 考虑如下包结构。若 test1 和 test2 均通过绝对导入使用了 dev_pkg,则在根目录执行
python test1
或python testdir/test2
可行,进入 testdir/ 执行python test2
不可行。
1
2
3
4
5 .
├── dev_pkg/ # your pkg
├── testdir/
│ └── test2.py
└── test1.py
考虑上一节目录结构中的 test/ 目录,该目录是不具备包结构的,无法使用相对路径导包。如果想调用模块进行测试,并要求切换工作目录到一个非根目录的路径,则 import 语句无法编写。
解决方案是 sys.path 动态导入包搜索路径。
1 | # setuptools develop 模式下,pip list 只能在包源码同级目录找到包 |
该导入会在脚本执行期间持续生效,因此只需放在顶层脚本中 (该脚本需要覆盖程序的整个生命周期),而不需要修改每一个文件。脚本执行结束时这个路径就失效了,不会影响其他程序执行。
快速迭代
由于模块间互相导入需要先打包安装,每次修改代码后都要 pip uninstall & python setup.py & pip uninstall
,十分麻烦。
setuptools 提供 develop
选项简化开发过程。通过创建符号链接,将包安装到特定位置,而不会复制整个包。开发过程中对源代码的修改能够立即生效,不需要重新安装。
步骤如下:
1 | # 没有系统目录的安装权限,则通过 --install-dir 指定,并添加到环境变量 |
之后便可快速测试。注意:
- 此时只能在 setup.py 同级目录下找到该包,否则报 ModuleNotFound
- 此时
setup.entry_points
中注册的命令行工具无效
卸载时:
1 | unset PYTHONPATH |
关于 import
以下两种方式均可
- from <file> import <member>
- from <module> import <file>
相对导入
具有模块结构的包 (with __init__.py) 可通过 .
以自身目录的相对路径导入其他包。
e.g., 考虑如下包结构
1 | package/ |
假设当前文件是moduleX.py
或 subpackage1/__init__.py
,以下是正确用法:
1 | from .moduleY import spam |
相比绝对导入,相对导入的优势是包结构更加灵活可维护。
PyInstaller
主要参考 https://www.cnblogs.com/zhangxingcomeon/p/14523893.html
打包为单文件
Directory be like
1 | $ tree |
指定 -F
参数
1 | pyinstaller -F main.py |
打包为多文件
1 | $ tree |
首先生成 .spec
1 | pyi-makespec main.p |
Q & A
pip uninstall
将包的根目录作为工作目录时出现 pip uninstall 失效问题。进一步检查发现,该情形仅出现在设置 $PYTHONPATH 后。
1 | # 找到了,就是不删 |
目前 Stackoverflow 上并无确切结论,但有数种解决方案。
- 进入 site-packages/ 手动删除 pkg 和 pkg.dist-info
- 退出包目录,执行
pip uninstall
生效 - 删除 ./.egg-info
unset PYTHONPATH
目前看来 pip 无法在用户设置了 sys.path,即本例的 .dev-pkgs/ 的情况下卸载包 ( 为什么 ? )。保险起见,写 Makefile 时先 clean 再 uninstall,或确保正式 pip install
之前清除用于开发环境的 sys.path.
References
- 打包与分发 ( setuptools )
- 非常完整的教程 [Blog]
- ModuleNotFound
- 命令行工具
- 打包 .exe | PyInstaller
- 导包 | python3-cookbook.readthedocs.io