Merge "Improve method resolution rules for multiple inheritance"

This commit is contained in:
Jenkins 2014-06-11 19:18:40 +00:00 committed by Gerrit Code Review
commit ab9d7d7077
6 changed files with 81 additions and 57 deletions

View File

@ -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

View File

@ -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 = []
for declaring_class, name in implementations:
method = declaring_class.get_method(name)
if not method:
continue
arguments_scheme = method.arguments_scheme arguments_scheme = method.arguments_scheme
try:
params = self._evaluate_parameters( params = self._evaluate_parameters(
arguments_scheme, context, this, *args) arguments_scheme, context, this, *args)
delegates.append(functools.partial( return self._invoke_method_implementation(
self._invoke_method_implementation, method, this, context, params)
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:

View File

@ -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):

View File

@ -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)

View File

@ -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:

View File

@ -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