External object IDs couldn't be used in template() contracts

Due to the bug in object serializer, when template() contract
serialized the template to a dictionary, instead of object IDs
for the objects outside of serialized tree it put the object body.
Thus when new objects were created from the template, they all
had copies of that object with different IDs rather than copies
of the ID string (i.e. references to the same object).

Also template() contract tried to serialize the template on each of
the 2 object model load passes even though the result from the first
pass is never used. However, during first pass the model could contain
forward-references to objects that are not loaded yet so attempt to
serialize such model could fail.

Change-Id: I9d81f10c160c44e6405b54f5470904064c1d4852
Closes-Bug: #1689769
This commit is contained in:
Stan Lagun 2017-05-10 02:11:06 -07:00
parent b85d5c1592
commit fb593ab439
5 changed files with 39 additions and 7 deletions

View File

@ -176,6 +176,8 @@ class Template(contracts.ContractMethod):
if self.value is None:
return None
object_store = helpers.get_object_store()
if object_store.initializing:
return {}
passkey = getattr(self.value, '__passkey__', None)
with helpers.thread_local_attribute(
constants.TL_CONTRACT_PASSKEY, passkey):

View File

@ -27,10 +27,11 @@ class ObjRef(object):
def serialize(obj, executor,
serialization_type=dsl_types.DumpTypes.Serializable):
serialization_type=dsl_types.DumpTypes.Serializable,
allow_refs=True):
with helpers.with_object_store(executor.object_store):
return serialize_model(
obj, executor, True,
obj, executor, allow_refs,
make_copy=False,
serialize_attributes=False,
serialize_actions=False,
@ -45,9 +46,12 @@ def _serialize_object(root_object, designer_attributes, allow_refs,
serialized_objects = set()
obj = root_object
if isinstance(obj, dsl.MuranoObjectInterface):
obj = obj.object
parent = obj.owner if isinstance(obj, dsl_types.MuranoObject) else None
while True:
obj, need_another_pass = _pass12_serialize(
obj, None, serialized_objects, designer_attributes, executor,
obj, parent, serialized_objects, designer_attributes, executor,
serialize_actions, serialization_type, allow_refs,
with_destruction_dependencies)
if not need_another_pass:
@ -130,8 +134,11 @@ def _pass12_serialize(value, parent, serialized_objects,
if value.owner is not parent or value.object_id in serialized_objects:
return ObjRef(value), True
elif isinstance(value, ObjRef):
if (value.ref_obj.object_id not in serialized_objects and
is_nested_in(value.ref_obj.owner, parent)):
can_move = value.ref_obj.object_id not in serialized_objects
if can_move and allow_refs and value.ref_obj.owner is not None:
can_move = (is_nested_in(parent, value.ref_obj.owner) and
value.ref_obj.owner.object_id in serialized_objects)
if can_move:
value = value.ref_obj
else:
return value, False

View File

@ -271,4 +271,18 @@ Name: TemplateTestChild
Properties:
bar:
Contract: $.int().notNull()
Contract: $.int().notNull()
---
Name: TemplatePropertyClass
Properties:
owned:
Contract: $.class(Node)
template:
Contract: $.template(Node)
Methods:
testTemplateWithExternallyOwnedObject:
Body:
- Return: new($.template).nodes.select(id($))

View File

@ -34,7 +34,7 @@ class TestConstruction(test_case.DslTestCase):
def test_new_with_ownership(self):
obj = serializer.serialize(self._runner.testNewWithOwnership(),
self._runner.executor)
self._runner.executor, allow_refs=False)
self.assertEqual('STRING', obj.get('property1'))
self.assertIsNotNone('string', obj.get('xxx'))
self.assertEqual('STR', obj['xxx'].get('property1'))

View File

@ -327,6 +327,15 @@ class TestContracts(test_case.DslTestCase):
self.assertEqual('PROPERTY', self._runner.testDefaultExpression())
self.assertEqual('value', self._runner.testDefaultExpression('value'))
def test_template_with_externally_owned_object(self):
node = om.Object('Node', 'OBJ_ID')
node_template = om.Object('Node', nodes=['OBJ_ID'])
model = om.Object(
'TemplatePropertyClass', owned=node, template=node_template)
runner = self.new_runner(model)
self.assertEqual(
['OBJ_ID'], runner.testTemplateWithExternallyOwnedObject())
class TestContractsTransform(test_case.DslTestCase):
def setUp(self):