diff --git a/confit/config.py b/confit/config.py index 14d3f76..32f7661 100644 --- a/confit/config.py +++ b/confit/config.py @@ -141,10 +141,11 @@ def from_cfg_str(cls, s: str, resolve: bool = False, registry: Any = None) -> An @classmethod def from_yaml_str(cls, s: str, resolve: bool = False, registry: Any = None) -> Any: import yaml - class ConfitYamlLoader(yaml.SafeLoader): def construct_object(self, x, deep=False): if isinstance(x, yaml.ScalarNode): + if x.style == '"' or x.style == "'": + return loads(x.style+x.value+x.style) return loads(x.value) return super().construct_object(x, deep) diff --git a/confit/utils/xjson.py b/confit/utils/xjson.py index f49f36a..61e4739 100644 --- a/confit/utils/xjson.py +++ b/confit/utils/xjson.py @@ -260,24 +260,19 @@ def __init__(self, value: str): def loads(s: str): """ - Load an extended JSON string into a python object. - Handles references and tuples, and detects malformed objects. + Load an extended JSON string into a Python object. + + Handles references, tuples, and detects malformed objects. """ try: return XJsonTransformer(s).transform(_json_parser.parse(s)) except Exception: - # Detect malformed strings with unmatched quotes - if (s.startswith("'") and not s.endswith("'")) or ( - s.startswith('"') and not s.endswith('"') - ): - raise MalformedValueError(s) - - # Detect suspicious malformed patterns - if any(char in s for char in ",{}[]") and not s.startswith(("'", '"')): + # Fail if we suspect that it is a malformed object + # (e.g. has ', ", {, }, [, ] in it) + if set(s) & set(",'\"{}[]$"): raise MalformedValueError(s) - return s - + def dumps(o: Any): """ diff --git a/tests/test_config_instance.py b/tests/test_config_instance.py index 34b879c..b8b8f0e 100644 --- a/tests/test_config_instance.py +++ b/tests/test_config_instance.py @@ -694,5 +694,23 @@ def test_very_long_yaml_config(): """.format( "x" * 4200 ) - ) + ).resolve(registry=registry) assert config == {"a": "x" * 4200} + + +def test_escaped_string(): + config = Config.from_yaml_str(""" +test: + a: "1" +section: + num: 1 + escaped_num: "1" + real_ref: ${test.a} + escaped_broken_ref: "${test.a" + escaped_ref: "${test.a}" +""").resolve(registry=registry) + assert config["section"]["num"] == 1 + assert config["section"]["escaped_num"] == "1" + assert config["section"]["real_ref"] == "1" + assert config["section"]["escaped_broken_ref"] == "${test.a" + assert config["section"]["escaped_ref"] == "${test.a}" \ No newline at end of file