From ff0db617e6367c4d21b2b0e27098cef24428aae6 Mon Sep 17 00:00:00 2001 From: sunlishuo Date: Sun, 4 Oct 2026 21:03:02 +0800 Subject: [PATCH 1/2] Fix shell quoting of get_cli_string paths --- CHANGELOG.md | 1 + src/dotenv/__init__.py | 5 ++++- tests/test_utils.py | 21 +++++++++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e5f25414..d604dca8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ### Fixed +- `get_cli_string` now quotes the `.env` path so spaces and quotes in directory names do not break the generated POSIX shell command. - Fix a package build deprecation warning caused by a non-string `license` value in `pyproject.toml` by [@kurtmckee] in [#648] - `set_key`, `unset_key` and the `dotenv set`/`unset` commands now name the `.env` path instead of an internal temporary file when its directory is missing or not writable, and the CLI prints a short error and exits with code 2 instead of a traceback by [@jamalkamaladdin] in [#711] - `set_key` and `unset_key` no longer leave a `.tmp_*` file behind on Windows when writing a read-only `.env` fails, and the error raised is the one from the failed write rather than from cleaning up the temporary file by [@MohammedAlkindi] in [#686] diff --git a/src/dotenv/__init__.py b/src/dotenv/__init__.py index dde24a01..0b9fe4a6 100644 --- a/src/dotenv/__init__.py +++ b/src/dotenv/__init__.py @@ -1,3 +1,4 @@ +import shlex from typing import Any, Optional from .main import dotenv_values, find_dotenv, get_key, load_dotenv, set_key, unset_key @@ -20,12 +21,14 @@ def get_cli_string( Useful for converting a arguments passed to a fabric task to be passed to a `local` or `run` command. + + The path is quoted for use in a POSIX shell. """ command = ["dotenv"] if quote: command.append(f"-q {quote}") if path: - command.append(f"-f {path}") + command.append(f"-f {shlex.quote(path)}") if action: command.append(action) if key: diff --git a/tests/test_utils.py b/tests/test_utils.py index 93b8bae2..f34263a4 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,4 +1,9 @@ +import shlex + +import pytest + from dotenv import get_cli_string as c +from dotenv.cli import cli as dotenv_cli def test_to_cli_string(): @@ -17,3 +22,19 @@ def test_to_cli_string(): c(action="set", key="SECRET", value="a b", quote="always") == 'dotenv -q always set SECRET "a b"' ) + + +@pytest.mark.parametrize( + "directory", + ["app", "my app", "app's"], +) +def test_to_cli_string_path(cli, tmp_path, directory): + project = tmp_path / directory + project.mkdir() + path = project / ".env" + path.write_text("DEBUG=True\n") + + command = c(path=str(path), action="get", key="DEBUG") + result = cli.invoke(dotenv_cli, shlex.split(command)[1:]) + + assert (result.exit_code, result.output) == (0, "True\n") From 9fa916af544a520562520ada477320b578e409f6 Mon Sep 17 00:00:00 2001 From: sunlishuo Date: Sun, 4 Oct 2026 21:20:14 +0800 Subject: [PATCH 2/2] Preserve Path inputs when quoting CLI paths --- CHANGELOG.md | 2 +- src/dotenv/__init__.py | 3 ++- tests/test_utils.py | 6 ++++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d604dca8..3a85684e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ### Fixed -- `get_cli_string` now quotes the `.env` path so spaces and quotes in directory names do not break the generated POSIX shell command. +- `get_cli_string` now quotes the `.env` path so spaces and quotes in directory names do not break the generated POSIX shell command. Shell variables and wildcards in the path are now treated literally. - Fix a package build deprecation warning caused by a non-string `license` value in `pyproject.toml` by [@kurtmckee] in [#648] - `set_key`, `unset_key` and the `dotenv set`/`unset` commands now name the `.env` path instead of an internal temporary file when its directory is missing or not writable, and the CLI prints a short error and exits with code 2 instead of a traceback by [@jamalkamaladdin] in [#711] - `set_key` and `unset_key` no longer leave a `.tmp_*` file behind on Windows when writing a read-only `.env` fails, and the error raised is the one from the failed write rather than from cleaning up the temporary file by [@MohammedAlkindi] in [#686] diff --git a/src/dotenv/__init__.py b/src/dotenv/__init__.py index 0b9fe4a6..85ae0636 100644 --- a/src/dotenv/__init__.py +++ b/src/dotenv/__init__.py @@ -23,12 +23,13 @@ def get_cli_string( to be passed to a `local` or `run` command. The path is quoted for use in a POSIX shell. + Shell variables and wildcards in the path are treated literally. """ command = ["dotenv"] if quote: command.append(f"-q {quote}") if path: - command.append(f"-f {shlex.quote(path)}") + command.append(f"-f {shlex.quote(str(path))}") if action: command.append(action) if key: diff --git a/tests/test_utils.py b/tests/test_utils.py index f34263a4..dd551466 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,4 +1,5 @@ import shlex +from pathlib import Path import pytest @@ -28,13 +29,14 @@ def test_to_cli_string(): "directory", ["app", "my app", "app's"], ) -def test_to_cli_string_path(cli, tmp_path, directory): +@pytest.mark.parametrize("path_type", [str, Path]) +def test_to_cli_string_path(cli, tmp_path, directory, path_type): project = tmp_path / directory project.mkdir() path = project / ".env" path.write_text("DEBUG=True\n") - command = c(path=str(path), action="get", key="DEBUG") + command = c(path=path_type(path), action="get", key="DEBUG") result = cli.invoke(dotenv_cli, shlex.split(command)[1:]) assert (result.exit_code, result.output) == (0, "True\n")