Merge "Support for static methods/properties"

This commit is contained in:
Jenkins 2016-02-19 15:34:16 +00:00 committed by Gerrit Code Review
commit 12a8b87302
24 changed files with 641 additions and 103 deletions

View File

@ -19,6 +19,7 @@ Name: Linux
Methods:
runCommand:
Usage: Static
Arguments:
- agent:
Contract: $.class(sys:Agent)
@ -54,6 +55,7 @@ Methods:
- Return: $agent.call($template, $resources)
putFile:
Usage: Static
Arguments:
- agent:
Contract: $.class(sys:Agent)

View File

@ -233,21 +233,22 @@ class MuranoTestRunner(object):
for m in test_cases:
# Create new executor for each test case to provide
# pure test environment
executer = executor.MuranoDslExecutor(
dsl_executor = executor.MuranoDslExecutor(
pkg_loader,
mock_context_manager.MockContextManager(),
test_env)
obj = package.find_class(pkg_class, False).new(
None, executer.object_store)(None)
self._call_service_method('setUp', executer, obj)
None, dsl_executor.object_store, dsl_executor)(None)
self._call_service_method('setUp', dsl_executor, obj)
obj.type.methods[m].usage = 'Action'
test_env.start()
try:
obj.type.invoke(m, executer, obj, (), {})
obj.type.invoke(m, dsl_executor, obj, (), {})
LOG.debug('\n.....{0}.{1}.....OK'.format(obj.type.name,
m))
self._call_service_method('tearDown', executer, obj)
self._call_service_method(
'tearDown', dsl_executor, obj)
except Exception:
LOG.exception('\n.....{0}.{1}.....FAILURE\n'
''.format(obj.type.name, m))

View File

@ -109,9 +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.MuranoTypeReference(helpers.get_class(
value = helpers.get_class(
murano_type.namespace_resolver.resolve_name(value),
context))
context).get_reference()
return value

View File

@ -45,8 +45,16 @@ class MuranoTypeReference(object):
def murano_class(self):
return self.__murano_class
def __str__(self):
return self.__murano_class.name
def __repr__(self):
return '*' + repr(self.murano_class)
def __eq__(self, other):
if not isinstance(other, MuranoTypeReference):
return False
return self.murano_class == other.murano_class
def __hash__(self):
return hash(self.murano_class)
class YaqlExpression(object):

View File

@ -160,3 +160,9 @@ class UninitializedPropertyAccessError(PropertyAccessError):
class CircularExpressionDependenciesError(Exception):
pass
class InvalidLhsTargetError(Exception):
def __init__(self, target):
super(InvalidLhsTargetError, self).__init__(
'Invalid assignment target "%s"' % target)

View File

