diff --git a/pontos/models/__init__.py b/pontos/models/__init__.py index 617a81ce..8e5015e4 100644 --- a/pontos/models/__init__.py +++ b/pontos/models/__init__.py @@ -150,6 +150,12 @@ def from_dict(cls, data: Dict[str, Any]): "updated_at": "2017-07-08T16:18:44-04:00", }) """ + if not isinstance(data, dict): + raise ValueError( + f"Invalid data for creating an instance of {cls.__name__} " + f"model. Data is {data!r}" + ) + kwargs = {} additional_attrs = {} type_hints = get_type_hints(cls) @@ -161,7 +167,7 @@ def from_dict(cls, data: Dict[str, Any]): elif value is not None: model_field_cls = type_hints.get(name) value = cls._get_value(model_field_cls, value) # type: ignore # pylint: disable=line-too-long # noqa: E501 - except TypeError as e: + except (ValueError, TypeError) as e: raise ModelError( f"Error while creating {cls.__name__} model. Could not set " f"value for property '{name}' from '{value}'." diff --git a/tests/models/test_models.py b/tests/models/test_models.py index 69e62c50..e826dfa6 100644 --- a/tests/models/test_models.py +++ b/tests/models/test_models.py @@ -25,6 +25,31 @@ from pontos.models import Model, ModelAttribute, ModelError, dotted_attributes +class DottedAttributesTestCase(unittest.TestCase): + def test_with_new_class(self): + class Foo: + pass + + foo = Foo() + attrs = {"bar": 123, "hello": "World", "baz": [1, 2, 3]} + + foo = dotted_attributes(foo, attrs) + + self.assertEqual(foo.bar, 123) + self.assertEqual(foo.baz, [1, 2, 3]) + self.assertEqual(foo.hello, "World") + + def test_with_github_model_attribute(self): + foo = ModelAttribute() + attrs = {"bar": 123, "hello": "World", "baz": [1, 2, 3]} + + foo = dotted_attributes(foo, attrs) + + self.assertEqual(foo.bar, 123) + self.assertEqual(foo.baz, [1, 2, 3]) + self.assertEqual(foo.hello, "World") + + class ModelTestCase(unittest.TestCase): def test_from_dict(self): model = Model.from_dict( @@ -43,6 +68,12 @@ def test_from_dict(self): self.assertEqual(model.baz, [1, 2, 3]) self.assertEqual(model.bar.a, "b") + def test_from_dict_failure(self): + with self.assertRaisesRegex( + ValueError, "Invalid data for creating an instance of.*" + ): + Model.from_dict("foo") + class ExampleModelTestCase(unittest.TestCase): def test_optional(self): @@ -165,31 +196,6 @@ class ExampleModel(Model): self.assertIsNotNone(model.ipsum) self.assertEqual(model.ipsum.something, "def") - -class DottedAttributesTestCase(unittest.TestCase): - def test_with_new_class(self): - class Foo: - pass - - foo = Foo() - attrs = {"bar": 123, "hello": "World", "baz": [1, 2, 3]} - - foo = dotted_attributes(foo, attrs) - - self.assertEqual(foo.bar, 123) - self.assertEqual(foo.baz, [1, 2, 3]) - self.assertEqual(foo.hello, "World") - - def test_with_github_model_attribute(self): - foo = ModelAttribute() - attrs = {"bar": 123, "hello": "World", "baz": [1, 2, 3]} - - foo = dotted_attributes(foo, attrs) - - self.assertEqual(foo.bar, 123) - self.assertEqual(foo.baz, [1, 2, 3]) - self.assertEqual(foo.hello, "World") - def test_list_with_dict(self): @dataclass class ExampleModel(Model): @@ -209,3 +215,23 @@ class ExampleModel(Model): "property 'foo' from '{'bar': 'baz'}'.", ): ExampleModel.from_dict({"foo": {"bar": "baz"}}) + + def test_model_error_2(self): + @dataclass + class OtherModel(Model): + something: str + + @dataclass + class ExampleModel(Model): + foo: Optional[OtherModel] + + with self.assertRaisesRegex( + ModelError, + "Error while creating ExampleModel model. Could not set value for " + "property 'foo' from 'abc'.", + ): + ExampleModel.from_dict( + { + "foo": "abc", + } + )