Skip to content
This repository has been archived by the owner on Feb 3, 2021. It is now read-only.

Commit

Permalink
Fix: models v2 deserialization (#584)
Browse files Browse the repository at this point in the history
* fix model deserialization

* whitespace
  • Loading branch information
jafreck committed May 30, 2018
1 parent 49a890a commit 1eeff23
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 46 deletions.
11 changes: 6 additions & 5 deletions .style.yapf
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[style]
based_on_style = pep8
spaces_before_comment = 4
split_before_logical_operator = true
indent_width = 4
column_limit = 140
based_on_style=pep8
spaces_before_comment=4
split_before_logical_operator=True
indent_width=4
column_limit=140
split_arguments_when_comma_terminated=True
3 changes: 0 additions & 3 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@
"${workspaceRoot}/node_scripts"
],
"python.formatting.provider": "yapf",
"python.formatting.yapfArgs": [
"--style=.style.yapf"
],
"python.venvPath": "${workspaceFolder}/.venv/",
"python.pythonPath": "${workspaceFolder}/.venv/Scripts/python.exe",
"python.unitTest.pyTestEnabled": true
Expand Down
10 changes: 7 additions & 3 deletions aztk/core/models/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def __setstate__(self, state):
"""
For pickle serialization. This update the current model with the given state
"""
self._update(state)
self._update(state, ignore_missing=True)

def validate(self):
"""
Expand Down Expand Up @@ -110,9 +110,13 @@ def to_dict(self):
def __str__(self):
return yaml.dump(self.to_dict(), default_flow_style=False)

def _update(self, values):
def _update(self, values, ignore_missing=False):
for k, v in values.items():
self[k] = v
try:
self[k] = v
except AztkAttributeError as e:
if not ignore_missing:
raise e

def _process_field_error(self, e: InvalidModelFieldError, field: str):
if not e.field:
Expand Down
17 changes: 9 additions & 8 deletions aztk/models/plugins/plugin_configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,17 @@ class PluginPort(Model):
public = fields.Field(default=None)
name = fields.Integer()

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.expose_publicly = bool(self.public)
self.public_port = None
@property
def expose_publicly(self):
return bool(self.public)

@property
def public_port(self):
if self.expose_publicly:
if self.public is True:
self.public_port = self.internal
else:
self.public_port = self.public

return self.internal
return self.public
return None

class PluginConfiguration(Model):
"""
Expand Down
65 changes: 38 additions & 27 deletions tests/core/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,18 @@

# pylint: disable=C1801


class UserState(Enum):
Creating = "creating"
Ready = "ready"
Deleting = "deleting"


class UserInfo(Model):
name = fields.String()
age = fields.Integer()


class User(Model):
info = fields.Model(UserInfo)
enabled = fields.Boolean(default=True)
Expand All @@ -38,6 +41,7 @@ def test_models():
assert user.enabled is False
assert user.state == UserState.Creating


def test_inherited_models():
class ServiceUser(User):
service = fields.String()
Expand All @@ -58,25 +62,27 @@ class ServiceUser(User):
assert user.state == UserState.Ready
assert user.service == "bus"


def test_raise_error_if_extra_parameters():
class SimpleNameModel(Model):
name = fields.String()

with pytest.raises(AztkAttributeError, match="SimpleNameModel doesn't have an attribute called abc"):
SimpleNameModel(name="foo", abc="123")


def test_enum_invalid_type_raise_error():
class SimpleStateModel(Model):
state = fields.Enum(UserState)


with pytest.raises(
InvalidModelFieldError,
match="SimpleStateModel state unknown is not a valid option. Use one of \\['creating', 'ready', 'deleting'\\]"):
InvalidModelFieldError,
match="SimpleStateModel state unknown is not a valid option. Use one of \\['creating', 'ready', 'deleting'\\]"):

obj = SimpleStateModel(state="unknown")
obj.validate()


def test_enum_parse_string():
class SimpleStateModel(Model):
state = fields.Enum(UserState)
Expand All @@ -87,7 +93,6 @@ class SimpleStateModel(Model):
assert obj.state == UserState.Creating