@ -28,6 +28,7 @@ from murano.common.i18n import _LW
from murano.dsl import attribute_store
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
@ -85,7 +86,9 @@ class MuranoDslExecutor(object):
context = self.create_method_context(
self.create_object_context(this, context), method)
this = this.real_this
if isinstance(this, dsl_types.MuranoObject):
this = this.real_this
if method.arguments_scheme is not None:
args, kwargs = self._canonize_parameters(
@ -119,7 +122,10 @@ class MuranoDslExecutor(object):
@contextlib.contextmanager
def _acquire_method_lock(self, func, this):
method_id = id(func)
this_id = this.object_id
if isinstance(this, dsl_types.MuranoClass):
this_id = id(this)
else:
this_id = this.object_id
thread_id = helpers.get_current_thread_id()
while True:
event, event_owner = self._locks.get(
@ -262,13 +268,24 @@ class MuranoDslExecutor(object):
return context
def create_object_context(self, obj, caller_context=None):
class_context = self.create_class_context(obj.type)
context = helpers.link_contexts(
class_context, self.context_manager.create_object_context(
obj)).create_child_context()
context[constants.CTX_THIS] = obj.real_this
context['this'] = obj.real_this
context[''] = obj.real_this
if isinstance(obj, dsl_types.MuranoClass):
obj_type = obj
obj = None
else:
obj_type = obj.type
class_context = self.create_class_context(obj_type)
if obj is not None:
context = helpers.link_contexts(
class_context, self.context_manager.create_object_context(
obj)).create_child_context()
context[constants.CTX_THIS] = obj.real_this
context['this'] = obj.real_this
context[''] = obj.real_this
else:
context = class_context.create_child_context()
type_ref = obj_type.get_reference()
context['this'] = type_ref
context[''] = type_ref
if caller_context is not None:
caller = caller_context

View File

@ -153,7 +153,9 @@ def get_environment(context=None):
def get_object_store(context=None):
context = context or get_context()
return context[constants.CTX_THIS].object_store
this = context[constants.CTX_THIS]
return this.object_store if isinstance(
this, dsl_types.MuranoObject) else None
def get_package_loader(context=None):

View File

@ -20,8 +20,10 @@ from yaql.language import utils
from yaql.language import yaqltypes
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 yaql_functions
from murano.dsl import yaql_integration
@ -68,6 +70,14 @@ class LhsExpression(object):
((key, value),))))
elif isinstance(src, dsl_types.MuranoObject):
src.set_property(key, value, root_context)
elif isinstance(src, (
dsl_types.MuranoTypeReference,
dsl_types.MuranoClass)):
if not isinstance(src, dsl_types.MuranoClass):
mc = src.murano_class
else:
mc = src
mc.set_property(key, value, root_context)
else:
raise ValueError(
'attribution may only be applied to '
@ -118,16 +128,51 @@ class LhsExpression(object):
else:
return attribution(this, index)
def _wrap_type_reference(tr):
return LhsExpression.Property(lambda: tr, self._invalid_target)
@specs.parameter('prefix', yaqltypes.Keyword())
@specs.parameter('name', yaqltypes.Keyword())
@specs.name('#operator_:')
def ns_resolve(prefix, name):
return _wrap_type_reference(
yaql_functions.ns_resolve(context, prefix, name))
@specs.parameter('name', yaqltypes.Keyword())
@specs.name('#unary_operator_:')
def ns_resolve_unary(context, name):
return _wrap_type_reference(
yaql_functions.ns_resolve_unary(context, name))
@specs.parameter('object_', dsl_types.MuranoObject)
def type_(object_):
return _wrap_type_reference(yaql_functions.type_(object_))
@specs.name('type')
@specs.parameter('cls', dsl.MuranoTypeName())
def type_from_name(cls):
return _wrap_type_reference(cls)
context = yaql_integration.create_empty_context()
context.register_function(get_context_data, '#get_context_data')
context.register_function(attribution, '#operator_.')
context.register_function(indexation, '#indexer')
context.register_function(ns_resolve)
context.register_function(ns_resolve_unary)
context.register_function(type_)
context.register_function(type_from_name)
return context
def _invalid_target(self, *args, **kwargs):
raise exceptions.InvalidLhsTargetError(self._expression)
def __call__(self, value, context):
new_context = self._create_context(context)
new_context[''] = context['$']
new_context[constants.CTX_TYPE] = context[constants.CTX_TYPE]
self._current_obj = None
self._current_obj_name = None
property = self._expression(context=new_context)
if not isinstance(property, LhsExpression.Property):
self._invalid_target()
property.set(value)

View File

@ -46,6 +46,7 @@ class MuranoClass(dsl_types.MuranoClass):
package.find_class(constants.CORE_LIBRARY_OBJECT)]
self._context = None
self._parent_mappings = self._build_parent_remappings()
self._property_values = {}
@classmethod
def create(cls, data, package, name=None):
@ -53,7 +54,7 @@ class MuranoClass(dsl_types.MuranoClass):
ns_resolver = namespace_resolver.NamespaceResolver(namespaces)
if not name:
name = ns_resolver.resolve_name(data['Name'])
name = ns_resolver.resolve_name(str(data['Name']))
parent_class_names = data.get('Extends')
parent_classes = []
@ -61,7 +62,7 @@ class MuranoClass(dsl_types.MuranoClass):
if not utils.is_sequence(parent_class_names):
parent_class_names = [parent_class_names]
for parent_name in parent_class_names:
full_name = ns_resolver.resolve_name(parent_name)
full_name = ns_resolver.resolve_name(str(parent_name))
parent_classes.append(package.find_class(full_name))
type_obj = cls(ns_resolver, name, package, parent_classes)
@ -189,6 +190,19 @@ class MuranoClass(dsl_types.MuranoClass):
return self._choose_symbol(
lambda cls: cls.properties.get(name))
def find_static_property(self, name):
def prop_func(cls):
prop = cls.properties.get(name)
if prop is not None and prop.usage == 'Static':
return prop
result = self._choose_symbol(prop_func)
if len(result) < 1:
raise exceptions.NoPropertyFound(name)
elif len(result) > 1:
raise exceptions.AmbiguousPropertyNameError(name)
return result[0]
def find_single_method(self, name):
result = self.find_method(name)
if len(result) < 1:
@ -242,12 +256,13 @@ class MuranoClass(dsl_types.MuranoClass):
return True
return any(cls is self for cls in obj.ancestors())
def new(self, owner, object_store, **kwargs):
obj = murano_object.MuranoObject(self, owner, object_store, **kwargs)
def new(self, owner, object_store, executor, **kwargs):
obj = murano_object.MuranoObject(
self, owner, object_store, executor, **kwargs)
def initializer(__context, **params):
if __context is None:
__context = object_store.executor.create_object_context(obj)
__context = executor.create_object_context(obj)
init_context = __context.create_child_context()
init_context[constants.CTX_ALLOW_PROPERTY_WRITES] = True
obj.initialize(init_context, object_store, params)
@ -359,3 +374,17 @@ class MuranoClass(dsl_types.MuranoClass):
m.yaql_function_definition,
name=m.yaql_function_definition.name)
return self._context
def get_property(self, name, context):
prop = self.find_static_property(name)
cls = prop.murano_class
value = cls._property_values.get(name, prop.default)
return prop.validate(value, cls, None, context)
def set_property(self, name, value, context):
prop = self.find_static_property(name)
cls = prop.murano_class
cls._property_values[name] = prop.validate(value, cls, None, context)
def get_reference(self):
return dsl_types.MuranoTypeReference(self)

