Skip to content

Commit 4b43436

Browse files
committed
Fix empty expanded value for duplicate key
Example problematic file: ```bash hello=hi greetings=${hello} goodbye=bye greetings=${goodbye} ``` It would result in `greetings` being associated with the empty string instead of `"bye"`. The problem came from the fact that bindings were converted to a dict, and so deduplicated by key, before being interpolated. The dict would be `{"hello": "hi", "greetings": "${goodbye}", "goodbye": "bye"}` in the earlier example, which shows why interpolation wouldn't work: `goodbye` would not be defined when `greetings` was interpolated. This commit fixes that by passing all values in order, even if there are duplicated keys.
1 parent e4bbb8a commit 4b43436

File tree

3 files changed

+15
-7
lines changed

3 files changed

+15
-7
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

88
## [Unreleased]
99

10-
*No unreleased change at this time.*
10+
### Fixed
11+
12+
- Fix potentially empty expanded value for duplicate key (#260 by [@bbc]).
1113

1214
## [0.14.0] - 2020-07-03
1315

src/dotenv/main.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
if IS_TYPE_CHECKING:
2020
from typing import (
21-
Dict, Iterator, Match, Optional, Pattern, Union, Text, IO, Tuple
21+
Dict, Iterable, Iterator, Match, Optional, Pattern, Union, Text, IO, Tuple
2222
)
2323
if sys.version_info >= (3, 6):
2424
_PathLike = os.PathLike
@@ -83,9 +83,13 @@ def dict(self):
8383
if self._dict:
8484
return self._dict
8585

86-
values = OrderedDict(self.parse())
87-
self._dict = resolve_nested_variables(values) if self.interpolate else values
88-
return self._dict
86+
if self.interpolate:
87+
values = resolve_nested_variables(self.parse())
88+
else:
89+
values = OrderedDict(self.parse())
90+
91+
self._dict = values
92+
return values
8993

9094
def parse(self):
9195
# type: () -> Iterator[Tuple[Text, Optional[Text]]]
@@ -211,7 +215,7 @@ def unset_key(dotenv_path, key_to_unset, quote_mode="always"):
211215

212216

213217
def resolve_nested_variables(values):
214-
# type: (Dict[Text, Optional[Text]]) -> Dict[Text, Optional[Text]]
218+
# type: (Iterable[Tuple[Text, Optional[Text]]]) -> Dict[Text, Optional[Text]]
215219
def _replacement(name, default):
216220
# type: (Text, Optional[Text]) -> Text
217221
default = default if default is not None else ""
@@ -229,7 +233,7 @@ def _re_sub_callback(match):
229233

230234
new_values = {}
231235

232-
for k, v in values.items():
236+
for (k, v) in values:
233237
new_values[k] = __posix_variable.sub(_re_sub_callback, v) if v is not None else None
234238

235239
return new_values

tests/test_main.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,8 @@ def test_dotenv_values_file(dotenv_file):
337337
338338
# Re-defined and used in file
339339
({"b": "c"}, "b=d\na=${b}", True, {"a": "d", "b": "d"}),
340+
({}, "a=b\na=c\nd=${a}", True, {"a": "c", "d": "c"}),
341+
({}, "a=b\nc=${a}\nd=e\nc=${d}", True, {"a": "b", "c": "e", "d": "e"}),
340342
],
341343
)
342344
def test_dotenv_values_stream(env, string, interpolate, expected):

0 commit comments

Comments
 (0)