Skip to content

Commit

Permalink
fix: proper native marshal for repeated enumeration fields (#180)
Browse files Browse the repository at this point in the history
Fix for #179 . Repeated Enumerations now show the correct type when interacted with via python.
  • Loading branch information
0x2b3bfa0 committed Feb 10, 2021
1 parent 326fd69 commit 30265d6
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 3 deletions.
10 changes: 8 additions & 2 deletions proto/marshal/collections/repeated.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class Repeated(collections.abc.MutableSequence):
modify the underlying field container directly.
"""

def __init__(self, sequence, *, marshal):
def __init__(self, sequence, *, marshal, proto_type=None):
"""Initialize a wrapper around a protobuf repeated field.
Args:
Expand All @@ -35,6 +35,7 @@ def __init__(self, sequence, *, marshal):
"""
self._pb = sequence
self._marshal = marshal
self._proto_type = proto_type

def __copy__(self):
"""Copy this object and return the copy."""
Expand All @@ -61,7 +62,7 @@ def __ne__(self, other):
return not self == other

def __repr__(self):
return repr(self.pb)
return repr([*self])

def __setitem__(self, key, value):
self.pb[key] = value
Expand Down Expand Up @@ -89,6 +90,11 @@ class RepeatedComposite(Repeated):
@cached_property
def _pb_type(self):
"""Return the protocol buffer type for this sequence."""
# Provide the marshal-given proto_type, if any.
# Used for RepeatedComposite of Enum.
if self._proto_type is not None:
return self._proto_type

# There is no public-interface mechanism to determine the type
# of what should go in the list (and the C implementation seems to
# have no exposed mechanism at all).
Expand Down
5 changes: 4 additions & 1 deletion proto/marshal/marshal.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,10 @@ def to_python(self, proto_type, value, *, absent: bool = None):
if value_type in compat.repeated_composite_types:
return RepeatedComposite(value, marshal=self)
if value_type in compat.repeated_scalar_types:
return Repeated(value, marshal=self)
if isinstance(proto_type, type):
return RepeatedComposite(value, marshal=self, proto_type=proto_type)
else:
return Repeated(value, marshal=self)

# Same thing for maps of messages.
if value_type in compat.map_composite_types:
Expand Down
12 changes: 12 additions & 0 deletions tests/test_fields_repeated_composite.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

from datetime import datetime
from datetime import timezone
from enum import Enum

import pytest

Expand Down Expand Up @@ -95,6 +96,17 @@ class Foo(proto.Message):
assert foo.timestamps[2].hour == 0


def test_repeated_composite_enum():
class Foo(proto.Message):
class Bar(proto.Enum):
BAZ = 0

bars = proto.RepeatedField(Bar, number=1)

foo = Foo(bars=[Foo.Bar.BAZ])
assert isinstance(foo.bars[0], Enum)


def test_repeated_composite_outer_write():
class Foo(proto.Message):
bar = proto.Field(proto.INT32, number=1)
Expand Down

0 comments on commit 30265d6

Please sign in to comment.