View File

@ -35,7 +35,8 @@ virtual_exceptions.register()
class MethodUsages(object):
Action = 'Action'
Runtime = 'Runtime'
All = set([Action, Runtime])
Static = 'Static'
All = set([Action, Runtime, Static])
class MuranoMethod(dsl_types.MuranoMethod):
@ -111,13 +112,18 @@ class MuranoMethod(dsl_types.MuranoMethod):
def invoke(self, executor, this, args, kwargs, context=None,
skip_stub=False):
if not self.murano_class.is_compatible(this):
if self.usage == 'Static':
this = None
elif not self.murano_class.is_compatible(this):
raise Exception("'this' must be of compatible type")
if isinstance(this, dsl.MuranoObjectInterface):
this = this.object
if this is not None:
this = this.cast(self.murano_class)
else:
this = self.murano_class
return executor.invoke_method(
self, this.cast(self.murano_class),
context, args, kwargs, skip_stub)
self, this, context, args, kwargs, skip_stub)
class MuranoMethodArgument(dsl_types.MuranoMethodArgument, typespec.Spec):

View File

@ -26,8 +26,9 @@ from murano.dsl import yaql_integration
class MuranoObject(dsl_types.MuranoObject):
def __init__(self, murano_class, owner, object_store, object_id=None,
name=None, known_classes=None, defaults=None, this=None):
def __init__(self, murano_class, owner, object_store, executor,
object_id=None, name=None, known_classes=None,
defaults=None, this=None):
if known_classes is None:
known_classes = {}
self.__owner = owner.real_this if owner else None
@ -39,7 +40,9 @@ class MuranoObject(dsl_types.MuranoObject):
self.__this = this
self.__name = name
self.__extension = None
self.__object_store = weakref.ref(object_store)
self.__object_store = \
None if object_store is None else weakref.ref(object_store)
self.__executor = weakref.ref(executor)
self.__config = murano_class.package.get_class_config(
murano_class.name)
if not isinstance(self.__config, dict):
@ -49,7 +52,7 @@ class MuranoObject(dsl_types.MuranoObject):
name = parent_class.name
if name not in known_classes:
obj = parent_class.new(
owner, object_store, object_id=self.__object_id,
owner, object_store, executor, object_id=self.__object_id,
known_classes=known_classes, defaults=defaults,
this=self.real_this).object
@ -72,7 +75,11 @@ class MuranoObject(dsl_types.MuranoObject):
@property
def object_store(self):
return self.__object_store()
return None if self.__object_store is None else self.__object_store()
@property
def executor(self):
return self.__executor()
def initialize(self, context, object_store, params):
if self.__initialized:
@ -105,7 +112,8 @@ class MuranoObject(dsl_types.MuranoObject):
if property_name in used_names:
continue
if spec.usage == typespec.PropertyUsages.Config:
if spec.usage in (typespec.PropertyUsages.Config,
typespec.PropertyUsages.Static):
used_names.add(property_name)
continue
if spec.usage == typespec.PropertyUsages.Runtime:
@ -133,7 +141,8 @@ class MuranoObject(dsl_types.MuranoObject):
last_errors = errors
executor = helpers.get_executor(context)
if not object_store.initializing and self.__extension is None:
if ((object_store is None or not object_store.initializing) and
self.__extension is None):
method = self.type.methods.get('__init__')
if method:
filtered_params = yaql_integration.filter_parameters(
@ -146,7 +155,7 @@ class MuranoObject(dsl_types.MuranoObject):
for parent in self.__parents.values():
parent.initialize(context, object_store, params)
if not object_store.initializing and init:
if (object_store is None or not object_store.initializing) and init:
context[constants.CTX_ARGUMENT_OWNER] = self.real_this
init.invoke(executor, self.real_this, (), init_args, context)
self.__initialized = True
@ -173,11 +182,18 @@ class MuranoObject(dsl_types.MuranoObject):
if caller_class is not None and caller_class.is_compatible(self):
start_type, derived = caller_class, True
if name in start_type.properties:
return self.cast(start_type)._get_property_value(name)
spec = start_type.properties[name]
if spec.usage == typespec.PropertyUsages.Static:
return spec.murano_class.get_property(name, context)
else:
return self.cast(start_type)._get_property_value(name)
else:
try:
spec = start_type.find_single_property(name)
return self.cast(spec.murano_class).__properties[name]
if spec.usage == typespec.PropertyUsages.Static:
return spec.murano_class.get_property(name, context)
else:
return self.cast(spec.murano_class).__properties[name]
except exceptions.NoPropertyFound:
if derived:
return self.cast(caller_class)._get_property_value(name)
@ -199,11 +215,12 @@ class MuranoObject(dsl_types.MuranoObject):
declared_properties = start_type.find_properties(
lambda p: p.name == name)
if context is None:
context = self.object_store.executor.create_object_context(self)
context = self.executor.create_object_context(self)
if len(declared_properties) > 0:
declared_properties = self.type.find_properties(
lambda p: p.name == name)
values_to_assign = []
classes_for_static_properties = []
for spec in declared_properties:
if (caller_class is not None and not
helpers.are_property_modifications_allowed(context) and
@ -211,16 +228,21 @@ class MuranoObject(dsl_types.MuranoObject):
not derived)):
raise exceptions.NoWriteAccessError(name)
default = self.__config.get(name, spec.default)
default = self.__defaults.get(name, default)
default = helpers.evaluate(default, context)
if spec.usage == typespec.PropertyUsages.Static:
classes_for_static_properties.append(spec.murano_class)
else:
default = self.__config.get(name, spec.default)
default = self.__defaults.get(name, default)
default = helpers.evaluate(default, context)
obj = self.cast(spec.murano_class)
values_to_assign.append((obj, spec.validate(
value, self.real_this,
self.real_this, default=default)))
obj = self.cast(spec.murano_class)
values_to_assign.append((obj, spec.validate(
value, self.real_this,
self.real_this, context, default=default)))
for obj, value in values_to_assign:
obj.__properties[name] = value
for cls in classes_for_static_properties:
cls.set_property(name, value, context)
elif derived:
obj = self.cast(caller_class)
obj.__properties[name] = value

