Skip to content

Commit cf846bb

Browse files
committed
Implement new Bytes field
1 parent 546be2f commit cf846bb

File tree

2 files changed

+55
-0
lines changed

2 files changed

+55
-0
lines changed

src/marshmallow/fields.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -878,6 +878,44 @@ def _deserialize(self, value, attr, data, **kwargs) -> str:
878878
raise self.make_error("invalid_utf8") from error
879879

880880

881+
class Bytes(Field[bytes]):
882+
"""
883+
Marshmallow field type for any bytes array.
884+
"""
885+
886+
def _deserialize(
887+
self,
888+
value: typing.Any,
889+
attr: str | None,
890+
data: typing.Mapping[str, typing.Any] | None,
891+
**kwargs: typing.Any,
892+
) -> bytes:
893+
try:
894+
match value:
895+
case bytes() as b:
896+
return b
897+
case bytearray() as ba:
898+
return bytes(ba)
899+
case str() as s:
900+
return bytes(
901+
s,
902+
encoding="utf-8",
903+
errors="ignore",
904+
)
905+
case int() as i:
906+
return i.to_bytes(
907+
length=max(1, (7 + i.bit_length()) // 8),
908+
byteorder="big",
909+
signed=i < 0,
910+
)
911+
case obj:
912+
if isinstance(obj, (typing.SupportsBytes, typing.Iterable)):
913+
return bytes(obj)
914+
raise ValidationError("not a bytes-like object")
915+
except TypeError as e:
916+
raise ValidationError("not a bytes-like object") from e
917+
918+
881919
class UUID(Field[uuid.UUID]):
882920
"""A UUID field."""
883921

tests/test_deserialization.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,23 @@ def test_string_field_deserialization(self):
322322
with pytest.raises(ValidationError):
323323
field.deserialize({})
324324

325+
def test_bytes_field_deserialization(self):
326+
field = fields.Bytes()
327+
assert field.deserialize(b"foo") == b"foo"
328+
assert field.deserialize(bytearray(b"foo")) == b"foo"
329+
assert field.deserialize("foo") == b"foo"
330+
assert field.deserialize(0xDEAD) == b"\xDE\xAD"
331+
assert field.deserialize([0xBE, 0xEF]) == b"\xBE\xEF"
332+
assert field.deserialize((0xB, 0xA, 0xB, 0xE)) == b"\x0B\x0A\x0B\x0E"
333+
334+
with pytest.raises(ValidationError) as excinfo:
335+
field.deserialize({"hi": 222})
336+
assert excinfo.value.args[0] == "not a bytes-like object"
337+
338+
with pytest.raises(ValidationError) as excinfo:
339+
field.deserialize(['12345'])
340+
assert excinfo.value.args[0] == "not a bytes-like object"
341+
325342
def test_boolean_field_deserialization(self):
326343
field = fields.Boolean()
327344
assert field.deserialize(True) is True

0 commit comments

Comments
 (0)