Skip to content

Upgrading python-dotenv 0.21 to 1

Breaking0.21.1 → 1.2.4Breaking3Security1Platform4New2Changed8Fixed16Other22

BreakingUpgrade verdict

2 breaking changes and 4 platform requirement changes and a security fix.

  • Editing a .env file that is a symlink now changes the link itself rather than the file it points to, unless you opt in.
  • Editing a .env file no longer changes its permissions; the existing permissions are kept.
  • The package now works on the newest Python version, including its experimental no-GIL build.
  • Adds support for Python 3.9.
Top 5 of 7 that need code or config changes

Python requirement changed in 1.2.2: >=3.7 → >=3.10.

Breaking2
dotenv.set_key and dotenv.unset_key no longer follow symlinks in some situations; use follow_symlinks=True to restore the old behavior.
dotenv.set_keydotenv.unset_keyfollow_symlinks
set_key, unset_key and CLI set/unset now preserve the original file mode instead of resetting it to 0o600; 0o600 is used only for new or non-regular files.
dotenv.set_keydotenv.unset_keysetunset
Security1
python-dotenv reads key-value pairs from a .env file and can set them as environment variables. Prior to version 1.2.2, set_key() and unset_key() in python-dotenv follow symbolic links when rewriting .env files, allowing a local attacker to overwrite arbitrary files via a crafted symlink when a cross-device rename fallback is triggered. Users should upgrade to v.1.2.2 or, as a workaround, apply the patch manually.
Medium · CVSS 6.6
Platform4
Adds support for Python 3.14, including the free-threaded (3.14t) build.
Adds support for Python 3.9.
Adds support for Python 3.13 and drops Python 3.8.
Drops support for Python 3.7 and adds Python 3.12-dev to the test matrix.

Generated by DepDelta from published Python source. Not mixed with prose notes.

  • removed: function list(ctx: click.Context, format: bool) -> None
  • removed: function set(ctx: click.Context, key: Any, value: Any) -> None
  • changed: function run(ctx: click.Context, override: bool, commandline: List[str]) -> None → function run(ctx: click.Context, override: bool, commandline: tuple[str, ...]) -> None
  • changed: function run_command(command: List[str], env: Dict[str, str]) -> int → function run_command(command: List[str], env: Dict[str, str]) -> None
  • added: function enumerate_env() -> Optional[str]
  • added: function list_values(ctx: click.Context, output_format: str) -> None
  • added: function set_value(ctx: click.Context, key: Any, value: Any) -> None
  • changed: function rewrite(path: StrPath, encoding: Optional[str]) -> Iterator[Tuple[IO[str], IO[str]]] → function rewrite(path: StrPath, encoding: Optional[str], follow_symlinks: bool=…) -> Iterator[Tuple[IO[str], IO[str]]]
  • changed: function set_key(dotenv_path: StrPath, key_to_set: str, value_to_set: str, quote_mode: str=…, export: bool=…, encoding: Optional[str]=…) -> Tuple[Optional[bool], str, str] → function set_key(dotenv_path: StrPath, key_to_set: str, value_to_set: str, quote_mode: str=…, export: bool=…, encoding: Optional[str]=…, follow_symlinks: bool=…) -> Tuple[Optional[bool], str, str]
  • changed: function unset_key(dotenv_path: StrPath, key_to_unset: str, quote_mode: str=…, encoding: Optional[str]=…) -> Tuple[Optional[bool], str] → function unset_key(dotenv_path: StrPath, key_to_unset: str, quote_mode: str=…, encoding: Optional[str]=…, follow_symlinks: bool=…) -> Tuple[Optional[bool], str]