Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion pyhocon/config_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,10 @@ def create_tree(value):
if isinstance(value, dict):
res = ConfigTree(root=root)
for key, child_value in value.items():
res.put(key, create_tree(child_value))
# Use _put with a single-element path to preserve the literal key.
# Using put() would split the key on dots (e.g. "a.b" → path ["a","b"]),
# truncating or nesting keys that contain HOCON path-separator characters.
res._put([key], create_tree(child_value))
return res
if isinstance(value, list):
return [create_tree(v) for v in value]
Expand Down
10 changes: 10 additions & 0 deletions pyhocon/config_tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,16 @@ def get(self, key, default=UndefinedKey):
:type default: object
:return: value in the tree located at key
"""
# Try an exact (literal) key lookup first so that keys containing dots
# that were stored verbatim (e.g. via from_dict()) are found before the
# dotted-path traversal interprets those dots as path separators.
exact = super(ConfigTree, self).get(key, UndefinedKey)
if exact is not UndefinedKey:
if isinstance(exact, NoneValue):
return None
if isinstance(exact, list):
return [None if isinstance(x, NoneValue) else x for x in exact]
return exact
return self._get(ConfigTree.parse_key(key), 0, default)

def get_string(self, key, default=UndefinedKey):
Expand Down
18 changes: 18 additions & 0 deletions tests/test_config_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -2310,6 +2310,24 @@ def test_from_dict_with_nested_dict(self):
config = ConfigFactory.from_dict(d)
assert config == d

def test_from_dict_preserves_dotted_keys(self):
"""Keys containing dots must be stored as literal keys, not split into nested paths."""
d = {
'myStuff': {
'myThing_r0.1': ['someVal'],
}
}
config = ConfigFactory.from_dict(d)
# The key 'myThing_r0.1' must appear verbatim, not be truncated to 'myThing_r0'.
assert list(config['myStuff'].keys()) == ['myThing_r0.1']
# Iterating items() must also yield the original key.
items = list(config['myStuff'].items())
assert items == [('myThing_r0.1', ['someVal'])]
# Direct get() must find the value.
assert config['myStuff'].get('myThing_r0.1') == ['someVal']
# Round-trip equality with the original dict.
assert config == d

def test_object_concat(self):
config = ConfigFactory.parse_string(
"""o1 = {
Expand Down