Skip to content

Commit

Permalink
Added dotted interpolation.
Browse files Browse the repository at this point in the history
Fixes #36.
  • Loading branch information
Tiago Requeijo committed Aug 5, 2020
1 parent c1913de commit 64601ad
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 9 deletions.
4 changes: 2 additions & 2 deletions config/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ def __getitem__(self, item: str) -> Union["Configuration", Any]: # noqa: D105
if isinstance(v, dict):
return Configuration(v)
elif self._interpolate is not False:
d = self.as_dict()
d = self.as_attrdict()
d.update(cast(Dict[str, str], self._interpolate))
return interpolate_object(item, v, [d], self._interpolate_type)
else:
Expand All @@ -173,7 +173,7 @@ def get(self, key: str, default: Any = None) -> Union[dict, Any]:

def as_dict(self) -> dict:
"""Return the representation as a dictionary."""
return self._config
return deepcopy(self._config)

def as_attrdict(self) -> AttributeDict:
"""Return the representation as an attribute dictionary."""
Expand Down
2 changes: 1 addition & 1 deletion config/configuration_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def _from_configs(self, attr: str, *args: Any, **kwargs: dict) -> Any:
result.update(v)
return Configuration(result)
elif self._interpolate is not False:
d = [d.as_dict() for d in self._configs]
d = [c.as_attrdict() for c in self._configs]
d[0].update(cast(Dict[str, str], self._interpolate))
return interpolate_object(args[0], values[0], d, self._interpolate_type)
else:
Expand Down
23 changes: 17 additions & 6 deletions config/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import string
from enum import Enum
from typing import Any, Dict, List, Set, Tuple, Union
from typing import Any, Dict, Sequence, Set, Tuple, Union


TRUTH_TEXT = frozenset(("t", "true", "y", "yes", "on", "1"))
Expand Down Expand Up @@ -85,6 +85,13 @@ def clean(key: str, value: Any, mask: str = "******") -> Any:
return value


def getattr_dotted(d: dict, item: str) -> Any:
"""Return the getattr method but looking through dotted items."""
for it in item.split("."):
d = d[it]
return d


def interpolate_standard(text: str, d: dict, found: Set[Tuple[str, ...]]) -> str:
"""
Return the string interpolated as many times as needed.
Expand All @@ -93,6 +100,8 @@ def interpolate_standard(text: str, d: dict, found: Set[Tuple[str, ...]]) -> str
:param d: dictionary
:param found: variables found so far
"""
from .configuration import Configuration

if not isinstance(text, str):
return text

Expand All @@ -108,14 +117,16 @@ def interpolate_standard(text: str, d: dict, found: Set[Tuple[str, ...]]) -> str
else:
found.add(variables)

interpolated = {v: interpolate_standard(d[v], d, found) for v in variables}
interpolated = Configuration(
{v: interpolate_standard(getattr_dotted(d, v), d, found) for v in variables}
).as_attrdict() # convert to attribute dict to play well with .format
return text.format(**interpolated)


def interpolate_deep(
attr: str,
text: str,
d: List[dict],
d: Sequence[dict],
resolved: Dict[str, str],
levels: Dict[str, int],
method: InterpolateEnumType,
Expand Down Expand Up @@ -154,7 +165,7 @@ def interpolate_deep(
levels[variable] = level + 1

new_d = (
([{}] * level) + d[level:]
([{}] * level) + d[level:] # type: ignore
if method == InterpolateEnumType.DEEP_NO_BACKTRACK
else d
)
Expand All @@ -165,7 +176,7 @@ def interpolate_deep(
return text.format(**resolved)


def flatten(d: List[dict]) -> dict:
def flatten(d: Sequence[dict]) -> dict:
"""
Flatten a list of dictionaries.
Expand All @@ -177,7 +188,7 @@ def flatten(d: List[dict]) -> dict:


def interpolate_object(
attr: str, obj: Any, d: List[dict], method: InterpolateEnumType
attr: str, obj: Any, d: Sequence[dict], method: InterpolateEnumType
) -> Any:
"""
Return the interpolated object.
Expand Down
16 changes: 16 additions & 0 deletions tests/test_interpolation.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,3 +269,19 @@ def test_interpolation_same_variable_4(): # type: ignore
)
assert cfg.var2 == "test/a/b" # var2(2) --> var1(2) --> var1(1) --> var2(1)
assert cfg.var1 == "test/a" # var1(2) --> var1(1) --> var2(1)


def test_interpolation_dotted(): # type: ignore
values_1 = {"a.b": "value"}
values_2 = {"var": "{a.b}"}

cfg = config(values_2, values_1, lowercase_keys=True, interpolate=True,)

assert cfg.var == "value"

values_1 = {"a": {"b": "value"}}
values_2 = {"var": "{a.b}"}

cfg = config(values_2, values_1, lowercase_keys=True, interpolate=True,)

assert cfg.var == "value"

0 comments on commit 64601ad

Please sign in to comment.