Poetry项目中Jupyter Notebook调用模块报‘No module named’错误
问题背景
我的项目目录结构如下:
./ /my_module (模块文件) /tests test_thing.py /jupyter demo.ipynb
测试文件和Jupyter Notebook里都用同样的导入代码:
from my_module import thingy, other_thingy
在项目根目录执行poetry run pytest能正常跑通测试,但用poetry run jupyter lab启动Jupyter Lab后运行demo.ipynb,却报No module named my_module错误。我试过用以下代码临时解决,但感觉会导致Poetry环境里的其他模块无法使用,想找彻底的解决办法:
import sys sys.path.append('../')
为什么pytest能找到模块,Jupyter不行?
- pytest运行时会自动把项目根目录加入Python的模块搜索路径(
sys.path),所以能直接找到my_module。 - 用
poetry run jupyter lab启动后,Jupyter的初始工作目录是项目根,但打开jupyter/demo.ipynb时,Notebook的执行环境会自动切换到jupyter文件夹,此时sys.path里没有项目根目录,自然找不到my_module。
彻底解决方法
方法1:用Poetry以可编辑模式安装本地模块
这是最推荐的方案,相当于把你的本地模块“注册”到Poetry的虚拟环境里,不管从哪里启动都能直接导入:
在项目根目录执行:
poetry install
Poetry会自动以可编辑模式安装my_module(虚拟环境里会生成指向本地模块的链接),之后再用poetry run jupyter lab启动,打开Notebook就能正常导入模块,完全不用改路径。
方法2:启动Jupyter时指定项目根为工作目录
如果你不想安装模块,可以启动Jupyter时强制指定工作目录为项目根:
poetry run jupyter lab --notebook-dir=.
这样打开Notebook时需要手动进入jupyter文件夹,但Notebook的执行环境会保留项目根在sys.path里,导入就正常了。
方法3:在Notebook里添加绝对路径(避免相对路径坑)
如果不想改启动命令,可以在Notebook开头用绝对路径添加项目根,避免相对路径的不确定性:
import sys from pathlib import Path # 获取项目根目录(当前Notebook所在目录的上级) project_root = Path.cwd().parent sys.path.append(str(project_root))
这种方式不管Notebook在目录里怎么移动,只要整体结构不变,就能正确找到模块,也不会影响Poetry环境里的其他模块。
关于临时解决方法的疑问
你提到sys.path.append('../')会导致其他模块无法使用,大概率是代码里的语法错误(你写的代码少了一个闭合引号,正确应该是sys.path.append('../')),或者是之后切换了Notebook的工作目录导致路径混乱。用上面的绝对路径方法就能避免这个问题,而且添加项目根路径不会影响Poetry环境里的其他模块——虚拟环境的模块路径优先级更高,不会冲突。
内容的提问来源于stack exchange,提问作者learnvst

