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:
Stan Lagun 2016-01-25 02:52:15 +03:00
parent 4f81772d97
commit f4ff2fb482
18 changed files with 508 additions and 120 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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):

View File

@ -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):

View File

@ -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)

View File

@ -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]

View File

@ -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)

View File

@ -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)

174
murano/dsl/reflection.py Normal file
View File

@ -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'
])

View File

@ -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])

View File

@ -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

View File

@ -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

View File

@ -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__()

View File

@ -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()

View File

@ -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())

View File

@ -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.