def test_codecs() -> None: """Test differences with codecs.""" @ioprepped @dataclass class _TestClass: bval: bytes # bytes to/from JSON (goes through base64) obj = _TestClass(bval=b'foo') out = dataclass_to_dict(obj, codec=Codec.JSON) assert isinstance(out['bval'], str) and out['bval'] == 'Zm9v' obj = dataclass_from_dict(_TestClass, out, codec=Codec.JSON) assert obj.bval == b'foo' # bytes to/from FIRESTORE (passed as-is) obj = _TestClass(bval=b'foo') out = dataclass_to_dict(obj, codec=Codec.FIRESTORE) assert isinstance(out['bval'], bytes) and out['bval'] == b'foo' obj = dataclass_from_dict(_TestClass, out, codec=Codec.FIRESTORE) assert obj.bval == b'foo' now = utc_now() @ioprepped @dataclass class _TestClass2: dval: datetime.datetime # datetime to/from JSON (turns into a list of values) obj2 = _TestClass2(dval=now) out = dataclass_to_dict(obj2, codec=Codec.JSON) assert (isinstance(out['dval'], list) and len(out['dval']) == 7 and all(isinstance(val, int) for val in out['dval'])) obj2 = dataclass_from_dict(_TestClass2, out, codec=Codec.JSON) assert obj2.dval == now # datetime to/from FIRESTORE (passed through as-is) obj2 = _TestClass2(dval=now) out = dataclass_to_dict(obj2, codec=Codec.FIRESTORE) assert isinstance(out['dval'], datetime.datetime) obj2 = dataclass_from_dict(_TestClass2, out, codec=Codec.FIRESTORE) assert obj2.dval == now
def test_assign() -> None: """Testing various assignments.""" # pylint: disable=too-many-statements @ioprepped @dataclass class _TestClass: ival: int = 0 sval: str = '' bval: bool = True fval: float = 1.0 nval: _NestedClass = field(default_factory=_NestedClass) enval: _EnumTest = _EnumTest.TEST1 oival: Optional[int] = None osval: Optional[str] = None obval: Optional[bool] = None ofval: Optional[float] = None oenval: Optional[_EnumTest] = _EnumTest.TEST1 lsval: List[str] = field(default_factory=list) lival: List[int] = field(default_factory=list) lbval: List[bool] = field(default_factory=list) lfval: List[float] = field(default_factory=list) lenval: List[_EnumTest] = field(default_factory=list) ssval: Set[str] = field(default_factory=set) anyval: Any = 1 dictval: Dict[int, str] = field(default_factory=dict) tupleval: Tuple[int, str, bool] = (1, 'foo', False) datetimeval: Optional[datetime.datetime] = None class _TestClass2: pass # Attempting to use with non-dataclass should fail. with pytest.raises(TypeError): dataclass_from_dict(_TestClass2, {}) # Attempting to pass non-dicts should fail. with pytest.raises(TypeError): dataclass_from_dict(_TestClass, []) # type: ignore with pytest.raises(TypeError): dataclass_from_dict(_TestClass, None) # type: ignore now = utc_now() # A dict containing *ALL* values should match what we # get when creating a dataclass and then converting back # to a dict. dict1 = { 'ival': 1, 'sval': 'foo', 'bval': True, 'fval': 2.0, 'nval': { 'ival': 1, 'sval': 'bar', 'dval': { '1': 'foof' }, }, 'enval': 'test1', 'oival': 1, 'osval': 'foo', 'obval': True, 'ofval': 1.0, 'oenval': 'test2', 'lsval': ['foo'], 'lival': [10], 'lbval': [False], 'lfval': [1.0], 'lenval': ['test1', 'test2'], 'ssval': ['foo'], 'dval': { 'k': 123 }, 'anyval': { 'foo': [1, 2, { 'bar': 'eep', 'rah': 1 }] }, 'dictval': { '1': 'foo' }, 'tupleval': [2, 'foof', True], 'datetimeval': [ now.year, now.month, now.day, now.hour, now.minute, now.second, now.microsecond ], } dc1 = dataclass_from_dict(_TestClass, dict1) assert dataclass_to_dict(dc1) == dict1 # A few other assignment checks. assert isinstance( dataclass_from_dict( _TestClass, { 'oival': None, 'osval': None, 'obval': None, 'ofval': None, 'lsval': [], 'lival': [], 'lbval': [], 'lfval': [], 'ssval': [] }), _TestClass) assert isinstance( dataclass_from_dict( _TestClass, { 'oival': 1, 'osval': 'foo', 'obval': True, 'ofval': 2.0, 'lsval': ['foo', 'bar', 'eep'], 'lival': [10, 11, 12], 'lbval': [False, True], 'lfval': [1.0, 2.0, 3.0] }), _TestClass) # Attr assigns mismatched with their value types should fail. with pytest.raises(TypeError): dataclass_from_dict(_TestClass, {'ival': 'foo'}) with pytest.raises(TypeError): dataclass_from_dict(_TestClass, {'sval': 1}) with pytest.raises(TypeError): dataclass_from_dict(_TestClass, {'bval': 2}) with pytest.raises(TypeError): dataclass_from_dict(_TestClass, {'oival': 'foo'}) with pytest.raises(TypeError): dataclass_from_dict(_TestClass, {'osval': 1}) with pytest.raises(TypeError): dataclass_from_dict(_TestClass, {'obval': 2}) with pytest.raises(TypeError): dataclass_from_dict(_TestClass, {'ofval': 'blah'}) with pytest.raises(ValueError): dataclass_from_dict(_TestClass, {'oenval': 'test3'}) with pytest.raises(TypeError): dataclass_from_dict(_TestClass, {'lsval': 'blah'}) with pytest.raises(TypeError): dataclass_from_dict(_TestClass, {'lsval': ['blah', None]}) with pytest.raises(TypeError): dataclass_from_dict(_TestClass, {'lsval': [1]}) with pytest.raises(TypeError): dataclass_from_dict(_TestClass, {'lsval': (1, )}) with pytest.raises(TypeError): dataclass_from_dict(_TestClass, {'lbval': [None]}) with pytest.raises(TypeError): dataclass_from_dict(_TestClass, {'lival': ['foo']}) with pytest.raises(TypeError): dataclass_from_dict(_TestClass, {'lfval': [True]}) with pytest.raises(ValueError): dataclass_from_dict(_TestClass, {'lenval': ['test1', 'test3']}) with pytest.raises(TypeError): dataclass_from_dict(_TestClass, {'ssval': [True]}) with pytest.raises(TypeError): dataclass_from_dict(_TestClass, {'ssval': {}}) with pytest.raises(TypeError): dataclass_from_dict(_TestClass, {'ssval': set()}) with pytest.raises(TypeError): dataclass_from_dict(_TestClass, {'tupleval': []}) with pytest.raises(TypeError): dataclass_from_dict(_TestClass, {'tupleval': [1, 1, 1]}) with pytest.raises(TypeError): dataclass_from_dict(_TestClass, {'tupleval': [2, 'foof', True, True]}) # Fields with type Any should accept all types which are directly # supported by json, but not ones such as tuples or non-string dict keys # which get implicitly translated by python's json module. dataclass_from_dict(_TestClass, {'anyval': {}}) dataclass_from_dict(_TestClass, {'anyval': None}) dataclass_from_dict(_TestClass, {'anyval': []}) dataclass_from_dict(_TestClass, {'anyval': [True, {'foo': 'bar'}, None]}) with pytest.raises(TypeError): dataclass_from_dict(_TestClass, {'anyval': {1: 'foo'}}) with pytest.raises(TypeError): dataclass_from_dict(_TestClass, {'anyval': set()}) with pytest.raises(TypeError): dataclass_from_dict(_TestClass, {'anyval': (1, 2, 3)}) # More subtle attr/type mismatches that should fail # (we currently require EXACT type matches). with pytest.raises(TypeError): dataclass_from_dict(_TestClass, {'ival': True}) with pytest.raises(TypeError): dataclass_from_dict(_TestClass, {'fval': 2}, coerce_to_float=False) with pytest.raises(TypeError): dataclass_from_dict(_TestClass, {'bval': 1}) with pytest.raises(TypeError): dataclass_from_dict(_TestClass, {'ofval': 1}, coerce_to_float=False) with pytest.raises(TypeError): dataclass_from_dict(_TestClass, {'lfval': [1]}, coerce_to_float=False) # Coerce-to-float should only work on ints; not bools or other types. dataclass_from_dict(_TestClass, {'fval': 1}, coerce_to_float=True) with pytest.raises(TypeError): dataclass_from_dict(_TestClass, {'fval': 1}, coerce_to_float=False) with pytest.raises(TypeError): dataclass_from_dict(_TestClass, {'fval': True}, coerce_to_float=True) with pytest.raises(TypeError): dataclass_from_dict(_TestClass, {'fval': None}, coerce_to_float=True) with pytest.raises(TypeError): dataclass_from_dict(_TestClass, {'fval': []}, coerce_to_float=True) # Datetime values should only be allowed with timezone set as utc. dataclass_to_dict(_TestClass(datetimeval=utc_now())) with pytest.raises(ValueError): dataclass_to_dict(_TestClass(datetimeval=datetime.datetime.now())) with pytest.raises(ValueError): # This doesn't actually set timezone on the datetime obj. dataclass_to_dict(_TestClass(datetimeval=datetime.datetime.utcnow()))