Merge "Improve method resolution rules for multiple inheritance"
This commit is contained in:
commit
ab9d7d7077
@ -23,7 +23,6 @@ from murano.common import config
|
|||||||
from murano.common.helpers import token_sanitizer
|
from murano.common.helpers import token_sanitizer
|
||||||
from murano.common import rpc
|
from murano.common import rpc
|
||||||
from murano.dsl import executor
|
from murano.dsl import executor
|
||||||
from murano.dsl import murano_method
|
|
||||||
from murano.dsl import results_serializer
|
from murano.dsl import results_serializer
|
||||||
from murano.engine import environment
|
from murano.engine import environment
|
||||||
from murano.engine import package_class_loader
|
from murano.engine import package_class_loader
|
||||||
@ -119,6 +118,7 @@ class TaskExecutor(object):
|
|||||||
if self.action:
|
if self.action:
|
||||||
self._invoke(exc)
|
self._invoke(exc)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
LOG.warn(e, exc_info=1)
|
||||||
reporter = status_reporter.StatusReporter()
|
reporter = status_reporter.StatusReporter()
|
||||||
reporter.initialize(obj)
|
reporter.initialize(obj)
|
||||||
reporter.report_error(obj, str(e))
|
reporter.report_error(obj, str(e))
|
||||||
@ -130,18 +130,4 @@ class TaskExecutor(object):
|
|||||||
method_name, args = self.action['method'], self.action['args']
|
method_name, args = self.action['method'], self.action['args']
|
||||||
|
|
||||||
if obj is not None:
|
if obj is not None:
|
||||||
if self._is_action(obj, method_name) is False:
|
|
||||||
raise Exception('%s is not an action' % (method_name,))
|
|
||||||
|
|
||||||
obj.type.invoke(method_name, mpl_executor, obj, args)
|
obj.type.invoke(method_name, mpl_executor, obj, args)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _is_action(obj, method_name):
|
|
||||||
implementations = obj.type.find_method(method_name)
|
|
||||||
if len(implementations) < 1:
|
|
||||||
raise Exception('Action %s is not found' % (method_name,))
|
|
||||||
if len(implementations) > 1:
|
|
||||||
raise Exception('Action %s name is ambiguous' % (method_name,))
|
|
||||||
declaring_class, _ = implementations[0]
|
|
||||||
method = declaring_class.get_method(method_name)
|
|
||||||
return method.usage == murano_method.MethodUsages.Action
|
|
||||||
|
@ -13,7 +13,6 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import collections
|
import collections
|
||||||
import functools
|
|
||||||
import inspect
|
import inspect
|
||||||
import types
|
import types
|
||||||
import uuid
|
import uuid
|
||||||
@ -23,9 +22,9 @@ import eventlet.event
|
|||||||
import yaql.context
|
import yaql.context
|
||||||
|
|
||||||
import murano.dsl.attribute_store as attribute_store
|
import murano.dsl.attribute_store as attribute_store
|
||||||
import murano.dsl.exceptions as exceptions
|
|
||||||
import murano.dsl.expressions as expressions
|
import murano.dsl.expressions as expressions
|
||||||
import murano.dsl.helpers as helpers
|
import murano.dsl.helpers as helpers
|
||||||
|
import murano.dsl.murano_method as murano_method
|
||||||
import murano.dsl.murano_object as murano_object
|
import murano.dsl.murano_object as murano_object
|
||||||
import murano.dsl.object_store as object_store
|
import murano.dsl.object_store as object_store
|
||||||
import murano.dsl.yaql_functions as yaql_functions
|
import murano.dsl.yaql_functions as yaql_functions
|
||||||
@ -71,44 +70,39 @@ class MuranoDslExecutor(object):
|
|||||||
raise ValueError()
|
raise ValueError()
|
||||||
|
|
||||||
def invoke_method(self, name, this, context, murano_class, *args):
|
def invoke_method(self, name, this, context, murano_class, *args):
|
||||||
|
external_call = False
|
||||||
if context is None:
|
if context is None:
|
||||||
|
external_call = True
|
||||||
context = self._root_context
|
context = self._root_context
|
||||||
implementations = this.type.find_method(name)
|
method = this.type.find_single_method(name)
|
||||||
|
|
||||||
|
is_special_method = name in ('initialize', 'destroy')
|
||||||
|
|
||||||
|
if external_call and not is_special_method and \
|
||||||
|
method.usage != murano_method.MethodUsages.Action:
|
||||||
|
raise Exception('{0} is not an action'.format(method.name))
|
||||||
|
# TODO (slagun): check method accessibility from murano_class
|
||||||
|
|
||||||
# restore this from upcast object (no change if there was no upcast)
|
# restore this from upcast object (no change if there was no upcast)
|
||||||
this = this.real_this
|
this = this.real_this
|
||||||
delegates = []
|
arguments_scheme = method.arguments_scheme
|
||||||
for declaring_class, name in implementations:
|
params = self._evaluate_parameters(
|
||||||
method = declaring_class.get_method(name)
|
arguments_scheme, context, this, *args)
|
||||||
if not method:
|
return self._invoke_method_implementation(
|
||||||
continue
|
method, this, context, params)
|
||||||
arguments_scheme = method.arguments_scheme
|
|
||||||
try:
|
|
||||||
params = self._evaluate_parameters(
|
|
||||||
arguments_scheme, context, this, *args)
|
|
||||||
delegates.append(functools.partial(
|
|
||||||
self._invoke_method_implementation,
|
|
||||||
method, this, declaring_class, context, params))
|
|
||||||
except TypeError:
|
|
||||||
continue
|
|
||||||
if len(delegates) < 1:
|
|
||||||
raise exceptions.NoMethodFound(name)
|
|
||||||
elif len(delegates) > 1:
|
|
||||||
raise exceptions.AmbiguousMethodName(name)
|
|
||||||
else:
|
|
||||||
return delegates[0]()
|
|
||||||
|
|
||||||
def _invoke_method_implementation(self, method, this, murano_class,
|
def _invoke_method_implementation(self, method, this, context, params):
|
||||||
context, params):
|
|
||||||
body = method.body
|
body = method.body
|
||||||
if not body:
|
if not body:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
murano_class = method.murano_class
|
||||||
current_thread = eventlet.greenthread.getcurrent()
|
current_thread = eventlet.greenthread.getcurrent()
|
||||||
if not hasattr(current_thread, '_murano_dsl_thread_marker'):
|
if not hasattr(current_thread, '_muranopl_thread_marker'):
|
||||||
thread_marker = current_thread._murano_dsl_thread_marker = \
|
thread_marker = current_thread._muranopl_thread_marker = \
|
||||||
uuid.uuid4().hex
|
uuid.uuid4().hex
|
||||||
else:
|
else:
|
||||||
thread_marker = current_thread._murano_dsl_thread_marker
|
thread_marker = current_thread._muranopl_thread_marker
|
||||||
|
|
||||||
method_id = id(body)
|
method_id = id(body)
|
||||||
this_id = this.object_id
|
this_id = this.object_id
|
||||||
@ -145,7 +139,7 @@ class MuranoDslExecutor(object):
|
|||||||
thread_marker=None):
|
thread_marker=None):
|
||||||
if thread_marker:
|
if thread_marker:
|
||||||
current_thread = eventlet.greenthread.getcurrent()
|
current_thread = eventlet.greenthread.getcurrent()
|
||||||
current_thread._murano_dsl_thread_marker = thread_marker
|
current_thread._muranopl_thread_marker = thread_marker
|
||||||
if callable(body):
|
if callable(body):
|
||||||
if '_context' in inspect.getargspec(body).args:
|
if '_context' in inspect.getargspec(body).args:
|
||||||
params['_context'] = self._create_context(
|
params['_context'] = self._create_context(
|
||||||
@ -253,10 +247,10 @@ class MuranoDslExecutor(object):
|
|||||||
try:
|
try:
|
||||||
self._object_store = gc_object_store
|
self._object_store = gc_object_store
|
||||||
for obj in objects_to_clean:
|
for obj in objects_to_clean:
|
||||||
methods = obj.type.find_method('destroy')
|
methods = obj.type.find_all_methods('destroy')
|
||||||
for cls, method in methods:
|
for method in methods:
|
||||||
try:
|
try:
|
||||||
cls.invoke(method, self, obj, {})
|
method.invoke(self, obj, {})
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
finally:
|
finally:
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
import collections
|
import collections
|
||||||
import inspect
|
import inspect
|
||||||
|
|
||||||
|
import murano.dsl.exceptions as exceptions
|
||||||
import murano.dsl.helpers as helpers
|
import murano.dsl.helpers as helpers
|
||||||
import murano.dsl.murano_method as murano_method
|
import murano.dsl.murano_method as murano_method
|
||||||
import murano.dsl.murano_object as murano_object
|
import murano.dsl.murano_object as murano_object
|
||||||
@ -90,15 +91,53 @@ class MuranoClass(object):
|
|||||||
def get_property(self, name):
|
def get_property(self, name):
|
||||||
return self._properties[name]
|
return self._properties[name]
|
||||||
|
|
||||||
def find_method(self, name):
|
def _find_method_chains(self, name):
|
||||||
|
initial = [self.methods[name]] if name in self.methods else []
|
||||||
|
yielded = False
|
||||||
|
for parent in self.parents:
|
||||||
|
for seq in parent._find_method_chains(name):
|
||||||
|
yield initial + list(seq)
|
||||||
|
yielded = True
|
||||||
|
if initial and not yielded:
|
||||||
|
yield initial
|
||||||
|
|
||||||
|
def find_single_method(self, name):
|
||||||
|
chains = sorted(self._find_method_chains(name), key=lambda t: len(t))
|
||||||
|
result = []
|
||||||
|
|
||||||
|
for i in range(len(chains)):
|
||||||
|
if chains[i][0] in result:
|
||||||
|
continue
|
||||||
|
add = True
|
||||||
|
for j in range(i + 1, len(chains)):
|
||||||
|
common = 0
|
||||||
|
if not add:
|
||||||
|
break
|
||||||
|
for p in range(len(chains[i])):
|
||||||
|
if chains[i][-p - 1] is chains[j][-p - 1]:
|
||||||
|
common += 1
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
if common == len(chains[i]):
|
||||||
|
add = False
|
||||||
|
break
|
||||||
|
if add:
|
||||||
|
result.append(chains[i][0])
|
||||||
|
if len(result) < 1:
|
||||||
|
raise exceptions.NoMethodFound(name)
|
||||||
|
elif len(result) > 1:
|
||||||
|
raise exceptions.AmbiguousMethodName(name)
|
||||||
|
return result[0]
|
||||||
|
|
||||||
|
def find_all_methods(self, name):
|
||||||
#resolved_name = self._namespace_resolver.resolve_name(name, self.name)
|
#resolved_name = self._namespace_resolver.resolve_name(name, self.name)
|
||||||
if name in self._methods:
|
if name in self._methods:
|
||||||
return [(self, name)]
|
return [self.methods[name]]
|
||||||
if not self._parents:
|
if not self._parents:
|
||||||
return []
|
return []
|
||||||
return list(set(reduce(
|
return list(reduce(
|
||||||
lambda x, y: x + y,
|
lambda x, y: x + [t for t in y if t not in x],
|
||||||
[p.find_method(name) for p in self._parents])))
|
[p.find_all_methods(name) for p in self._parents]))
|
||||||
|
|
||||||
def find_property(self, name):
|
def find_property(self, name):
|
||||||
types = collections.deque([self])
|
types = collections.deque([self])
|
||||||
@ -110,8 +149,10 @@ class MuranoClass(object):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def invoke(self, name, executor, this, parameters):
|
def invoke(self, name, executor, this, parameters):
|
||||||
|
if not self.is_compatible(this):
|
||||||
|
raise Exception("'this' must be of compatible type")
|
||||||
args = executor.to_yaql_args(parameters)
|
args = executor.to_yaql_args(parameters)
|
||||||
return executor.invoke_method(name, this, None, self, *args)
|
return executor.invoke_method(name, this.cast(self), None, self, *args)
|
||||||
|
|
||||||
def is_compatible(self, obj):
|
def is_compatible(self, obj):
|
||||||
if isinstance(obj, murano_object.MuranoObject):
|
if isinstance(obj, murano_object.MuranoObject):
|
||||||
|
@ -110,3 +110,6 @@ class MuranoMethod(object):
|
|||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return 'MuranoMethod({0})'.format(self.name)
|
return 'MuranoMethod({0})'.format(self.name)
|
||||||
|
|
||||||
|
def invoke(self, executor, this, parameters):
|
||||||
|
return self.murano_class.invoke(self.name, executor, this, parameters)
|
||||||
|
@ -156,7 +156,7 @@ class MuranoObject(object):
|
|||||||
raise AttributeError(key)
|
raise AttributeError(key)
|
||||||
|
|
||||||
def cast(self, type):
|
def cast(self, type):
|
||||||
if self.type == type:
|
if self.type is type:
|
||||||
return self
|
return self
|
||||||
for parent in self.__parents.values():
|
for parent in self.__parents.values():
|
||||||
try:
|
try:
|
||||||
|
@ -87,9 +87,9 @@ class ObjectStore(object):
|
|||||||
|
|
||||||
if not self.initializing:
|
if not self.initializing:
|
||||||
executor = helpers.get_executor(context)
|
executor = helpers.get_executor(context)
|
||||||
methods = obj.type.find_method('initialize')
|
methods = obj.type.find_all_methods('initialize')
|
||||||
for cls, method in methods:
|
for method in methods:
|
||||||
cls.invoke(method, executor, obj, {})
|
method.invoke(executor, obj, {})
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
Loading…
Reference in New Issue
Block a user