View File

@ -12,26 +12,42 @@
# License for the specific language governing permissions and limitations
# under the License.
import re
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*|=)$')
class NamespaceResolver(object):
def __init__(self, namespaces):
self._namespaces = namespaces
for prefix, ns in namespaces.items():
if PREFIX_RE.match(prefix) is None:
raise ValueError(
'Invalid namespace prefix "{0}"'.format(prefix))
if NS_RE.match(ns) is None:
raise ValueError('Invalid namespace "{0}"'.format(ns))
self._namespaces = namespaces.copy()
self._namespaces.setdefault('=', '')
self._namespaces[''] = ''
def resolve_name(self, name, relative=None):
if name is None:
raise ValueError()
if name and name.startswith(':'):
return name[1:]
if ':' in name:
def resolve_name(self, name):
if name is None or TYPE_NAME_RE.match(name) is None:
raise ValueError('Invalid type name "{0}"'.format(name))
if ':' not in name:
if '.' in name:
parts = ['', name]
else:
parts = ['=', name]
else:
parts = name.split(':')
if len(parts) != 2 or not parts[1]:
raise NameError('Incorrectly formatted name ' + name)
if parts[0] not in self._namespaces:
raise KeyError('Unknown namespace prefix ' + parts[0])
return '.'.join((self._namespaces[parts[0]], parts[1]))
if not relative and '=' in self._namespaces and '.' not in name:
return '.'.join((self._namespaces['='], name))
if relative and '.' not in name:
return '.'.join((relative, name))
return name
if not parts[0]:
parts[0] = '='
if parts[0] not in self._namespaces:
raise KeyError('Unknown namespace prefix ' + parts[0])
ns = self._namespaces[parts[0]]
if not ns:
return name
return '.'.join((ns, parts[1]))

View File

@ -81,7 +81,7 @@ class ObjectStore(object):
return factory
else:
factory = class_obj.new(
owner, self,
owner, self, self.executor,
name=system_key.get('name'),
object_id=object_id, defaults=defaults)
self._store[object_id] = factory

View File

@ -157,6 +157,12 @@ def argument_owner(method_argument):
return method_argument.murano_method
@specs.yaql_property(dsl_types.MuranoClass)
@specs.name('type')
def type_to_type_ref(murano_class):
return murano_class.get_reference()
def register(context):
funcs = (
class_name, methods, properties, ancestors, package, class_version,
@ -164,7 +170,8 @@ def register(context):
property_usage,
method_name, arguments, method_owner,
types, package_name, package_version,
argument_name, argument_has_default, argument_owner
argument_name, argument_has_default, argument_owner,
type_to_type_ref
)
for f in funcs:
context.register_function(f)

