024ac109ef
ObjectStore used to replace object factories with actual objects only after 2 passes instead of after first pass as it was before then. Until that happened object could not be obtained from outside despite it was already created by the 2nd pass so ID reference resolution that happened on the second pass failed. With this fix ObjectStore doesn't tries to hide partially-initialized objects anymore. So if object A references object B by ID it may get B after B has been loaded but before its .init was called. That's not a problem because we just loading (relinking) model and do not depend on anything that could be initialized in B's .init. However because B is not owned by A and A is not owned by B it may happen that .init of A will be executed prior to .init of B so if A's initializer will see object B that wasn't initialized yet. Generally this is unavoidable because there may be circular references. So there were 2 options: either to do more that 2 passes and try to to avoid such situations for cases when there are no circular references or just require classes not to do anything with objects they don't own from .init. Because .init is also called from GC and should not have side effects at all the second options was chosen but we may reconsider this in the future Closes-Bug: #1492640 Change-Id: Id38a780f9084323f0806b044262aa3b89eb5da74
111 lines
3.7 KiB
Python
111 lines
3.7 KiB
Python
# Copyright (c) 2014 Mirantis, Inc.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
import weakref
|
|
|
|
|
|
from murano.dsl import dsl_types
|
|
from murano.dsl import helpers
|
|
|
|
|
|
class ObjectStore(object):
|
|
def __init__(self, executor, parent_store=None):
|
|
self._parent_store = parent_store
|
|
self._store = {}
|
|
self._designer_attributes_store = {}
|
|
self._executor = weakref.ref(executor)
|
|
self._initializing = False
|
|
|
|
@property
|
|
def initializing(self):
|
|
return self._initializing
|
|
|
|
@property
|
|
def executor(self):
|
|
return self._executor()
|
|
|
|
def get(self, object_id):
|
|
if object_id in self._store:
|
|
result = self._store[object_id]
|
|
if not isinstance(result, dsl_types.MuranoObject):
|
|
result = result.object
|
|
return result
|
|
if self._parent_store:
|
|
return self._parent_store.get(object_id)
|
|
return None
|
|
|
|
def has(self, object_id):
|
|
return object_id in self._store
|
|
|
|
def put(self, murano_object):
|
|
self._store[murano_object.object_id] = murano_object
|
|
|
|
def load(self, value, owner, context=None, defaults=None):
|
|
if value is None:
|
|
return None
|
|
if '?' not in value or 'type' not in value['?']:
|
|
raise ValueError()
|
|
system_key = value['?']
|
|
object_id = system_key['id']
|
|
obj_type = system_key['type']
|
|
version_spec = helpers.parse_version_spec(
|
|
system_key.get('classVersion'))
|
|
|
|
if 'package' not in system_key:
|
|
package = self.executor.package_loader.load_class_package(
|
|
obj_type, version_spec)
|
|
else:
|
|
package = self.executor.package_loader.load_package(
|
|
system_key['package'], version_spec)
|
|
class_obj = package.find_class(obj_type, False)
|
|
|
|
try:
|
|
if owner is None:
|
|
self._initializing = True
|
|
|
|
if object_id in self._store:
|
|
factory = self._store[object_id]
|
|
if isinstance(factory, dsl_types.MuranoObject):
|
|
return factory
|
|
else:
|
|
factory = class_obj.new(
|
|
owner, self,
|
|
name=system_key.get('name'),
|
|
object_id=object_id, defaults=defaults)
|
|
self._store[object_id] = factory
|
|
system_value = ObjectStore._get_designer_attributes(system_key)
|
|
self._designer_attributes_store[object_id] = system_value
|
|
|
|
init_context = self.executor.create_object_context(
|
|
factory.object, context)
|
|
obj = factory(init_context, **value)
|
|
if not self._initializing:
|
|
self._store[object_id] = obj
|
|
if owner is None:
|
|
self._initializing = False
|
|
self._store[object_id] = factory(init_context, **value)
|
|
finally:
|
|
if owner is None:
|
|
self._initializing = False
|
|
|
|
return factory.object
|
|
|
|
@staticmethod
|
|
def _get_designer_attributes(header):
|
|
return dict((k, v) for k, v in header.iteritems()
|
|
if str(k).startswith('_'))
|
|
|
|
def designer_attributes(self, object_id):
|
|
return self._designer_attributes_store.get(object_id, {})
|