diff --git a/changelog.md b/changelog.md index cc92a4d..1e89cbb 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,9 @@ # Changelog +## v0.10.3 (2025-01-19) + +- Don't fail but warn, when we suspect that un unquoted string might actually be an object in config files. + ## v0.10.2 (2025-12-11) - Confit should not complain anymore when given multiline strings diff --git a/confit/config.py b/confit/config.py index a6735e1..fb1acf9 100644 --- a/confit/config.py +++ b/confit/config.py @@ -14,7 +14,6 @@ from confit.errors import ( ConfitValidationError, CyclicReferenceError, - ErrorWrapper, MissingReference, patch_errors, remove_lib_from_traceback, @@ -120,11 +119,7 @@ def from_cfg_str(cls, s: str, resolve: bool = False, registry: Any = None) -> An current.clear() errors = [] for k, v in parser.items(section): - parsed_k = loads(k) - try: - current[parsed_k] = loads(v) - except ValueError as e: - errors.append(ErrorWrapper(e, loc=parsed_k)) + current[loads(k)] = loads(v) if errors: raise ConfitValidationError(errors=errors) @@ -425,9 +420,9 @@ def rec(obj, loc: Tuple[Union[str, int]] = ()): for key, value in resolved.items() if isinstance(key, str) and key.startswith("@") ] - assert ( - len(registries) <= 1 - ), f"Cannot resolve using multiple registries at {'.'.join(loc)}" + assert len(registries) <= 1, ( + f"Cannot resolve using multiple registries at {'.'.join(loc)}" + ) if len(registries) == 1: raw_key, value, registry_value = registries[0] @@ -437,7 +432,7 @@ def rec(obj, loc: Tuple[Union[str, int]] = ()): value = value.strip() is_draft = value.endswith("!draft") if is_draft: - value = value[:-len("!draft")].strip() + value = value[: -len("!draft")].strip() fn = registry_value.get(value) try: if is_draft: diff --git a/confit/utils/xjson.py b/confit/utils/xjson.py index dd55740..dcfdbfe 100644 --- a/confit/utils/xjson.py +++ b/confit/utils/xjson.py @@ -1,4 +1,5 @@ import ast +import warnings from typing import Any, Callable from lark import Lark, Transformer, Tree @@ -253,12 +254,6 @@ def _iterencode(o): return _iterencode -class MalformedValueError(ValueError): - def __init__(self, value: str): - self.value = value - super().__init__(f"Malformed value: {value!r}") - - def loads(s: str): """ Load an extended JSON string into a python object. @@ -280,7 +275,10 @@ def loads(s: str): # Fail if we suspect that it is a malformed object # (e.g. has ', ", {, }, [, ] in it) if set(s) & set(",'\"{}[]$"): - raise MalformedValueError(s) + warnings.warn( + f"Some values may be malformed JSON objects. Got: {s!r}", + UserWarning, + ) return s diff --git a/tests/test_config_instance.py b/tests/test_config_instance.py index 4fb728c..cbbff12 100644 --- a/tests/test_config_instance.py +++ b/tests/test_config_instance.py @@ -556,22 +556,23 @@ def test_list_interpolation(): assert config["section"]["b"] == ["foo", "bar", "baz"] -def test_fail_if_suspected_json_malformation(): - with pytest.raises(ConfitValidationError) as exc_info: - Config.from_str( +def test_warn_if_suspected_json_malformation(): + with pytest.warns(UserWarning) as record: + config = Config.from_str( """ [section] string = 'ok list = 'ok'] """ ) - assert str(exc_info.value) == ( - "2 validation errors\n" - "-> string\n" - ' Malformed value: "\'ok"\n' - "-> list\n" - " Malformed value: \"'ok']\"" - ) + + assert len(record) == 2 + assert [str(w.message) for w in record] == [ + 'Some values may be malformed JSON objects. Got: "\'ok"', + "Some values may be malformed JSON objects. Got: \"'ok']\"", + ] + + assert config == {"section": {"string": "'ok", "list": "'ok']"}} def test_string():