diff --git a/CHANGELOG.md b/CHANGELOG.md index e5f25414..9dcc8110 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ### Fixed +- `${name:-default}` now uses the default when `name` is empty. `b=` followed by `a=${b:-d}` yields `d` instead of an empty string. ([#715]) - Fix a package build deprecation warning caused by a non-string `license` value in `pyproject.toml` by [@kurtmckee] in [#648] - `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] diff --git a/src/dotenv/variables.py b/src/dotenv/variables.py index 667f2f26..32c18711 100644 --- a/src/dotenv/variables.py +++ b/src/dotenv/variables.py @@ -62,9 +62,14 @@ def __hash__(self) -> int: return hash((self.__class__, self.name, self.default)) 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 "" + # ${name:-default} uses the default when name is missing or empty. + # ${name} has no default, so those cases stay empty. + result = env.get(self.name) + if result: + return result + if self.default is not None: + return self.default + return result or "" def parse_variables(value: str) -> Iterator[Atom]: diff --git a/tests/test_main.py b/tests/test_main.py index 930ab171..a3c460b3 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"}), + # Empty is unset for the :- form + ({"b": ""}, "a=${b:-d}", True, {"a": "d"}), + ({}, "b=\na=${b:-d}", True, {"a": "d", "b": ""}), # 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..bdb0b05e 100644 --- a/tests/test_variables.py +++ b/tests/test_variables.py @@ -33,3 +33,10 @@ def test_parse_variables(value, expected): result = parse_variables(value) assert list(result) == expected + + +def test_empty_value_uses_default(): + assert Variable(name="b", default="d").resolve({"b": ""}) == "d" + assert Variable(name="b", default="d").resolve({}) == "d" + assert Variable(name="b", default="d").resolve({"b": "c"}) == "c" + assert Variable(name="b", default=None).resolve({"b": ""}) == ""