如何编写setup.py让Conda识别Python包的数据与配置文件?
我来帮你搞定这个从venv切换到Conda后遇到的问题,核心原因是你的setup.py配置没处理好包的模块结构,加上脚本安装方式和数据文件打包的问题,导致Conda环境里找不到对应的文件。下面是一步步的解决方案:
1. 先修复模块导入错误
你遇到的ModuleNotFoundError: No module named my_params.py,本质是用scripts参数安装脚本时,Conda会把脚本复制到环境的bin目录下,脱离了原来的my_pkg/bin包结构,这时候脚本里直接import my_params就找不到模块了。我们需要调整包结构和安装方式:
给子目录添加__init__.py
在my_pkg/bin、my_pkg/lib、my_pkg/data这三个目录下都创建一个空的__init__.py文件,这样setuptools才会把它们当作包的子模块处理,确保安装后这些目录的内容能被Python找到。
改用entry_points替代scripts
修改你的setup.py,用entry_points生成规范的命令行入口,这样Conda会自动处理路径问题,不用手动复制脚本。修改后的setup.py示例:
from setuptools import setup, find_packages setup( name="my_pkg", version="0.1.0", # 自动发现所有子包(包括bin、lib、data) packages=find_packages(), # 定义命令行入口,格式:命令名 = 包.模块:入口函数 entry_points={ "console_scripts": [ "my_main = my_pkg.bin.my_main:main", "my_script1 = my_pkg.bin.my_script1:main", ] }, # 启用包数据包含 include_package_data=True, )
注意:这里假设你的my_main.py和my_script1.py里都有一个main()函数作为执行入口,如果没有的话,你可以在脚本末尾添加:
if __name__ == "__main__": main()
或者直接指定脚本的全局执行逻辑(比如my_pkg.bin.my_main:__main__,但推荐用入口函数的方式)。
2. 确保数据文件被正确打包
虽然你设置了include_package_data=True,但setuptools需要明确知道要包含哪些非代码文件,所以需要创建MANIFEST.in文件:
在项目根目录(和setup.py同目录)新建MANIFEST.in,内容如下:
# 包含bin目录下的params.json include my_pkg/bin/params.json # 包含data目录下的所有.dat文件 include my_pkg/data/*.dat
这个文件会告诉setuptools在打包时把这些数据文件一起复制到安装后的包目录里。
3. 修改脚本中的文件读取逻辑
为了避免依赖相对路径(在不同环境下可能失效),推荐用pkg_resources来读取包内的文件路径。比如在my_params.py中读取params.json:
import pkg_resources import json # 获取包内文件的绝对路径 params_path = pkg_resources.resource_filename("my_pkg", "bin/params.json") # 读取文件内容 with open(params_path, "r") as f: params = json.load(f)
同样,在需要读取my_data_file.dat的地方(比如utils.py):
import pkg_resources data_path = pkg_resources.resource_filename("my_pkg", "data/my_data_file.dat") with open(data_path, "rb") as f: data = f.read()
4. 在Conda环境中重新安装包
进入项目根目录,激活你的Conda环境,然后运行:
# 正常安装(生产环境) pip install . # 或者开发模式安装(修改代码后无需重新安装) pip install -e .
安装完成后,你就可以直接在命令行运行my_main或者my_script1,而不需要指定脚本路径,而且所有模块和数据文件都能被正确找到。
内容的提问来源于stack exchange,提问作者David Pellow

