Basic reflection capabilities were added to MuranoPL
Now it is possible to get type info with typeinfo() function and using it as a starting point obtain information about the class, its methods and properties as well as the package of the class. See TestReflection.yaml for detailed usage. Also some refactoring to existing code model were made. Change-Id: Ic8a719e874126d735c26b956a49cb790d434c64c
This commit is contained in:
parent
4f81772d97
commit
f4ff2fb482
|
@ -23,7 +23,7 @@ class AttributeStore(object):
|
|||
|
||||
@staticmethod
|
||||
def _get_attribute_key(tagged_object, owner_type, name):
|
||||
if isinstance(owner_type, dsl_types.MuranoClassReference):
|
||||
if isinstance(owner_type, dsl_types.MuranoTypeReference):
|
||||
owner_type = owner_type.murano_class
|
||||
if isinstance(tagged_object, dsl_types.MuranoObjectInterface):
|
||||
tagged_object = tagged_object.object
|
||||
|
|
|
@ -95,7 +95,8 @@ class MuranoTypeName(yaqltypes.PythonType):
|
|||
def __init__(self, nullable=False, context=None):
|
||||
self._context = context
|
||||
super(MuranoTypeName, self).__init__(
|
||||
(dsl_types.MuranoClassReference, six.string_types), nullable)
|
||||
(dsl_types.MuranoTypeReference,
|
||||
six.string_types), nullable)
|
||||
|
||||
def convert(self, value, sender, context, function_spec, engine,
|
||||
*args, **kwargs):
|
||||
|
@ -108,10 +109,9 @@ class MuranoTypeName(yaqltypes.PythonType):
|
|||
if function_spec.meta.get(constants.META_MURANO_METHOD):
|
||||
context = helpers.get_caller_context(context)
|
||||
murano_type = helpers.get_type(context)
|
||||
value = dsl_types.MuranoClassReference(
|
||||
helpers.get_class(
|
||||
murano_type.namespace_resolver.resolve_name(
|
||||
value), context))
|
||||
value = dsl_types.MuranoTypeReference(helpers.get_class(
|
||||
murano_type.namespace_resolver.resolve_name(value),
|
||||
context))
|
||||
return value
|
||||
|
||||
|
||||
|
|
|
@ -25,11 +25,19 @@ class MuranoMethod(object):
|
|||
pass
|
||||
|
||||
|
||||
class MuranoMethodArgument(object):
|
||||
pass
|
||||
|
||||
|
||||
class MuranoPackage(object):
|
||||
pass
|
||||
|
||||
|
||||
class MuranoClassReference(object):
|
||||
class MuranoProperty(object):
|
||||
pass
|
||||
|
||||
|
||||
class MuranoTypeReference(object):
|
||||
def __init__(self, murano_class):
|
||||
self.__murano_class = murano_class
|
||||
|
||||
|
|
|
@ -43,6 +43,12 @@ class NoMethodFound(Exception):
|
|||
super(NoMethodFound, self).__init__('Method "%s" is not found' % name)
|
||||
|
||||
|
||||
class NoPropertyFound(Exception):
|
||||
def __init__(self, name):
|
||||
super(NoPropertyFound, self).__init__(
|
||||
'Property "%s" is not found' % name)
|
||||
|
||||
|
||||
class NoClassFound(Exception):
|
||||
def __init__(self, name, packages=None):
|
||||
if packages is None:
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
import collections
|
||||
import contextlib
|
||||
import functools
|
||||
import itertools
|
||||
import string
|
||||
import sys
|
||||
import uuid
|
||||
|
@ -284,7 +285,7 @@ def cast(obj, murano_class, pov_or_version_spec=None):
|
|||
elif isinstance(pov_or_version_spec, six.string_types):
|
||||
pov_or_version_spec = parse_version_spec(pov_or_version_spec)
|
||||
|
||||
if isinstance(murano_class, dsl_types.MuranoClassReference):
|
||||
if isinstance(murano_class, dsl_types.MuranoTypeReference):
|
||||
murano_class = murano_class.murano_class
|
||||
if isinstance(murano_class, dsl_types.MuranoClass):
|
||||
if pov_or_version_spec is None:
|
||||
|
@ -292,7 +293,7 @@ def cast(obj, murano_class, pov_or_version_spec=None):
|
|||
murano_class = murano_class.name
|
||||
|
||||
candidates = []
|
||||
for cls in obj.type.ancestors():
|
||||
for cls in itertools.chain((obj.type,), obj.type.ancestors()):
|
||||
if cls.name != murano_class:
|
||||
continue
|
||||
elif isinstance(pov_or_version_spec, semantic_version.Version):
|
||||
|
|
|
@ -17,7 +17,6 @@ import weakref
|
|||
|
||||
import semantic_version
|
||||
import six
|
||||
from six.moves import range
|
||||
from yaql.language import utils
|
||||
|
||||
from murano.dsl import constants
|
||||
|
@ -27,8 +26,8 @@ from murano.dsl import exceptions
|
|||
from murano.dsl import helpers
|
||||
from murano.dsl import murano_method
|
||||
from murano.dsl import murano_object
|
||||
from murano.dsl import murano_property
|
||||
from murano.dsl import namespace_resolver
|
||||
from murano.dsl import typespec
|
||||
from murano.dsl import yaql_integration
|
||||
|
||||
|
||||
|
@ -69,8 +68,8 @@ class MuranoClass(dsl_types.MuranoClass):
|
|||
|
||||
properties = data.get('Properties') or {}
|
||||
for property_name, property_spec in six.iteritems(properties):
|
||||
spec = typespec.PropertySpec(
|
||||
property_name, property_spec, type_obj)
|
||||
spec = murano_property.MuranoProperty(
|
||||
type_obj, property_name, property_spec)
|
||||
type_obj.add_property(property_name, spec)
|
||||
|
||||
methods = data.get('Methods') or data.get('Workflow') or {}
|
||||
|
@ -106,6 +105,13 @@ class MuranoClass(dsl_types.MuranoClass):
|
|||
def methods(self):
|
||||
return self._methods
|
||||
|
||||
@property
|
||||
def all_method_names(self):
|
||||
names = set(self.methods.keys())
|
||||
for c in self.ancestors():
|
||||
names.update(c.methods.keys())
|
||||
return tuple(names)
|
||||
|
||||
@property
|
||||
def parent_mappings(self):
|
||||
return self._parent_mappings
|
||||
|
@ -114,9 +120,6 @@ class MuranoClass(dsl_types.MuranoClass):
|
|||
ctor = yaql_integration.get_class_factory_definition(cls, self)
|
||||
self.add_method('__init__', ctor)
|
||||
|
||||
def get_method(self, name):
|
||||
return self._methods.get(name)
|
||||
|
||||
def add_method(self, name, payload):
|
||||
method = murano_method.MuranoMethod(self, name, payload)
|
||||
self._methods[name] = method
|
||||
|
@ -125,21 +128,26 @@ class MuranoClass(dsl_types.MuranoClass):
|
|||
|
||||
@property
|
||||
def properties(self):
|
||||
return self._properties.keys()
|
||||
return self._properties
|
||||
|
||||
@property
|
||||
def all_property_names(self):
|
||||
names = set(self.properties.keys())
|
||||
for c in self.ancestors():
|
||||
names.update(c.properties.keys())
|
||||
return tuple(names)
|
||||
|
||||
def add_property(self, name, property_typespec):
|
||||
if not isinstance(property_typespec, typespec.PropertySpec):
|
||||
if not isinstance(property_typespec, murano_property.MuranoProperty):
|
||||
raise TypeError('property_typespec')
|
||||
self._properties[name] = property_typespec
|
||||
|
||||
def get_property(self, name):
|
||||
return self._properties[name]
|
||||
|
||||
def _find_method_chains(self, name, origin):
|
||||
def _find_symbol_chains(self, func, origin):
|
||||
queue = collections.deque([(self, ())])
|
||||
while queue:
|
||||
cls, path = queue.popleft()
|
||||
segment = (cls.methods[name],) if name in cls.methods else ()
|
||||
symbol = func(cls)
|
||||
segment = (symbol,) if symbol is not None else ()
|
||||
leaf = True
|
||||
for p in cls.parents(origin):
|
||||
leaf = False
|
||||
|
@ -149,9 +157,10 @@ class MuranoClass(dsl_types.MuranoClass):
|
|||
if path:
|
||||
yield path
|
||||
|
||||
def find_single_method(self, name):
|
||||
chains = sorted(self._find_method_chains(name, self),
|
||||
key=lambda t: len(t))
|
||||
def _choose_symbol(self, func):
|
||||
chains = sorted(
|
||||
self._find_symbol_chains(func, self),
|
||||
key=lambda t: len(t))
|
||||
result = []
|
||||
for i in range(len(chains)):
|
||||
if chains[i][0] in result:
|
||||
|
@ -171,6 +180,17 @@ class MuranoClass(dsl_types.MuranoClass):
|
|||
break
|
||||
if add:
|
||||
result.append(chains[i][0])
|
||||
return result
|
||||
|
||||
def find_method(self, name):
|
||||
return self._choose_symbol(lambda cls: cls.methods.get(name))
|
||||
|
||||
def find_property(self, name):
|
||||
return self._choose_symbol(
|
||||
lambda cls: cls.properties.get(name))
|
||||
|
||||
def find_single_method(self, name):
|
||||
result = self.find_method(name)
|
||||
if len(result) < 1:
|
||||
raise exceptions.NoMethodFound(name)
|
||||
elif len(result) > 1:
|
||||
|
@ -178,18 +198,23 @@ class MuranoClass(dsl_types.MuranoClass):
|
|||
return result[0]
|
||||
|
||||
def find_methods(self, predicate):
|
||||
result = []
|
||||
result = list(filter(predicate, self.methods.values()))
|
||||
for c in self.ancestors():
|
||||
for method in six.itervalues(c.methods):
|
||||
if predicate(method) and method not in result:
|
||||
result.append(method)
|
||||
return result
|
||||
|
||||
def _iterate_unique_methods(self):
|
||||
names = set()
|
||||
def find_properties(self, predicate):
|
||||
result = list(filter(predicate, self.properties.values()))
|
||||
for c in self.ancestors():
|
||||
names.update(c.methods.keys())
|
||||
for name in names:
|
||||
for prop in c.properties.values():
|
||||
if predicate(prop) and prop not in result:
|
||||
result.append(prop)
|
||||
return result
|
||||
|
||||
def _iterate_unique_methods(self):
|
||||
for name in self.all_method_names:
|
||||
try:
|
||||
yield self.find_single_method(name)
|
||||
except exceptions.AmbiguousMethodName as e:
|
||||
|
@ -197,29 +222,13 @@ class MuranoClass(dsl_types.MuranoClass):
|
|||
raise e
|
||||
yield murano_method.MuranoMethod(self, name, func)
|
||||
|
||||
def find_property(self, name):
|
||||
result = []
|
||||
for mc in self.ancestors():
|
||||
if name in mc.properties and mc not in result:
|
||||
result.append(mc)
|
||||
return result
|
||||
|
||||
def find_single_property(self, name):
|
||||
result = None
|
||||
parents = None
|
||||
gen = helpers.traverse(self)
|
||||
while True:
|
||||
try:
|
||||
mc = gen.send(parents)
|
||||
if name in mc.properties:
|
||||
if result and result != mc:
|
||||
raise exceptions.AmbiguousPropertyNameError(name)
|
||||
result = mc
|
||||
parents = []
|
||||
else:
|
||||
parents = mc.parents(self)
|
||||
except StopIteration:
|
||||
return result
|
||||
result = self.find_property(name)
|
||||
if len(result) < 1:
|
||||
raise exceptions.NoPropertyFound(name)
|
||||
elif len(result) > 1:
|
||||
raise exceptions.AmbiguousPropertyNameError(name)
|
||||
return result[0]
|
||||
|
||||
def invoke(self, name, executor, this, args, kwargs, context=None):
|
||||
method = self.find_single_method(name)
|
||||
|
@ -229,6 +238,8 @@ class MuranoClass(dsl_types.MuranoClass):
|
|||
if isinstance(obj, (murano_object.MuranoObject,
|
||||
dsl.MuranoObjectInterface)):
|
||||
obj = obj.type
|
||||
if obj is self:
|
||||
return True
|
||||
return any(cls is self for cls in obj.ancestors())
|
||||
|
||||
def new(self, owner, object_store, **kwargs):
|
||||
|
@ -336,7 +347,8 @@ class MuranoClass(dsl_types.MuranoClass):
|
|||
|
||||
def ancestors(self):
|
||||
for c in helpers.traverse(self, lambda t: t.parents(self)):
|
||||
yield c
|
||||
if c is not self:
|
||||
yield c
|
||||
|
||||
@property
|
||||
def context(self):
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
# under the License.
|
||||
|
||||
import collections
|
||||
import sys
|
||||
import weakref
|
||||
|
||||
import six
|
||||
|
@ -20,6 +21,7 @@ from yaql.language import specs
|
|||
|
||||
from murano.dsl import dsl
|
||||
from murano.dsl import dsl_types
|
||||
from murano.dsl import exceptions
|
||||
from murano.dsl import macros
|
||||
from murano.dsl import typespec
|
||||
from murano.dsl import virtual_exceptions
|
||||
|
@ -69,8 +71,8 @@ class MuranoMethod(dsl_types.MuranoMethod):
|
|||
len(record) > 1):
|
||||
raise ValueError()
|
||||
name = list(record.keys())[0]
|
||||
self._arguments_scheme[name] = typespec.ArgumentSpec(
|
||||
self.name, name, record[name], self.murano_class)
|
||||
self._arguments_scheme[name] = MuranoMethodArgument(
|
||||
self, self.name, name, record[name])
|
||||
self._yaql_function_definition = \
|
||||
yaql_integration.build_wrapper_function_definition(
|
||||
weakref.proxy(self))
|
||||
|
@ -116,3 +118,35 @@ class MuranoMethod(dsl_types.MuranoMethod):
|
|||
return executor.invoke_method(
|
||||
self, this.cast(self.murano_class),
|
||||
context, args, kwargs, skip_stub)
|
||||
|
||||
|
||||
class MuranoMethodArgument(dsl_types.MuranoMethodArgument, typespec.Spec):
|
||||
def __init__(self, murano_method, method_name, arg_name, declaration):
|
||||
super(MuranoMethodArgument, self).__init__(
|
||||
declaration, murano_method.murano_class)
|
||||
self._method_name = method_name
|
||||
self._arg_name = arg_name
|
||||
self._murano_method = weakref.ref(murano_method)
|
||||
|
||||
def validate(self, *args, **kwargs):
|
||||
try:
|
||||
return super(MuranoMethodArgument, self).validate(*args, **kwargs)
|
||||
except exceptions.ContractViolationException as e:
|
||||
msg = u'[{0}::{1}({2}{3})] {4}'.format(
|
||||
self.murano_method.murano_class.name,
|
||||
self.murano_method.name, self.name,
|
||||
e.path, six.text_type(e))
|
||||
six.reraise(exceptions.ContractViolationException,
|
||||
msg, sys.exc_info()[2])
|
||||
|
||||
@property
|
||||
def murano_method(self):
|
||||
return self._murano_method()
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._arg_name
|
||||
|
||||
def __repr__(self):
|
||||
return 'MuranoMethodArgument({method}::{name})'.format(
|
||||
method=self.murano_method.name, name=self.name)
|
||||
|
|
|
@ -78,7 +78,7 @@ class MuranoObject(dsl_types.MuranoObject):
|
|||
if self.__initialized:
|
||||
return
|
||||
for property_name in self.__type.properties:
|
||||
spec = self.__type.get_property(property_name)
|
||||
spec = self.__type.properties[property_name]
|
||||
if spec.usage == typespec.PropertyUsages.Config:
|
||||
if property_name in self.__config:
|
||||
property_value = self.__config[property_name]
|
||||
|
@ -100,7 +100,7 @@ class MuranoObject(dsl_types.MuranoObject):
|
|||
spec = init.arguments_scheme[property_name]
|
||||
is_init_arg = True
|
||||
else:
|
||||
spec = self.__type.get_property(property_name)
|
||||
spec = self.__type.properties[property_name]
|
||||
is_init_arg = False
|
||||
|
||||
if property_name in used_names:
|
||||
|
@ -175,13 +175,14 @@ class MuranoObject(dsl_types.MuranoObject):
|
|||
if name in start_type.properties:
|
||||
return self.cast(start_type)._get_property_value(name)
|
||||
else:
|
||||
declared_properties = start_type.find_single_property(name)
|
||||
if declared_properties:
|
||||
return self.cast(declared_properties).__properties[name]
|
||||
elif derived:
|
||||
return self.cast(caller_class)._get_property_value(name)
|
||||
else:
|
||||
raise exceptions.PropertyReadError(name, start_type)
|
||||
try:
|
||||
spec = start_type.find_single_property(name)
|
||||
return self.cast(spec.murano_class).__properties[name]
|
||||
except exceptions.NoPropertyFound:
|
||||
if derived:
|
||||
return self.cast(caller_class)._get_property_value(name)
|
||||
else:
|
||||
raise exceptions.PropertyReadError(name, start_type)
|
||||
|
||||
def _get_property_value(self, name):
|
||||
try:
|
||||
|
@ -195,14 +196,15 @@ class MuranoObject(dsl_types.MuranoObject):
|
|||
caller_class = None if not context else helpers.get_type(context)
|
||||
if caller_class is not None and caller_class.is_compatible(self):
|
||||
start_type, derived = caller_class, True
|
||||
declared_properties = start_type.find_property(name)
|
||||
declared_properties = start_type.find_properties(
|
||||
lambda p: p.name == name)
|
||||
if context is None:
|
||||
context = self.object_store.executor.create_object_context(self)
|
||||
if len(declared_properties) > 0:
|
||||
declared_properties = self.type.find_property(name)
|
||||
declared_properties = self.type.find_properties(
|
||||
lambda p: p.name == name)
|
||||
values_to_assign = []
|
||||
for mc in declared_properties:
|
||||
spec = mc.get_property(name)
|
||||
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
|
||||
|
@ -213,7 +215,7 @@ class MuranoObject(dsl_types.MuranoObject):
|
|||
default = self.__defaults.get(name, default)
|
||||
default = helpers.evaluate(default, context)
|
||||
|
||||
obj = self.cast(mc)
|
||||
obj = self.cast(spec.murano_class)
|
||||
values_to_assign.append((obj, spec.validate(
|
||||
value, self.real_this,
|
||||
self.real_this, default=default)))
|
||||
|
@ -251,7 +253,7 @@ class MuranoObject(dsl_types.MuranoObject):
|
|||
else:
|
||||
for property_name in self.type.properties:
|
||||
if property_name in self.__properties:
|
||||
spec = self.type.get_property(property_name)
|
||||
spec = self.type.properties[property_name]
|
||||
if spec.usage != typespec.PropertyUsages.Runtime:
|
||||
result[property_name] = self.__properties[
|
||||
property_name]
|
||||
|
|
|
@ -164,3 +164,6 @@ class MuranoPackage(dsl_types.MuranoPackage):
|
|||
@property
|
||||
def context(self):
|
||||
return None
|
||||
|
||||
def __repr__(self):
|
||||
return 'MuranoPackage({name})'.format(name=self.name)
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
# 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 sys
|
||||
import weakref
|
||||
|
||||
import six
|
||||
|
||||
from murano.dsl import dsl_types
|
||||
from murano.dsl import exceptions
|
||||
from murano.dsl import typespec
|
||||
|
||||
|
||||
class MuranoProperty(dsl_types.MuranoProperty, typespec.Spec):
|
||||
def __init__(self, murano_class, property_name, declaration):
|
||||
super(MuranoProperty, self).__init__(declaration, murano_class)
|
||||
self._property_name = property_name
|
||||
self._murano_class = weakref.ref(murano_class)
|
||||
|
||||
def validate(self, *args, **kwargs):
|
||||
try:
|
||||
return super(MuranoProperty, self).validate(*args, **kwargs)
|
||||
except exceptions.ContractViolationException as e:
|
||||
msg = u'[{0}.{1}{2}] {3}'.format(
|
||||
self.murano_class.name, self.name, e.path, six.text_type(e))
|
||||
six.reraise(exceptions.ContractViolationException,
|
||||
msg, sys.exc_info()[2])
|
||||
|
||||
@property
|
||||
def murano_class(self):
|
||||
return self._murano_class()
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._property_name
|
||||
|
||||
def __repr__(self):
|
||||
return 'MuranoProperty({type}::{name})'.format(
|
||||
type=self.murano_class.name, name=self.name)
|
|
@ -0,0 +1,174 @@
|
|||
# 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 semantic_version
|
||||
from yaql.language import specs
|
||||
from yaql import yaqlization
|
||||
|
||||
from murano.dsl import dsl_types
|
||||
|
||||
|
||||
@specs.yaql_property(dsl_types.MuranoClass)
|
||||
@specs.name('name')
|
||||
def class_name(murano_class):
|
||||
return murano_class.name
|
||||
|
||||
|
||||
@specs.yaql_property(dsl_types.MuranoClass)
|
||||
def methods(murano_class):
|
||||
all_method_names = murano_class.all_method_names
|
||||
return tuple(
|
||||
murano_method
|
||||
for name in all_method_names
|
||||
if not name.startswith('__') and not name.startswith('.')
|
||||
for murano_method in murano_class.find_method(name)
|
||||
)
|
||||
|
||||
|
||||
@specs.yaql_property(dsl_types.MuranoClass)
|
||||
def properties(murano_class):
|
||||
all_property_names = murano_class.all_property_names
|
||||
return tuple(
|
||||
prop
|
||||
for prop_name in all_property_names
|
||||
if not prop_name.startswith('__') and not prop_name.startswith('.')
|
||||
for prop in murano_class.find_property(prop_name)
|
||||
)
|
||||
|
||||
|
||||
@specs.yaql_property(dsl_types.MuranoClass)
|
||||
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.MuranoClass)
|
||||
@specs.name('version')
|
||||
def class_version(murano_class):
|
||||
return murano_class.version
|
||||
|
||||
|
||||
@specs.yaql_property(dsl_types.MuranoProperty)
|
||||
@specs.name('name')
|
||||
def property_name(murano_property):
|
||||
return murano_property.name
|
||||
|
||||
|
||||
# TODO(ativelkov): add 'default' to return some wrapped YAQL expression
|
||||
# @specs.yaql_property(dsl_types.MuranoProperty)
|
||||
# @specs.name('default')
|
||||
# def property_default(murano_property):
|
||||
# return murano_property.default
|
||||
|
||||
|
||||
@specs.yaql_property(dsl_types.MuranoProperty)
|
||||
@specs.name('has_default')
|
||||
def property_has_default(murano_property):
|
||||
return murano_property.has_default
|
||||
|
||||
|
||||
@specs.yaql_property(dsl_types.MuranoProperty)
|
||||
@specs.name('usage')
|
||||
def property_usage(murano_property):
|
||||
return murano_property.usage
|
||||
|
||||
|
||||
@specs.yaql_property(dsl_types.MuranoProperty)
|
||||
@specs.name('declaring_type')
|
||||
def property_owner(murano_property):
|
||||
return murano_property.murano_class
|
||||
|
||||
|
||||
@specs.yaql_property(dsl_types.MuranoMethod)
|
||||
@specs.name('name')
|
||||
def method_name(murano_method):
|
||||
return murano_method.name
|
||||
|
||||
|
||||
@specs.yaql_property(dsl_types.MuranoMethod)
|
||||
def arguments(murano_method):
|
||||
if murano_method.arguments_scheme is None:
|
||||
return None
|
||||
return tuple(murano_method.arguments_scheme.values())
|
||||
|
||||
|
||||
@specs.yaql_property(dsl_types.MuranoMethod)
|
||||
@specs.name('declaring_type')
|
||||
def method_owner(murano_method):
|
||||
return murano_method.murano_class
|
||||
|
||||
|
||||
@specs.yaql_property(dsl_types.MuranoPackage)
|
||||
def types(murano_package):
|
||||
return tuple(
|
||||
murano_package.find_class(cls, False)
|
||||
for cls in murano_package.classes
|
||||
)
|
||||
|
||||
|
||||
@specs.yaql_property(dsl_types.MuranoPackage)
|
||||
@specs.name('name')
|
||||
def package_name(murano_package):
|
||||
return murano_package.name
|
||||
|
||||
|
||||
@specs.yaql_property(dsl_types.MuranoPackage)
|
||||
@specs.name('version')
|
||||
def package_version(murano_package):
|
||||
return murano_package.version
|
||||
|
||||
|
||||
@specs.yaql_property(dsl_types.MuranoMethodArgument)
|
||||
@specs.name('name')
|
||||
def argument_name(method_argument):
|
||||
return method_argument.name
|
||||
|
||||
# TODO(ativelkov): add 'default' to return some wrapped YAQL expression
|
||||
# @specs.yaql_property(dsl_types.MuranoMethodArgument)
|
||||
# @specs.name('default')
|
||||
# def argument_default(method_argument):
|
||||
# return method_argument.default
|
||||
|
||||
|
||||
@specs.yaql_property(dsl_types.MuranoMethodArgument)
|
||||
@specs.name('has_default')
|
||||
def argument_has_default(method_argument):
|
||||
return method_argument.has_default
|
||||
|
||||
|
||||
@specs.yaql_property(dsl_types.MuranoMethodArgument)
|
||||
@specs.name('declaring_method')
|
||||
def argument_owner(method_argument):
|
||||
return method_argument.murano_method
|
||||
|
||||
|
||||
def register(context):
|
||||
funcs = (
|
||||
class_name, methods, properties, ancestors, package, class_version,
|
||||
property_name, property_has_default, property_owner,
|
||||
property_usage,
|
||||
method_name, arguments, method_owner,
|
||||
types, package_name, package_version,
|
||||
argument_name, argument_has_default, argument_owner
|
||||
)
|
||||
for f in funcs:
|
||||
context.register_function(f)
|
||||
|
||||
yaqlization.yaqlize(semantic_version.Version, whitelist=[
|
||||
'major', 'minor', 'patch', 'prerelease', 'build'
|
||||
])
|
|
@ -12,11 +12,8 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import sys
|
||||
import weakref
|
||||
|
||||
import six
|
||||
|
||||
from murano.dsl import exceptions
|
||||
from murano.dsl import type_scheme
|
||||
|
||||
|
@ -56,6 +53,10 @@ class Spec(object):
|
|||
def default(self):
|
||||
return self._default
|
||||
|
||||
@property
|
||||
def contract(self):
|
||||
return self._contract
|
||||
|
||||
@property
|
||||
def has_default(self):
|
||||
return self._has_default
|
||||
|
@ -63,37 +64,3 @@ class Spec(object):
|
|||
@property
|
||||
def usage(self):
|
||||
return self._usage
|
||||
|
||||
|
||||
class PropertySpec(Spec):
|
||||
def __init__(self, property_name, declaration, container_class):
|
||||
super(PropertySpec, self).__init__(declaration, container_class)
|
||||
self.property_name = property_name
|
||||
self.class_name = container_class.name
|
||||
|
||||
def validate(self, *args, **kwargs):
|
||||
try:
|
||||
return super(PropertySpec, self).validate(*args, **kwargs)
|
||||
except exceptions.ContractViolationException as e:
|
||||
msg = u'[{0}.{1}{2}] {3}'.format(
|
||||
self.class_name, self.property_name, e.path, six.text_type(e))
|
||||
six.reraise(exceptions.ContractViolationException,
|
||||
msg, sys.exc_info()[2])
|
||||
|
||||
|
||||
class ArgumentSpec(Spec):
|
||||
def __init__(self, method_name, arg_name, declaration, container_class):
|
||||
super(ArgumentSpec, self).__init__(declaration, container_class)
|
||||
self.method_name = method_name
|
||||
self.arg_name = arg_name
|
||||
self.class_name = container_class.name
|
||||
|
||||
def validate(self, *args, **kwargs):
|
||||
try:
|
||||
return super(ArgumentSpec, self).validate(*args, **kwargs)
|
||||
except exceptions.ContractViolationException as e:
|
||||
msg = u'[{0}::{1}({2}{3})] {4}'.format(
|
||||
self.class_name, self.method_name, self.arg_name,
|
||||
e.path, six.text_type(e))
|
||||
six.reraise(exceptions.ContractViolationException,
|
||||
msg, sys.exc_info()[2])
|
||||
|
|
|
@ -22,6 +22,7 @@ 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 reflection
|
||||
|
||||
|
||||
@specs.parameter('object_', dsl.MuranoObjectParameterType())
|
||||
|
@ -112,6 +113,11 @@ def type_(object_):
|
|||
return None if object_ is None else object_.type.name
|
||||
|
||||
|
||||
@specs.parameter('object_', dsl.MuranoObjectParameterType(nullable=True))
|
||||
def typeinfo(object_):
|
||||
return None if object_ is None else object_.type
|
||||
|
||||
|
||||
@specs.parameter('object_', dsl.MuranoObjectParameterType(nullable=True))
|
||||
def name(object_):
|
||||
return None if object_ is None else object_.name
|
||||
|
@ -143,10 +149,9 @@ def op_dot(context, receiver, expr, operator):
|
|||
@specs.name('#operator_:')
|
||||
def ns_resolve(context, prefix, name):
|
||||
murano_type = helpers.get_type(context)
|
||||
return dsl_types.MuranoClassReference(
|
||||
helpers.get_class(
|
||||
murano_type.namespace_resolver.resolve_name(
|
||||
prefix + ':' + name), context))
|
||||
return dsl_types.MuranoTypeReference(helpers.get_class(
|
||||
murano_type.namespace_resolver.resolve_name(
|
||||
prefix + ':' + name), context))
|
||||
|
||||
|
||||
@specs.parameter('obj', dsl.MuranoObjectParameterType(nullable=True))
|
||||
|
@ -169,10 +174,12 @@ def register(context, runtime_version):
|
|||
context.register_function(find)
|
||||
context.register_function(sleep_)
|
||||
context.register_function(type_)
|
||||
context.register_function(typeinfo)
|
||||
context.register_function(name)
|
||||
context.register_function(obj_attribution)
|
||||
context.register_function(op_dot)
|
||||
context.register_function(ns_resolve)
|
||||
reflection.register(context)
|
||||
context.register_function(is_instance_of)
|
||||
|
||||
if runtime_version <= constants.RUNTIME_VERSION_1_1:
|
||||
|
@ -181,4 +188,5 @@ def register(context, runtime_version):
|
|||
for spec in utils.to_extension_method(t, context):
|
||||
context2.register_function(spec)
|
||||
return context2
|
||||
|
||||
return context
|
||||
|
|
|
@ -97,10 +97,11 @@ def with_original(context, **kwargs):
|
|||
return new_context
|
||||
|
||||
|
||||
@specs.parameter('target',
|
||||
yaqltypes.AnyOf(dsl.MuranoTypeName(), dsl_types.MuranoObject))
|
||||
@specs.parameter(
|
||||
'target',
|
||||
yaqltypes.AnyOf(dsl.MuranoTypeName(), dsl.MuranoObjectParameterType()))
|
||||
@specs.parameter('target_method', yaqltypes.String())
|
||||
@specs.parameter('mock_object', dsl_types.MuranoObject)
|
||||
@specs.parameter('mock_object', dsl.MuranoObjectParameterType())
|
||||
@specs.parameter('mock_name', yaqltypes.String())
|
||||
def inject_method_with_str(context, target, target_method,
|
||||
mock_object, mock_name):
|
||||
|
@ -109,7 +110,7 @@ def inject_method_with_str(context, target, target_method,
|
|||
current_class = helpers.get_type(context)
|
||||
mock_func = current_class.find_single_method(mock_name)
|
||||
|
||||
if isinstance(target, dsl_types.MuranoClassReference):
|
||||
if isinstance(target, dsl_types.MuranoTypeReference):
|
||||
original_class = target.murano_class
|
||||
else:
|
||||
original_class = target.type
|
||||
|
@ -128,13 +129,14 @@ def inject_method_with_str(context, target, target_method,
|
|||
existing_mocks.append(result_fd)
|
||||
|
||||
|
||||
@specs.parameter('target',
|
||||
yaqltypes.AnyOf(dsl.MuranoTypeName(), dsl_types.MuranoObject))
|
||||
@specs.parameter(
|
||||
'target',
|
||||
yaqltypes.AnyOf(dsl.MuranoTypeName(), dsl.MuranoObjectParameterType()))
|
||||
@specs.parameter('target_method', yaqltypes.String())
|
||||
@specs.parameter('expr', yaqltypes.Lambda(with_context=True))
|
||||
def inject_method_with_yaql_expr(context, target, target_method, expr):
|
||||
ctx_manager = helpers.get_executor(context).context_manager
|
||||
if isinstance(target, dsl_types.MuranoClassReference):
|
||||
if isinstance(target, dsl_types.MuranoTypeReference):
|
||||
original_class = target.murano_class
|
||||
else:
|
||||
original_class = target.type
|
||||
|
|
|
@ -17,6 +17,7 @@ import os.path
|
|||
|
||||
import six
|
||||
|
||||
from murano.dsl import constants
|
||||
from murano.dsl import murano_package
|
||||
from murano.dsl import namespace_resolver
|
||||
from murano.dsl import package_loader
|
||||
|
@ -54,7 +55,8 @@ class TestPackageLoader(package_loader.MuranoPackageLoader):
|
|||
self._parent = parent_loader
|
||||
self._configs = {}
|
||||
self._package = TestPackage(
|
||||
self, package_name, None, '1.0', None, self._configs)
|
||||
self, package_name, None, constants.RUNTIME_VERSION_1_0,
|
||||
None, self._configs)
|
||||
for name, payload in six.iteritems(self._classes):
|
||||
self._package.register_class(payload, name)
|
||||
super(TestPackageLoader, self).__init__()
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
Name: TestReflection
|
||||
|
||||
Properties:
|
||||
prop:
|
||||
Contract: $.string()
|
||||
Default: qwerty
|
||||
|
||||
Methods:
|
||||
testTypeInfo:
|
||||
Body:
|
||||
- $typeinfo: typeinfo($)
|
||||
- Return:
|
||||
name: $typeinfo.name
|
||||
versionStr: str($typeinfo.version)
|
||||
versionMajor: $typeinfo.version.major
|
||||
versionMinor: $typeinfo.version.minor
|
||||
versionPatch: $typeinfo.version.patch
|
||||
ancestors: $typeinfo.ancestors.name
|
||||
properties: $typeinfo.properties.name
|
||||
methods: $typeinfo.methods.name.orderBy($)
|
||||
package: $typeinfo.package.name
|
||||
|
||||
testMethodInfo:
|
||||
Body:
|
||||
- $method: typeinfo($).methods.where($.name = foo).single()
|
||||
- $bar: $method.arguments.where($.name = bar).single()
|
||||
- $baz: $method.arguments.where($.name = baz).single()
|
||||
- Return:
|
||||
name: $method.name
|
||||
arguments: $method.arguments.name
|
||||
barHasDefault: $bar.hasDefault
|
||||
bazHasDefault: $baz.hasDefault
|
||||
declaringType: $method.declaringType.name
|
||||
barMethod: $bar.declaringMethod.name
|
||||
bazMethod: $baz.declaringMethod.name
|
||||
|
||||
testPropertyInfo:
|
||||
Body:
|
||||
- $property: typeinfo($).properties.first()
|
||||
- Return:
|
||||
name: $property.name
|
||||
hasDefault: $property.hasDefault
|
||||
usage: $property.usage
|
||||
|
||||
foo:
|
||||
Arguments:
|
||||
- bar:
|
||||
Contract: $.string()
|
||||
Default: null
|
||||
- baz:
|
||||
Contract: $.string()
|
|
@ -0,0 +1,62 @@
|
|||
# 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 TestReflection(test_case.DslTestCase):
|
||||
def setUp(self):
|
||||
super(TestReflection, self).setUp()
|
||||
self._runner = self.new_runner(om.Object('TestReflection'))
|
||||
|
||||
def test_type_info(self):
|
||||
self.assertEqual(
|
||||
{
|
||||
'name': 'TestReflection',
|
||||
'versionStr': '0.0.0',
|
||||
'versionMajor': 0,
|
||||
'versionMinor': 0,
|
||||
'versionPatch': 0,
|
||||
'ancestors': ['io.murano.Object'],
|
||||
'properties': ['prop'],
|
||||
'package': 'tests',
|
||||
'methods': [
|
||||
'foo', 'getAttr', 'setAttr',
|
||||
'testMethodInfo', 'testPropertyInfo', 'testTypeInfo'
|
||||
]
|
||||
},
|
||||
self._runner.testTypeInfo())
|
||||
|
||||
def test_method_info(self):
|
||||
self.assertEqual(
|
||||
{
|
||||
'name': 'foo',
|
||||
'arguments': ['bar', 'baz'],
|
||||
'barHasDefault': True,
|
||||
'bazHasDefault': False,
|
||||
'barMethod': 'foo',
|
||||
'bazMethod': 'foo',
|
||||
'declaringType': 'TestReflection'
|
||||
},
|
||||
self._runner.testMethodInfo())
|
||||
|
||||
def test_property_info(self):
|
||||
self.assertEqual(
|
||||
{
|
||||
'name': 'prop',
|
||||
'hasDefault': True,
|
||||
'usage': 'In'
|
||||
},
|
||||
self._runner.testPropertyInfo())
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
features:
|
||||
- Basic reflection capabilities were added to MuranoPL. Now it is possible
|
||||
to get type info with typeinfo() function and using it as a starting point
|
||||
obtain information about the class, its methods and properties as well as
|
||||
the package of the class.
|
Loading…
Reference in New Issue