Skip to content

Commit

Permalink
Read legacy data with Repeated Structured Expando properties.
Browse files Browse the repository at this point in the history
  • Loading branch information
Chris Rossi committed Aug 28, 2019
1 parent 78ad37e commit 4ff9545
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 11 deletions.
31 changes: 20 additions & 11 deletions google/cloud/ndb/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,9 @@ def _entity_from_ds_entity(ds_entity, model_class=None):
# subvalue, instead of an array, like you'd expect when just
# marshalling the entity normally (instead of in a projection query).
#
def new_entity(key):
return _BaseValue(ds_entity_module.Entity(key))

if prop is None and "." in name:
supername, subname = name.split(".", 1)
structprop = getattr(model_class, supername, None)
Expand All @@ -560,27 +563,33 @@ def _entity_from_ds_entity(ds_entity, model_class=None):
if structprop._repeated:
if isinstance(subvalue, list):
# Not a projection
value = [
_BaseValue(ds_entity_module.Entity(key._key))
for _ in subvalue
]
value = [new_entity(key._key) for _ in subvalue]
else:
# Is a projection, so subvalue is scalar. Only need
# one subentity.
value = [
_BaseValue(ds_entity_module.Entity(key._key))
]
value = [new_entity(key._key)]
else:
value = ds_entity_module.Entity(key._key)
value = _BaseValue(value)
value = new_entity(key._key)

structprop._store_value(entity, value)

if structprop._repeated:
# Branch coverage bug,
# See: https://github.com/nedbat/coveragepy/issues/817
if isinstance(subvalue, list):
# Not a projection

# In the rare case of using a repeated
# StructuredProperty where the sub-model is an Expando,
# legacy NDB could write repeated properties of
# different lengths for the subproperties, which was a
# bug. We work around this when reading out such values
# by making sure our repeated property is the same
# length as the longest suproperty.
while len(subvalue) > len(value):
# Need to make some more subentities
value.append(new_entity(key._key))

# Branch coverage bug,
# See: https://github.com/nedbat/coveragepy/issues/817
for subentity, subsubvalue in zip( # pragma no branch
value, subvalue
):
Expand Down
33 changes: 33 additions & 0 deletions tests/unit/test_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -4692,6 +4692,39 @@ class ThisKind(model.Model):
assert entity.baz[0].bar == "himom"
assert entity.copacetic is True

@staticmethod
@pytest.mark.usefixtures("in_context")
def test_legacy_repeated_structured_property_uneven():
class OtherKind(model.Model):
foo = model.IntegerProperty()
bar = model.StringProperty()

class ThisKind(model.Model):
baz = model.StructuredProperty(OtherKind, repeated=True)
copacetic = model.BooleanProperty()

key = datastore.Key("ThisKind", 123, project="testing")
datastore_entity = datastore.Entity(key=key)
datastore_entity.items = unittest.mock.Mock(
return_value=(
# Order counts for coverage
("baz.foo", [42, 144]),
("baz.bar", ["himom", "hellodad", "iminjail"]),
("copacetic", True),
)
)

protobuf = helpers.entity_to_protobuf(datastore_entity)
entity = model._entity_from_protobuf(protobuf)
assert isinstance(entity, ThisKind)
assert entity.baz[0].foo == 42
assert entity.baz[0].bar == "himom"
assert entity.baz[1].foo == 144
assert entity.baz[1].bar == "hellodad"
assert entity.baz[2].foo is None
assert entity.baz[2].bar == "iminjail"
assert entity.copacetic is True


class Test_entity_to_protobuf:
@staticmethod
Expand Down

0 comments on commit 4ff9545

Please sign in to comment.