def test_convert_nested_dict_to_model():
user = User(
info=dict(
Expand All @@ -103,6 +108,7 @@ def test_convert_nested_dict_to_model():
assert user.enabled is False
assert user.state == UserState.Deleting


def test_raise_error_if_missing_required_field():
class SimpleRequiredModel(Model):
name = fields.String()
Expand All @@ -112,6 +118,7 @@ class SimpleRequiredModel(Model):
with pytest.raises(InvalidModelFieldError, match="SimpleRequiredModel name is required"):
missing.validate()


def test_raise_error_if_string_field_invalid_type():
class SimpleStringModel(Model):
name = fields.String()
Expand All @@ -121,6 +128,7 @@ class SimpleStringModel(Model):
with pytest.raises(InvalidModelFieldError, match="SimpleStringModel name 123 should be a string"):
missing.validate()


def test_raise_error_if_int_field_invalid_type():
class SimpleIntegerModel(Model):
age = fields.Integer()
Expand All @@ -130,6 +138,7 @@ class SimpleIntegerModel(Model):
with pytest.raises(InvalidModelFieldError, match="SimpleIntegerModel age 123 should be an integer"):
missing.validate()


def test_raise_error_if_bool_field_invalid_type():
class SimpleBoolModel(Model):
enabled = fields.Boolean()
Expand All @@ -139,6 +148,7 @@ class SimpleBoolModel(Model):
with pytest.raises(InvalidModelFieldError, match="SimpleBoolModel enabled false should be a boolean"):
missing.validate()


def test_merge_with_default_value():
class SimpleMergeModel(Model):
name = fields.String()
Expand All @@ -164,13 +174,9 @@ class ComplexModel(Model):
info=dict(
name="John",
age=29,
)
)
obj2 = ComplexModel(
info=dict(
age=38,
)
),
)
obj2 = ComplexModel(info=dict(age=38))

assert obj1.info.age == 29
assert obj2.info.age == 38
Expand All @@ -179,6 +185,7 @@ class ComplexModel(Model):
assert obj1.info.name == "John"
assert obj1.info.age == 38


def test_merge_nested_model_override_strategy():
class ComplexModel(Model):
model_id = fields.String()
Expand All @@ -188,13 +195,9 @@ class ComplexModel(Model):
info=dict(
name="John",
age=29,
)
)
obj2 = ComplexModel(
info=dict(
age=38,
)
),
)
obj2 = ComplexModel(info=dict(age=38))

assert obj1.info.age == 29
assert obj2.info.age == 38
Expand All @@ -203,6 +206,7 @@ class ComplexModel(Model):
assert obj1.info.name is None
assert obj1.info.age == 38


def test_list_field_convert_model_correctly():
class UserList(Model):
infos = fields.List(UserInfo)
Expand All @@ -212,8 +216,8 @@ class UserList(Model):
dict(
name="John",
age=29,
)
]
),
],
)
obj.validate()

Expand All @@ -222,34 +226,37 @@ class UserList(Model):
assert obj.infos[0].name == "John"
assert obj.infos[0].age == 29


def test_list_field_is_never_required():
class UserList(Model):
infos = fields.List(UserInfo)

obj = UserList()
obj.validate()

assert isinstance(obj.infos, (list,))
assert isinstance(obj.infos, (list, ))
assert len(obj.infos) == 0

infos = obj.infos
infos.append(UserInfo())
assert len(obj.infos) == 1

obj2 = UserList(infos=None)
assert isinstance(obj2.infos, (list,))
assert isinstance(obj2.infos, (list, ))
assert len(obj2.infos) == 0


def test_list_field_ignore_none_entries():
class UserList(Model):
infos = fields.List(UserInfo)

obj = UserList(infos=[None, None])
obj.validate()

assert isinstance(obj.infos, (list,))
assert isinstance(obj.infos, (list, ))
assert len(obj.infos) == 0


def test_merge_nested_model_append_strategy():
class UserList(Model):
infos = fields.List(UserInfo, merge_strategy=ListMergeStrategy.Append)
Expand All @@ -259,14 +266,17 @@ class UserList(Model):
dict(
name="John",
age=29,
)
]
),
],
)

obj2 = UserList(
infos=[dict(
name="Frank",
age=38,
)]
infos=[
dict(
name="Frank",
age=38,
),
],
)

assert len(obj1.infos) == 1
Expand Down Expand Up @@ -296,6 +306,7 @@ def test_serialize_simple_model_to_yaml():
assert info_parsed.name == "John"
assert info_parsed.age == 29


def test_serialize_nested_model_to_yaml():
user = User(
info=dict(name="John", age=29),
Expand Down

0 comments on commit 1eeff23

Please sign in to comment.