Python标准打包工具不支持post_install钩子;最可靠方式是通过console_scripts暴露显式命令供用户手动调用,或采用运行时惰性初始化。

setup.py里没有post_install这个钩子
Python标准打包工具(setuptools)根本不支持post_install这种命名的钩子。你搜到的很多教程写的post_install,要么是旧版distutils的遗留写法(已弃用),要么是第三方工具(如pipx、poetry插件)的扩展机制,不是pip install原生行为。
真正能被pip install触发的、最接近“安装后执行”的机制只有console_scripts入口点 + 手动调用,或者利用setup.py中的cmdclass自定义命令(但仅在运行python setup.py install时生效,而该命令已被pip弃用)。
- pip install xxx 默认走的是 PEP 517 构建流程,绕过
setup.py直接调用build_backend,cmdclass完全不执行 -
console_scripts只是注册可执行命令,不会自动运行 - 试图在
setup.py的install类中写os.system或subprocess.run,在现代pip安装中大概率静默失败或被跳过
想让用户装完就跑脚本?只能靠文档+约定
目前最可靠、被主流包广泛采用的方式是:不自动执行,而是把初始化动作包装成一个显式命令,写进README,并通过console_scripts暴露。
例如,在setup.py或pyproject.toml中声明:
entry_points={
"console_scripts": [
"mytool-init = mytool.cli:run_init",
]
}
然后用户装完必须手动运行:
pip install mytool && mytool-init
- 这避免了权限问题(比如写系统目录、改环境变量,pip默认以非root运行)
- 用户清楚知道发生了什么,不会因后台静默执行导致调试困难
- 兼容venv、conda、CI环境,不依赖特定安装路径
- 如果你硬要“自动”,唯一可行的变通是让用户用
pip install -e .开发安装,再配合post-link.sh(conda)或shell alias,但这已脱离pip范畴
pyproject.toml里更不能写post_install
PEP 621和现代构建后端(如setuptools >=64.0、hatchling、flit)明确不支持任何安装后钩子。你在[project]或[build-system]里加post_install字段,会被直接忽略——连警告都不会有。
某些博客提到的[project.optional-dependencies]或[project.scripts]也不是执行入口,只是元数据。
-
[project.scripts]等价于console_scripts,仍需用户主动调用 - 试图用
build-backend返回自定义build_editable逻辑来塞执行代码,属于破坏构建契约,会导致wheel缓存失效、CI失败、其他工具(如ruff、mypy)无法解析项目 - hook类方案(如
setuptools-scm)只影响构建过程,不影响安装后行为
真有强需求?考虑替代路径
如果业务上确实要求“装完即用”(比如CLI工具要初始化配置文件、下载模型权重),优先走运行时惰性初始化,而不是安装时。
示例逻辑:
def get_config():
cfg_path = Path("~/.mytool/config.toml").expanduser()
if not cfg_path.exists():
cfg_path.parent.mkdir(exist_ok=True)
cfg_path.write_text(DEFAULT_CONFIG)
return load_toml(cfg_path)
这样第一次运行mytool do-something时才生成配置,用户可控、可重试、不污染安装流程。
- 避免安装失败后残留半截脚本状态
- 适配不同平台路径规则(Windows vs macOS vs Linux)
- 方便测试:单元测试无需模拟安装过程
- 如果非得预加载大资源,提供
mytool download-models --force子命令,而非绑定到install
安装阶段的不可控性远高于运行阶段——路径、权限、网络、并发、用户意图,全都不确定。把动作往后推,是多数成熟Python包的选择。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











