Move .init call into separate load phase
Before this commit .init method were called as the last step of second phase initialization. This is also the phase when forward references are get resolved. Because load phases work recursively second phase of load for the nested objects will happen before the outer counterparts. If those nested objects were to access their owners in the .init method it can cause exception to be thrown because forward reference of the owner were not yet resolved. This commits moves .init calls out of the second phase. Now during the second phase we collect everything that need to be called and call it after the complete graph load. This also guarantees that we call .init only on the object graph that can be loaded (since it already loaded by that time) Change-Id: Ie86655bcc668be880a7b97695835ccddb9adda0f
This commit is contained in:
parent
d6456f00d2
commit
01725515bc
@ -52,7 +52,6 @@ class MuranoObject(dsl_types.MuranoObject):
|
||||
self.__parents[name] = known_classes[name] = obj
|
||||
else:
|
||||
self.__parents[name] = known_classes[name]
|
||||
self.__initialized = False
|
||||
|
||||
@property
|
||||
def extension(self):
|
||||
@ -67,8 +66,6 @@ class MuranoObject(dsl_types.MuranoObject):
|
||||
self.__extension = value
|
||||
|
||||
def initialize(self, context, params, used_names=None):
|
||||
if self.__initialized:
|
||||
return
|
||||
context = context.create_child_context()
|
||||
context[constants.CTX_ALLOW_PROPERTY_WRITES] = True
|
||||
object_store = helpers.get_object_store()
|
||||
@ -134,19 +131,20 @@ class MuranoObject(dsl_types.MuranoObject):
|
||||
if method:
|
||||
filtered_params = yaql_integration.filter_parameters(
|
||||
method.body, **params)
|
||||
with helpers.with_object_store(object_store.parent_store):
|
||||
self.__extension = method.invoke(
|
||||
yield lambda: method.invoke(
|
||||
self, filtered_params[0], filtered_params[1], context)
|
||||
|
||||
for parent in self.__parents.values():
|
||||
parent.initialize(context, params, used_names)
|
||||
for t in parent.initialize(context, params, used_names):
|
||||
yield t
|
||||
|
||||
if not object_store.initializing and init:
|
||||
def run_init():
|
||||
context[constants.CTX_ARGUMENT_OWNER] = self.real_this
|
||||
with helpers.with_object_store(object_store.parent_store):
|
||||
init.invoke(self.real_this, (), init_args,
|
||||
context.create_child_context())
|
||||
self.__initialized = True
|
||||
|
||||
if not object_store.initializing and init:
|
||||
yield run_init
|
||||
|
||||
@property
|
||||
def object_id(self):
|
||||
|
@ -99,6 +99,7 @@ class InitializationObjectStore(ObjectStore):
|
||||
self._initializing = False
|
||||
self._root_owner = root_owner
|
||||
self._keep_ids = keep_ids
|
||||
self._initializers = []
|
||||
|
||||
@property
|
||||
def initializing(self):
|
||||
@ -136,11 +137,19 @@ class InitializationObjectStore(ObjectStore):
|
||||
|
||||
if context is None:
|
||||
context = self.executor.create_object_context(obj)
|
||||
obj.initialize(context, parsed['properties'])
|
||||
|
||||
def run_initialize():
|
||||
self._initializers.extend(
|
||||
obj.initialize(context, parsed['properties']))
|
||||
|
||||
run_initialize()
|
||||
if owner is self._root_owner:
|
||||
self._initializing = False
|
||||
obj.initialize(context, parsed['properties'])
|
||||
run_initialize()
|
||||
finally:
|
||||
if owner is self._root_owner:
|
||||
self._initializing = False
|
||||
with helpers.with_object_store(self.parent_store):
|
||||
for fn in self._initializers:
|
||||
fn()
|
||||
|
||||
return obj
|
||||
|
@ -370,9 +370,9 @@ def get_class_factory_definition(cls, murano_class):
|
||||
args = tuple(dsl.to_mutable(arg, engine) for arg in args)
|
||||
kwargs = dsl.to_mutable(kwargs, engine)
|
||||
with helpers.contextual(__context):
|
||||
__context[constants.CTX_NAMES_SCOPE] = \
|
||||
murano_class
|
||||
return helpers.evaluate(cls(*args, **kwargs), __context)
|
||||
__context[constants.CTX_NAMES_SCOPE] = murano_class
|
||||
result = helpers.evaluate(cls(*args, **kwargs), __context)
|
||||
__receiver.object.extension = result
|
||||
|
||||
try:
|
||||
fd = specs.get_function_definition(
|
||||
|
@ -74,6 +74,19 @@ Methods:
|
||||
Body:
|
||||
Return: new(:ConstructionFromInit).out.nodes[1]
|
||||
|
||||
testReferenceAccessFromInit:
|
||||
Body:
|
||||
- $model:
|
||||
:Node:
|
||||
value: rootNode
|
||||
nodes:
|
||||
- childNode
|
||||
- :NodeWithReferenceAccess:
|
||||
value: childNode
|
||||
id: childNode
|
||||
- $.out: new($model, $this)
|
||||
|
||||
|
||||
---
|
||||
|
||||
Name: Node
|
||||
@ -130,3 +143,12 @@ Methods:
|
||||
nodes: [childNode1]
|
||||
id: childNode2
|
||||
- $.out: new($model, $this)
|
||||
|
||||
---
|
||||
|
||||
Name: NodeWithReferenceAccess
|
||||
Extends: Node
|
||||
|
||||
Methods:
|
||||
.init:
|
||||
Body: $.find(Node).nodes.select(trace($.value))
|
@ -67,3 +67,7 @@ class TestConstruction(test_case.DslTestCase):
|
||||
def test_nested_new_loads_in_separate_store(self):
|
||||
res = self._runner.testNestedNewLoadsInSeparateStore()
|
||||
self.assertIsInstance(res, dsl.MuranoObjectInterface)
|
||||
|
||||
def test_reference_access_from_init(self):
|
||||
self._runner.testReferenceAccessFromInit()
|
||||
self.assertEqual(2, self.traces.count('childNode'))
|
||||
|
Loading…
Reference in New Issue
Block a user