Allow static methods to detect calls on object

Static methods can be called both on on class instance
and on the type object. With this refactoring it is now
possible in Python static/class methods to distinguish
which way it was called and in case of instance call
get the instance.

This commit also reworks ResourceManager to use new
functionality. Now all ResourceManager methods
are static so no class instance is necessary. If resources
belonging to some other type are required it is possible
to provide the type object explicitly to each method.
However it is still possible to create ResourceManager
instance and call statics as a normal methods. In this
case they will recognize it and will operate that were
captured at class construction. Thus it is still possible
to create ResourceManager at one place and pass it to
another class in another package so that it will access
resources of the package where the instance was created
rather than that where it used.

Change-Id: Ib47be86a99eb7903f7d3f7e1c5f2570df819c2d8
This commit is contained in:
Stan Lagun 2016-02-22 19:50:38 +03:00
parent c8bd742478
commit 8fb4eb7ac2
6 changed files with 79 additions and 26 deletions

View File

@ -76,8 +76,10 @@ class ThisParameterType(yaqltypes.HiddenParameterType, yaqltypes.SmartType):
def convert(self, value, sender, context, function_spec, engine,
*args, **kwargs):
this = helpers.get_this(context)
executor = helpers.get_executor(context)
return MuranoObjectInterface(this, executor)
if isinstance(this, dsl_types.MuranoObject):
executor = helpers.get_executor(context)
return MuranoObjectInterface(this, executor)
return this
class InterfacesParameterType(yaqltypes.HiddenParameterType,

View File

@ -90,8 +90,12 @@ class MuranoDslExecutor(object):
murano_method.MethodUsages.Action):
raise Exception('{0} is not an action'.format(method.name))
context = self.create_method_context(
self.create_object_context(this, context), method)
if method.is_static:
obj_context = self.create_object_context(
method.murano_class, context)
else:
obj_context = self.create_object_context(this, context)
context = self.create_method_context(obj_context, method)
if isinstance(this, dsl_types.MuranoObject):
this = this.real_this
@ -109,10 +113,10 @@ class MuranoDslExecutor(object):
def call():
if isinstance(method.body, specs.FunctionDefinition):
if isinstance(this, dsl_types.MuranoClass):
native_this = utils.NO_VALUE
native_this = this.get_reference()
else:
native_this = this.cast(
method.murano_class).extension
native_this = dsl.MuranoObjectInterface(this.cast(
method.murano_class), self)
return method.body(
yaql_engine, context, native_this)(*args, **kwargs)
else:
@ -301,6 +305,7 @@ class MuranoDslExecutor(object):
else:
context = class_context.create_child_context()
type_ref = obj_type.get_reference()
context[constants.CTX_THIS] = type_ref
context['this'] = type_ref
context[''] = type_ref

View File

@ -125,12 +125,13 @@ class MuranoMethod(dsl_types.MuranoMethod):
def invoke(self, executor, this, args, kwargs, context=None,
skip_stub=False):
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 and not self.murano_class.is_compatible(this):
raise Exception("'this' must be of compatible type")
if not this and not self.is_static:
raise Exception("A class instance is required")
if this is not None:
this = this.cast(self.murano_class)
else:

View File

@ -159,7 +159,8 @@ def get_function_definition(func, murano_method, original_name):
fd.is_method = True
fd.is_function = False
if helpers.inspect_is_method(cls, original_name):
fd.set_parameter(0, yaqltypes.PythonType(cls), overwrite=True)
fd.set_parameter(
0, dsl.MuranoType(murano_method.murano_class), overwrite=True)
if helpers.inspect_is_classmethod(cls, original_name):
_remove_first_parameter(fd)
body = func
@ -168,12 +169,24 @@ def get_function_definition(func, murano_method, original_name):
fd.name = name
fd.insert_parameter(specs.ParameterDefinition(
'?1', yaqltypes.Context(), 0))
is_static = (helpers.inspect_is_static(cls, original_name) or
helpers.inspect_is_classmethod(cls, original_name))
if is_static:
fd.insert_parameter(specs.ParameterDefinition(
'?2', yaqltypes.PythonType(object), 1))
def payload(__context, *args, **kwargs):
def payload(__context, __self, *args, **kwargs):
with helpers.contextual(__context):
return body(__self.extension, *args, **kwargs)
def static_payload(__context, __receiver, *args, **kwargs):
with helpers.contextual(__context):
return body(*args, **kwargs)
fd.payload = payload
if is_static:
fd.payload = static_payload
else:
fd.payload = payload
fd.meta[constants.META_MURANO_METHOD] = murano_method
return fd
@ -252,7 +265,7 @@ def get_class_factory_definition(cls, murano_class):
engine = choose_yaql_engine(runtime_version)
def payload(__context, __receiver, *args, **kwargs):
assert __receiver is None
# assert __receiver is None
args = tuple(dsl.to_mutable(arg, engine) for arg in args)
kwargs = dsl.to_mutable(kwargs, engine)
with helpers.contextual(__context):

