From 7d6d6fa102e662d0a59010e0184fbeebb9f4fa4d Mon Sep 17 00:00:00 2001 From: Shweta Sharma Date: Fri, 27 Oct 2017 19:32:35 +0530 Subject: [PATCH 1/3] Respecting default value in defined for attribute within attr class while structuring --- cattr/converters.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cattr/converters.py b/cattr/converters.py index dc7f8b0f..20d8d569 100644 --- a/cattr/converters.py +++ b/cattr/converters.py @@ -283,12 +283,12 @@ def _structure_attr_from_dict(self, a, name, mapping): type_ = a.metadata.get(TYPE_METADATA_KEY) if type_ is None: # No type. - return mapping[name] + return mapping.get(name, a.default) if _is_union_type(type_): # This is a union. val = mapping.get(name, NOTHING) if NoneType in type_.__args__ and val is NOTHING: - return None + return a.default return self._structure_union(val, type_) return self._structure.dispatch(type_)(mapping.get(a.name), type_) From e6136a68e042739acda32645fffc2a57ddf5659a Mon Sep 17 00:00:00 2001 From: Shweta Sharma Date: Tue, 31 Oct 2017 17:48:43 +0530 Subject: [PATCH 2/3] added unit test --- cattr/converters.py | 4 ++-- tests/test_structure_attrs.py | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/cattr/converters.py b/cattr/converters.py index 20d8d569..c19a9131 100644 --- a/cattr/converters.py +++ b/cattr/converters.py @@ -286,9 +286,9 @@ def _structure_attr_from_dict(self, a, name, mapping): return mapping.get(name, a.default) if _is_union_type(type_): # This is a union. - val = mapping.get(name, NOTHING) + val = mapping.get(name, a.default) if NoneType in type_.__args__ and val is NOTHING: - return a.default + return None return self._structure_union(val, type_) return self._structure.dispatch(type_)(mapping.get(a.name), type_) diff --git a/tests/test_structure_attrs.py b/tests/test_structure_attrs.py index 77ba9ef9..b975c882 100644 --- a/tests/test_structure_attrs.py +++ b/tests/test_structure_attrs.py @@ -1,10 +1,10 @@ """Loading of attrs classes.""" -from attr import asdict, astuple, fields +from attr import asdict, astuple, fields, make_class from hypothesis import assume, given from typing import Union -from . import simple_classes +from . import simple_classes, simple_attrs @given(simple_classes()) @@ -20,6 +20,16 @@ def test_structure_simple_from_dict(converter, cl_and_vals): assert obj == loaded +@given(simple_attrs(defaults=True)) +def test_structure_simple_from_dict_default(converter, cl_and_vals): + """Test structuring non-nested attrs classes with default value.""" + a, _ = cl_and_vals + cl = make_class("HypClass", {"a": a}) + obj = cl() + loaded = converter.structure({}, cl) + assert obj == loaded + + @given(simple_classes()) def test_roundtrip(converter, cl_and_vals): # type: (Converter, Any) -> None From 3f220ac158d6f9b3fe7f03802c3e723fee083b5b Mon Sep 17 00:00:00 2001 From: Shweta Sharma Date: Wed, 1 Nov 2017 19:27:55 +0530 Subject: [PATCH 3/3] Added unit tests for more scenarios, Specifically attr class with typed on primitive types --- cattr/converters.py | 8 ++++---- tests/metadata/test_roundtrips.py | 18 ++++++++++++++++-- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/cattr/converters.py b/cattr/converters.py index c19a9131..05844a1d 100644 --- a/cattr/converters.py +++ b/cattr/converters.py @@ -281,16 +281,16 @@ def structure_attrs_fromdict(self, obj, cl): def _structure_attr_from_dict(self, a, name, mapping): """Handle an individual attrs attribute structuring.""" type_ = a.metadata.get(TYPE_METADATA_KEY) + val = mapping.get(name, a.default) if type_ is None: # No type. - return mapping.get(name, a.default) + return val if _is_union_type(type_): - # This is a union. - val = mapping.get(name, a.default) + if NoneType in type_.__args__ and val is NOTHING: return None return self._structure_union(val, type_) - return self._structure.dispatch(type_)(mapping.get(a.name), type_) + return self._structure.dispatch(type_)(val, type_) def _structure_list(self, obj, cl): # type: (Type[GenericMeta], Iterable[T]) -> List[T] diff --git a/tests/metadata/test_roundtrips.py b/tests/metadata/test_roundtrips.py index 2dbe3228..6426cd34 100644 --- a/tests/metadata/test_roundtrips.py +++ b/tests/metadata/test_roundtrips.py @@ -2,14 +2,14 @@ import attr import pytest -from attr import fields +from attr import fields, make_class from hypothesis import assume, given from hypothesis.strategies import sampled_from from cattr import UnstructureStrategy, typed from typing import Union, Optional -from . import simple_typed_classes, nested_typed_classes +from . import simple_typed_classes, nested_typed_classes, simple_typed_attrs unstructure_strats = sampled_from(list(UnstructureStrategy)) @@ -25,6 +25,20 @@ def test_simple_roundtrip(converter, cls_and_vals, strat): assert inst == converter.structure(converter.unstructure(inst), cl) +@given(simple_typed_attrs(defaults=True), unstructure_strats) +def test_simple_roundtrip_defaults(converter, cls_and_vals, strat): + """ + Simple classes with metadata can be unstructured and restructured. + """ + a, _ = cls_and_vals + cl = make_class("HypClass", {"a": a}) + converter.unstruct_strat = strat + inst = cl() + assert converter.unstructure(converter.structure( + {}, cl)) == converter.unstructure(inst) + assert inst == converter.structure(converter.unstructure(inst), cl) + + @given(nested_typed_classes, unstructure_strats) def test_nested_roundtrip(converter, cls_and_vals, strat): """