Support of MuranoPL extended metadata was added
Now it is possible to 1) Define metadata classes that describe arbitrary metadata. Meta-classes has all the capabilities of regular classes and in addition has 3 new attributes: Cardinality, Applies and Inherited 2) It is possible to attach meta-class instances to packages, classes (including other meta-classes), properties, methods and method arguments. Each of them got new "Meta" key containing list (or single scalar) of meta-class instances Implements: blueprint metadata-in-muranopl Change-Id: Ieac999a877221a9ecd7d7379b39557036e882aa7
This commit is contained in:

committed by
Alexander Tivelkov

parent
3aa97d0f5b
commit
f36fe4929d
@@ -41,6 +41,8 @@ DM_ATTRIBUTES = 'Attributes'
|
||||
|
||||
META_MURANO_METHOD = '?muranoMethod'
|
||||
META_NO_TRACE = '?noTrace'
|
||||
META_MPL_META = 'Meta'
|
||||
META_USAGE = 'Usage'
|
||||
|
||||
CORE_LIBRARY = 'io.murano'
|
||||
CORE_LIBRARY_OBJECT = 'io.murano.Object'
|
||||
|
@@ -17,6 +17,7 @@ import os.path
|
||||
|
||||
import six
|
||||
from yaql.language import expressions as yaql_expressions
|
||||
from yaql.language import specs
|
||||
from yaql.language import utils
|
||||
from yaql.language import yaqltypes
|
||||
from yaql import yaql_interface
|
||||
@@ -335,3 +336,12 @@ def to_mutable(obj, yaql_engine=None):
|
||||
|
||||
limiter = lambda it: utils.limit_iterable(it, constants.ITERATORS_LIMIT)
|
||||
return converter(obj, limiter, yaql_engine, converter)
|
||||
|
||||
|
||||
def meta(type_name, value):
|
||||
def wrapper(func):
|
||||
fd = specs.get_function_definition(func)
|
||||
mpl_meta = fd.meta.get(constants.META_MPL_META, [])
|
||||
mpl_meta.append({type_name: value})
|
||||
specs.meta(type_name, mpl_meta)(func)
|
||||
return wrapper
|
||||
|
@@ -13,6 +13,46 @@
|
||||
# under the License.
|
||||
|
||||
|
||||
class ClassUsages(object):
|
||||
Class = 'Class'
|
||||
Meta = 'Meta'
|
||||
All = {Class, Meta}
|
||||
|
||||
|
||||
class MetaCardinality(object):
|
||||
One = 'One'
|
||||
Many = 'Many'
|
||||
All = {One, Many}
|
||||
|
||||
|
||||
class MetaTargets(object):
|
||||
Package = 'Package'
|
||||
Type = 'Type'
|
||||
Property = 'Property'
|
||||
Method = 'Method'
|
||||
Argument = 'Argument'
|
||||
All = {Package, Type, Property, Method, Argument}
|
||||
|
||||
|
||||
class PropertyUsages(object):
|
||||
In = 'In'
|
||||
Out = 'Out'
|
||||
InOut = 'InOut'
|
||||
Runtime = 'Runtime'
|
||||
Const = 'Const'
|
||||
Config = 'Config'
|
||||
Static = 'Static'
|
||||
All = {In, Out, InOut, Runtime, Const, Config, Static}
|
||||
Writable = {Out, InOut, Runtime, Static}
|
||||
|
||||
|
||||
class MethodUsages(object):
|
||||
Action = 'Action'
|
||||
Runtime = 'Runtime'
|
||||
Static = 'Static'
|
||||
All = {Action, Runtime, Static}
|
||||
|
||||
|
||||
class MuranoType(object):
|
||||
pass
|
||||
|
||||
@@ -21,6 +61,10 @@ class MuranoClass(MuranoType):
|
||||
pass
|
||||
|
||||
|
||||
class MuranoMetaClass(MuranoClass):
|
||||
pass
|
||||
|
||||
|
||||
class MuranoObject(object):
|
||||
pass
|
||||
|
||||
|
@@ -166,3 +166,7 @@ class InvalidLhsTargetError(Exception):
|
||||
def __init__(self, target):
|
||||
super(InvalidLhsTargetError, self).__init__(
|
||||
'Invalid assignment target "%s"' % target)
|
||||
|
||||
|
||||
class InvalidInheritanceError(Exception):
|
||||
pass
|
||||
|
@@ -30,7 +30,6 @@ from murano.dsl import constants
|
||||
from murano.dsl import dsl
|
||||
from murano.dsl import dsl_types
|
||||
from murano.dsl import helpers
|
||||
from murano.dsl import murano_method
|
||||
from murano.dsl import object_store
|
||||
from murano.dsl.principal_objects import stack_trace
|
||||
from murano.dsl import yaql_integration
|
||||
@@ -87,7 +86,7 @@ class MuranoDslExecutor(object):
|
||||
yaql_engine, method_context, this.real_this)(*args, **kwargs)
|
||||
|
||||
if (context[constants.CTX_ACTIONS_ONLY] and method.usage !=
|
||||
murano_method.MethodUsages.Action):
|
||||
dsl_types.MethodUsages.Action):
|
||||
raise Exception('{0} is not an action'.format(method.name))
|
||||
|
||||
if method.is_static:
|
||||
|
@@ -469,3 +469,66 @@ def inspect_is_property(cls, name):
|
||||
if m is None:
|
||||
return False
|
||||
return inspect.isdatadescriptor(m)
|
||||
|
||||
|
||||
def updated_dict(d, val):
|
||||
if d is None:
|
||||
d = {}
|
||||
else:
|
||||
d = d.copy()
|
||||
if val is not None:
|
||||
d.update(val)
|
||||
return d
|
||||
|
||||
|
||||
def resolve_type(value, scope_type, return_reference=False):
|
||||
if value is None:
|
||||
return None
|
||||
if isinstance(scope_type, dsl_types.MuranoTypeReference):
|
||||
scope_type = scope_type.type
|
||||
if not isinstance(value, (dsl_types.MuranoType,
|
||||
dsl_types.MuranoTypeReference)):
|
||||
name = scope_type.namespace_resolver.resolve_name(value)
|
||||
result = scope_type.package.find_class(name)
|
||||
else:
|
||||
result = value
|
||||
|
||||
if isinstance(result, dsl_types.MuranoTypeReference):
|
||||
if return_reference:
|
||||
return result
|
||||
return result.type
|
||||
elif return_reference:
|
||||
return result.get_reference()
|
||||
return result
|
||||
|
||||
|
||||
def instantiate(data, owner, object_store, context, scope_type,
|
||||
default_type=None, defaults=None):
|
||||
if data is None:
|
||||
data = {}
|
||||
if not isinstance(data, yaqlutils.MappingType):
|
||||
raise ValueError('Incorrect object initialization format')
|
||||
default_type = resolve_type(default_type, scope_type)
|
||||
if len(data) == 1:
|
||||
key = next(iter(data.keys()))
|
||||
ns_resolver = scope_type.namespace_resolver
|
||||
if ns_resolver.is_typename(key, False) or isinstance(
|
||||
key, (dsl_types.MuranoTypeReference, dsl_types.MuranoType)):
|
||||
type_obj = resolve_type(key, scope_type)
|
||||
props = yaqlutils.filter_parameters_dict(data[key] or {})
|
||||
return type_obj.new(
|
||||
owner, object_store, object_store.executor)(
|
||||
context, **props)
|
||||
|
||||
data = updated_dict(defaults, data)
|
||||
if '?' not in data:
|
||||
if not default_type:
|
||||
raise ValueError('Type information is missing')
|
||||
data.update({'?': {
|
||||
'type': default_type.name,
|
||||
'classVersion': str(default_type.version)
|
||||
}})
|
||||
if 'id' not in data['?']:
|
||||
data['?']['id'] = uuid.uuid4().hex
|
||||
|
||||
return object_store.load(data, owner, context)
|
||||
|
@@ -14,7 +14,6 @@
|
||||
|
||||
|
||||
import six
|
||||
from six.moves import range
|
||||
|
||||
from murano.dsl import constants
|
||||
from murano.dsl import dsl_exception
|
||||
|
117
murano/dsl/meta.py
Normal file
117
murano/dsl/meta.py
Normal file
@@ -0,0 +1,117 @@
|
||||
# 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
|
||||
from murano.dsl import object_store
|
||||
|
||||
|
||||
class MetaProvider(object):
|
||||
@abc.abstractmethod
|
||||
def get_meta(self, context):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class MetaData(MetaProvider):
|
||||
def __init__(self, definition, target, scope_type):
|
||||
scope_type = weakref.ref(scope_type)
|
||||
if not definition:
|
||||
definition = []
|
||||
elif not isinstance(definition, list):
|
||||
definition = [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, scope_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)
|
||||
factory_maker = lambda template: \
|
||||
lambda context, store: helpers.instantiate(
|
||||
template, owner=None, object_store=store,
|
||||
context=context, scope_type=scope_type())
|
||||
|
||||
factories.append(factory_maker({type_obj: props}))
|
||||
self._meta_factories = factories
|
||||
self._meta = None
|
||||
|
||||
def get_meta(self, context):
|
||||
if self._meta is None:
|
||||
executor = helpers.get_executor(context)
|
||||
store = object_store.ObjectStore(executor)
|
||||
self._meta = list(map(
|
||||
lambda x: x(context, store),
|
||||
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 is not 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(initial_class), cls_skip_list))
|
||||
meta_types = {}
|
||||
for cls, item in all_meta:
|
||||
entry = meta_types.get(item.type)
|
||||
if entry is not None:
|
||||
if entry is not 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))
|
@@ -19,11 +19,13 @@ import weakref
|
||||
import six
|
||||
from yaql.language import specs
|
||||
|
||||
from murano.dsl import constants
|
||||
from murano.dsl import dsl
|
||||
from murano.dsl import dsl_types
|
||||
from murano.dsl import exceptions
|
||||
from murano.dsl import helpers
|
||||
from murano.dsl import macros
|
||||
from murano.dsl import meta
|
||||
from murano.dsl import typespec
|
||||
from murano.dsl import virtual_exceptions
|
||||
from murano.dsl import yaql_integration
|
||||
@@ -33,18 +35,12 @@ macros.register()
|
||||
virtual_exceptions.register()
|
||||
|
||||
|
||||
class MethodUsages(object):
|
||||
Action = 'Action'
|
||||
Runtime = 'Runtime'
|
||||
Static = 'Static'
|
||||
All = set([Action, Runtime, Static])
|
||||
|
||||
|
||||
class MuranoMethod(dsl_types.MuranoMethod):
|
||||
class MuranoMethod(dsl_types.MuranoMethod, meta.MetaProvider):
|
||||
def __init__(self, declaring_type, name, payload, original_name=None):
|
||||
self._name = name
|
||||
original_name = original_name or name
|
||||
self._declaring_type = weakref.ref(declaring_type)
|
||||
self._meta_values = None
|
||||
|
||||
if callable(payload):
|
||||
if isinstance(payload, specs.FunctionDefinition):
|
||||
@@ -58,19 +54,23 @@ class MuranoMethod(dsl_types.MuranoMethod):
|
||||
declaring_type.extension_class, original_name),
|
||||
helpers.inspect_is_classmethod(
|
||||
declaring_type.extension_class, original_name))):
|
||||
self._usage = MethodUsages.Static
|
||||
self._usage = dsl_types.MethodUsages.Static
|
||||
else:
|
||||
self._usage = (self._body.meta.get('usage') or
|
||||
self._body.meta.get('Usage') or
|
||||
MethodUsages.Runtime)
|
||||
self._usage = (self._body.meta.get(constants.META_USAGE) or
|
||||
dsl_types.MethodUsages.Runtime)
|
||||
if (self._body.name.startswith('#') or
|
||||
self._body.name.startswith('*')):
|
||||
raise ValueError(
|
||||
'Import of special yaql functions is forbidden')
|
||||
self._meta = meta.MetaData(
|
||||
self._body.meta.get(constants.META_MPL_META),
|
||||
dsl_types.MetaTargets.Method,
|
||||
declaring_type)
|
||||
else:
|
||||
payload = payload or {}
|
||||
self._body = macros.MethodBlock(payload.get('Body') or [], name)
|
||||
self._usage = payload.get('Usage') or MethodUsages.Runtime
|
||||
self._usage = payload.get(
|
||||
'Usage') or dsl_types.MethodUsages.Runtime
|
||||
arguments_scheme = payload.get('Arguments') or []
|
||||
if isinstance(arguments_scheme, dict):
|
||||
arguments_scheme = [{key: value} for key, value in
|
||||
@@ -83,6 +83,10 @@ class MuranoMethod(dsl_types.MuranoMethod):
|
||||
name = list(record.keys())[0]
|
||||
self._arguments_scheme[name] = MuranoMethodArgument(
|
||||
self, self.name, name, record[name])
|
||||
self._meta = meta.MetaData(
|
||||
payload.get('Meta'),
|
||||
dsl_types.MetaTargets.Method,
|
||||
declaring_type)
|
||||
self._yaql_function_definition = \
|
||||
yaql_integration.build_wrapper_function_definition(
|
||||
weakref.proxy(self))
|
||||
@@ -117,7 +121,19 @@ class MuranoMethod(dsl_types.MuranoMethod):
|
||||
|
||||
@property
|
||||
def is_static(self):
|
||||
return self.usage == MethodUsages.Static
|
||||
return self.usage == dsl_types.MethodUsages.Static
|
||||
|
||||
def get_meta(self, context):
|
||||
def meta_producer(cls):
|
||||
method = cls.methods.get(self.name)
|
||||
if method is None:
|
||||
return None
|
||||
return method._meta
|
||||
|
||||
if self._meta_values is None:
|
||||
self._meta_values = meta.merge_providers(
|
||||
self.declaring_type, meta_producer, context)
|
||||
return self._meta_values
|
||||
|
||||
def __repr__(self):
|
||||
return 'MuranoMethod({0}::{1})'.format(
|
||||
@@ -140,13 +156,17 @@ class MuranoMethod(dsl_types.MuranoMethod):
|
||||
self, this, context, args, kwargs, skip_stub)
|
||||
|
||||
|
||||
class MuranoMethodArgument(dsl_types.MuranoMethodArgument, typespec.Spec):
|
||||
class MuranoMethodArgument(dsl_types.MuranoMethodArgument, typespec.Spec,
|
||||
meta.MetaProvider):
|
||||
def __init__(self, murano_method, method_name, arg_name, declaration):
|
||||
super(MuranoMethodArgument, self).__init__(
|
||||
declaration, murano_method.declaring_type)
|
||||
self._method_name = method_name
|
||||
self._arg_name = arg_name
|
||||
self._murano_method = weakref.ref(murano_method)
|
||||
self._meta = meta.MetaData(
|
||||
declaration.get('Meta'),
|
||||
dsl_types.MetaTargets.Argument, self.murano_method.declaring_type)
|
||||
|
||||
def validate(self, *args, **kwargs):
|
||||
try:
|
||||
@@ -167,6 +187,9 @@ class MuranoMethodArgument(dsl_types.MuranoMethodArgument, typespec.Spec):
|
||||
def name(self):
|
||||
return self._arg_name
|
||||
|
||||
def get_meta(self, context):
|
||||
return self._meta.get_meta(context)
|
||||
|
||||
def __repr__(self):
|
||||
return 'MuranoMethodArgument({method}::{name})'.format(
|
||||
method=self.murano_method.name, name=self.name)
|
||||
|
@@ -21,14 +21,12 @@ from murano.dsl import dsl
|
||||
from murano.dsl import dsl_types
|
||||
from murano.dsl import exceptions
|
||||
from murano.dsl import helpers
|
||||
from murano.dsl import typespec
|
||||
from murano.dsl import yaql_integration
|
||||
|
||||
|
||||
class MuranoObject(dsl_types.MuranoObject):
|
||||
def __init__(self, murano_class, owner, object_store, executor,
|
||||
object_id=None, name=None, known_classes=None,
|
||||
defaults=None, this=None):
|
||||
object_id=None, name=None, known_classes=None, this=None):
|
||||
if known_classes is None:
|
||||
known_classes = {}
|
||||
self.__owner = owner.real_this if owner else None
|
||||
@@ -36,7 +34,6 @@ class MuranoObject(dsl_types.MuranoObject):
|
||||
self.__type = murano_class
|
||||
self.__properties = {}
|
||||
self.__parents = {}
|
||||
self.__defaults = defaults or {}
|
||||
self.__this = this
|
||||
self.__name = name
|
||||
self.__extension = None
|
||||
@@ -53,8 +50,7 @@ class MuranoObject(dsl_types.MuranoObject):
|
||||
if name not in known_classes:
|
||||
obj = parent_class.new(
|
||||
owner, object_store, executor, object_id=self.__object_id,
|
||||
known_classes=known_classes, defaults=defaults,
|
||||
this=self.real_this).object
|
||||
known_classes=known_classes, this=self.real_this).object
|
||||
|
||||
self.__parents[name] = known_classes[name] = obj
|
||||
else:
|
||||
@@ -86,7 +82,7 @@ class MuranoObject(dsl_types.MuranoObject):
|
||||
return
|
||||
for property_name in self.__type.properties:
|
||||
spec = self.__type.properties[property_name]
|
||||
if spec.usage == typespec.PropertyUsages.Config:
|
||||
if spec.usage == dsl_types.PropertyUsages.Config:
|
||||
if property_name in self.__config:
|
||||
property_value = self.__config[property_name]
|
||||
else:
|
||||
@@ -112,11 +108,11 @@ class MuranoObject(dsl_types.MuranoObject):
|
||||
|
||||
if property_name in used_names:
|
||||
continue
|
||||
if spec.usage in (typespec.PropertyUsages.Config,
|
||||
typespec.PropertyUsages.Static):
|
||||
if spec.usage in (dsl_types.PropertyUsages.Config,
|
||||
dsl_types.PropertyUsages.Static):
|
||||
used_names.add(property_name)
|
||||
continue
|
||||
if spec.usage == typespec.PropertyUsages.Runtime:
|
||||
if spec.usage == dsl_types.PropertyUsages.Runtime:
|
||||
if not spec.has_default:
|
||||
used_names.add(property_name)
|
||||
continue
|
||||
@@ -127,12 +123,13 @@ class MuranoObject(dsl_types.MuranoObject):
|
||||
if is_init_arg:
|
||||
init_args[property_name] = property_value
|
||||
else:
|
||||
self.set_property(property_name, property_value)
|
||||
self.set_property(
|
||||
property_name, property_value, context)
|
||||
used_names.add(property_name)
|
||||
except exceptions.UninitializedPropertyAccessError:
|
||||
errors += 1
|
||||
except exceptions.ContractViolationException:
|
||||
if spec.usage != typespec.PropertyUsages.Runtime:
|
||||
if spec.usage != dsl_types.PropertyUsages.Runtime:
|
||||
raise
|
||||
if not errors:
|
||||
break
|
||||
@@ -183,14 +180,14 @@ class MuranoObject(dsl_types.MuranoObject):
|
||||
start_type, derived = caller_class, True
|
||||
if name in start_type.properties:
|
||||
spec = start_type.properties[name]
|
||||
if spec.usage == typespec.PropertyUsages.Static:
|
||||
if spec.usage == dsl_types.PropertyUsages.Static:
|
||||
return spec.declaring_type.get_property(name, context)
|
||||
else:
|
||||
return self.cast(start_type)._get_property_value(name)
|
||||
else:
|
||||
try:
|
||||
spec = start_type.find_single_property(name)
|
||||
if spec.usage == typespec.PropertyUsages.Static:
|
||||
if spec.usage == dsl_types.PropertyUsages.Static:
|
||||
return spec.declaring_type.get_property(name, context)
|
||||
else:
|
||||
return self.cast(spec.declaring_type).__properties[name]
|
||||
@@ -224,16 +221,15 @@ class MuranoObject(dsl_types.MuranoObject):
|
||||
for spec in declared_properties:
|
||||
if (caller_class is not None and not
|
||||
helpers.are_property_modifications_allowed(context) and
|
||||
(spec.usage not in typespec.PropertyUsages.Writable or
|
||||
(spec.usage not in dsl_types.PropertyUsages.Writable or
|
||||
not derived)):
|
||||
raise exceptions.NoWriteAccessError(name)
|
||||
|
||||
if spec.usage == typespec.PropertyUsages.Static:
|
||||
if spec.usage == dsl_types.PropertyUsages.Static:
|
||||
classes_for_static_properties.append(spec.declaring_type)
|
||||
else:
|
||||
default = self.__config.get(name, spec.default)
|
||||
default = self.__defaults.get(name, default)
|
||||
default = helpers.evaluate(default, context)
|
||||
# default = helpers.evaluate(default, context)
|
||||
|
||||
obj = self.cast(spec.declaring_type)
|
||||
values_to_assign.append((obj, spec.validate(
|
||||
@@ -276,7 +272,7 @@ class MuranoObject(dsl_types.MuranoObject):
|
||||
for property_name in self.type.properties:
|
||||
if property_name in self.__properties:
|
||||
spec = self.type.properties[property_name]
|
||||
if spec.usage != typespec.PropertyUsages.Runtime:
|
||||
if spec.usage != dsl_types.PropertyUsages.Runtime:
|
||||
result[property_name] = self.__properties[
|
||||
property_name]
|
||||
return result
|
||||
|
@@ -24,6 +24,7 @@ from murano.dsl import constants
|
||||
from murano.dsl import dsl_types
|
||||
from murano.dsl import exceptions
|
||||
from murano.dsl import helpers
|
||||
from murano.dsl import meta as dslmeta
|
||||
from murano.dsl import murano_object
|
||||
from murano.dsl import murano_type
|
||||
from murano.dsl import namespace_resolver
|
||||
@@ -31,12 +32,13 @@ from murano.dsl import principal_objects
|
||||
from murano.dsl import yaql_integration
|
||||
|
||||
|
||||
class MuranoPackage(dsl_types.MuranoPackage):
|
||||
class MuranoPackage(dsl_types.MuranoPackage, dslmeta.MetaProvider):
|
||||
def __init__(self, package_loader, name, version=None,
|
||||
runtime_version=None, requirements=None):
|
||||
runtime_version=None, requirements=None, meta=None):
|
||||
super(MuranoPackage, self).__init__()
|
||||
self._package_loader = weakref.proxy(package_loader)
|
||||
self._name = name
|
||||
self._meta = None
|
||||
self._version = helpers.parse_version(version)
|
||||
self._runtime_version = helpers.parse_version(runtime_version)
|
||||
self._requirements = {
|
||||
@@ -54,6 +56,9 @@ class MuranoPackage(dsl_types.MuranoPackage):
|
||||
self._native_load_queue = {}
|
||||
if self.name == constants.CORE_LIBRARY:
|
||||
principal_objects.register(self)
|
||||
self._package_class = self._create_package_class()
|
||||
self._meta = dslmeta.MetaData(
|
||||
meta, dsl_types.MetaTargets.Package, self._package_class)
|
||||
|
||||
@property
|
||||
def package_loader(self):
|
||||
@@ -87,7 +92,7 @@ class MuranoPackage(dsl_types.MuranoPackage):
|
||||
def get_class_config(self, name):
|
||||
return {}
|
||||
|
||||
def _register_mpl_classes(self, data, name):
|
||||
def _register_mpl_classes(self, data, name=None):
|
||||
type_obj = self._classes.get(name)
|
||||
if type_obj is not None:
|
||||
return type_obj
|
||||
@@ -187,7 +192,8 @@ class MuranoPackage(dsl_types.MuranoPackage):
|
||||
except exceptions.NoClassFound:
|
||||
pkgs_for_search.append(referenced_package)
|
||||
continue
|
||||
raise exceptions.NoClassFound(name, packages=pkgs_for_search)
|
||||
raise exceptions.NoClassFound(
|
||||
name, packages=pkgs_for_search + [self])
|
||||
|
||||
raise exceptions.NoClassFound(name, packages=[self])
|
||||
|
||||
@@ -195,5 +201,15 @@ class MuranoPackage(dsl_types.MuranoPackage):
|
||||
def context(self):
|
||||
return None
|
||||
|
||||
def _create_package_class(self):
|
||||
ns_resolver = namespace_resolver.NamespaceResolver(None)
|
||||
return murano_type.MuranoClass(
|
||||
ns_resolver, self.name, self, utils.NO_VALUE)
|
||||
|
||||
def get_meta(self, context):
|
||||
if not self._meta:
|
||||
return []
|
||||
return self._meta.get_meta(context)
|
||||
|
||||
def __repr__(self):
|
||||
return 'MuranoPackage({name})'.format(name=self.name)
|
||||
|
@@ -19,14 +19,20 @@ import six
|
||||
|
||||
from murano.dsl import dsl_types
|
||||
from murano.dsl import exceptions
|
||||
from murano.dsl import meta
|
||||
from murano.dsl import typespec
|
||||
|
||||
|
||||
class MuranoProperty(dsl_types.MuranoProperty, typespec.Spec):
|
||||
class MuranoProperty(dsl_types.MuranoProperty, typespec.Spec,
|
||||
meta.MetaProvider):
|
||||
def __init__(self, declaring_type, property_name, declaration):
|
||||
super(MuranoProperty, self).__init__(declaration, declaring_type)
|
||||
self._property_name = property_name
|
||||
self._declaring_type = weakref.ref(declaring_type)
|
||||
self._meta = meta.MetaData(
|
||||
declaration.get('Meta'),
|
||||
dsl_types.MetaTargets.Property, declaring_type)
|
||||
self._meta_values = None
|
||||
|
||||
def validate(self, *args, **kwargs):
|
||||
try:
|
||||
@@ -45,6 +51,18 @@ class MuranoProperty(dsl_types.MuranoProperty, typespec.Spec):
|
||||
def name(self):
|
||||
return self._property_name
|
||||
|
||||
def get_meta(self, context):
|
||||
def meta_producer(cls):
|
||||
prop = cls.properties.get(self.name)
|
||||
if prop is None:
|
||||
return None
|
||||
return prop._meta
|
||||
|
||||
if self._meta_values is None:
|
||||
self._meta_values = meta.merge_providers(
|
||||
self.declaring_type, meta_producer, context)
|
||||
return self._meta_values
|
||||
|
||||
def __repr__(self):
|
||||
return 'MuranoProperty({type}::{name})'.format(
|
||||
type=self.declaring_type.name, name=self.name)
|
||||
|
@@ -12,6 +12,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import abc
|
||||
import collections
|
||||
import weakref
|
||||
|
||||
@@ -24,6 +25,7 @@ from murano.dsl import dsl
|
||||
from murano.dsl import dsl_types
|
||||
from murano.dsl import exceptions
|
||||
from murano.dsl import helpers
|
||||
from murano.dsl import meta as dslmeta
|
||||
from murano.dsl import murano_method
|
||||
from murano.dsl import murano_object
|
||||
from murano.dsl import murano_property
|
||||
@@ -48,22 +50,48 @@ class MuranoType(dsl_types.MuranoType):
|
||||
def namespace_resolver(self):
|
||||
return self._namespace_resolver
|
||||
|
||||
@abc.abstractproperty
|
||||
@property
|
||||
def usage(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
class MuranoClass(dsl_types.MuranoClass, MuranoType):
|
||||
def __init__(self, ns_resolver, name, package, parents=None):
|
||||
@property
|
||||
def version(self):
|
||||
return self.package.version
|
||||
|
||||
def get_reference(self):
|
||||
return dsl_types.MuranoTypeReference(self)
|
||||
|
||||
|
||||
class MuranoClass(dsl_types.MuranoClass, MuranoType, dslmeta.MetaProvider):
|
||||
_allowed_usages = {dsl_types.ClassUsages.Class}
|
||||
|
||||
def __init__(self, ns_resolver, name, package, parents, meta=None):
|
||||
super(MuranoClass, self).__init__(ns_resolver, name, package)
|
||||
self._methods = {}
|
||||
self._properties = {}
|
||||
self._config = {}
|
||||
self._extension_class = None
|
||||
if self._name == constants.CORE_LIBRARY_OBJECT:
|
||||
if (self._name == constants.CORE_LIBRARY_OBJECT or
|
||||
parents is utils.NO_VALUE):
|
||||
self._parents = []
|
||||
else:
|
||||
self._parents = parents or [
|
||||
package.find_class(constants.CORE_LIBRARY_OBJECT)]
|
||||
for p in self._parents:
|
||||
if p.usage not in self._allowed_usages:
|
||||
raise exceptions.InvalidInheritanceError(
|
||||
u'Type {0} cannot have parent with Usage {1}'.format(
|
||||
self.name, p.usage))
|
||||
self._context = None
|
||||
self._parent_mappings = self._build_parent_remappings()
|
||||
self._property_values = {}
|
||||
self._meta = dslmeta.MetaData(meta, dsl_types.MetaTargets.Type, self)
|
||||
self._meta_values = None
|
||||
|
||||
@property
|
||||
def usage(self):
|
||||
return dsl_types.ClassUsages.Class
|
||||
|
||||
@property
|
||||
def declared_parents(self):
|
||||
@@ -111,10 +139,10 @@ class MuranoClass(dsl_types.MuranoClass, MuranoType):
|
||||
names.update(c.properties.keys())
|
||||
return tuple(names)
|
||||
|
||||
def add_property(self, name, property_typespec):
|
||||
def add_property(self, property_typespec):
|
||||
if not isinstance(property_typespec, murano_property.MuranoProperty):
|
||||
raise TypeError('property_typespec')
|
||||
self._properties[name] = property_typespec
|
||||
self._properties[property_typespec.name] = property_typespec
|
||||
|
||||
def _find_symbol_chains(self, func, origin):
|
||||
queue = collections.deque([(self, ())])
|
||||
@@ -248,10 +276,6 @@ class MuranoClass(dsl_types.MuranoClass, MuranoType):
|
||||
def __repr__(self):
|
||||
return 'MuranoClass({0}/{1})'.format(self.name, self.version)
|
||||
|
||||
@property
|
||||
def version(self):
|
||||
return self.package.version
|
||||
|
||||
def _build_parent_remappings(self):
|
||||
"""Remaps class parents.
|
||||
|
||||
@@ -360,11 +384,67 @@ class MuranoClass(dsl_types.MuranoClass, MuranoType):
|
||||
cls = prop.declaring_type
|
||||
cls._property_values[name] = prop.validate(value, cls, None, context)
|
||||
|
||||
def get_reference(self):
|
||||
return dsl_types.MuranoTypeReference(self)
|
||||
def get_meta(self, context):
|
||||
if self._meta_values is None:
|
||||
self._meta_values = dslmeta.merge_providers(
|
||||
self, lambda cls: cls._meta, context)
|
||||
return self._meta_values
|
||||
|
||||
|
||||
class MuranoMetaClass(dsl_types.MuranoMetaClass, MuranoClass):
|
||||
_allowed_usages = {dsl_types.ClassUsages.Meta, dsl_types.ClassUsages.Class}
|
||||
|
||||
def __init__(self, ns_resolver, name, package, parents, meta=None):
|
||||
super(MuranoMetaClass, self).__init__(
|
||||
ns_resolver, name, package, parents, meta)
|
||||
self._cardinality = dsl_types.MetaCardinality.One
|
||||
self._targets = list(dsl_types.MetaCardinality.All)
|
||||
self._inherited = False
|
||||
|
||||
@property
|
||||
def usage(self):
|
||||
return dsl_types.ClassUsages.Meta
|
||||
|
||||
@property
|
||||
def cardinality(self):
|
||||
return self._cardinality
|
||||
|
||||
@cardinality.setter
|
||||
def cardinality(self, value):
|
||||
self._cardinality = value
|
||||
|
||||
@property
|
||||
def targets(self):
|
||||
return self._targets
|
||||
|
||||
@targets.setter
|
||||
def targets(self, value):
|
||||
self._targets = value
|
||||
|
||||
@property
|
||||
def inherited(self):
|
||||
return self._inherited
|
||||
|
||||
@inherited.setter
|
||||
def inherited(self, value):
|
||||
self._inherited = value
|
||||
|
||||
def __repr__(self):
|
||||
return 'MuranoMetaClass({0}/{1})'.format(self.name, self.version)
|
||||
|
||||
|
||||
def create(data, package, name, ns_resolver):
|
||||
usage = data.get('Usage', dsl_types.ClassUsages.Class)
|
||||
if usage == dsl_types.ClassUsages.Class:
|
||||
return _create_class(MuranoClass, name, ns_resolver, data, package)
|
||||
elif usage == dsl_types.ClassUsages.Meta:
|
||||
return _create_meta_class(
|
||||
MuranoMetaClass, name, ns_resolver, data, package)
|
||||
else:
|
||||
raise ValueError(u'Invalid type Usage: "{}"'.format(usage))
|
||||
|
||||
|
||||
def _create_class(cls, name, ns_resolver, data, package, *args, **kwargs):
|
||||
parent_class_names = data.get('Extends')
|
||||
parent_classes = []
|
||||
if parent_class_names:
|
||||
@@ -374,13 +454,15 @@ def create(data, package, name, ns_resolver):
|
||||
full_name = ns_resolver.resolve_name(str(parent_name))
|
||||
parent_classes.append(package.find_class(full_name))
|
||||
|
||||
type_obj = MuranoClass(ns_resolver, name, package, parent_classes)
|
||||
type_obj = cls(
|
||||
ns_resolver, name, package, parent_classes, data.get('Meta'),
|
||||
*args, **kwargs)
|
||||
|
||||
properties = data.get('Properties') or {}
|
||||
for property_name, property_spec in six.iteritems(properties):
|
||||
spec = murano_property.MuranoProperty(
|
||||
type_obj, property_name, property_spec)
|
||||
type_obj.add_property(property_name, spec)
|
||||
type_obj.add_property(spec)
|
||||
|
||||
methods = data.get('Methods') or data.get('Workflow') or {}
|
||||
|
||||
@@ -394,3 +476,32 @@ def create(data, package, name, ns_resolver):
|
||||
method_mappings.get(method_name, method_name), payload)
|
||||
|
||||
return type_obj
|
||||
|
||||
|
||||
def _create_meta_class(cls, name, ns_resolver, data, package, *args, **kwargs):
|
||||
cardinality = data.get('Cardinality', dsl_types.MetaCardinality.One)
|
||||
if cardinality not in dsl_types.MetaCardinality.All:
|
||||
raise ValueError(u'Invalid MetaClass Cardinality "{}"'.format(
|
||||
cardinality))
|
||||
applies_to = data.get('Applies', dsl_types.MetaTargets.All)
|
||||
if isinstance(applies_to, six.string_types):
|
||||
applies_to = [applies_to]
|
||||
if isinstance(applies_to, list):
|
||||
applies_to = set(applies_to)
|
||||
delta = applies_to - dsl_types.MetaTargets.All - {'All'}
|
||||
if delta:
|
||||
raise ValueError(u'Invalid MetaClass target(s) {}:'.format(
|
||||
', '.join(map(u'"{}"'.format, delta)))
|
||||
)
|
||||
if 'All' in applies_to:
|
||||
applies_to = dsl_types.MetaTargets.All
|
||||
inherited = data.get('Inherited', False)
|
||||
if not isinstance(inherited, bool):
|
||||
raise ValueError('Invalid Inherited value. Must be true or false')
|
||||
|
||||
meta_cls = _create_class(
|
||||
cls, name, ns_resolver, data, package, *args, **kwargs)
|
||||
meta_cls.targets = list(applies_to)
|
||||
meta_cls.cardinality = cardinality
|
||||
meta_cls.inherited = inherited
|
||||
return meta_cls
|
||||
|
@@ -14,6 +14,8 @@
|
||||
|
||||
import re
|
||||
|
||||
import six
|
||||
|
||||
TYPE_NAME_RE = re.compile(r'^([a-zA-Z_]\w*:|:)?[a-zA-Z_]\w*(\.[a-zA-Z_]\w*)*$')
|
||||
NS_RE = re.compile(r'^([a-zA-Z_]\w*(\.[a-zA-Z_]\w*)*)?$')
|
||||
PREFIX_RE = re.compile(r'^([a-zA-Z_]\w*|=)$')
|
||||
@@ -21,7 +23,11 @@ PREFIX_RE = re.compile(r'^([a-zA-Z_]\w*|=)$')
|
||||
|
||||
class NamespaceResolver(object):
|
||||
def __init__(self, namespaces):
|
||||
if namespaces is None:
|
||||
namespaces = {}
|
||||
for prefix, ns in namespaces.items():
|
||||
if ns is None:
|
||||
ns = ''
|
||||
if PREFIX_RE.match(prefix) is None:
|
||||
raise ValueError(
|
||||
'Invalid namespace prefix "{0}"'.format(prefix))
|
||||
@@ -32,8 +38,9 @@ class NamespaceResolver(object):
|
||||
self._namespaces[''] = ''
|
||||
|
||||
def resolve_name(self, name):
|
||||
if name is None or TYPE_NAME_RE.match(name) is None:
|
||||
if not self.is_typename(name, True):
|
||||
raise ValueError('Invalid type name "{0}"'.format(name))
|
||||
name = six.text_type(name)
|
||||
if ':' not in name:
|
||||
if '.' in name:
|
||||
parts = ['', name]
|
||||
@@ -51,3 +58,12 @@ class NamespaceResolver(object):
|
||||
if not ns:
|
||||
return parts[1]
|
||||
return '.'.join((ns, parts[1]))
|
||||
|
||||
@staticmethod
|
||||
def is_typename(name, relaxed):
|
||||
if not name:
|
||||
return False
|
||||
name = six.text_type(name)
|
||||
if not relaxed and ':' not in name:
|
||||
return False
|
||||
return TYPE_NAME_RE.match(name) is not None
|
||||
|
@@ -52,7 +52,7 @@ class ObjectStore(object):
|
||||
def put(self, murano_object):
|
||||
self._store[murano_object.object_id] = murano_object
|
||||
|
||||
def load(self, value, owner, context=None, defaults=None):
|
||||
def load(self, value, owner, context=None):
|
||||
if value is None:
|
||||
return None
|
||||
if '?' not in value or 'type' not in value['?']:
|
||||
@@ -83,7 +83,7 @@ class ObjectStore(object):
|
||||
factory = class_obj.new(
|
||||
owner, self, self.executor,
|
||||
name=system_key.get('name'),
|
||||
object_id=object_id, defaults=defaults)
|
||||
object_id=object_id)
|
||||
self._store[object_id] = factory
|
||||
system_value = ObjectStore._get_designer_attributes(system_key)
|
||||
self._designer_attributes_store[object_id] = system_value
|
||||
|
@@ -19,12 +19,19 @@ from yaql import yaqlization
|
||||
from murano.dsl import dsl
|
||||
from murano.dsl import dsl_types
|
||||
from murano.dsl import helpers
|
||||
from murano.dsl import meta
|
||||
|
||||
|
||||
@specs.yaql_property(dsl_types.MuranoType)
|
||||
@specs.name('name')
|
||||
def class_name(murano_class):
|
||||
return murano_class.name
|
||||
def type_name(murano_type):
|
||||
return murano_type.name
|
||||
|
||||
|
||||
@specs.yaql_property(dsl_types.MuranoType)
|
||||
@specs.name('usage')
|
||||
def type_usage(murano_type):
|
||||
return murano_type.usage
|
||||
|
||||
|
||||
@specs.yaql_property(dsl_types.MuranoClass)
|
||||
@@ -54,15 +61,15 @@ def ancestors(murano_class):
|
||||
return tuple(murano_class.ancestors())
|
||||
|
||||
|
||||
@specs.yaql_property(dsl_types.MuranoClass)
|
||||
def package(murano_class):
|
||||
return murano_class.package
|
||||
@specs.yaql_property(dsl_types.MuranoType)
|
||||
def package(murano_type):
|
||||
return murano_type.package
|
||||
|
||||
|
||||
@specs.yaql_property(dsl_types.MuranoClass)
|
||||
@specs.name('version')
|
||||
def class_version(murano_class):
|
||||
return murano_class.version
|
||||
def type_version(murano_type):
|
||||
return murano_type.version
|
||||
|
||||
|
||||
@specs.yaql_property(dsl_types.MuranoProperty)
|
||||
@@ -195,21 +202,44 @@ def argument_owner(method_argument):
|
||||
return method_argument.murano_method
|
||||
|
||||
|
||||
@specs.yaql_property(dsl_types.MuranoClass)
|
||||
@specs.yaql_property(dsl_types.MuranoType)
|
||||
@specs.name('type')
|
||||
def type_to_type_ref(murano_class):
|
||||
return murano_class.get_reference()
|
||||
def type_to_type_ref(murano_type):
|
||||
return murano_type.get_reference()
|
||||
|
||||
|
||||
@specs.parameter('provider', meta.MetaProvider)
|
||||
@specs.name('#property#meta')
|
||||
def get_meta(context, provider):
|
||||
return provider.get_meta(context)
|
||||
|
||||
|
||||
@specs.yaql_property(dsl_types.MuranoMetaClass)
|
||||
def cardinality(murano_meta_class):
|
||||
return murano_meta_class.cardinality
|
||||
|
||||
|
||||
@specs.yaql_property(dsl_types.MuranoMetaClass)
|
||||
def targets(murano_meta_class):
|
||||
return murano_meta_class.targets
|
||||
|
||||
|
||||
@specs.yaql_property(dsl_types.MuranoMetaClass)
|
||||
def inherited(murano_meta_class):
|
||||
return murano_meta_class.inherited
|
||||
|
||||
|
||||
def register(context):
|
||||
funcs = (
|
||||
class_name, methods, properties, ancestors, package, class_version,
|
||||
type_name, type_usage, type_version, type_to_type_ref,
|
||||
methods, properties, ancestors, package,
|
||||
property_name, property_has_default, property_owner,
|
||||
property_usage, property_get_value, property_set_value,
|
||||
method_name, arguments, method_owner, method_invoke,
|
||||
types, package_name, package_version,
|
||||
argument_name, argument_has_default, argument_owner,
|
||||
type_to_type_ref
|
||||
cardinality, targets, inherited,
|
||||
get_meta
|
||||
)
|
||||
for f in funcs:
|
||||
context.register_function(f)
|
||||
|
@@ -18,7 +18,6 @@ from yaql import utils
|
||||
|
||||
from murano.dsl import dsl
|
||||
from murano.dsl import dsl_types
|
||||
from murano.dsl import murano_method
|
||||
|
||||
|
||||
class ObjRef(object):
|
||||
@@ -73,7 +72,7 @@ def serialize_model(root_object, executor, allow_refs=False):
|
||||
def _serialize_available_action(obj, current_actions):
|
||||
result = {}
|
||||
actions = obj.type.find_methods(
|
||||
lambda m: m.usage == murano_method.MethodUsages.Action)
|
||||
lambda m: m.usage == dsl_types.MethodUsages.Action)
|
||||
for action in actions:
|
||||
action_id = '{0}_{1}'.format(obj.object_id, action.name)
|
||||
entry = current_actions.get(action_id, {'enabled': True})
|
||||
|
@@ -13,7 +13,6 @@
|
||||
# under the License.
|
||||
|
||||
import sys
|
||||
import uuid
|
||||
|
||||
import six
|
||||
from yaql.language import specs
|
||||
@@ -35,7 +34,7 @@ class TypeScheme(object):
|
||||
self._spec = spec
|
||||
|
||||
@staticmethod
|
||||
def prepare_context(root_context, this, owner, default):
|
||||
def prepare_context(root_context, this, owner, default, calling_type):
|
||||
@specs.parameter('value', nullable=True)
|
||||
@specs.method
|
||||
def int_(value):
|
||||
@@ -164,17 +163,9 @@ class TypeScheme(object):
|
||||
elif isinstance(value, dsl_types.MuranoObjectInterface):
|
||||
obj = value.object
|
||||
elif isinstance(value, utils.MappingType):
|
||||
if '?' not in value:
|
||||
new_value = {'?': {
|
||||
'id': uuid.uuid4().hex,
|
||||
'type': default_name.type.name,
|
||||
'classVersion': str(default_name.type.version)
|
||||
}}
|
||||
new_value.update(value)
|
||||
value = new_value
|
||||
|
||||
obj = object_store.load(
|
||||
value, owner, root_context, defaults=default)
|
||||
obj = helpers.instantiate(
|
||||
value, owner, object_store, root_context,
|
||||
calling_type, default_name, default)
|
||||
elif isinstance(value, six.string_types):
|
||||
obj = object_store.get(value)
|
||||
if obj is None:
|
||||
@@ -304,7 +295,7 @@ class TypeScheme(object):
|
||||
else:
|
||||
return self._map_scalar(data, spec)
|
||||
|
||||
def __call__(self, data, context, this, owner, default):
|
||||
def __call__(self, data, context, this, owner, default, calling_type):
|
||||
# TODO(ativelkov, slagun): temporary fix, need a better way of handling
|
||||
# composite defaults
|
||||
# A bug (#1313694) has been filed
|
||||
@@ -312,7 +303,8 @@ class TypeScheme(object):
|
||||
if data is dsl.NO_VALUE:
|
||||
data = helpers.evaluate(default, context)
|
||||
|
||||
context = self.prepare_context(context, this, owner, default)
|
||||
context = self.prepare_context(
|
||||
context, this, owner, default, calling_type)
|
||||
return self._map(data, self._spec, context, '')
|
||||
|
||||
|
||||
|
@@ -20,29 +20,17 @@ from murano.dsl import helpers
|
||||
from murano.dsl import type_scheme
|
||||
|
||||
|
||||
class PropertyUsages(object):
|
||||
In = 'In'
|
||||
Out = 'Out'
|
||||
InOut = 'InOut'
|
||||
Runtime = 'Runtime'
|
||||
Const = 'Const'
|
||||
Config = 'Config'
|
||||
Static = 'Static'
|
||||
All = set([In, Out, InOut, Runtime, Const, Config, Static])
|
||||
Writable = set([Out, InOut, Runtime, Static])
|
||||
|
||||
|
||||
class Spec(object):
|
||||
def __init__(self, declaration, container_class):
|
||||
self._container_class = weakref.ref(container_class)
|
||||
def __init__(self, declaration, container_type):
|
||||
self._container_type = weakref.ref(container_type)
|
||||
self._contract = type_scheme.TypeScheme(declaration['Contract'])
|
||||
self._usage = declaration.get('Usage') or 'In'
|
||||
self._usage = declaration.get('Usage') or dsl_types.PropertyUsages.In
|
||||
self._default = declaration.get('Default')
|
||||
self._has_default = 'Default' in declaration
|
||||
if self._usage not in PropertyUsages.All:
|
||||
if self._usage not in dsl_types.PropertyUsages.All:
|
||||
raise exceptions.DslSyntaxError(
|
||||
'Unknown type {0}. Must be one of ({1})'.format(
|
||||
self._usage, ', '.join(PropertyUsages.All)))
|
||||
self._usage, ', '.join(dsl_types.PropertyUsages.All)))
|
||||
|
||||
def validate(self, value, this, owner, context, default=None):
|
||||
if default is None:
|
||||
@@ -51,12 +39,12 @@ class Spec(object):
|
||||
if isinstance(this, dsl_types.MuranoType):
|
||||
return self._contract(
|
||||
value, executor.create_object_context(this),
|
||||
None, None, default)
|
||||
None, None, default, helpers.get_type(context))
|
||||
else:
|
||||
return self._contract(
|
||||
value, executor.create_object_context(
|
||||
this.cast(self._container_class())),
|
||||
this, owner, default)
|
||||
this.cast(self._container_type())),
|
||||
this, owner, default, helpers.get_type(context))
|
||||
|
||||
@property
|
||||
def default(self):
|
||||
|
@@ -32,7 +32,8 @@ class MuranoPackage(murano_package.MuranoPackage):
|
||||
application_package.full_name,
|
||||
application_package.version,
|
||||
application_package.runtime_version,
|
||||
application_package.requirements
|
||||
application_package.requirements,
|
||||
application_package.meta
|
||||
)
|
||||
|
||||
def get_class_config(self, name):
|
||||
|
@@ -26,6 +26,7 @@ class MuranoPlPackage(package_base.PackageBase):
|
||||
self._classes = manifest.get('Classes')
|
||||
self._ui_file = manifest.get('UI', 'ui.yaml')
|
||||
self._requirements = manifest.get('Require') or {}
|
||||
self._meta = manifest.get('Meta')
|
||||
|
||||
@property
|
||||
def classes(self):
|
||||
@@ -54,3 +55,7 @@ class MuranoPlPackage(package_base.PackageBase):
|
||||
name, 'File with class definition not found')
|
||||
with open(full_path) as stream:
|
||||
return stream.read(), full_path
|
||||
|
||||
@property
|
||||
def meta(self):
|
||||
return self._meta
|
||||
|
@@ -109,6 +109,10 @@ class Package(object):
|
||||
def ui(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractproperty
|
||||
def meta(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
def _zip_dir(path, zip_file):
|
||||
for root, _, files in os.walk(path):
|
||||
|
@@ -107,6 +107,10 @@ class PackageBase(package.Package):
|
||||
def logo(self):
|
||||
return self._load_image(self._logo, 'logo.png', 'logo')
|
||||
|
||||
@property
|
||||
def meta(self):
|
||||
return None
|
||||
|
||||
@property
|
||||
def supplier_logo(self):
|
||||
return self._load_image(
|
||||
|
@@ -27,11 +27,11 @@ from murano.tests.unit.dsl.foundation import object_model
|
||||
|
||||
class TestPackage(murano_package.MuranoPackage):
|
||||
def __init__(self, pkg_loader, name, version,
|
||||
runtime_version, requirements, configs):
|
||||
runtime_version, requirements, configs, meta):
|
||||
self.__configs = configs
|
||||
super(TestPackage, self).__init__(
|
||||
pkg_loader, name, version,
|
||||
runtime_version, requirements)
|
||||
runtime_version, requirements, meta)
|
||||
|
||||
def get_class_config(self, name):
|
||||
return self.__configs.get(name, {})
|
||||
@@ -43,7 +43,7 @@ class TestPackage(murano_package.MuranoPackage):
|
||||
class TestPackageLoader(package_loader.MuranoPackageLoader):
|
||||
_classes_cache = {}
|
||||
|
||||
def __init__(self, directory, package_name, parent_loader=None):
|
||||
def __init__(self, directory, package_name, parent_loader=None, meta=None):
|
||||
self._package_name = package_name
|
||||
self._yaml_loader = yaql_yaml_loader.get_loader('1.0')
|
||||
if directory in TestPackageLoader._classes_cache:
|
||||
@@ -56,7 +56,7 @@ class TestPackageLoader(package_loader.MuranoPackageLoader):
|
||||
self._configs = {}
|
||||
self._package = TestPackage(
|
||||
self, package_name, None, constants.RUNTIME_VERSION_1_0,
|
||||
None, self._configs)
|
||||
None, self._configs, meta)
|
||||
for name, payload in six.iteritems(self._classes):
|
||||
self._package.register_class(payload, name)
|
||||
super(TestPackageLoader, self).__init__()
|
||||
|
275
murano/tests/unit/dsl/meta/TestMeta.yaml
Normal file
275
murano/tests/unit/dsl/meta/TestMeta.yaml
Normal file
@@ -0,0 +1,275 @@
|
||||
Namespaces:
|
||||
=: metatests
|
||||
|
||||
Name: InheritedMultiMeta
|
||||
Usage: Meta
|
||||
Cardinality: Many
|
||||
Applies: All
|
||||
Inherited: true
|
||||
|
||||
Properties:
|
||||
val:
|
||||
Contract: $.int().notNull()
|
||||
Default: 111
|
||||
|
||||
--- # ------------------------------------------------------------------------
|
||||
|
||||
Name: InheritedSingleMeta
|
||||
Usage: Meta
|
||||
Cardinality: One
|
||||
Applies: All
|
||||
Inherited: true
|
||||
|
||||
Properties:
|
||||
val:
|
||||
Contract: $.int().notNull()
|
||||
Default: 222
|
||||
|
||||
--- # ------------------------------------------------------------------------
|
||||
|
||||
Name: InheritedSingleMeta2
|
||||
Usage: Meta
|
||||
Cardinality: One
|
||||
Applies: All
|
||||
Inherited: true
|
||||
Extends: InheritedSingleMeta
|
||||
|
||||
--- # ------------------------------------------------------------------------
|
||||
|
||||
Name: MultiMeta
|
||||
Usage: Meta
|
||||
Cardinality: Many
|
||||
Applies: All
|
||||
Inherited: false
|
||||
|
||||
Properties:
|
||||
val:
|
||||
Contract: $.int().notNull()
|
||||
Default: 333
|
||||
|
||||
--- # ------------------------------------------------------------------------
|
||||
|
||||
Name: SingleMeta
|
||||
Usage: Meta
|
||||
Cardinality: One
|
||||
Applies: All
|
||||
Inherited: false
|
||||
|
||||
Properties:
|
||||
val:
|
||||
Contract: $.int().notNull()
|
||||
Default: 444
|
||||
|
||||
--- # ------------------------------------------------------------------------
|
||||
|
||||
Name: SingleMeta2
|
||||
Usage: Meta
|
||||
Cardinality: One
|
||||
Applies: All
|
||||
Inherited: false
|
||||
Extends: SingleMeta
|
||||
|
||||
--- # ------------------------------------------------------------------------
|
||||
|
||||
Name: ComplexMeta
|
||||
Usage: Meta
|
||||
Cardinality: Many
|
||||
Properties:
|
||||
cls:
|
||||
Contract: $.class(PropertyType).notNull()
|
||||
|
||||
|
||||
--- # ------------------------------------------------------------------------
|
||||
|
||||
Name: ParentClass0
|
||||
Meta: [InheritedMultiMeta, SingleMeta]
|
||||
|
||||
Properties:
|
||||
prop1:
|
||||
Contract: $
|
||||
Meta:
|
||||
- SingleMeta:
|
||||
val: 1
|
||||
|
||||
Methods:
|
||||
foo:
|
||||
Meta:
|
||||
- InheritedMultiMeta:
|
||||
val: 1
|
||||
- InheritedSingleMeta:
|
||||
val: 2
|
||||
- SingleMeta:
|
||||
val: 3
|
||||
- InheritedSingleMeta2:
|
||||
val: 10
|
||||
- SingleMeta2:
|
||||
val: 11
|
||||
|
||||
Arguments:
|
||||
arg:
|
||||
Contract: $.string()
|
||||
|
||||
--- # ------------------------------------------------------------------------
|
||||
|
||||
Name: ParentClass1
|
||||
Extends: ParentClass0
|
||||
Meta:
|
||||
- InheritedMultiMeta:
|
||||
val: 1
|
||||
- InheritedSingleMeta:
|
||||
val: 6
|
||||
- SingleMeta:
|
||||
val: 7
|
||||
|
||||
Methods:
|
||||
foo:
|
||||
Meta:
|
||||
- InheritedMultiMeta:
|
||||
val: 4
|
||||
- InheritedSingleMeta:
|
||||
val: 5
|
||||
- SingleMeta:
|
||||
val: 6
|
||||
|
||||
Arguments:
|
||||
arg:
|
||||
Contract: $.string()
|
||||
|
||||
|
||||
--- # ------------------------------------------------------------------------
|
||||
|
||||
Name: ParentClass2
|
||||
Extends: ParentClass0
|
||||
Usage: Class
|
||||
Meta:
|
||||
- InheritedMultiMeta:
|
||||
val: 2
|
||||
- SingleMeta:
|
||||
val: 3
|
||||
|
||||
Properties:
|
||||
prop2:
|
||||
Contract: $
|
||||
Meta:
|
||||
- InheritedMultiMeta:
|
||||
val: 1
|
||||
- MultiMeta:
|
||||
val: 2
|
||||
- SingleMeta:
|
||||
val: 3
|
||||
|
||||
--- # ------------------------------------------------------------------------
|
||||
|
||||
Name: TestMeta
|
||||
Extends: [ParentClass1, ParentClass2]
|
||||
Meta:
|
||||
- 'metatests.InheritedMultiMeta':
|
||||
val: 4
|
||||
- :SingleMeta:
|
||||
val: 5
|
||||
|
||||
Properties:
|
||||
prop2:
|
||||
Contract: $
|
||||
Meta:
|
||||
- InheritedMultiMeta:
|
||||
val: 4
|
||||
|
||||
Methods:
|
||||
testClassInheritedMeta:
|
||||
Body:
|
||||
- Return: typeinfo($).meta.where($ is InheritedMultiMeta).val
|
||||
|
||||
testClassNotInheritedMeta:
|
||||
Body:
|
||||
- Return: typeinfo($).meta.
|
||||
where($ is SingleMeta or $ is InheritedSingleMeta).val
|
||||
|
||||
testParentClassNotInheritedMeta:
|
||||
Body:
|
||||
- Return: typeinfo(ParentClass2).meta.
|
||||
where(not typeinfo($).inherited).single().val
|
||||
|
||||
testMethodMeta:
|
||||
Body:
|
||||
- Return: typeinfo($).methods.where($.name = foo).single().meta.val
|
||||
|
||||
testMethodArgumentMeta:
|
||||
Body:
|
||||
- Return: typeinfo($).
|
||||
methods.where($.name = foo).single().
|
||||
arguments.single().meta.val
|
||||
|
||||
testInheritedPropertyMeta:
|
||||
Body:
|
||||
- Return: typeinfo($).properties.
|
||||
where($.name = prop1).single().meta.val
|
||||
|
||||
testOverriddenPropertyMeta:
|
||||
Body:
|
||||
- Return: typeinfo($).properties.
|
||||
where($.name = prop2).single().meta.val
|
||||
|
||||
testPackageMeta:
|
||||
Body:
|
||||
- Return: typeinfo($).package.meta
|
||||
|
||||
testComplexMeta:
|
||||
Body:
|
||||
- Return: typeinfo($).
|
||||
methods.where($.name = bar).single().meta.cls.
|
||||
select([$.prop, typeinfo($).name])
|
||||
|
||||
foo:
|
||||
Meta:
|
||||
- InheritedMultiMeta:
|
||||
val: 7
|
||||
- InheritedSingleMeta:
|
||||
val: 8
|
||||
- SingleMeta:
|
||||
val: 9
|
||||
|
||||
Arguments:
|
||||
arg:
|
||||
Contract: $.string()
|
||||
Meta:
|
||||
- SingleMeta:
|
||||
val: 1
|
||||
- MultiMeta:
|
||||
val: 2
|
||||
- MultiMeta:
|
||||
val: 3
|
||||
bar:
|
||||
Meta:
|
||||
- ComplexMeta:
|
||||
cls:
|
||||
:PropertyType:
|
||||
prop: 1
|
||||
- ComplexMeta:
|
||||
cls:
|
||||
prop: 2
|
||||
- :ComplexMeta:
|
||||
cls:
|
||||
:PropertyType2:
|
||||
prop: 3
|
||||
- 'metatests.ComplexMeta':
|
||||
cls:
|
||||
prop: 4
|
||||
- ComplexMeta:
|
||||
cls:
|
||||
?:
|
||||
type: 'metatests.PropertyType2'
|
||||
prop: 5
|
||||
|
||||
--- # ------------------------------------------------------------------------
|
||||
|
||||
Name: PropertyType
|
||||
Properties:
|
||||
prop:
|
||||
Contract: $.int().notNull()
|
||||
Default: 44
|
||||
|
||||
--- # ------------------------------------------------------------------------
|
||||
|
||||
Name: PropertyType2
|
||||
Extends: PropertyType
|
63
murano/tests/unit/dsl/test_meta.py
Normal file
63
murano/tests/unit/dsl/test_meta.py
Normal file
@@ -0,0 +1,63 @@
|
||||
# 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.
|
||||
|
||||
|
||||
from murano.tests.unit.dsl.foundation import object_model as om
|
||||
from murano.tests.unit.dsl.foundation import test_case
|
||||
|
||||
|
||||
class TestMeta(test_case.DslTestCase):
|
||||
def setUp(self):
|
||||
super(TestMeta, self).setUp()
|
||||
self._runner = self.new_runner(om.Object('metatests.TestMeta'))
|
||||
|
||||
def test_class_inherited_meta(self):
|
||||
self.assertItemsEqual(
|
||||
[4, 1, 111, 2], self._runner.testClassInheritedMeta())
|
||||
|
||||
def test_class_not_inherited_meta(self):
|
||||
self.assertItemsEqual(
|
||||
[5, 6], self._runner.testClassNotInheritedMeta())
|
||||
|
||||
def test_parent_class_not_inherited_meta(self):
|
||||
self.assertEqual(3, self._runner.testParentClassNotInheritedMeta())
|
||||
|
||||
def test_method_meta(self):
|
||||
self.assertItemsEqual(
|
||||
[7, 8, 9, 4, 1, 10], self._runner.testMethodMeta())
|
||||
|
||||
def test_method_argument_meta(self):
|
||||
self.assertItemsEqual(
|
||||
[1, 2, 3], self._runner.testMethodArgumentMeta())
|
||||
|
||||
def test_inherited_property_meta(self):
|
||||
self.assertEqual(
|
||||
[1], self._runner.testInheritedPropertyMeta())
|
||||
|
||||
def test_overridden_property_meta(self):
|
||||
self.assertItemsEqual(
|
||||
[1, 4], self._runner.testOverriddenPropertyMeta())
|
||||
|
||||
def test_package_meta(self):
|
||||
self.assertEqual(
|
||||
[], self._runner.testPackageMeta())
|
||||
|
||||
def test_complex_meta(self):
|
||||
self.assertItemsEqual([
|
||||
[1, 'metatests.PropertyType'],
|
||||
[2, 'metatests.PropertyType'],
|
||||
[3, 'metatests.PropertyType2'],
|
||||
[4, 'metatests.PropertyType'],
|
||||
[5, 'metatests.PropertyType2']
|
||||
], self._runner.testComplexMeta())
|
@@ -14,7 +14,7 @@
|
||||
|
||||
import mock
|
||||
|
||||
from murano.dsl import murano_method
|
||||
from murano.dsl import dsl_types
|
||||
from murano.dsl import serializer
|
||||
from murano.services import actions
|
||||
from murano.tests.unit import base
|
||||
@@ -26,13 +26,13 @@ class TestActionsSerializer(base.MuranoTestCase):
|
||||
|
||||
def _get_mocked_obj(self):
|
||||
method1 = mock.Mock()
|
||||
method1.usage = murano_method.MethodUsages.Action
|
||||
method1.usage = dsl_types.MethodUsages.Action
|
||||
method1.name = 'method1'
|
||||
method2 = mock.Mock()
|
||||
method2.usage = murano_method.MethodUsages.Runtime
|
||||
method2.usage = dsl_types.MethodUsages.Runtime
|
||||
method2.name = 'method2'
|
||||
method3 = mock.Mock()
|
||||
method3.usage = murano_method.MethodUsages.Action
|
||||
method3.usage = dsl_types.MethodUsages.Action
|
||||
method3.name = 'method3'
|
||||
|
||||
obj2_type = mock.Mock()
|
||||
|
12
releasenotes/notes/meta-e76d5c747b0a0fb6.yaml
Normal file
12
releasenotes/notes/meta-e76d5c747b0a0fb6.yaml
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
features:
|
||||
- Added ability to extend MuranoPL entities with custom metadata.
|
||||
- >
|
||||
For MuranoPL classes new key "Usage" was added. By default it is "Class".
|
||||
But it can also be "Meta" to define meta-class. Meta-class has all the
|
||||
capabilities of regular classes and in addition has 3 new attributes:
|
||||
Cardinality, Applies and Inherited.
|
||||
- It is possible to attach meta-class instances to packages,
|
||||
classes (including other meta-classes), properties, methods and
|
||||
method arguments. Each of them got new "Meta" key containing
|
||||
list (or single scalar) of meta-class instances.
|
Reference in New Issue
Block a user