diff --git a/CHANGELOG.md b/CHANGELOG.md index e5f25414..8a6cd59d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - `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] - `load_dotenv`, `dotenv_values`, `get_key`, `set_key`, `unset_key` and the CLI `--file` option now expand a leading `~` to the user's home directory by [@veeceey] in [#615] - `find_dotenv` and the IPython `%dotenv` magic now expand a leading `~` in the file name by [@theskumar] in [#714] +- `dotenv run` now accepts a `.env` file that is a FIFO (named pipe), such as a 1Password-mounted `.env` or a `<(...)` process substitution, like `load_dotenv` and `dotenv list`/`get` already do, instead of failing with "does not exist" by [@sungbin1015] in [#723] ## [1.2.4] - 2026-10-01 @@ -467,6 +468,7 @@ os.PathLike]` instead of just `os.PathLike` (#347 by [@bbc2]). [#700]: https://github.com/theskumar/python-dotenv/pull/700 [#711]: https://github.com/theskumar/python-dotenv/pull/711 [#714]: https://github.com/theskumar/python-dotenv/pull/714 +[#723]: https://github.com/theskumar/python-dotenv/pull/723 [790c5c0]: https://github.com/theskumar/python-dotenv/commit/790c5c02991100aa1bf41ee5330aca75edc51311 @@ -520,6 +522,7 @@ os.PathLike]` instead of just `os.PathLike` (#347 by [@bbc2]). [@ShamikOfficial]: https://github.com/ShamikOfficial [@sidharth-sudhir]: https://github.com/sidharth-sudhir [@snobu]: https://github.com/snobu +[@sungbin1015]: https://github.com/sungbin1015 [@techalchemy]: https://github.com/techalchemy [@theGOTOguy]: https://github.com/theGOTOguy [@theskumar]: https://github.com/theskumar diff --git a/src/dotenv/cli.py b/src/dotenv/cli.py index c06c7068..fbcac702 100644 --- a/src/dotenv/cli.py +++ b/src/dotenv/cli.py @@ -17,7 +17,7 @@ ) sys.exit(1) -from .main import DotEnv, dotenv_values, set_key, unset_key +from .main import DotEnv, _is_file_or_fifo, dotenv_values, set_key, unset_key from .version import __version__ @@ -193,7 +193,7 @@ def unset(ctx: click.Context, key: Any) -> None: def run(ctx: click.Context, override: bool, commandline: tuple[str, ...]) -> None: """Run command with environment variables present.""" file = ctx.obj["FILE"] - if not os.path.isfile(file): + if not _is_file_or_fifo(file): raise click.BadParameter( f"Invalid value for '-f' \"{file}\" does not exist.", ctx=ctx ) diff --git a/tests/test_fifo_dotenv.py b/tests/test_fifo_dotenv.py index 2aa31779..7cec1c06 100644 --- a/tests/test_fifo_dotenv.py +++ b/tests/test_fifo_dotenv.py @@ -6,6 +6,7 @@ import pytest from dotenv import load_dotenv +from tests.test_lib import check_process, run_dotenv pytestmark = pytest.mark.skipif(sys.platform == "win32", reason="FIFOs are Unix-only") @@ -29,3 +30,22 @@ def writer(): assert ok is True assert os.getenv("MY_PASSWORD") == "pipe-secret" + + +def test_cli_run_from_fifo(tmp_path: pathlib.Path): + fifo = tmp_path / ".env" + os.mkfifo(fifo) + + def writer(): + with open(fifo, "w", encoding="utf-8") as w: + w.write("MY_PASSWORD=pipe-secret\n") + + # A daemon thread, so that a reader which never opens the FIFO can't hang + # the test session. + t = threading.Thread(target=writer, daemon=True) + t.start() + + result = run_dotenv(["run", "printenv", "MY_PASSWORD"], cwd=tmp_path) + t.join(timeout=2) + + check_process(result, exit_code=0, stdout="pipe-secret\n")