View File

@ -14,7 +14,9 @@
import weakref
from murano.dsl import dsl_types
from murano.dsl import exceptions
from murano.dsl import helpers
from murano.dsl import type_scheme
@ -25,8 +27,9 @@ class PropertyUsages(object):
Runtime = 'Runtime'
Const = 'Const'
Config = 'Config'
All = set([In, Out, InOut, Runtime, Const, Config])
Writable = set([Out, InOut, Runtime])
Static = 'Static'
All = set([In, Out, InOut, Runtime, Const, Config, Static])
Writable = set([Out, InOut, Runtime, Static])
class Spec(object):
@ -41,13 +44,19 @@ class Spec(object):
'Unknown type {0}. Must be one of ({1})'.format(
self._usage, ', '.join(PropertyUsages.All)))
def validate(self, value, this, owner, default=None):
def validate(self, value, this, owner, context, default=None):
if default is None:
default = self.default
return self._contract(
value, this.object_store.executor.create_object_context(
this.cast(self._container_class())),
this, owner, default)
executor = helpers.get_executor(context)
if isinstance(this, dsl_types.MuranoClass):
return self._contract(
value, executor.create_object_context(this),
None, None, default)
else:
return self._contract(
value, executor.create_object_context(
this.cast(self._container_class())),
this, owner, default)
@property
def default(self):

View File

@ -68,7 +68,7 @@ class YaqlExpression(dsl_types.YaqlExpression):
def is_expression(expression, version):
if not isinstance(expression, six.string_types):
return False
if re.match('^[\s\w\d.:]*$', expression):
if re.match('^[\s\w\d.]*$', expression):
return False
try:
yaql_integration.parse(expression, version)

View File

@ -46,12 +46,14 @@ def cast(context, object_, type__, version_spec=None):
def new(__context, __type_name, __owner=None, __object_name=None, __extra=None,
**parameters):
object_store = helpers.get_object_store(__context)
executor = helpers.get_executor(__context)
new_context = __context.create_child_context()
for key, value in six.iteritems(parameters):
if utils.is_keyword(key):
new_context[key] = value
return __type_name.murano_class.new(
__owner, object_store, name=__object_name)(new_context, **parameters)
__owner, object_store, executor, name=__object_name)(
new_context, **parameters)
@specs.parameter('type_name', dsl.MuranoTypeName())
@ -110,14 +112,32 @@ def sleep_(seconds):
@specs.parameter('object_', dsl.MuranoObjectParameterType(nullable=True))
def type_(object_):
return None if object_ is None else object_.type.get_reference()
@specs.name('type')
@specs.parameter('object_', dsl.MuranoObjectParameterType(nullable=True))
def type_legacy(object_):
return None if object_ is None else object_.type.name
@specs.name('type')
@specs.parameter('cls', dsl.MuranoTypeName())
def type_from_name(cls):
return cls
@specs.parameter('object_', dsl.MuranoObjectParameterType(nullable=True))
def typeinfo(object_):
return None if object_ is None else object_.type
@specs.parameter('cls', dsl.MuranoTypeName())
@specs.name('typeinfo')
def typeinfo_for_class(cls):
return cls.murano_class
@specs.parameter('object_', dsl.MuranoObjectParameterType(nullable=True))
def name(object_):
return None if object_ is None else object_.name
@ -130,6 +150,13 @@ def obj_attribution(context, obj, property_name):
return obj.get_property(property_name, context)
@specs.parameter('cls', dsl_types.MuranoTypeReference)
@specs.parameter('property_name', yaqltypes.Keyword())
@specs.name('#operator_.')
def obj_attribution_static(context, cls, property_name):
return cls.murano_class.get_property(property_name, context)
@specs.parameter('receiver', dsl.MuranoObjectParameterType())
@specs.parameter('expr', yaqltypes.Lambda(method=True))
@specs.inject('operator', yaqltypes.Super(with_context=True))
@ -144,14 +171,32 @@ def op_dot(context, receiver, expr, operator):
return operator(ctx2, receiver, expr)
@specs.parameter('sender', dsl_types.MuranoTypeReference)
@specs.parameter('expr', yaqltypes.Lambda(method=True))
@specs.inject('operator', yaqltypes.Super(with_context=True))
@specs.name('#operator_.')
def op_dot_static(context, sender, expr, operator):
executor = helpers.get_executor(context)
type_context = executor.context_manager.create_class_context(
sender.murano_class)
ctx2 = helpers.link_contexts(context, type_context)
return operator(ctx2, None, expr)
@specs.parameter('prefix', yaqltypes.Keyword())
@specs.parameter('name', yaqltypes.Keyword())
@specs.name('#operator_:')
def ns_resolve(context, prefix, name):
murano_type = helpers.get_type(context)
return dsl_types.MuranoTypeReference(helpers.get_class(
return helpers.get_class(
murano_type.namespace_resolver.resolve_name(
prefix + ':' + name), context))
prefix + ':' + name), context).get_reference()
@specs.parameter('name', yaqltypes.Keyword())
@specs.name('#unary_operator_:')
def ns_resolve_unary(context, name):
return ns_resolve(context, '', name)
@specs.parameter('obj', dsl.MuranoObjectParameterType(nullable=True))
@ -173,20 +218,28 @@ def register(context, runtime_version):
context.register_function(require)
context.register_function(find)
context.register_function(sleep_)
context.register_function(type_)
context.register_function(typeinfo)
context.register_function(typeinfo_for_class)
context.register_function(name)
context.register_function(obj_attribution)
context.register_function(obj_attribution_static)
context.register_function(op_dot)
context.register_function(op_dot_static)
context.register_function(ns_resolve)
context.register_function(ns_resolve_unary)
reflection.register(context)
context.register_function(is_instance_of)
if runtime_version <= constants.RUNTIME_VERSION_1_3:
context.register_function(type_legacy)
else:
context.register_function(type_)
if runtime_version <= constants.RUNTIME_VERSION_1_1:
context2 = context.create_child_context()
context = context.create_child_context()
for t in ('id', 'cast', 'super', 'psuper', 'type'):
for spec in utils.to_extension_method(t, context):
context2.register_function(spec)
return context2
context.register_function(spec)
context.register_function(type_from_name)
return context

