Skip to content
This repository was archived by the owner on Mar 31, 2025. It is now read-only.

Commit 8b7a54b

Browse files
Merge pull request #23 from color/mpcusack-lists
Add list value support to clrenv 0.2
2 parents e11b67f + a315c78 commit 8b7a54b

File tree

6 files changed

+47
-21
lines changed

6 files changed

+47
-21
lines changed

clrenv/evaluate.py

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
from collections import abc
2828
from pathlib import Path
2929
from typing import (
30-
Any,
3130
Iterable,
3231
Iterator,
3332
List,
@@ -41,15 +40,16 @@
4140
)
4241

4342
from .path import environment_paths
44-
from .read import EnvReader, NestedMapping, PrimitiveValue
43+
from .read import EnvReader
44+
from .types import LeafValue, NestedMapping, check_valid_leaf_value
4545

4646
logger = logging.getLogger(__name__)
4747

4848
DEBUG_MODE = os.environ.get("CLRENV_DEBUG", "").lower() in ("true", "1")
4949

5050
# Access to an attribute might return a primitive or if it is not a leaf node
5151
# another SubClrEnv.
52-
Value = Union[PrimitiveValue, "SubClrEnv"]
52+
Value = Union[LeafValue, "SubClrEnv"]
5353

5454

5555
class SubClrEnv(abc.MutableMapping):
@@ -90,10 +90,10 @@ def __getattr__(self, key: str) -> Value:
9090
except KeyError as e:
9191
raise AttributeError(str(e))
9292

93-
def __setitem__(self, key: str, value: PrimitiveValue):
93+
def __setitem__(self, key: str, value: LeafValue):
9494
self._root.set_runtime_override(self._sub_key_path(key), value)
9595

96-
def __setattr__(self, key: str, value: PrimitiveValue):
96+
def __setattr__(self, key: str, value: LeafValue):
9797
"""Sets a runtime override as an attribute."""
9898
# Internal fields are prefixed with a _ and should be treated normally.
9999
if key.startswith("_"):
@@ -145,7 +145,7 @@ def _sub_keys(self) -> Set[str]:
145145
subkeys.add(env_var.split("__")[0].lower())
146146
return subkeys
147147

