为何Python项目pip install .失败但pip install -e .可正常运行
Python导入模块的核心逻辑很简单:执行import语句时,解释器会按顺序遍历sys.path列表中存储的所有目录,查找匹配的模块/包。运行脚本时,解释器会默认把当前运行的脚本所在的目录插入到sys.path的最前端。两种安装模式的核心差异,就是对sys.path的修改逻辑完全不同:
常规安装:pip install .
执行该命令时,setuptools会将配置中声明的包文件复制到当前Python环境的site-packages目录(该目录默认在sys.path中),安装完成后项目源码本身的路径不会和Python环境产生关联。
你的配置本身存在错误:find_packages(where="my_package")的作用是在my_package目录下递归查找包,而你的项目结构里my_package本身就是顶层包,内部没有嵌套子包,这个配置根本没有识别到需要安装的my_package包,因此安装完成后site-packages里根本不存在my_package相关文件。
此时你运行python3 my_package/main.py,解释器自动加入sys.path的只有./my_package(main.py所在目录),遍历路径查找my_package.test时,相当于要在./my_package目录下找一个名为my_package的子文件夹,自然会抛出ModuleNotFoundError。
可编辑安装:pip install -e .
可编辑模式(开发模式)不会把源码复制到site-packages,它只会在site-packages下生成一个路径配置文件(.pth后缀或.egg-link链接文件),直接把**你执行安装命令时所在的项目根目录(存放setup.py的目录)**加入到Python全局的sys.path搜索列表中,修改源码不需要重新安装就能生效。
值得注意的是,可编辑模式下只要项目根目录被加入sys.path,就算setup.py的包配置写错、根本没有正确声明要安装的包,只要根目录下存在对应名称的文件夹,导入依然可以成功。
此时你再运行python3 my_package/main.py,除了脚本所在的./my_package目录,项目根目录.也在sys.path的搜索范围内。解释器遍历到根目录路径时,能直接找到根目录下的my_package包以及内部的test.py模块,因此导入可以正常执行,输出预期的hello there!。
额外说明
- 你当前的
find_packages配置写法有误,如果要正确识别顶层my_package包,要么直接显式声明packages=["my_package"],要么不带参数调用find_packages()(默认从setup.py所在的当前目录查找包)。 - 不建议把启动入口脚本直接放在包目录内部,更规范的做法是通过setup.py的
entry_points配置命令行入口,或者将启动脚本放在项目根目录,避免导入路径混乱。
内容的提问来源于stack exchange,提问作者Mochitto

