Skip to content

Commit

Permalink
refactor proto_fill_from_dict & proto_to_dict; #56
Browse files Browse the repository at this point in the history
* added tests for both function
* both function now support py3 iterators types like map, range, filter
* proto_fill_from_dict will correctly overwrite lists when clear=False
  • Loading branch information
rossengeorgiev committed Nov 28, 2016
1 parent cf80947 commit 17eac0c
Show file tree
Hide file tree
Showing 4 changed files with 409 additions and 10 deletions.
25 changes: 25 additions & 0 deletions protobufs/test_messages.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
syntax = "proto2";

message ComplexProtoMessage {
message InnerMessage {
optional string text = 1;
repeated uint32 numbers = 2;
}
message InnerBuffer {
message Flags {
optional bool flag = 1;
}

optional bytes data = 1;
repeated .ComplexProtoMessage.InnerBuffer.Flags flags = 2;
}

optional uint32 number32 = 1;
optional uint64 number64 = 2;

repeated uint32 list_number32 = 3;
repeated uint64 list_number64 = 4;

repeated .ComplexProtoMessage.InnerMessage messages = 5;
repeated .ComplexProtoMessage.InnerBuffer buffers = 6;
}
238 changes: 238 additions & 0 deletions steam/protobufs/test_messages_pb2.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

41 changes: 31 additions & 10 deletions steam/util/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,16 @@
import struct
import socket
import sys
import six
from six.moves import xrange as _range
from types import GeneratorType as _GeneratorType
from google.protobuf.internal.python_message import GeneratedProtocolMessageType as _ProtoMessageType

if six.PY2:
_list_types = list, xrange, _GeneratorType
else:
_list_types = list, range, _GeneratorType, map, filter


def ip_from_int(ip):
"""Convert IP to :py:class:`int`
Expand Down Expand Up @@ -61,13 +70,17 @@ def proto_to_dict(message):
: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.__class__, _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] = map(proto_to_dict, field)
data[desc.name] = list(map(proto_to_dict, field))
else:
data[desc.name] = proto_to_dict(field)
else:
Expand All @@ -86,6 +99,11 @@ def proto_fill_from_dict(message, data, clear=True):
:return: value of message paramater
:raises: incorrect types or values will raise
"""
if not isinstance(message.__class__, _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

Expand All @@ -94,24 +112,27 @@ def proto_fill_from_dict(message, data, clear=True):

if desc.type == desc.TYPE_MESSAGE:
if desc.label == desc.LABEL_REPEATED:
if not isinstance(val, list):
raise TypeError("Expected %s to be of type list, got %s" % (
repr(key), type(val)
))
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)
))
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):
getattr(message, key).extend(val)
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)

Expand Down
Loading

0 comments on commit 17eac0c

Please sign in to comment.