Skip to content

Commit da4018f

Browse files
author
Dominik Andreas
committed
added binary support in dictionaries via base64 encoding
1 parent 1fb1687 commit da4018f

File tree

2 files changed

+48
-16
lines changed

2 files changed

+48
-16
lines changed

capnp/lib/capnp.pyx

+27-16
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import array
2626
import asyncio
2727
import collections as _collections
2828
import contextlib
29+
import base64
2930
import enum as _enum
3031
import inspect as _inspect
3132
import os as _os
@@ -957,17 +958,17 @@ cdef _DynamicStructBuilder temp_msg_b
957958
cdef _DynamicStructReader temp_msg_r
958959

959960

960-
cdef _to_dict(msg, bint verbose, bint ordered):
961+
cdef _to_dict(msg, bint verbose, bint ordered, bint encode_bytes_as_base64=False):
961962
msg_type = type(msg)
962963
if msg_type is _DynamicListBuilder:
963964
temp_list_b = msg
964-
return [_to_dict(temp_list_b._get(i), verbose, ordered) for i in range(len(msg))]
965+
return [_to_dict(temp_list_b._get(i), verbose, ordered, encode_bytes_as_base64) for i in range(len(msg))]
965966
elif msg_type is _DynamicListReader:
966967
temp_list_r = msg
967-
return [_to_dict(temp_list_r._get(i), verbose, ordered) for i in range(len(msg))]
968+
return [_to_dict(temp_list_r._get(i), verbose, ordered, encode_bytes_as_base64) for i in range(len(msg))]
968969
elif msg_type is _DynamicResizableListBuilder:
969970
temp_list_rb = msg
970-
return [_to_dict(temp_list_rb._get(i), verbose, ordered) for i in range(len(msg))]
971+
return [_to_dict(temp_list_rb._get(i), verbose, ordered, encode_bytes_as_base64) for i in range(len(msg))]
971972

972973
if msg_type is _DynamicStructBuilder or isinstance(msg, _Request):
973974
temp_msg_b = msg
@@ -977,13 +978,13 @@ cdef _to_dict(msg, bint verbose, bint ordered):
977978
ret = {}
978979
try:
979980
which = temp_msg_b.which()
980-
ret[which] = _to_dict(temp_msg_b._get(which), verbose, ordered)
981+
ret[which] = _to_dict(temp_msg_b._get(which), verbose, ordered, encode_bytes_as_base64)
981982
except KjException:
982983
pass
983984

984985
for field in temp_msg_b.schema.non_union_fields:
985986
if verbose or temp_msg_b._has(field):
986-
ret[field] = _to_dict(temp_msg_b._get(field), verbose, ordered)
987+
ret[field] = _to_dict(temp_msg_b._get(field), verbose, ordered, encode_bytes_as_base64)
987988

988989
return ret
989990
elif msg_type is _DynamicStructReader or isinstance(msg, _Response):
@@ -994,13 +995,13 @@ cdef _to_dict(msg, bint verbose, bint ordered):
994995
ret = {}
995996
try:
996997
which = temp_msg_r.which()
997-
ret[which] = _to_dict(temp_msg_r._get(which), verbose, ordered)
998+
ret[which] = _to_dict(temp_msg_r._get(which), verbose, ordered, encode_bytes_as_base64)
998999
except KjException:
9991000
pass
10001001

10011002
for field in temp_msg_r.schema.non_union_fields:
10021003
if verbose or temp_msg_r._has(field):
1003-
ret[field] = _to_dict(temp_msg_r._get(field), verbose, ordered)
1004+
ret[field] = _to_dict(temp_msg_r._get(field), verbose, ordered, encode_bytes_as_base64)
10041005

10051006
return ret
10061007

@@ -1010,6 +1011,10 @@ cdef _to_dict(msg, bint verbose, bint ordered):
10101011
if msg_type is _DynamicEnum:
10111012
return str(msg)
10121013