View File

@ -51,7 +51,10 @@ def _add_operators(engine_factory):
engine_factory.insert_operator(
'>', True, 'is', factory.OperatorType.BINARY_LEFT_ASSOCIATIVE, False)
engine_factory.insert_operator(
'.', True, ':', factory.OperatorType.BINARY_LEFT_ASSOCIATIVE, True)
None, True, ':', factory.OperatorType.BINARY_LEFT_ASSOCIATIVE, True)
engine_factory.insert_operator(
':', True, ':', factory.OperatorType.PREFIX_UNARY, False)
engine_factory.operators.insert(0, ())
def _create_engine(runtime_version):
@ -86,7 +89,7 @@ class ContractedValue(yaqltypes.GenericType):
lambda value, sender, context, *args, **kwargs:
self._value_spec.validate(
value, sender.real_this,
context[constants.CTX_ARGUMENT_OWNER]))
context[constants.CTX_ARGUMENT_OWNER], context))
def convert(self, value, *args, **kwargs):
if value is None:
@ -220,8 +223,9 @@ def _build_mpl_wrapper_function_definition(murano_method):
fd.set_parameter(specs.ParameterDefinition(
'__context', yaqltypes.Context(), 0))
fd.set_parameter(specs.ParameterDefinition(
'__sender', yaqltypes.PythonType(dsl_types.MuranoObject, False), 1))
nullable = murano_method.usage == 'Static'
sender_type = yaqltypes.PythonType(dsl_types.MuranoObject, nullable)
fd.set_parameter(specs.ParameterDefinition('__sender', sender_type, 1))
fd.meta[constants.META_MURANO_METHOD] = murano_method
return fd

View File

@ -80,7 +80,9 @@ class TestPackageLoader(package_loader.MuranoPackageLoader):
def _build_index(self, directory):
yamls = [os.path.join(dirpath, f)
for dirpath, _, files in os.walk(directory)
for f in fnmatch.filter(files, '*.yaml')]
for f in fnmatch.filter(files, '*.yaml')
if f != 'manifest.yaml'
]
for class_def_file in yamls:
self._load_class(class_def_file)

View File

