Support for static methods/properties

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.

In static method $ / $this are referencing current class rather than object.
Static properties are not loaded from object model.

Also methods of io.murano.configuration.Linux class are now static.
Since static methods can be called on the instance it doesn't break
backward compatibility.

Implements blueprint: muranopl-statics
Change-Id: Ic7c6beed9222f4bca118877a60fdabfdd9d65e5a
This commit is contained in:
Stan Lagun 2016-01-25 02:52:15 +03:00
parent f4ff2fb482
commit 94c904e1cb
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