diff --git a/news/435.bugfix b/news/435.bugfix new file mode 100644 index 000000000..4065af67e --- /dev/null +++ b/news/435.bugfix @@ -0,0 +1 @@ +When initializing a Structured Config with an incorrectly-typed value, the resulting ValidationError now properly reports the offending value in its error message. diff --git a/omegaconf/_utils.py b/omegaconf/_utils.py index a01051d5c..2dec6c1e9 100644 --- a/omegaconf/_utils.py +++ b/omegaconf/_utils.py @@ -15,6 +15,7 @@ ConfigTypeError, ConfigValueError, OmegaConfBaseException, + ValidationError, ) from .grammar_parser import parse @@ -217,13 +218,16 @@ def get_attr_data(obj: Any, allow_objects: Optional[bool] = None) -> Dict[str, A ) format_and_raise(node=None, key=None, value=value, cause=e, msg=str(e)) - d[name] = _maybe_wrap( - ref_type=type_, - is_optional=is_optional, - key=name, - value=value, - parent=dummy_parent, - ) + try: + d[name] = _maybe_wrap( + ref_type=type_, + is_optional=is_optional, + key=name, + value=value, + parent=dummy_parent, + ) + except ValidationError as ex: + format_and_raise(node=None, key=name, value=value, cause=ex, msg=str(ex)) d[name]._set_parent(None) return d @@ -257,13 +261,16 @@ def get_dataclass_data( f"Union types are not supported:\n{name}: {type_str(type_)}" ) format_and_raise(node=None, key=None, value=value, cause=e, msg=str(e)) - d[name] = _maybe_wrap( - ref_type=type_, - is_optional=is_optional, - key=name, - value=value, - parent=dummy_parent, - ) + try: + d[name] = _maybe_wrap( + ref_type=type_, + is_optional=is_optional, + key=name, + value=value, + parent=dummy_parent, + ) + except ValidationError as ex: + format_and_raise(node=None, key=name, value=value, cause=ex, msg=str(ex)) d[name]._set_parent(None) return d diff --git a/tests/structured_conf/test_structured_basic.py b/tests/structured_conf/test_structured_basic.py index a46e257fa..97b7f41bf 100644 --- a/tests/structured_conf/test_structured_basic.py +++ b/tests/structured_conf/test_structured_basic.py @@ -48,6 +48,13 @@ def test_error_on_non_structured_nested_config_class(self, module: Any) -> None: assert list(ret.keys()) == ["bar"] assert ret.bar == module.NotStructuredConfig() + def test_error_on_creation_with_bad_value_type(self, module: Any) -> None: + with raises( + ValidationError, + match=re.escape("Value 'seven' could not be converted to Integer"), + ): + OmegaConf.structured(module.User(age="seven")) + def test_assignment_of_subclass(self, module: Any) -> None: cfg = OmegaConf.create({"plugin": module.Plugin}) cfg.plugin = OmegaConf.structured(module.ConcretePlugin) diff --git a/tests/test_errors.py b/tests/test_errors.py index 1ccc811bd..f68f29528 100644 --- a/tests/test_errors.py +++ b/tests/test_errors.py @@ -532,23 +532,22 @@ def finalize(self, cfg: Any) -> None: pytest.param( Expected( create=lambda: None, - op=lambda cfg: OmegaConf.structured(NotOptionalInt), + op=lambda _: OmegaConf.structured(NotOptionalInt), exception_type=ValidationError, msg="Non optional field cannot be assigned None", - object_type_str=None, - ref_type_str=None, + key="foo", + full_key="", ), id="dict:create_none_optional_with_none", ), pytest.param( Expected( create=lambda: None, - op=lambda cfg: OmegaConf.structured(NotOptionalInt), + op=lambda _: OmegaConf.structured(NotOptionalInt), exception_type=ValidationError, - object_type=None, msg="Non optional field cannot be assigned None", - object_type_str="NotOptionalInt", - ref_type_str=None, + key="foo", + full_key="", ), id="dict:create:not_optional_int_field_with_none", ), @@ -577,6 +576,19 @@ def finalize(self, cfg: Any) -> None: ), id="dict_create_from_illegal_type", ), + pytest.param( + Expected( + create=lambda: None, + op=lambda _: OmegaConf.structured( + ConcretePlugin(params=ConcretePlugin.FoobarParams(foo="x")) # type: ignore + ), + exception_type=ValidationError, + msg="Value 'x' could not be converted to Integer", + key="foo", + full_key="", + ), + id="structured:create_with_invalid_value", + ), pytest.param( Expected( create=lambda: None,