使用setup.py install安装Python包时遭遇python-dotenv路径错误
开发阶段用python-dotenv加载.env文件管理环境变量;分发应用(通过setuptools安装)时,环境变量由其他方式提供。
但通过setup.py install在虚拟环境安装包后,运行调用load_dotenv的脚本会抛出OSError: Starting path not found错误:
Traceback (most recent call last): File "/home/dev/testpkg/venv/bin/run", line 11, in <module> load_entry_point('testpkg==0.0.1', 'console_scripts', 'run')() File "/home/dev/testpkg/venv/lib/python3.8/site-packages/pkg_resources/__init__.py", line 489, in load_entry_point return get_distribution(dist).load_entry_point(group, name) File "/home/dev/testpkg/venv/lib/python3.8/site-packages/pkg_resources/__init__.py", line 2852, in load_entry_point return ep.load() File "/home/dev/testpkg/venv/lib/python3.8/site-packages/pkg_resources/__init__.py", line 2443, in load return self.resolve() File "/home/dev/testpkg/venv/lib/python3.8/site-packages/pkg_resources/__init__.py", line 2449, in resolve module = __import__(self.module_name, fromlist=['__name__'], level=0) File "/home/dev/testpkg/venv/bin/run.py", line 4, in <module> __import__('pkg_resources').run_script('testpkg==0.0.1', 'run.py') File "/home/dev/testpkg/venv/lib/python3.8/site-packages/pkg_resources/__init__.py", line 666, in run_script self.require(requires)[0].run_script(script_name, ns) File "/home/dev/testpkg/venv/lib/python3.8/site-packages/pkg_resources/__init__.py", line 1469, in run_script exec(script_code, namespace, namespace) File "/home/dev/testpkg/venv/lib/python3.8/site-packages/testpkg-0.0.1-py3.8.egg/EGG-INFO/scripts/run.py", line 7, in <module> File "/home/dev/testpkg/venv/lib/python3.8/site-packages/dotenv/main.py", line 336, in load_dotenv dotenv_path = find_dotenv() File "/home/dev/testpkg/venv/lib/python3.8/site-packages/dotenv/main.py", line 300, in find_dotenv for dirname in _walk_to_root(path): File "/home/dev/testpkg/venv/lib/python3.8/site-packages/dotenv/main.py", line 257, in _walk_to_root raise IOError('Starting path not found') OSError: Starting path not found
目前用pip install .可以正常运行,但想知道有没有办法在setup.py install的方式下解决这个问题。
基于Python 3.8、采用src-layout结构的最小项目,在虚拟环境中安装:
项目目录结构
root_project_dir/ |--- .env |--- setup.py |--- src/ |--- run.py |--- pkg/ |--- __init__.py |--- mod.py
.env仅用于本地测试,不随包分发;run.py的main()是应用入口。
setup.py 内容
from setuptools import setup, find_namespace_packages setup( name='testpkg', version='0.0.1', install_requires=["python-dotenv"], packages=find_namespace_packages(where='src'), package_dir={"": "src"}, scripts=['src/run.py'], entry_points={ 'console_scripts': [ 'run = run:main', ] } )
run.py 内容
import os from dotenv import load_dotenv from pkg.mod import Mod load_dotenv() VAR = os.getenv('VAR') def main(): m = Mod(VAR) m.run() if __name__ == "__main__": main()
mod.py 内容
class Mod(object): def __init__(self, var): print("init with var:", var) def run(self): print("Ok I ran!")
.env 内容
VAR=some text
环境准备
创建并激活虚拟环境:
python3 -m venv venv source ./venv/bin/activate
本地测试(正常)
本地运行可正常读取.env:
(venv) root_project_dir/$ pip install python-dotenv (venv) root_project_dir/$ python src/run.py init with var: some text Ok I ran!
安装并运行(触发错误)
执行安装:
(venv) root_project_dir/$ python setup.py install ... Using /home/dev/testpkg/venv/lib/python3.8/site-packages Finished processing dependencies for testpkg==0.0.1
运行入口点触发错误:
(venv) root_project_dir/$ run Traceback (most recent call last): File "/home/dev/testpkg/venv/bin/run", line 11, in <module> ... File "/home/dev/testpkg/venv/lib/python3.8/site-packages/dotenv/main.py", line 300, in find_dotenv for dirname in _walk_to_root(path): File "/home/dev/testpkg/venv/lib/python3.8/site-packages/dotenv/main.py", line 257, in _walk_to_root raise IOError('Starting path not found') OSError: Starting path not found
错误原因是find_dotenv()尝试访问的路径/home/dev/testpkg/venv/lib/python3.8/site-packages/testpkg-0.0.1-py3.8.egg/EGG-INFO/scripts实际是一个egg文件(而非目录),导致无法遍历目录查找.env。
方案1:限制load_dotenv仅在开发环境执行
因为.env只用于本地开发,分发后不需要加载,所以可以通过判断运行场景来决定是否调用load_dotenv:
修改run.py:
import os from dotenv import load_dotenv from pkg.mod import Mod # 仅在本地存在.env文件时加载,安装后跳过 if os.path.exists('.env'): load_dotenv() VAR = os.getenv('VAR') def main(): m = Mod(VAR) m.run() if __name__ == "__main__": main()
方案2:强制setup.py install使用非egg格式安装
执行安装时添加参数,避免生成egg归档文件:
python setup.py install --single-version-externally-managed --root=/
该参数会让setuptools以类似pip的方式安装,将文件直接解压到site-packages目录,而非生成egg文件,这样find_dotenv()不会触发路径不存在的错误,安装后环境变量由外部提供即可。
方案3:明确指定load_dotenv的查找路径
如果需要在特定场景下加载.env,可以手动指定路径,避免自动遍历:
import os from dotenv import load_dotenv # 可让用户通过环境变量指定路径,默认读取用户主目录的.env dotenv_path = os.getenv('DOTENV_PATH', os.path.expanduser('~/.env')) if os.path.exists(dotenv_path): load_dotenv(dotenv_path)
内容的提问来源于stack exchange,提问作者daouzli