148-
def _evaluate_key(self, key: str) -> Union[PrimitiveValue, Mapping, None]:
148+
def _evaluate_key(self, key: str) -> Union[LeafValue, Mapping, None]:
149149
"""Returns the stored value for the given key.
150150
151151
There are three potential sources of data (in order of priority):
@@ -219,7 +219,7 @@ def __init__(self, paths: Optional[List[Path]] = None):
219219
# efficent lookup for subkeys.
220220
# env.a.b.c = 'd' ==> _runtime_overrides = {('a', 'b'): {'c': 'd'}}
221221
self._runtime_overrides: MutableMapping[
222-
Tuple[str, ...], MutableMapping[str, PrimitiveValue]
222+
Tuple[str, ...], MutableMapping[str, LeafValue]
223223
] = {}
224224

225225
def _make_env(self) -> NestedMapping:
@@ -230,7 +230,9 @@ def clear_runtime_overrides(self):
230230
"""Clear all runtime overrides."""
231231
self._runtime_overrides.clear()
232232

233-
def set_runtime_override(self, key_path: Sequence[str], value: PrimitiveValue):
233+
def set_runtime_override(
234+
self, key_path: Union[str, Sequence[str]], value: LeafValue
235+
):
234236
"""Sets a runtime override.
235237
236238
Only do this in tests and ideally use unittest.mock.patch or monkeypath.setattr
@@ -239,20 +241,19 @@ def set_runtime_override(self, key_path: Sequence[str], value: PrimitiveValue):
239241
Notice that this method is only on the root node."""
240242
if not key_path:
241243
raise ValueError("key_path can not be empty.")
244+
# No support for nested runtime overrides. Only allow primitives.
245+
check_valid_leaf_value(key_path, value)
246+
242247
if isinstance(key_path, str):
243248
key_path = key_path.split(".")
244249

245250
# Check that the key already exists.
246-
parent: Union[SubClrEnv, PrimitiveValue] = self
251+
parent: Union[SubClrEnv, LeafValue] = self
247252
for name in key_path:
248253
assert isinstance(parent, Mapping)
249254
assert name in parent, f"{name, parent}"
250255
parent = parent[name]
251256

252-
# No support for nested runtime overrides. Only allow primitives.
253-
if not isinstance(value, PrimitiveValue.__args__): # type: ignore
254-
raise ValueError("Env values must be one of {str, int, float, boolean}.")
255-
256257
# Ideally we wouldn't be overriding global state like this at all, but at least
257258
# make it loud.
258259
logger.warning(f"Manually overriding env.{'.'.join(key_path)} to {value}.")

clrenv/read.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,19 @@
1515
import os
1616
from collections import abc, deque
1717
from pathlib import Path
18-
from typing import Any, Deque, Iterable, Mapping, MutableMapping, Optional, Tuple, Union
18+
from typing import Any, Deque, Iterable, Mapping, Optional, Tuple
1919

2020
import boto3
2121
from botocore.exceptions import EndpointConnectionError # type: ignore
2222

2323
from .deepmerge import deepmerge
2424
from .path import environment_paths
25+
from .types import MutableNestedMapping, NestedMapping, check_valid_leaf_value
2526

2627
logger = logging.getLogger(__name__)
2728

2829
# Types that can be read or set as values of leaf nodes.
29-
PrimitiveValue = Union[bool, int, float, str]
30-
NestedMapping = Mapping[str, Union[PrimitiveValue, Mapping[str, Any]]]
31-
MutableNestedMapping = MutableMapping[
32-
str, Union[PrimitiveValue, MutableMapping[str, Any]]
33-
]
30+
3431

3532
# Flag to prevent clrenv from throwing errors
3633
# if it cannot connect to the Parameter Store API.
@@ -106,6 +103,8 @@ def read(self) -> NestedMapping:
106103
mapping[key] = "" # type: ignore
107104
elif isinstance(value, str):
108105
mapping[key] = self.postprocess_str(value)
106+
else:
107+
check_valid_leaf_value(key_prefix + key, value)
109108

110109
return result
111110

clrenv/tests/__init__.py

Whitespace-only changes.

clrenv/tests/test_evaluate.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,6 @@ def test_env_var(monkeypatch, default_env):
179179

180180
def test_underscored_keys(default_env):
181181
with pytest.raises(KeyError):
182-
default_env["__env"]
182+
default_env["__env"] # pylint: disable=pointless-statement
183183
with pytest.raises(AttributeError):
184184
getattr(default_env.__unknown)

clrenv/types.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from typing import Any, List, Mapping, MutableMapping, Union
2+
3+
"""Defines type annotations for use in clrenv.
4+
5+
Ideally we would only allow str leaf values so that they can be migrated to an env
6+
var based system. For now we must supports more complex values.
7+
"""
8+
9+
# Type that can be read or set as values of leaf nodes.
10+
LeafValue = Union[bool, int, float, str, List[Union[bool, int, float, str]]]
11+
# Type of a non leaf node.
12+
NestedMapping = Mapping[str, Union[LeafValue, Mapping[str, Any]]]
13+
MutableNestedMapping = MutableMapping[str, Union[LeafValue, MutableMapping[str, Any]]]
14+
15+
PRIMITIVE_TYPES = (bool, int, float, str)
16+
17+
18+
def check_valid_leaf_value(key: Any, value: Any) -> None:
19+
"""Raises a ValueError is the value is not a valid type.
20+
21+
key is only used for the error message."""
22+
if isinstance(value, PRIMITIVE_TYPES):
23+
return
24+
if isinstance(value, list) and all(isinstance(_, PRIMITIVE_TYPES) for _ in value):
25+
return
26+
raise ValueError(f"Non primitive value type: {key}={value}")

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
setup(
77
name="clrenv",
8-
version="0.2.0",
8+
version="0.2.1",
99
description="A tool to give easy access to environment yaml file to python.",
1010
author="Color",
1111
author_email="[email protected]",

0 commit comments

Comments
 (0)