如何基于setuptools与pyproject.toml分发Python包关联的Shell脚本?
解决pyproject.toml项目中分发Shell脚本的问题
我完全懂你的困扰——想用纯pyproject.toml构建项目,还要绑定一个Shell脚本一起分发,官方的[project.scripts]又只能配置Python函数,直接调用的话加载整个项目太慢,拆成两个项目又破坏了二者的强耦合性。这里有两个可行的方案,帮你搞定这个问题:
方案一:用setuptools的data_files直接安装Shell脚本
这个方案最直接,不需要额外的Python包装层,直接把Shell脚本安装到系统可执行路径。在你的pyproject.toml里添加:
[tool.setuptools.data_files] # 目标安装路径,类Unix系统一般用/usr/local/bin,Windows可根据需求调整为Scripts目录 "/usr/local/bin" = ["path/to/your/my_script.sh"]
注意事项:
- 打包前记得给Shell脚本加可执行权限:
chmod +x path/to/your/my_script.sh,这样安装后用户直接就能在终端调用my_script.sh - 跨平台场景下,Windows需要额外编写对应的
.bat脚本,再分平台配置data_files路径
方案二:轻量Python包装器(推荐,跨平台友好)
如果担心data_files的路径兼容性问题,或者需要一点灵活度,可以写一个完全不依赖项目核心代码的极简Python包装器,这样既符合[project.scripts]的规范,又不会加载整个项目导致耗时。
步骤如下:
- 把Shell脚本放到你的Python包目录下(比如
my_project/my_script.sh),确保它有可执行权限 - 在包内创建一个轻量的包装模块,比如
my_project/scripts/script_wrapper.py,内容如下:
#!/usr/bin/env python3 import subprocess import sys from importlib.resources import files def main(): # 定位打包后Shell脚本的实际路径 script_path = files("my_project").joinpath("my_script.sh") # 把命令行参数传递给Shell脚本 subprocess.run([str(script_path)] + sys.argv[1:], check=True) if __name__ == "__main__": main()
- 在pyproject.toml里配置两项:
- 把Shell脚本作为包数据包含进去:
[tool.setuptools.package_data] my_project = ["my_script.sh"] - 配置
project.scripts指向这个包装器:[project.scripts] my_script = "my_project.scripts.script_wrapper:main"
- 把Shell脚本作为包数据包含进去:
这样,用户执行my_script时,只会加载这个几行代码的包装器,不会触发项目核心代码的加载,完美解决耗时问题,而且Shell脚本和项目完全绑定,不需要拆分。
另外补充一点:关于setup.py的scripts参数,虽然现在官方文档提及较少,但它是setuptools的传统特性,短期内不会被移除,但既然你用纯pyproject.toml,优先用上面的方案更符合现代Python打包规范。
内容的提问来源于stack exchange,提问作者Luting Wang
相关产品推荐
相关产品推荐

