From 13a2906f7ce9b5fa7297ae02909d2c094644e3a3 Mon Sep 17 00:00:00 2001 From: Jake Freck Date: Wed, 30 May 2018 16:17:52 -0700 Subject: [PATCH 1/2] fix model deserialization --- .style.yapf | 11 ++-- .vscode/settings.json | 3 - aztk/core/models/model.py | 10 +++- aztk/models/plugins/plugin_configuration.py | 17 +++--- tests/core/test_models.py | 65 ++++++++++++--------- 5 files changed, 60 insertions(+), 46 deletions(-) diff --git a/.style.yapf b/.style.yapf index 074a1be2..0beef6f0 100644 --- a/.style.yapf +++ b/.style.yapf @@ -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 \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 8e3053ac..44354925 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -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 diff --git a/aztk/core/models/model.py b/aztk/core/models/model.py index f45fbf84..6f016f49 100644 --- a/aztk/core/models/model.py +++ b/aztk/core/models/model.py @@ -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): """ @@ -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: diff --git a/aztk/models/plugins/plugin_configuration.py b/aztk/models/plugins/plugin_configuration.py index 1125c18c..d46ab29b 100644 --- a/aztk/models/plugins/plugin_configuration.py +++ b/aztk/models/plugins/plugin_configuration.py @@ -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): """ diff --git a/tests/core/test_models.py b/tests/core/test_models.py index 9afaacea..3ec9682d 100644 --- a/tests/core/test_models.py +++ b/tests/core/test_models.py @@ -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) @@ -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() @@ -58,6 +62,7 @@ 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() @@ -65,18 +70,19 @@ class SimpleNameModel(Model): 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) @@ -87,7 +93,6 @@ class SimpleStateModel(Model): assert obj.state == UserState.Creating - def test_convert_nested_dict_to_model(): user = User( info=dict( @@ -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() @@ -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() @@ -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() @@ -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() @@ -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() @@ -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 @@ -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() @@ -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 @@ -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) @@ -212,8 +216,8 @@ class UserList(Model): dict( name="John", age=29, - ) - ] + ), + ], ) obj.validate() @@ -222,6 +226,7 @@ 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) @@ -229,7 +234,7 @@ class UserList(Model): obj = UserList() obj.validate() - assert isinstance(obj.infos, (list,)) + assert isinstance(obj.infos, (list, )) assert len(obj.infos) == 0 infos = obj.infos @@ -237,9 +242,10 @@ class UserList(Model): 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) @@ -247,9 +253,10 @@ class UserList(Model): 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) @@ -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 @@ -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), From 48a2b376ec56b9a9131f770d9a0d777929f8fba2 Mon Sep 17 00:00:00 2001 From: Jake Freck Date: Wed, 30 May 2018 16:34:40 -0700 Subject: [PATCH 2/2] whitespace --- .style.yapf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.style.yapf b/.style.yapf index 0beef6f0..4463b094 100644 --- a/.style.yapf +++ b/.style.yapf @@ -4,4 +4,4 @@ spaces_before_comment=4 split_before_logical_operator=True indent_width=4 column_limit=140 -split_arguments_when_comma_terminated=True \ No newline at end of file +split_arguments_when_comma_terminated=True