Skip to content

Commit

Permalink
Change: Improve Model class for invalid data while parsing child mode…
Browse files Browse the repository at this point in the history
…ls (#904)

Handle the situation more carefully when child models should be parsed
but the response doesn't contain an object/dict.
  • Loading branch information
bjoernricks committed Oct 12, 2023
1 parent aaa084c commit 7394814
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 26 deletions.
8 changes: 7 additions & 1 deletion pontos/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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}'."
Expand Down
76 changes: 51 additions & 25 deletions tests/models/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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):
Expand Down Expand Up @@ -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):
Expand All @@ -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",
}
)

0 comments on commit 7394814

Please sign in to comment.