diff options
-rw-r--r-- | configlib/model.py | 6 | ||||
-rw-r--r-- | configlib/model_impl.py | 29 | ||||
-rw-r--r-- | tests/test_something.py | 12 |
3 files changed, 44 insertions, 3 deletions
diff --git a/configlib/model.py b/configlib/model.py index 5f5c694..0bc94e1 100644 --- a/configlib/model.py +++ b/configlib/model.py @@ -11,6 +11,12 @@ from typing import Type, Union, AnyStr, TextIO from configlib.util import snake_case +class InvalidConfigEscapeException(Exception): + """ + Some config uses an invalid escape like `$invalidescapecode:argument` + """ + + class InvalidConfigTypingException(Exception): """ The typing found in the given class is missing arguments. diff --git a/configlib/model_impl.py b/configlib/model_impl.py index dc8cde0..d18611f 100644 --- a/configlib/model_impl.py +++ b/configlib/model_impl.py @@ -1,10 +1,11 @@ """ Implementations for the modules of :module:`configlib.model` """ - +import os from typing import Type, Dict, List -from .model import Config, ConfigValueMissingException, InvalidConfigTypingException +from .model import Config, ConfigValueMissingException, InvalidConfigTypingException, \ + InvalidConfigEscapeException from .util import snake_case @@ -57,6 +58,23 @@ def parse_dict_impl(val_type: Type[object], val, path) -> dict: return dic +_ESCAPES = { + 'env': os.environ.get, +} + + +def _resolve_string(val: str): + if val[0] != '$': + return val + if val[1] == '$': + return val[1:] + descriptor, arg, *_ = val[1:].split(':', 2) + [''] + escape = _ESCAPES.get(descriptor) + if not escape: + raise InvalidConfigEscapeException(descriptor) + return escape(arg) + + # noinspection PyUnresolvedReferences def parse_single_item(val_type: Type[object], val, path): """ @@ -67,8 +85,13 @@ def parse_single_item(val_type: Type[object], val, path): :param path: the path inside the config. used for error reporting :return: the parsed something """ + if isinstance(val, str): + if len(val) > 2 and val[0] == '$': + val = _resolve_string(val) + if issubclass(val_type, (str, int, float)): - return val + # noinspection PyArgumentList + return val_type(val) if isinstance(val_type, List): if len(val_type.__args__) != 1: raise InvalidConfigTypingException(path + ': List must be supplied exactly one type') diff --git a/tests/test_something.py b/tests/test_something.py index 8091f46..7bb979a 100644 --- a/tests/test_something.py +++ b/tests/test_something.py @@ -1,4 +1,5 @@ import json +import os from unittest import TestCase from configlib.model_impl import BaseConfig @@ -19,6 +20,12 @@ test_dict = { 'a': 1 } } +env_dict = { + 'something': '$env:ENVVAR', + 'ye': { + 'a': 1 + } +} def verify_test_dict(conf): @@ -37,3 +44,8 @@ class TestSomething(TestCase): def test_text(self): conf: SomeConfig = SomeConfig.loads(json.dumps(test_dict)) verify_test_dict(conf) + + def test_environ(self): + os.environ['ENVVAR'] = 'hmm' + conf: SomeConfig = SomeConfig.parse_dict(env_dict) + verify_test_dict(conf) |