From 7bb806a96d4f0ad4dd37a6db8adcf118bd523540 Mon Sep 17 00:00:00 2001 From: romangraef Date: Sun, 26 Aug 2018 11:04:55 +0200 Subject: added $ escapes\n\nfor now only `$env:envname` is supported --- configlib/model.py | 6 ++++++ configlib/model_impl.py | 29 ++++++++++++++++++++++++++--- 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) -- cgit