@ -0,0 +1,192 @@
Namespaces:
ns: test
=: test
Name: TestStatics
Extends: TestStaticsBase
Properties:
staticProperty:
Contract: $.string()
Usage: Static
Default: xxx
conflictingStaticProperty:
Contract: $.string()
Default: 'conflictingStaticProperty-child'
Usage: Static
instanceProperty:
Contract: $.string()
Default: instanceProperty
staticProperty2:
Contract: $.string()
Default: staticProperty
Usage: Static
Methods:
testStaticTest:
Usage: Static
Body:
Return: $
testCallStaticMethodOnObject:
Body:
Return: $.simpleStaticMethod()
testCallStaticMethodOnClassName:
Body:
Return: :TestStatics.simpleStaticMethod()
testCallStaticMethodOnClassNameWithNs:
Body:
Return: ns:TestStatics.simpleStaticMethod()
testCallStaticMethodFromAnotherMethod:
Body:
Return: ns:TestStatics.simpleStaticMethod2()
testStaticThis:
Body:
Return: $.returnStaticThis()
testNoAccessToInstanceProperties:
Body:
Return: $.accessInstanceProperty()
testAccessStaticPropertyFromInstanceMethod:
Body:
Return: $.staticProperty
testAccessStaticPropertyFromStaticMethod:
Body:
Return: $.accessStaticProperty()
simpleStaticMethod:
Usage: Static
Body:
Return: 123
simpleStaticMethod2:
Usage: Static
Body:
Return: $.simpleStaticMethod() +
$this.simpleStaticMethod() +
ns:TestStatics.simpleStaticMethod() +
:TestStatics.simpleStaticMethod() +
type('test.TestStatics').simpleStaticMethod()
returnStaticThis:
Usage: Static
Body:
Return: $
accessInstanceProperty:
Usage: Static
Body:
Return: $.instanceProperty
accessStaticProperty:
Usage: Static
Body:
Return: $.staticProperty
testModifyStaticPropertyUsingDollar:
Body:
Return: $.modifyStaticPropertyUsingDollar()
modifyStaticPropertyUsingDollar:
Usage: Static
Body:
- $.staticProperty: qq
- Return: $.staticProperty
testModifyStaticPropertyUsingThis:
Body:
Return: $.modifyStaticPropertyUsingThis()
modifyStaticPropertyUsingThis:
Usage: Static
Body:
- $this.staticProperty: qq
- Return: $this.staticProperty
testModifyStaticPropertyUsingClassName:
Body:
Return: $.modifyStaticPropertyUsingClassName()
modifyStaticPropertyUsingClassName:
Usage: Static
Body:
- :TestStatics.staticProperty: qq
- Return: :TestStatics.staticProperty
testModifyStaticPropertyUsingNsClassName:
Body:
Return: $.modifyStaticPropertyUsingNsClassName()
modifyStaticPropertyUsingNsClassName:
Usage: Static
Body:
- ns:TestStatics.staticProperty: qq
- Return: ns:TestStatics.staticProperty
testModifyStaticPropertyUsingTypeFunc:
Body:
Return: $.modifyStaticPropertyUsingTypeFunc()
modifyStaticPropertyUsingTypeFunc:
Usage: Static
Body:
- type('test.TestStatics').staticProperty: qq
- Return: type('test.TestStatics').staticProperty
testPropertyIsStatic:
Body:
Return: $.modifyStaticPropertyOnInstance()
modifyStaticPropertyOnInstance:
Usage: Static
Body:
- $obj1: new(TestStatics)
- $obj2: new(TestStatics)
- $obj1.modifyStaticPropertyUsingClassName()
- Return: $obj2.staticProperty
testStaticPropertisNotLoaded:
Body:
Return: $.staticProperty2
testTypeIsSingleton:
Body:
- $t11: :TestStatics
- $t12: :TestStatics
- $t21: ns:TestStatics
- $t22: ns:TestStatics
- $t31: type('test.TestStatics')
- $t32: type('test.TestStatics')
- Return: $t11 = $t12 and $t21 = $t22 and $t31 = $t32
testStaticPropertyInheritance:
Body:
Return: $.baseStaticProperty +
:TestStaticsBase.baseStaticProperty +
:TestStatics.baseStaticProperty
testStaticPropertyOverride:
Body:
Return:
- $.conflictingStaticProperty
- :TestStatics.conflictingStaticProperty
- :TestStaticsBase.conflictingStaticProperty
- type('test.TestStatics').conflictingStaticProperty
- type('test.TestStaticsBase').conflictingStaticProperty
testTypeinfoOfType:
Body:
- $typeObj: type('test.TestStatics')
- $typeInfoOfType: typeinfo($typeObj)
- $obj: new('TestStatics')
- Return: typeinfo($obj) = $typeInfoOfType

View File

@ -0,0 +1,15 @@
Namespaces:
=: test
Name: TestStaticsBase
Properties:
baseStaticProperty:
Contract: $.string()
Default: baseStaticProperty
Usage: Static
conflictingStaticProperty:
Contract: $.string()
Default: 'conflictingStaticProperty-base'
Usage: Static

View File

