From 550d4117a63eebd3c680dac6b631dd8e7c096f6b Mon Sep 17 00:00:00 2001 From: Omarinho12345 Date: Wed, 11 Nov 2026 18:41:24 +0100 Subject: [PATCH] fix: apply `${VAR:-default}` default to variables declared without a value A variable written without a value (`FOO`) is unset rather than empty, so a provided default must still apply. `Variable.resolve()` turned a `None` entry into an empty string before the default could be used. Fixes #750 --- src/dotenv/variables.py | 4 +++- tests/test_main.py | 3 +++ tests/test_variables.py | 13 +++++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/dotenv/variables.py b/src/dotenv/variables.py index 667f2f26..26e57ddb 100644 --- a/src/dotenv/variables.py +++ b/src/dotenv/variables.py @@ -64,7 +64,9 @@ def __hash__(self) -> int: def resolve(self, env: Mapping[str, Optional[str]]) -> str: default = self.default if self.default is not None else "" result = env.get(self.name, default) - return result if result is not None else "" + # A variable declared without a value (`FOO`) is unset, not empty, so + # a provided default applies to it just as it does to a missing name. + return result if result is not None else default def parse_variables(value: str) -> Iterator[Atom]: diff --git a/tests/test_main.py b/tests/test_main.py index 930ab171..217c650f 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -894,6 +894,9 @@ def test_dotenv_values_file(dotenv_path): # Undefined ({}, "a=${b}", True, {"a": ""}), ({}, "a=${b:-d}", True, {"a": "d"}), + # Declared without a value, but not empty + ({}, "b\na=${b:-d}", True, {"a": "d", "b": None}), + ({}, "b\na=${b}", True, {"a": "", "b": None}), # With quotes ({"b": "c"}, 'a="${b}"', True, {"a": "c"}), ({"b": "c"}, "a='${b}'", True, {"a": "c"}), diff --git a/tests/test_variables.py b/tests/test_variables.py index 6f2b2203..994ac79f 100644 --- a/tests/test_variables.py +++ b/tests/test_variables.py @@ -33,3 +33,16 @@ def test_parse_variables(value, expected): result = parse_variables(value) assert list(result) == expected + + +@pytest.mark.parametrize( + "env,default,expected", + [ + ({"a": "b"}, "c", "b"), + ({}, "c", "c"), + ({"a": ""}, "c", ""), + ({"a": None}, "c", "c"), + ], +) +def test_variable_resolve(env, default, expected): + assert Variable(name="a", default=default).resolve(env) == expected