Skip to content

Commit 32f5f57

Browse files
authored
#256 - Process substitution overrides in order (#257)
1 parent 2fb6eb3 commit 32f5f57

File tree

3 files changed

+27
-1
lines changed

3 files changed

+27
-1
lines changed

pyhocon/config_parser.py

+10-1
Original file line numberDiff line numberDiff line change
@@ -641,9 +641,10 @@ def _do_substitute(cls, substitution, resolved_value, is_optional_resolved=True)
641641
if transformation is None and not is_optional_resolved \
642642
else transformation
643643

644+
# When the result is None, remove the key.
644645
if result is None and config_values.key in config_values.parent:
645646
del config_values.parent[config_values.key]
646-
else:
647+
elif result is not None:
647648
config_values.parent[config_values.key] = result
648649
s = cls._find_substitutions(result)
649650
if s:
@@ -692,6 +693,12 @@ def resolve_substitutions(cls, config, accept_unresolved=False):
692693
_substitutions = substitutions[:]
693694

694695
for substitution in _substitutions:
696+
# If this substitution is an override, and the parent is still being processed,
697+
# skip this entry, it will be processed on the next loop.
698+
if substitution.parent.overriden_value:
699+
if substitution.parent.overriden_value in [s.parent for s in substitutions]:
700+
continue
701+
695702
is_optional_resolved, resolved_value = cls._resolve_variable(config, substitution)
696703

697704
# if the substitution is optional
@@ -717,6 +724,8 @@ def resolve_substitutions(cls, config, accept_unresolved=False):
717724

718725
unresolved, new_substitutions, result = cls._do_substitute(substitution, resolved_value, is_optional_resolved)
719726
any_unresolved = unresolved or any_unresolved
727+
# Detected substitutions may already be listed to process
728+
new_substitutions = [n for n in new_substitutions if n not in substitutions]
720729
substitutions.extend(new_substitutions)
721730
if not isinstance(result, ConfigValues):
722731
substitutions.remove(substitution)

pyhocon/config_tree.py

+3
Original file line numberDiff line numberDiff line change
@@ -473,9 +473,12 @@ def has_substitution(self):
473473
return len(self.get_substitutions()) > 0
474474

475475
def get_substitutions(self):
476+
# Returns a list of ConfigSubstitution tokens, in string order
476477
lst = []
477478
node = self
478479
while node:
480+
# walking up the override chain and append overrides to the front.
481+
# later, parent overrides will be processed first, followed by children
479482
lst = [token for token in node.tokens if isinstance(token, ConfigSubstitution)] + lst
480483
if hasattr(node, 'overriden_value'):
481484
node = node.overriden_value

tests/test_config_parser.py

+14
Original file line numberDiff line numberDiff line change
@@ -1437,6 +1437,20 @@ def test_substitution_flat_override(self):
14371437
assert config['database.name'] == 'peopledb'
14381438
assert config['database.pass'] == 'peoplepass'
14391439

1440+
def test_substitution_multiple_override(self):
1441+
config = ConfigFactory.parse_string(
1442+
"""
1443+
a: 1
1444+
b: foo
1445+
c: ${a} ${b}
1446+
c: ${b} ${a}
1447+
d: ${a} ${b}
1448+
d: ${a} bar
1449+
""")
1450+
1451+
assert config['c'] == 'foo 1'
1452+
assert config['d'] == '1 bar'
1453+
14401454
def test_substitution_nested_override(self):
14411455
config = ConfigFactory.parse_string(
14421456
"""

0 commit comments

Comments
 (0)