@ -0,0 +1,106 @@
# 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.dsl import dsl_types
from murano.dsl import exceptions
from murano.tests.unit.dsl.foundation import object_model as om
from murano.tests.unit.dsl.foundation import test_case
class TestStatics(test_case.DslTestCase):
def setUp(self):
super(TestStatics, self).setUp()
self._runner = self.new_runner(
om.Object('test.TestStatics', staticProperty2='INVALID'))
def test_call_static_method_on_object(self):
self.assertEqual(123, self._runner.testCallStaticMethodOnObject())
def test_call_static_method_on_class_name(self):
self.assertEqual(123, self._runner.testCallStaticMethodOnClassName())
def test_call_static_method_on_class_name_with_ns(self):
self.assertEqual(
123, self._runner.testCallStaticMethodOnClassNameWithNs())
def test_call_static_method_from_another_method(self):
self.assertEqual(
123 * 5, self._runner.testCallStaticMethodFromAnotherMethod())
def test_static_this(self):
self.assertIsInstance(
self._runner.testStaticThis(), dsl_types.MuranoTypeReference)
def test_no_access_to_instance_properties(self):
self.assertRaises(
exceptions.NoPropertyFound,
self._runner.testNoAccessToInstanceProperties)
def test_access_static_property_from_instance_method(self):
self.assertEqual(
'xxx', self._runner.testAccessStaticPropertyFromInstanceMethod())
def test_access_static_property_from_static_method(self):
self.assertEqual(
'xxx', self._runner.testAccessStaticPropertyFromStaticMethod())
def test_modify_static_property_using_dollar(self):
self.assertEqual(
'qq', self._runner.testModifyStaticPropertyUsingDollar())
def test_modify_static_property_using_this(self):
self.assertEqual(
'qq', self._runner.testModifyStaticPropertyUsingThis())
def test_modify_static_property_using_class_name(self):
self.assertEqual(
'qq', self._runner.testModifyStaticPropertyUsingClassName())
def test_modify_static_property_using_ns_class_name(self):
self.assertEqual(
'qq', self._runner.testModifyStaticPropertyUsingNsClassName())
def test_modify_static_property_using_type_func(self):
self.assertEqual(
'qq', self._runner.testModifyStaticPropertyUsingTypeFunc())
def test_property_is_static(self):
self.assertEqual('qq', self._runner.testPropertyIsStatic())
def test_static_properties_excluded_from_object_model(self):
self.assertEqual(
'staticProperty',
self._runner.testStaticPropertisNotLoaded())
def test_type_is_singleton(self):
self.assertTrue(self._runner.testTypeIsSingleton())
def test_static_property_inheritance(self):
self.assertEqual(
'baseStaticProperty' * 3,
self._runner.testStaticPropertyInheritance())
def test_static_property_override(self):
self.assertEqual(
[
'conflictingStaticProperty-child',
'conflictingStaticProperty-child',
'conflictingStaticProperty-base',
'conflictingStaticProperty-child',
'conflictingStaticProperty-base'
], self._runner.testStaticPropertyOverride())
def test_type_info_of_type(self):
self.assertTrue(self._runner.testTypeinfoOfType())

View File

@ -50,23 +50,24 @@ class TestNamespaceResolving(base.MuranoTestCase):
resolver = ns_resolver.NamespaceResolver({'=': 'com.example.murano'})
name = 'sys:'
self.assertRaises(NameError, resolver.resolve_name, name)
self.assertRaises(ValueError, resolver.resolve_name, name)
def test_fails_w_excessive_prefix(self):
ns = {'sys': 'com.example.murano.system'}
resolver = ns_resolver.NamespaceResolver(ns)
invalid_name = 'sys:excessive_ns:muranoResource'
self.assertRaises(NameError, resolver.resolve_name, invalid_name)
self.assertRaises(ValueError, resolver.resolve_name, invalid_name)
def test_cuts_empty_prefix(self):
def test_empty_prefix_is_default(self):
resolver = ns_resolver.NamespaceResolver({'=': 'com.example.murano'})
# name without prefix delimiter
name = 'some.arbitrary.name'
resolved_name = resolver.resolve_name(':' + name)
self.assertEqual(name, resolved_name)
self.assertEqual(
'com.example.murano.some.arbitrary.name', resolved_name)
def test_resolves_specified_ns_prefix(self):
ns = {'sys': 'com.example.murano.system'}
@ -85,20 +86,6 @@ class TestNamespaceResolving(base.MuranoTestCase):
self.assertEqual(full_name, resolved_name)
def test_resolves_explicit_base(self):
resolver = ns_resolver.NamespaceResolver({'=': 'com.example.murano'})
resolved_name = resolver.resolve_name('Resource', relative='com.base')
self.assertEqual('com.base.Resource', resolved_name)
def test_resolves_explicit_base_w_empty_namespaces(self):
resolver = ns_resolver.NamespaceResolver({})
resolved_name = resolver.resolve_name('File', 'com.base')
self.assertEqual('com.base.File', resolved_name)
def test_resolves_w_empty_namespaces(self):
resolver = ns_resolver.NamespaceResolver({})

View File

@ -0,0 +1,9 @@
---
features:
- "Static methods and properties were introduced.
Both properties and methods can be marked as Usage: Static
Statics can be accessed using ns:Class.property / ns:Class.method(),
:Class.property / :Class.method() to access class from current namespace
or type('full.name').property / type('full.name').method() to use full
type name."
- io.murano.configuration.Linux methods are now static