View File

@ -16,8 +16,11 @@
import json as jsonlib
import yaml as yamllib
from yaql.language import specs
from yaql.language import yaqltypes
from murano.dsl import dsl
from murano.dsl import dsl_types
from murano.dsl import helpers
if hasattr(yamllib, 'CSafeLoader'):
@ -46,13 +49,33 @@ class ResourceManager(object):
murano_class = helpers.get_type(helpers.get_caller_context(context))
self._package = murano_class.package
def string(self, name):
path = self._package.get_resource(name)
@staticmethod
@specs.parameter('owner', dsl.MuranoTypeName(nullable=True))
@specs.inject('receiver', yaqltypes.Receiver())
def string(receiver, name, owner=None):
path = ResourceManager._get_package(owner, receiver).get_resource(name)
with open(path) as file:
return file.read()
def json(self, name):
return jsonlib.loads(self.string(name))
@classmethod
@specs.parameter('owner', dsl.MuranoTypeName(nullable=True))
@specs.inject('receiver', yaqltypes.Receiver())
def json(cls, receiver, name, owner=None):
return jsonlib.loads(cls.string(receiver, name, owner))
def yaml(self, name):
return yamllib.load(self.string(name), Loader=yaml_loader)
@classmethod
@specs.parameter('owner', dsl.MuranoTypeName(nullable=True))
@specs.inject('receiver', yaqltypes.Receiver())
def yaml(cls, receiver, name, owner=None):
return yamllib.load(
cls.string(receiver, name, owner), Loader=yaml_loader)
@staticmethod
def _get_package(owner, receiver):
if owner is None:
if isinstance(receiver, dsl_types.MuranoObjectInterface):
return receiver.extension._package
murano_class = helpers.get_type(helpers.get_caller_context())
else:
murano_class = owner.murano_class
return murano_class.package

View File

@ -29,17 +29,24 @@ class TestStatics(test_case.DslTestCase):
class PythonClass(object):
@staticmethod
@specs.parameter('arg', yaqltypes.Integer())
def static_python_method(arg):
@specs.inject('receiver', yaqltypes.Receiver())
def static_python_method(arg, receiver):
if isinstance(receiver, dsl_types.MuranoObjectInterface):
return 3 * arg
return 7 * arg
@classmethod
def classmethod_python_method(cls, arg):
@specs.inject('receiver', yaqltypes.Receiver())
def classmethod_python_method(cls, arg, receiver):
if isinstance(receiver, dsl_types.MuranoObjectInterface):
return cls.__name__.upper() + str(arg)
return cls.__name__ + str(arg)
super(TestStatics, self).setUp()
self.package_loader.load_class_package(
'test.TestStatics', None).register_class(PythonClass)
self._runner = self.new_runner(
om.Object('test.TestStatics', staticProperty2='INVALID'))
self._runner.root.type.package.register_class(PythonClass)
def test_call_static_method_on_object(self):
self.assertEqual(123, self._runner.testCallStaticMethodOnObject())
@ -122,9 +129,11 @@ class TestStatics(test_case.DslTestCase):
self.assertTrue(self._runner.testTypeinfoOfType())
def test_call_python_static_method(self):
self.assertEqual([777] * 4, self._runner.testCallPythonStaticMethod())
self.assertEqual(
[333] + [777] * 3,
self._runner.testCallPythonStaticMethod())
def test_call_python_classmethod(self):
self.assertEqual(
['PythonClass!'] * 4,
['PYTHONCLASS!'] + ['PythonClass!'] * 3,
self._runner.testCallPythonClassMethod())