diff --git a/keyosk/fields.py b/keyosk/fields.py index 8f74dfa..5da914f 100644 --- a/keyosk/fields.py +++ b/keyosk/fields.py @@ -78,7 +78,7 @@ class PathString(msh.fields.String): """Translate between a string and a path object""" def _serialize(self, value: Union[str, Path], attr, obj, **kwargs) -> str: - return super()._serialize(str(value), attr, obj, **kwargs) + return super()._serialize(str(value) if value else value, attr, obj, **kwargs) def _deserialize(self, value: str, attr, data, **kwargs) -> Path: return Path(super()._deserialize(value, attr, data, **kwargs)) diff --git a/tests/test_fields.py b/tests/test_fields.py index 464efa0..167886c 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -1,4 +1,6 @@ +import datetime import enum +import time from pathlib import Path import marshmallow @@ -135,3 +137,126 @@ def test_pathstring(): for data in bad_data: with pytest.raises(marshmallow.ValidationError): serializer.load(data) + + +def test_pathstring_none(): + class TestSchema(marshmallow.Schema): + iampath = fields.PathString(allow_none=True) + + good_data = [ + {"iampath": "/etc/sooper/seekret/place.stuff"}, + {"iampath": "fizzbuzz.foobar"}, + {"iampath": None}, + ] + + serializer = TestSchema() + + for item in good_data: + loaded = serializer.load(item) + if item["iampath"] is None: + assert loaded["iampath"] is None + else: + assert isinstance(loaded["iampath"], Path) + assert item == serializer.dump(loaded) + + +def test_epoch(): + class TestSchema(marshmallow.Schema): + iamepoch = fields.Epoch() + + good_data = [ + {"iamepoch": 123456789}, + {"iamepoch": 0}, + {"iamepoch": int(time.time())}, + ] + + bad_data = [ + {"iamepoch": -1}, + {"iamepoch": (1, 2, 3)}, + {"iamepoch": datetime.datetime.utcnow()}, + {"iamepoch": -1234}, + {"iamepoch": None}, + ] + + serializer = TestSchema() + + for data in good_data: + loaded = serializer.load(data) + assert isinstance(loaded["iamepoch"], datetime.datetime) + assert int(loaded["iamepoch"].timestamp()) == data["iamepoch"] + assert data == serializer.dump(loaded) + + for data in bad_data: + with pytest.raises(marshmallow.ValidationError): + serializer.load(data) + + +def test_epoch_none(): + class TestSchema(marshmallow.Schema): + iamepoch = fields.Epoch(allow_none=True) + + good_data = [ + {"iamepoch": 123456789}, + {"iamepoch": 0}, + {"iamepoch": int(time.time())}, + {"iamepoch": None}, + ] + + serializer = TestSchema() + + for item in good_data: + loaded = serializer.load(item) + if item["iamepoch"] is None: + assert loaded["iamepoch"] is None + else: + assert isinstance(loaded["iamepoch"], datetime.datetime) + assert int(loaded["iamepoch"].timestamp()) == item["iamepoch"] + assert item == serializer.dump(loaded) + + +def test_rawmultitype(): + class TestSchema(marshmallow.Schema): + iamraw = fields.RawMultiType([str, bool, datetime.datetime, Path]) + + good_data = [ + {"iamraw": "haveyoueverheardthetragedyofdarthplageiusthewise"}, + {"iamraw": True}, + {"iamraw": datetime.datetime.utcnow()}, + {"iamraw": Path("/all", "your", "hackz", "are", "belong", "to", "me")}, + ] + + bad_data = [ + {"iamraw": 1234}, + {"iamraw": datetime.timedelta(seconds=30)}, + {"iamraw": ["hello", "there"]}, + {"iamraw": None}, + ] + + serializer = TestSchema() + + for data in good_data: + loaded = serializer.load(data) + assert loaded == data + assert data == serializer.dump(loaded) + + for data in bad_data: + with pytest.raises(marshmallow.ValidationError): + serializer.load(data) + + +def test_rawmultitype_none(): + class TestSchema(marshmallow.Schema): + iamraw = fields.RawMultiType([str, datetime.datetime], allow_none=True) + + good_data = [ + {"iamraw": "haveyoueverheardthetragedyofdarthplageiusthewise"}, + {"iamraw": datetime.datetime.utcnow()}, + {"iamraw": None}, + ] + + serializer = TestSchema() + + for data in good_data: + loaded = serializer.load(data) + assert loaded == data + assert data == serializer.dump(loaded)