From 0a54fa1bc5f5988fba7f094873b808488d221d9c Mon Sep 17 00:00:00 2001
From: VM Orchestrator <232492212+cucuwang@users.noreply.github.com>
Date: Sun, 6 Sep 2026 04:46:34 +0000
Subject: [PATCH] Fix dotenv discovery in interactive consoles
---
CHANGELOG.md | 4 ++++
src/dotenv/main.py | 14 +++++++++++---
tests/test_main.py | 29 +++++++++++++++++++++++++++++
3 files changed, 44 insertions(+), 3 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 00f08f58..734978ba 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,8 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
### Fixed
+- Restore current-directory `.env` discovery in interactive consoles that report
+ pseudo-filenames such as `` by [@cucuwang] in [#596]
- An unquoted empty value followed by an inline comment (e.g. `KEY= # comment`) is now parsed as an empty string instead of the comment text by [@Noethix55555] in [#663]
## [1.2.3] - 2026-08-16
@@ -436,6 +438,7 @@ os.PathLike]` instead of just `os.PathLike` (#347 by [@bbc2]).
[#583]: https://github.com/theskumar/python-dotenv/issues/583
[#586]: https://github.com/theskumar/python-dotenv/issues/586
[#590]: https://github.com/theskumar/python-dotenv/issues/590
+[#596]: https://github.com/theskumar/python-dotenv/issues/596
[#607]: https://github.com/theskumar/python-dotenv/issues/607
[#588]: https://github.com/theskumar/python-dotenv/issues/588
[#579]: https://github.com/theskumar/python-dotenv/pull/579
@@ -508,6 +511,7 @@ os.PathLike]` instead of just `os.PathLike` (#347 by [@bbc2]).
[@JYOuyang]: https://github.com/JYOuyang
[@burnout-projects]: https://github.com/burnout-projects
[@cpackham-atlnz]: https://github.com/cpackham-atlnz
+[@cucuwang]: https://github.com/cucuwang
[Unreleased]: https://github.com/theskumar/python-dotenv/compare/v1.2.3...HEAD
[1.2.3]: https://github.com/theskumar/python-dotenv/compare/v1.2.2...v1.2.3
[1.2.2]: https://github.com/theskumar/python-dotenv/compare/v1.2.1...v1.2.2
diff --git a/src/dotenv/main.py b/src/dotenv/main.py
index 3123690a..251a34da 100644
--- a/src/dotenv/main.py
+++ b/src/dotenv/main.py
@@ -358,6 +358,9 @@ def _is_interactive():
def _is_debugger():
return sys.gettrace() is not None
+ def _is_pseudo_filename(source_path: str) -> bool:
+ return source_path.startswith("<") and source_path.endswith(">")
+
if usecwd or _is_interactive() or _is_debugger() or getattr(sys, "frozen", False):
# Should work without __file__, e.g. in REPL or IPython notebook.
path = os.getcwd()
@@ -366,13 +369,18 @@ def _is_debugger():
frame = sys._getframe()
current_file = __file__
- while frame.f_code.co_filename == current_file or not os.path.exists(
- frame.f_code.co_filename
+ while frame.f_code.co_filename == current_file or (
+ not _is_pseudo_filename(frame.f_code.co_filename)
+ and not os.path.exists(frame.f_code.co_filename)
):
assert frame.f_back is not None
frame = frame.f_back
frame_filename = frame.f_code.co_filename
- path = os.path.dirname(os.path.abspath(frame_filename))
+ path = (
+ os.getcwd()
+ if _is_pseudo_filename(frame_filename)
+ else os.path.dirname(os.path.abspath(frame_filename))
+ )
for dirname in _walk_to_root(path):
check_path = os.path.join(dirname, filename)
diff --git a/tests/test_main.py b/tests/test_main.py
index 6f9d4c5c..00c53342 100644
--- a/tests/test_main.py
+++ b/tests/test_main.py
@@ -412,6 +412,35 @@ def test_find_dotenv_found(tmp_path):
assert result == str(dotenv_path)
+def test_find_dotenv_from_pseudo_filename(tmp_path):
+ project_dir = tmp_path / "project"
+ project_dir.mkdir()
+ dotenv_path = project_dir / ".env"
+ dotenv_path.write_text("TEST=test\n")
+
+ console_driver = tmp_path / "console_driver.py"
+ console_driver.write_text(
+ textwrap.dedent(
+ """
+ from dotenv import find_dotenv
+
+ console_code = compile("print(find_dotenv())", "", "exec")
+ exec(console_code)
+ """
+ )
+ )
+
+ result = subprocess.run(
+ [sys.executable, str(console_driver)],
+ cwd=project_dir,
+ check=True,
+ capture_output=True,
+ text=True,
+ )
+
+ assert result.stdout.strip() == str(dotenv_path)
+
+
@pytest.mark.skipif(
sys.platform == "win32", reason="This test assumes case-sensitive variable names"
)