- 后端
【免费下载链接】python-dotenv
Reads key-value pairs from a .env file and can set them as environment variables. It helps in developing applications following the 12-factor principles.
本文以 python-dotenv 官方 变更日志 为骨架,结合仓库源码与测试逐版本解读关键演进:从 .env 文件解析、变量插值、load_dotenv/dotenv_values加载语义,到set_key/unset_key写入机制与 CLI 命令的完整能力边界。读完本文,你将掌握该库每个版本引入/修复的核心行为、各 API 参数的准确语义,以及如何用当前仓库源码验证这些行为。
项目与文档概览
python-dotenv 是一个读取.env文件中的 key-value 对并将其设置为环境变量的工具,遵循十二要素(12-factor)应用开发原则,将配置与代码分离。当前仓库版本为1.2.2(见 src/dotenv/version.py),支持 Python 3.10 及以上(见 pyproject.toml)。
变更日志 遵循 Keep a Changelog 格式,并遵循语义化版本控制(Semantic Versioning)。本文将逐版本解读其中的 Added(新增)、Changed(变更)、Fixed(修复)、Breaking Changes(破坏性变更)条目,并用仓库中的源码与测试印证其真实行为。
最新版本 1.2.2(2026-03-01):BOM、符号链接与 CLI 行为
新增:Python 3.14 与自由线程构建支持
- 支持 Python 3.14,包括 free-threaded(3.14t)构建。对应 pyproject.toml 中新增的
Programming Language :: Python :: 3.14classifier 以及requires-python = ">=3.10"。 - 同步将 PyPy 支持更新到 3.11,并移除了 Python 3.9 支持(Dropped Support for Python 3.9)。
修复:UTF-8 BOM 处理
变更日志 [Unreleased] 与 1.2.2 之前的提交都提及一个关键修复:剥离.env文件内容开头的 UTF-8 BOM,避免当文件以 BOM 保存(例如部分 JetBrains IDE 在 Windows 上保存文件时)导致第一个变量被静默丢弃。
该行为在源码中可直接验证:在 src/dotenv/parser.py 的Reader.__init__中,读取流内容后立即执行self.string = stream.read().removeprefix("\ufeff"),即在解析开始前就移除 BOM 字符。
修复:set_key与unset_key的符号链接行为
1.2.2 修复了set_key/unset_key与符号链接(symlink)交互时的行为(由 src/dotenv/main.py 中的rewrite()上下文管理器实现):
- 默认不再跟随符号链接:修改时若路径是符号链接,默认会替换链接本身(用普通文件覆盖),而不是修改链接指向的目标文件,避免意外修改到不可信路径下的文件。测试 tests/test_main.py 中
test_set_key_symlink_to_existing_file验证:对指向target.env的符号链接调用set_key后,目标文件内容保持不变,而符号链接被替换为普通文件。 - 恢复旧行为需显式传
follow_symlinks=True:此时会先os.path.realpath(path)解析到真实路径再修改。test_set_key_follow_symlinks验证此时目标文件内容被修改且符号链接保持不变。
破坏性变更:文件权限位不再被重置
1.2.2 引入三项 Breaking Changes:
dotenv.set_key和dotenv.unset_key不再在部分场景下跟随符号链接(需follow_symlinks=True恢复)。- CLI 的
set和unset命令同样不再跟随符号链接。 set_key、unset_key及 CLIset/unset不再将修改后的.env文件权限重置为0o600:原文件的权限位现在被保留;仅当文件需要被新建或原本不是普通文件时,才使用0o600。
源码验证:rewrite()中通过os.lstat(path)读取原文件模式,若stat.S_ISREG为真则保存stat.S_IMODE(...);写入临时文件后若存在original_mode则os.chmod(dest_path, original_mode),再os.replace原子替换。测试test_set_key_preserves_file_mode验证将文件 chmod 为0o640后执行set_key,权限保持不变。
其他变更
dotenv run命令现在将额外 flags 直接透传给指定的命令。- 改进了 override 行为与 reference 页面的文档说明。
- 增加了 FIFO 文件支持的文档(对应下文 1.2.1 的 FIFO 支持)。
- 修正了包元数据中的 license 说明并补齐了 Python 3.14 classifier。
版本 1.2.1(2025-10-26):FIFO 与构建配置精简
- 新增从 FIFO(Unix 命名管道)读取
.env的能力:load_dotenv可以接收一个 FIFO 路径作为dotenv_path。源码中_is_file_or_fifo()(见 src/dotenv/main.py)在os.path.isfile不成立时进一步通过os.stat判断stat.S_ISFIFO;find_dotenv查找文件时也使用该判定。- 测试 tests/test_fifo_dotenv.py 演示了完整用法:
os.mkfifo(fifo)创建命名管道,在一个线程中向管道写入MY_PASSWORD=pipe-secret,主线程调用load_dotenv(dotenv_path=str(fifo), override=True)即可把值读入环境变量——适合从外部进程"注入"机密而避免落盘。
- 测试 tests/test_fifo_dotenv.py 演示了完整用法:
- 构建配置进一步迁入
pyproject.toml,移除了setup.cfg。
版本 1.2.0(2025-10-26):PYTHON_DOTENV_DISABLED与 PEP 517 构建
新增:全局禁用开关
本版本新增通过环境变量PYTHON_DOTENV_DISABLED禁用load_dotenv()的能力。其语义为:
- 若该变量被设置为真值(
1、true、t、yes、y,大小写不敏感),load_dotenv()直接返回False且不加载任何内容。 - 该判定在 src/dotenv/main.py 的
_load_dotenv_disabled()中实现:先检查变量是否存在于os.environ,再用.casefold()归一化后与真值集合比对。 - 测试 tests/test_main.py 的
test_load_dotenv_disabled用"true"、"yes"、"1"、"t"、"y"及其大写形式验证返回False;test_load_dotenv_enabled用""、"false"、"no"、"0"、"f"、"n"等验证仍正常加载。
典型使用场景:在 CI、容器或测试环境中需要临时关闭.env加载时,无需修改代码,只需注入该环境变量。
其他
- 构建系统升级为 PEP 517 & PEP 518,使用
build与 pyproject.toml([build-system]声明setuptools >= 77.0)。 - 增加 Python 3.14 支持。
版本 1.1.x(2025):dotenv run的 execvpe 化与 Python 3.13
1.1.0(2025-03-25)
- 新增 Python 3.13 支持。
dotenv run切换到execvpe:在非 Windows 平台,run_command()(src/dotenv/cli.py)用os.execvpe(command[0], args=command, env=cmd_env)替换当前进程,从而获得更好的资源管理与信号处理——子进程信号直接作用于运行中的命令本身;在 Windows 平台则回退为subprocess.Popen+communicate()+sys.exit(returncode)。- 修复
find_dotenv与load_dotenv在调试器/pdb 中运行时的查找目录:现在会正确地在当前目录查找。源码中find_dotenv通过_is_debugger()(sys.gettrace() is not None)判断是否处于调试器环境,若是则从os.getcwd()开始向上查找。
1.1.1(2025-06-24)
- 修复 CLI 中
find_dotenv在 Python 3.13 上的可靠性。 - 在 Windows 上回退了
execvpe的使用(即采用上述 Popen 方案)。
Misc
- 移除了 Python 3.8 支持。
版本 1.0.x(2023-2024):稳定版收尾
1.0.0(2023-02-24)
- 移除 Python 3.7 支持,新增 Python 3.12-dev 支持。
- 处理当前工作目录(cwd)不存在的情况:
enumerate_env()(src/dotenv/cli.py)捕获os.getcwd()抛出的FileNotFoundError并返回None,避免 CLI 在异常目录下崩溃。
1.0.1(2024-01-23)
- 优雅处理从 zipfile 导入的代码(
test_zip_imports.py有对应测试):当模块位于 zip 归档中时__file__指向 zip 内部路径,find_dotenv的帧遍历逻辑会跳过不存在路径的帧。 - 允许在独立线程中启动时重载使用
load_dotenv的模块。 - 修复删除后文件句柄未关闭、rewrite 函数中的错误处理:对应 src/dotenv/main.py
rewrite()中try/finally式的关闭逻辑;测试test_rewrite_closes_file_handle_on_lstat_failure验证 lstat 失败时所有打开的句柄都被关闭。
版本 0.21.x:CLI 与类型现代化(2022-2023)
- CLI 支持
python -m dotenv调用:入口在 src/dotenv/main.py,if __name__ == "__main__": cli();同时 pyproject.toml 中[project.scripts]声明dotenv = "dotenv.__main__:cli",因此dotenv命令与python -m dotenv等价。 load_dotenv现在返回False而非此前可能抛错,语义更明确:True表示至少设置了一个环境变量,False表示没有。- CLI
list命令新增--format=选项,取值simple(默认)、json、shell、export(见 tests/test_cli.py 的参数化测试)。 - 修复
get/list命令在 env 文件无法打开时的错误信息(src/dotenv/cli.py 的stream_file()打印Error opening env file: ...并以退出码 2 退出)。 - 修复 IPython 测试中已弃用的
magic警告;为dotenv_path添加StrPath类型别名;License 对齐 BSD OSI 模板。
版本 0.20.0(2022-03-24):encoding参数全面化
- 为
get_key、set_key、unset_key增加encoding(Optional[str])参数。至此库内所有读写入口(load_dotenv、dotenv_values、get_key、set_key、unset_key)都支持自定义编码。 - 测试 tests/test_main.py 中
test_set_key_encoding/test_get_key_encoding/test_unset_encoding用latin-1编码验证了非 UTF-8 场景。 - 不再构建 universal wheel(
py2.py3-none-any),仅构建 py3 wheel。
版本 0.19.x:Python 版本边界与参数类型放宽
- 0.19.0:要求 Python 3.5+,正式放弃 Python 2 与 3.4。
set_key/unset_key的dotenv_path参数类型从os.PathLike放宽为Union[str, os.PathLike]。load_dotenv/dotenv_values的stream参数现在接受文本流IO[str],包括io.StringIO("foo")与open("file.env", "r")这类对象(对应test_load_dotenv_string_io_utf_8、test_load_dotenv_file_stream测试)。
- 0.19.2:修复
set_key在追加新条目时若文件末尾缺少换行符则补上\n的问题(对应 src/dotenv/main.pyset_key中的missing_newline逻辑)。
版本 0.18.0(2021-06-20):引号策略的形式化
本版本重新定义了set_key与dotenv set <key> <value>写值时的引号规则:
- 非法
quote_mode抛ValueError:quote_mode必须是always、auto、never三者之一(src/dotenv/main.pyset_key开头即校验)。 - 写入时优先使用单引号而非双引号。
- 不再剥离值两侧的引号(如值本身是
'b'会原样写入a='\'b\'',见test_set_key参数化用例)。 auto模式下仅当值由纯字母数字组成(str.isalnum())时不加引号:源码quote = quote_mode == "always" or (quote_mode == "auto" and not value_to_set.isalnum())。
注意 CLI 全局选项-q/--quote的三个取值always/never/auto与此一一对应(src/dotenv/cli.py)。
版本 0.17.x-0.16.0:run 覆盖控制与插值解析顺序
- 0.17.0:
dotenv get <key>只输出值本身(b),不再输出key=value;新增dotenv run --override/--no-override选项(run命令默认override=True,见 src/dotenv/cli.py)。 - 0.16.0:
load_dotenv/dotenv_values的encoding默认值从None改为"utf-8"(当前签名即encoding: Optional[str] = "utf-8")。- 修复
override=False时变量展开(variable expansion)的解析顺序:源码resolve_variables()(src/dotenv/main.py)中,override=True时先合并os.environ再合并.env内新值(文件优先);override=False时先合并新值再合并os.environ(环境优先)。对应测试test_load_dotenv_redefine_var_used_in_file_no_override(a=c已存在、文件写a=b、d="${a}",结果为d=c)。
版本 0.15.0(2020-10-28):export前缀与自动建文件
- CLI
set命令新增--export选项:写入时在绑定前加export前缀(如export KEY=value),使.env文件可直接作为 bash 脚本source执行。源码set_key的export: bool = False参数控制line_out = f"export {key_to_set}={value_out}\n"或普通形式。 set命令在未找到.env文件时,会在当前目录创建.env。对应test_set_key_no_file:对不存在的路径调用set_key返回(True, "foo", "bar")且文件被创建。- 修复重复 key 时可能出现的空展开值;修复未加引号值中多个相邻空格/制表符的解析(对应
_unquoted_value正则([^\r\n]*)与parse_unquoted_value中re.sub(r"\s+#.*", "", part).rstrip())。
版本 0.14.0-0.11.0:插值语义的演进
- 0.14.0:变量展开时文件中的定义优先于环境变量(对应上述
resolve_variables的override=True合并顺序)。 - 0.13.0:新增 Bash 风格的默认值语法
${VAR:-default}。源码 src/dotenv/variables.py 的正则_posix_variable支持可选(?::-(?P<default>[^\}]*))?分组;Variable.resolve()中default = self.default if self.default is not None else "",未定义变量时回退到默认值。 - 0.12.0:使用 PyInstaller 打包时改用当前工作目录查找
.env(对应find_dotenv中getattr(sys, "frozen", False)判定)。 - 0.11.0:
load_dotenv/dotenv_values新增interpolate参数:设为False可禁用 POSIX 变量插值(见 src/dotenv/main.pyDotEnv.dict()中if self.interpolate: resolve_variables(...)的分支)。- 从
warnings切换为logging输出(当前为logger.warning(...)/logger.info(...),见with_warn_for_invalid_lines)。
插值行为一览(可用dotenv_values验证,测试见 tests/test_main.py):
| 输入 | 是否插值 | 结果 |
|---|---|---|
a=$b(环境b=c) | 任意 | a=$b($b不展开) |
a=${b}(环境b=c) | 是 | a=c |
a=${b}(未定义) | 是 | a=(空串) |
a=${b:-d}(未定义) | 是 | a=d |
a=${b:-d}(环境b=c) | 是 | a=c |
文件内b=d后a=${b} | 是 | a=d(文件定义优先) |
a=${b}${b}(环境b=c) | 是 | a=cc |
版本 0.10.x:解析器重构与容错
- 0.10.0:支持 UTF-8 非引号值、行尾注释、值中的反斜杠、值中的换行;Windows 上 Python 2 强制将环境变量转为
str;移除 Python 3.3 支持。 - 0.10.1:修复无值变量的解析。
- 0.10.2:添加类型提示并对用户公开;
load_dotenv/dotenv_values接受encoding参数(当时默认None)。 - 0.10.3:改进交互式环境检测(
_is_interactive()判断sys.ps1/sys.ps2或__main__无__file__);重构解析器统一行为:- 转义仅在双引号字符串中被解释为控制字符(
_double_quote_escapes匹配\\[\\'\"abfnrtv])。 #仅在其前有空白时才被当作注释开始(parse_unquoted_value中re.sub(r"\s+#.*", "", part))。
- 转义仅在双引号字符串中被解释为控制字符(
- 0.10.4:类型标注变为可选;格式错误的行打印警告;支持无值的 key(解析结果中 value 为
None,dotenv_values返回{"foo": None})。 - 0.10.5:进一步拒绝更多畸形行(如
A: B、a='b',c),无值 key 不再告警,纯注释行正确处理。
版本 0.9.0-0.6.0:CLI 成型期
- 0.9.0:CLI 新增
--version参数(由 src/dotenv/cli.py 的@click.version_option(version=__version__)提供);支持从当前目录加载;新增dotenv run命令——用.env中的变量运行任意 shell 命令。 - 0.8.1:
cli支持变为可选,需pip install python-dotenv[cli](pyproject.toml 中[project.optional-dependencies] cli = ["click>=5.0"];src/dotenv/cli.py 在 import click 失败时提示该安装命令)。 - 0.8.0:
set_key/unset_key改为只修改受影响的行而不是解析后重写整个文件,注释与其余内容原样保留(rewrite()中逐行比对mapping.key == key_to_set,其余行dest.write(mapping.original.string));支持行内export前缀;load_dotenv/dotenv_values支持StringIO。 - 0.7.1:移除对 iPython 的硬依赖。
- 0.7.0:支持通过
.env覆盖系统环境变量(override);".env not found" 警告默认关闭(verbose=False)。 - 0.6.x:支持特殊字符
\;修复单引号问题;CLIlist命令修复;新增 iPython 支持(%dotenvmagic,见 src/dotenv/ipython.py,%load_ext dotenv后可通过%dotenv加载,支持-o/-v选项)。 - 0.6.0:移除 Python 2.6 支持;处理引号值中的转义字符与换行;去除未加引号 key/value 周围空白;新增 POSIX 变量展开。
早期版本(0.4.0-0.5.1):能力奠基
- 0.5.0:新增
find_dotenv方法——从调用处所在文件目录开始逐级向根目录查找.env(_walk_to_root逐级向上遍历,命中_is_file_or_fifo即返回)。 - 0.5.1:修复
find_dotenv从调用该函数的文件处开始搜索(当前实现通过sys._getframe()回溯栈帧获取调用方文件路径)。 - 0.4.0:CLI 新增
-q/--quote选项控制.env中值的引号行为(即现在的always/never/auto前身)。
从变更日志看当前 API 全貌
综合上述演进,当前版本的公开 API(见 src/dotenv/init.py 的__all__)为:
| API | 核心参数 | 说明 |
|---|---|---|
load_dotenv(dotenv_path=None, stream=None, verbose=False, override=False, interpolate=True, encoding="utf-8") | 路径或流二选一 | 解析并写入os.environ;返回是否至少设置了 1 个变量;受PYTHON_DOTENV_DISABLED控制 |
dotenv_values(dotenv_path=None, stream=None, verbose=False, interpolate=True, encoding="utf-8") | 同上 | 只返回 dict,不写环境;无值 key 对应None;内部override=True |
find_dotenv(filename=".env", raise_error_if_not_found=False, usecwd=False) | 查找策略 | 从调用文件目录逐级向上查找,交互式/调试器/PyInstaller 下从 cwd 开始 |
get_key(dotenv_path, key_to_get, encoding="utf-8") | 单键读取 | 找不到或无值返回None |
set_key(dotenv_path, key_to_set, value_to_set, quote_mode="always", export=False, encoding="utf-8", follow_symlinks=False) | 写入/更新 | 文件不存在则创建;默认单引号、不跟随符号链接、保留原文件权限 |
unset_key(dotenv_path, key_to_unset, quote_mode="always", encoding="utf-8", follow_symlinks=False) | 删除键 | 文件或 key 不存在时返回(None, key)并告警 |
get_cli_string(path=None, action=None, key=None, value=None, quote=None) | CLI 辅助 | 生成适合 shell 执行的dotenv命令字符串 |
load_ipython_extension(ipython) | iPython 集成 | 注册%dotenvmagic |
CLI 命令面(python -m dotenv或dotenv,依赖python-dotenv[cli]):
- 全局选项:
-f/--file(默认当前目录.env)、-q/--quote(always/never/auto)、-e/--export、--version。 dotenv list [--format simple|json|shell|export]:列出所有键值。dotenv get <key>:只输出值。dotenv set <key> <value>:写入并回显key=value。dotenv unset <key>:删除键。dotenv run [--override/--no-override] <command>...:携带.env变量执行命令(非 Windows 下通过execvpe替换进程)。
结语
从 0.4.0 的-q/--quote选项,到 1.2.2 的 BOM 剥离与符号链接安全策略,python-dotenv 的变更日志完整记录了一个配置加载工具走向生产可用的全过程。透过这些版本条目去阅读 src/dotenv/main.py、src/dotenv/parser.py、src/dotenv/cli.py 与 tests/test_main.py 等源码,可以精确理解每个参数的真实语义——这正是把变更日志从"新闻列表"升华为"技术手册"的正确方式。
- 后端
【免费下载链接】python-dotenv
Reads key-value pairs from a .env file and can set them as environment variables. It helps in developing applications following the 12-factor principles.
相关推荐
PaddleOCR 版本更新全览:从 3.2.0 核心能力到 2.x 历史演进
PaddleOCR 版本更新全览:从 3.2.0 核心能力到 2.x 历史演进 PaddleOCR 的 版本更新记录 https://link.gitcode.
人工智能计算机视觉深度学习从 Pyroscope 版本演进看连续剖析平台的核心能力构建:v0.0.11 到 v0.37.2 变更史深度解析
从 Pyroscope 版本演进看连续剖析平台的核心能力构建:v0.0.11 到 v0.37.2 变更史深度解析 本篇文章以 Pyroscope(连续剖析平台,
可观测性性能剖析后端运维观测python-for-android 版本演进全览:从版本历史看 Android Python 打包工具链的关键能力变迁
python for android 版本演进全览:从版本历史看 Android Python 打包工具链的关键能力变迁 python for android
开发工具构建工具移动开发
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考