d6456f00d2
Due to how versioning works, when class inheritance graph for particular class might be changed so that there won't be several versions of the same class along different inheritance paths if they all can be merged into a single class without breaking the requirements. This means that parent of some base class might be different from those that would be if that base class would created alone. Before this commit there was a class remapping table that was saying which parent class should be replaced with which in the inheritance graph (considering that both classes differ in version only). Each class used to have such table. Thus in oder to do the graph traversal type origin (root of that graph) need to be known in order to obtain remappings table. However in certain cases information on type origin got lost. For example if one would do cast($obj, Parent).method() the method would be looked up in the Parent class and all in ancestors like if the Parent was the type origin. This could cause different resolution from what would happen if the lookup algorithm new that the true type origin is type($obj) instead. This commit brings different solution to the original problem eliminating the need to know type origin. Instead when the base class parents need to be changed, because we cannot change the class parents due to the fact that this change is only relevant in context of origin type, we create a clone MuranoType instance which differs in its parents ony. The downside of this solution is that now there could be several MuranoType instances representing the same type and attempt to lookup the type in its package may return different object from what you would get from type(cast($obj, Parent)). This is solved by making them comparable (with == and !=). So now instead of saying "obj.type is Type" one should use "obj.type == Type" instead. Because there is no "is" comparision in MuranoPL nothing breaks there from this change. Another issue is that there are static properties which values must be shared across all type clones. This is solved by making a clone with copy.copy() shallow cloning function. Thus internal dictionary where property values are stored remains the same for all clones. Closes-Bug: #1608712 Change-Id: Ib29f731f2598eaf7e7ea5f69f75e023d7155af5e
127 lines
4.6 KiB
Python
127 lines
4.6 KiB
Python
# Copyright (c) 2016 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 abc
|
|
import operator
|
|
import weakref
|
|
|
|
import six
|
|
|
|
from murano.dsl import dsl_types
|
|
from murano.dsl import helpers
|
|
|
|
|
|
class MetaProvider(object):
|
|
@abc.abstractmethod
|
|
def get_meta(self, context):
|
|
raise NotImplementedError()
|
|
|
|
|
|
class MetaData(MetaProvider):
|
|
def __init__(self, definition, target, declaring_type):
|
|
declaring_type = weakref.proxy(declaring_type)
|
|
definition = helpers.list_value(definition)
|
|
factories = []
|
|
used_types = set()
|
|
for d in definition:
|
|
if isinstance(d, dict):
|
|
if len(d) != 1:
|
|
raise ValueError('Invalid Meta format')
|
|
name = next(iter(d.keys()))
|
|
props = d[name] or {}
|
|
else:
|
|
name = d
|
|
props = {}
|
|
type_obj = helpers.resolve_type(name, declaring_type)
|
|
if type_obj.usage != dsl_types.ClassUsages.Meta:
|
|
raise ValueError('Only Meta classes can be attached')
|
|
if target not in type_obj.targets:
|
|
raise ValueError(
|
|
u'Meta class {} is not applicable here'.format(
|
|
type_obj.name))
|
|
if type_obj in used_types and (
|
|
type_obj.cardinality != dsl_types.MetaCardinality.Many):
|
|
raise ValueError('Cannot attach several Meta instances '
|
|
'with cardinality One')
|
|
|
|
used_types.add(type_obj)
|
|
|
|
def factory_maker(template):
|
|
def instantiate(context):
|
|
obj = helpers.get_object_store().load(
|
|
template, owner=None,
|
|
context=context, scope_type=declaring_type)
|
|
obj.declaring_type = declaring_type
|
|
return obj
|
|
return instantiate
|
|
|
|
factories.append(factory_maker({type_obj: props}))
|
|
self._meta_factories = factories
|
|
self._meta = None
|
|
|
|
def get_meta(self, context):
|
|
if self._meta is None:
|
|
self._meta = list(map(lambda x: x(context), self._meta_factories))
|
|
return self._meta
|
|
|
|
|
|
def merge_providers(initial_class, producer, context):
|
|
def merger(cls_list, skip_list):
|
|
result = set()
|
|
all_meta = []
|
|
for cls in cls_list:
|
|
cls_skip_list = skip_list.copy()
|
|
provider = producer(cls)
|
|
meta = [] if provider is None else provider.get_meta(context)
|
|
for item in meta:
|
|
cardinality = item.type.cardinality
|
|
inherited = item.type.inherited
|
|
if cls != initial_class and (
|
|
not inherited or item.type in skip_list):
|
|
continue
|
|
if cardinality == dsl_types.MetaCardinality.One:
|
|
cls_skip_list.add(item.type)
|
|
all_meta.append((cls, item))
|
|
all_meta.extend(merger(cls.parents, cls_skip_list))
|
|
meta_types = {}
|
|
for cls, item in all_meta:
|
|
entry = meta_types.get(item.type)
|
|
if entry is not None:
|
|
if entry != cls:
|
|
raise ValueError(
|
|
u'Found more than one instance of Meta {} '
|
|
u'with Cardinality One'.format(item.type.name))
|
|
else:
|
|
continue
|
|
|
|
if item.type.cardinality == dsl_types.MetaCardinality.One:
|
|
meta_types[item.type] = cls
|
|
result.add((cls, item))
|
|
return result
|
|
|
|
meta = merger([initial_class], set())
|
|
return list(six.moves.map(operator.itemgetter(1), meta))
|
|
|
|
|
|
def aggregate_meta(provider, context, group_by_name=True):
|
|
key_func = lambda m: m.type.name if group_by_name else m.type
|
|
meta = provider.get_meta(context)
|
|
result = {}
|
|
for item in meta:
|
|
if item.type.cardinality == dsl_types.MetaCardinality.One:
|
|
result[key_func(item)] = item
|
|
else:
|
|
result.setdefault(key_func(item), []).append(item)
|
|
return result
|