14 changed files with 173 additions and 154 deletions
@ -0,0 +1,114 @@ |
|||
|
|||
import six |
|||
from types import GeneratorType as _GeneratorType |
|||
from google.protobuf.message import Message as _ProtoMessageType |
|||
|
|||
|
|||
if six.PY2: |
|||
_list_types = list, xrange, _GeneratorType |
|||
else: |
|||
_list_types = list, range, _GeneratorType, map, filter |
|||
|
|||
protobuf_mask = 0x80000000 |
|||
|
|||
|
|||
def is_proto(emsg): |
|||
""" |
|||
:param emsg: emsg number |
|||
:type emsg: int |
|||
:return: True or False |
|||
:rtype: bool |
|||
""" |
|||
return (int(emsg) & protobuf_mask) > 0 |
|||
|
|||
def set_proto_bit(emsg): |
|||
""" |
|||
:param emsg: emsg number |
|||
:type emsg: int |
|||
:return: emsg with proto bit set |
|||
:rtype: int |
|||
""" |
|||
return int(emsg) | protobuf_mask |
|||
|
|||
def clear_proto_bit(emsg): |
|||
""" |
|||
:param emsg: emsg number |
|||
:type emsg: int |
|||
:return: emsg with proto bit removed |
|||
:rtype: int |
|||
""" |
|||
return int(emsg) & ~protobuf_mask |
|||
|
|||
def proto_to_dict(message): |
|||
"""Converts protobuf message instance to dict |
|||
|
|||
:param message: protobuf message instance |
|||
:return: parameters and their values |
|||
:rtype: dict |
|||
:raises: :class:`.TypeError` if ``message`` is not a proto message |
|||
""" |
|||
if not isinstance(message, _ProtoMessageType): |
|||
raise TypeError("Expected `message` to be a instance of protobuf message") |
|||
|
|||
data = {} |
|||
|
|||
for desc, field in message.ListFields(): |
|||
if desc.type == desc.TYPE_MESSAGE: |
|||
if desc.label == desc.LABEL_REPEATED: |
|||
data[desc.name] = list(map(proto_to_dict, field)) |
|||
else: |
|||
data[desc.name] = proto_to_dict(field) |
|||
else: |
|||
data[desc.name] = list(field) if desc.label == desc.LABEL_REPEATED else field |
|||
|
|||
return data |
|||
|
|||
def proto_fill_from_dict(message, data, clear=True): |
|||
"""Fills protobuf message parameters inplace from a :class:`dict` |
|||
|
|||
:param message: protobuf message instance |
|||
:param data: parameters and values |
|||
:type data: dict |
|||
:param clear: whether clear exisiting values |
|||
:type clear: bool |
|||
:return: value of message paramater |
|||
:raises: incorrect types or values will raise |
|||
""" |
|||
if not isinstance(message, _ProtoMessageType): |
|||
raise TypeError("Expected `message` to be a instance of protobuf message") |
|||
if not isinstance(data, dict): |
|||
raise TypeError("Expected `data` to be of type `dict`") |
|||
|
|||
if clear: message.Clear() |
|||
field_descs = message.DESCRIPTOR.fields_by_name |
|||
|
|||
for key, val in data.items(): |
|||
desc = field_descs[key] |
|||
|
|||
if desc.type == desc.TYPE_MESSAGE: |
|||
if desc.label == desc.LABEL_REPEATED: |
|||
if not isinstance(val, _list_types): |
|||
raise TypeError("Expected %s to be of type list, got %s" % (repr(key), type(val))) |
|||
|
|||
list_ref = getattr(message, key) |
|||
|
|||
# Takes care of overwriting list fields when merging partial data (clear=False) |
|||
if not clear: del list_ref[:] # clears the list |
|||
|
|||
for item in val: |
|||
item_message = getattr(message, key).add() |
|||
proto_fill_from_dict(item_message, item) |
|||
else: |
|||
if not isinstance(val, dict): |
|||
raise TypeError("Expected %s to be of type dict, got %s" % (repr(key), type(dict))) |
|||
|
|||
proto_fill_from_dict(getattr(message, key), val) |
|||
else: |
|||
if isinstance(val, _list_types): |
|||
list_ref = getattr(message, key) |
|||
if not clear: del list_ref[:] # clears the list |
|||
list_ref.extend(val) |
|||
else: |
|||
setattr(message, key, val) |
|||
|
|||
return message |
Loading…
Reference in new issue