单仓库拆分多Python包后setup.py位置不当致安装失败的解决方案
我是开源程序GridCal的开发者,该程序包含基于Qt的GUI和计算引擎。最初因实用考量将GUI与引擎置于同一包下,如下结构可正常生成并上传PyPI包,原因是setup.py与GridCal包同级:
repository_folder | |_ src |_ GridCal | |_ GUI | |_ Core | |_ IO | |_ Simulations | |_ __init__.py | |_ setup.py |_ upload_to_pypi.py
近期我将代码拆分为GridCal和GridCalEngine两个包,新结构如下:
repository_folder | |_ src |_ GridCal | |_ GUI | |_ __init__.py | |_ setup.py | |_ GridCalEngine | |_ Core | |_ IO | |_ Simulations | |_ __init__.py | |_ setup.py | |_ upload_to_pypi.py
该拆分结构便于开发,GUI可正常引用引擎,也能生成并上传PyPI包,但安装时失败,原因是setup.py未像之前那样置于包外。已参考相关资源但未找到可安装包的解决方案,请问如何解决setup.py的位置问题?
方案1:调整目录结构,将setup.py移至包外(推荐)
这是最贴合Python包规范的做法,把每个包的setup.py放到对应包的同级目录,而非包内部。调整后的结构可参考两种方式:
方式A:同一src目录下区分不同包的构建文件
repository_folder | |_ src |_ GridCal | |_ GUI | |_ __init__.py | |_ GridCalEngine | |_ Core | |_ IO | |_ Simulations | |_ __init__.py | |_ setup_gridcal.py # 原GridCal内的setup.py迁移至此,可改名区分两个包的构建文件 |_ setup_engine.py # 原GridCalEngine内的setup.py迁移至此 |_ upload_to_pypi.py
方式B:给每个包单独创建独立目录
repository_folder | |_ packages |_ GridCal | |_ src | | |_ GridCal | | |_ GUI | | |_ __init__.py | |_ setup.py | |_ GridCalEngine | |_ src | | |_ GridCalEngine | | |_ Core | | |_ IO | | |_ Simulations | | |_ __init__.py | |_ setup.py | |_ upload_to_pypi.py
两种结构都让setup.py位于包的根目录(与该包的源代码文件夹同级),符合PyPA的包结构规范,安装时能正确识别包的根路径。
方案2:修改setup.py的package_dir参数(无需调整目录)
如果不想改动现有目录结构,可在包内的setup.py中通过package_dir参数,明确告诉setuptools包的源代码位置。
以GridCalEngine内的setup.py为例,修改核心配置:
from setuptools import setup, find_packages setup( name="GridCalEngine", # 其他元数据配置... package_dir={"": "."}, # 指定包的根目录为当前setup.py所在的目录 packages=find_packages(where="."), # 在当前目录下查找包模块 # 其他依赖、脚本等配置... )
GridCal内的setup.py做同样修改即可。这种方式能临时解决安装问题,但长期维护建议遵循标准结构,避免后续出现路径相关的隐藏问题。
方案3:改用pyproject.toml+setup.cfg(现代包规范)
现在Python官方推荐使用pyproject.toml声明构建依赖,配合setup.cfg管理包的元数据,可完全替代传统的setup.py(或仅保留极简的空setup.py兼容旧工具)。
以GridCalEngine为例,调整后的结构:
repository_folder | |_ src |_ GridCalEngine | |_ Core | |_ IO | |_ Simulations | |_ __init__.py | |_ pyproject.toml |_ setup.cfg |_ upload_to_pypi.py
pyproject.toml示例:
[build-system] requires = ["setuptools>=61.0"] build-backend = "setuptools.build_meta"
setup.cfg示例:
[metadata] name = GridCalEngine version = 0.1.0 author = Santi Peñate-Vera description = GridCal's core calculation engine [options] packages = find: package_dir = = src python_requires = >=3.8
这种方式无需纠结setup.py的位置,更符合现代Python包的构建规范,同时能简化发布和维护流程。
内容的提问来源于stack exchange,提问作者Santi Peñate-Vera

