From 4c45981aac38efa5bc17b4e4e4eeffcad7c7b839 Mon Sep 17 00:00:00 2001 From: Saurabh Kumar Date: Thu, 1 Oct 2026 12:28:07 +0530 Subject: [PATCH 1/2] fix: expand ~ in find_dotenv and the %dotenv magic find_dotenv joins filename onto each directory it searches, so an absolute filename is already returned as is when it exists, but `~/.env` was searched for literally. Expand it first, which also fixes IPython's `%dotenv ~/.env`. Follows #615, which did the same for dotenv_path and the CLI --file option. --- README.md | 1 + src/dotenv/main.py | 4 ++++ tests/test_ipython.py | 15 +++++++++++++++ tests/test_main.py | 13 +++++++++++++ 4 files changed, 33 insertions(+) diff --git a/README.md b/README.md index a08d6141..3400e67c 100644 --- a/README.md +++ b/README.md @@ -131,6 +131,7 @@ You can also specify a path: ```python %dotenv relative/or/absolute/path/to/.env +%dotenv ~/path/to/.env ``` Optional flags: diff --git a/src/dotenv/main.py b/src/dotenv/main.py index 7dfc3cba..5faa7f0e 100644 --- a/src/dotenv/main.py +++ b/src/dotenv/main.py @@ -381,8 +381,12 @@ def find_dotenv( """ Search in increasingly higher folders for the given file + A leading `~` in `filename` is expanded to the user's home directory, and + an absolute `filename` is returned as is if it exists. + Returns path to the file if found, or an empty string otherwise """ + filename = os.path.expanduser(filename) def _is_interactive(): """Decide whether this is running in a REPL or IPython notebook""" diff --git a/tests/test_ipython.py b/tests/test_ipython.py index 6eda086b..93bffd66 100644 --- a/tests/test_ipython.py +++ b/tests/test_ipython.py @@ -61,3 +61,18 @@ def test_ipython_new_variable(tmp_path): ipshell.run_line_magic("dotenv", "") assert os.environ == {"a": "b"} + + +@mock.patch.dict(os.environ, {}, clear=False) +def test_ipython_path_expands_user(tmp_path, monkeypatch): + from IPython.terminal.embed import InteractiveShellEmbed + + (tmp_path / ".env").write_text("dotenv_ipython_home=b\n") + monkeypatch.setenv("HOME", str(tmp_path)) + monkeypatch.setenv("USERPROFILE", str(tmp_path)) + + ipshell = InteractiveShellEmbed() + ipshell.run_line_magic("load_ext", "dotenv") + ipshell.run_line_magic("dotenv", "~/.env") + + assert os.environ["dotenv_ipython_home"] == "b" diff --git a/tests/test_main.py b/tests/test_main.py index cd21d70d..930ab171 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -591,6 +591,19 @@ def test_find_dotenv_found(tmp_path): assert result == str(dotenv_path) +def test_find_dotenv_expands_user(tmp_path, monkeypatch): + home = tmp_path / "home" + home.mkdir() + (home / ".env").write_text("a=x\n") + monkeypatch.setenv("HOME", str(home)) + monkeypatch.setenv("USERPROFILE", str(home)) + monkeypatch.chdir(tmp_path) + + result = dotenv.find_dotenv("~/.env", usecwd=True) + + assert result == f"{home}/.env" + + @pytest.mark.skipif( sys.platform == "win32", reason="This test assumes case-sensitive variable names" ) From 9c69aa49e4a44d1708191fd0e71bd26ed5346423 Mon Sep 17 00:00:00 2001 From: Saurabh Kumar Date: Thu, 1 Oct 2026 12:28:29 +0530 Subject: [PATCH 2/2] docs: add CHANGELOG entry for #714 --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e4a5cead..e5f25414 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - `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] - `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] ## [1.2.4] - 2026-10-01 @@ -465,6 +466,7 @@ os.PathLike]` instead of just `os.PathLike` (#347 by [@bbc2]). [#698]: https://github.com/theskumar/python-dotenv/pull/698 [#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 [790c5c0]: https://github.com/theskumar/python-dotenv/commit/790c5c02991100aa1bf41ee5330aca75edc51311