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:
parent
f4ff2fb482
commit
94c904e1cb
@ -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)
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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]))
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
192
murano/tests/unit/dsl/meta/TestStatics.yaml
Normal file
192
murano/tests/unit/dsl/meta/TestStatics.yaml
Normal 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
|
15
murano/tests/unit/dsl/meta/TestStaticsBase.yaml
Normal file
15
murano/tests/unit/dsl/meta/TestStaticsBase.yaml
Normal 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
|
106
murano/tests/unit/dsl/test_statics.py
Normal file
106
murano/tests/unit/dsl/test_statics.py
Normal 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())
|
@ -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({})
|
||||
|
||||
|
9
releasenotes/notes/statics-9943fe9873138dac.yaml
Normal file
9
releasenotes/notes/statics-9943fe9873138dac.yaml
Normal 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
|
Loading…
Reference in New Issue
Block a user