From c0eeea6046065f09f647e9b4ebd9a99e2549a63d Mon Sep 17 00:00:00 2001 From: Kulraj <59130414+Kulraj69@users.noreply.github.com> Date: Sat, 31 Oct 2026 22:32:10 +0530 Subject: [PATCH 1/2] fix: report command permission errors without a traceback --- src/dotenv/cli.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/dotenv/cli.py b/src/dotenv/cli.py index c06c7068..7525edec 100644 --- a/src/dotenv/cli.py +++ b/src/dotenv/cli.py @@ -244,6 +244,9 @@ def run_command(command: List[str], env: Dict[str, str]) -> None: except FileNotFoundError: print(f"Command not found: {command[0]}", file=sys.stderr) sys.exit(1) + except PermissionError: + print(f"Permission denied: {command[0]}", file=sys.stderr) + sys.exit(1) _, _ = p.communicate() @@ -254,3 +257,6 @@ def run_command(command: List[str], env: Dict[str, str]) -> None: except FileNotFoundError: print(f"Command not found: {command[0]}", file=sys.stderr) sys.exit(1) + except PermissionError: + print(f"Permission denied: {command[0]}", file=sys.stderr) + sys.exit(1) From 69fda1e37d3b51c67d20e0178c473f9c030d6051 Mon Sep 17 00:00:00 2001 From: Kulraj <59130414+Kulraj69@users.noreply.github.com> Date: Sat, 31 Oct 2026 22:32:38 +0530 Subject: [PATCH 2/2] fix: report command permission errors without a traceback --- tests/test_cli_permission.py | 45 ++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 tests/test_cli_permission.py diff --git a/tests/test_cli_permission.py b/tests/test_cli_permission.py new file mode 100644 index 00000000..116415fe --- /dev/null +++ b/tests/test_cli_permission.py @@ -0,0 +1,45 @@ +"""Command-launch permission failures should be CLI diagnostics, not tracebacks.""" + +import os +import sys + +import pytest + +from dotenv.cli import run_command +from tests.test_lib import run_dotenv + + +@pytest.mark.parametrize("platform", ["linux", "win32"]) +def test_permission_error_is_reported_on_both_launch_paths( + monkeypatch, capsys, platform +): + import dotenv.cli as module + + def denied(*args, **kwargs): + raise PermissionError(13, "Permission denied") + + monkeypatch.setattr(module.sys, "platform", platform) + monkeypatch.setattr(module.os, "execvpe", denied) + monkeypatch.setattr(module, "Popen", denied, raising=False) + with pytest.raises(SystemExit) as caught: + run_command(["no-execute"], {"PRIVATE_TOKEN": "must-not-appear"}) + assert caught.value.code == 1 + output = capsys.readouterr().err + assert "Permission denied: no-execute" in output + assert "must-not-appear" not in output + + +@pytest.mark.skipif(sys.platform == "win32", reason="POSIX execute bits") +@pytest.mark.parametrize("target_kind", ["file", "directory"]) +def test_run_nonexecutable_target_has_no_traceback(tmp_path, target_kind): + (tmp_path / ".env").write_text("A=b\n") + target = tmp_path / "not-executable" + if target_kind == "file": + target.write_text("#!/bin/sh\nexit 0\n") + target.chmod(0o600) + else: + target.mkdir() + result = run_dotenv(["run", os.fspath(target)], cwd=tmp_path) + assert result.returncode == 1 + assert "Permission denied:" in result.stderr + assert "Traceback" not in result.stderr