Repository: sloria/environs
Pull request #370 (2024-11-15 18:32:38)

Fix read_env recursive bug
The feature added by #364 introduced a behavior that `read_env()` would always recurse the tree directory even when it has already found a .env file. Sorry about that.

If you would have a .env file on your current working directory this file would be chosen instead another .env more deep on the tree where `read_env()` was called.

Bug fixed and I added tests to caught this behavior on future releases. 


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index eb26dd6..96c2563 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,13 @@
 # Changelog
 
+## 11.2.1 (unreleased)
+
+Bug fixes:
+
+- Fix `Env.read_env(recurse=True)` so that it returns as soon as a `.env`
+  file is found ([#370](https://github.com/sloria/environs/pull/370)).
+  Thanks [senese](https://github.com/senese) for the PR.
+
 ## 11.2.0 (2024-11-14)
 
 Features:
diff --git a/src/environs/__init__.py b/src/environs/__init__.py
index 8014260..2fb8e40 100644
--- a/src/environs/__init__.py
+++ b/src/environs/__init__.py
@@ -454,7 +454,7 @@ class Env:
         verbose: _BoolType = False,
         override: _BoolType = False,
         return_path: _BoolType = False,
-    ) -> typing.Union[_BoolType, _StrType]:
+    ) -> typing.Union[_BoolType, typing.Optional[_StrType]]:
         """Read a .env file into os.environ.
 
         If .env is not found in the directory from which this method is called,
@@ -462,7 +462,7 @@ class Env:
         file is found. If you do not wish to recurse up the tree, you may pass
         False as a second positional argument.
         """
-
+        env_path = None
         is_env_loaded = False
         if path is None:
             # By default, start search from the same directory this function is called
@@ -488,6 +488,8 @@ class Env:
                         check_path, verbose=verbose, override=override
                     )
                     env_path = str(check_path)
+                    break
+
         else:
             is_env_loaded = load_dotenv(str(start), verbose=verbose, override=override)
             env_path = str(start)


Test patch:
diff --git a/tests/test_environs.py b/tests/test_environs.py
index 1920c52..102e0b8 100644
--- a/tests/test_environs.py
+++ b/tests/test_environs.py
@@ -407,6 +407,33 @@ class TestEnvFileReading:
         env_path = str(HERE / ".env")
         assert path == env_path
 
+    def test_read_env_return_path_with_dotenv_in_working_dir(self, env):
+        working_dir = pathlib.Path(os.getcwd())
+        temp_env = working_dir / ".env"
+        try:
+            # Create an empty .env file in working dir
+            temp_env.touch()
+            path = env.read_env(return_path=True)
+        finally:
+            if temp_env.exists():
+                temp_env.unlink()
+
+        env_path = str(HERE / ".env")
+        assert path == env_path
+
+    def test_read_env_return_path_if_env_not_found(self, env, tmp_path):
+        # Move .env file to temp location
+        env_path = HERE / ".env"
+        temp_env = tmp_path / ".env"
+        try:
+            env_path.rename(temp_env)
+            path = env.read_env(return_path=True)
+            assert path is None
+        finally:
+            # Restore .env file
+            if temp_env.exists():
+                temp_env.rename(env_path)
+
 
 def always_fail(value):
     raise environs.EnvError("something went wrong")