1014+
if encode_bytes_as_base64 and msg_type is bytes:
1015+
# encode the message as base64 and return utf-8 string
1016+
return base64.b64encode(msg).decode('utf-8')
1017+
10131018
return msg
10141019

10151020

@@ -1220,8 +1225,8 @@ cdef class _DynamicStructReader:
12201225
def __repr__(self):
12211226
return '<%s reader %s>' % (self.schema.node.displayName, <char*>strStructReader(self.thisptr).cStr())
12221227

1223-
def to_dict(self, verbose=False, ordered=False):
1224-
return _to_dict(self, verbose, ordered)
1228+
def to_dict(self, verbose=False, ordered=False, encode_bytes_as_base64=False):
1229+
return _to_dict(self, verbose, ordered, encode_bytes_as_base64)
12251230

12261231
cpdef as_builder(self, num_first_segment_words=None):
12271232
"""A method for casting this Reader to a Builder
@@ -1598,12 +1603,18 @@ cdef class _DynamicStructBuilder:
15981603
def __repr__(self):
15991604
return '<%s builder %s>' % (self.schema.node.displayName, <char*>strStructBuilder(self.thisptr).cStr())
16001605

1601-
def to_dict(self, verbose=False, ordered=False):
1602-
return _to_dict(self, verbose, ordered)
1606+
def to_dict(self, verbose=False, ordered=False, encode_bytes_as_base64=False):
1607+
return _to_dict(self, verbose, ordered, encode_bytes_as_base64)
16031608

16041609
def from_dict(self, dict d):
16051610
for key, val in d.iteritems():
16061611
if key != 'which':
1612+
field = self.schema.fields.get(key)
1613+
if isinstance(val, str):
1614+
dtype = field.proto.slot.type.which()
1615+
if dtype == "data":
1616+
# decode bytes from utf-8 base64 encoding
1617+
val = base64.b64decode(val)
16071618
try:
16081619
self._set(key, val)
16091620
except Exception as e:
@@ -1683,8 +1694,8 @@ cdef class _DynamicStructPipeline:
16831694
# def __repr__(self):
16841695
# return '<%s reader %s>' % (self.schema.node.displayName, strStructReader(self.thisptr).cStr())
16851696

1686-
def to_dict(self, verbose=False, ordered=False):
1687-
return _to_dict(self, verbose, ordered)
1697+
def to_dict(self, verbose=False, ordered=False, encode_bytes_as_base64=False):
1698+
return _to_dict(self, verbose, ordered, encode_bytes_as_base64)
16881699

16891700

16901701
cdef class _DynamicOrphan:
@@ -2065,8 +2076,8 @@ cdef class _RemotePromise:
20652076
def __dir__(self):
20662077
return list(set(self.schema.fieldnames + tuple(dir(self.__class__))))
20672078

2068-
def to_dict(self, verbose=False, ordered=False):
2069-
return _to_dict(self, verbose, ordered)
2079+
def to_dict(self, verbose=False, ordered=False, encode_bytes_as_base64=False):
2080+
return _to_dict(self, verbose, ordered, encode_bytes_as_base64)
20702081

20712082
cpdef cancel(self) except +reraise_kj_exception:
20722083
self.thisptr = Own[RemotePromise]()

test/test_blob_to_dict_base64.py

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import os
2+
import capnp
3+
import base64
4+
import pytest
5+
6+
this_dir = os.path.dirname(__file__)
7+
8+
9+
@pytest.fixture
10+
def blob_schema():
11+
return capnp.load(os.path.join(this_dir, "blob_test.capnp"))
12+
13+
14+
def test_blob_to_dict(blob_schema):
15+
blob_value = b"hello world"
16+
blob = blob_schema.BlobTest(blob=blob_value)
17+
blob_dict = blob.to_dict(encode_bytes_as_base64=True)
18+
assert base64.b64decode(blob_dict["blob"]) == blob_value
19+
msg = blob_schema.BlobTest.new_message()
20+
msg.from_dict(blob_dict)
21+
assert blob.blob == blob_value

0 commit comments

Comments
 (0)