Some further cleanup in the meta branch

* adds squashing to objects, which allows them to be squashed down
  to a final "view" before post_validate to avoid expensive evaluations
  of parent attributes
This commit is contained in:
James Cammarata 2016-08-25 11:34:08 -05:00
parent 96e2be9bf8
commit cddf1cf98e
5 changed files with 76 additions and 96 deletions

View file

@ -72,6 +72,8 @@ class TaskExecutor:
self._connection = None
self._rslt_q = rslt_q
self._task.squash()
def run(self):
'''
The main executor entrypoint, where we determine if the specified

View file

@ -24,7 +24,7 @@ import itertools
import operator
import uuid
from copy import deepcopy
from copy import copy as shallowcopy, deepcopy
from functools import partial
from inspect import getmembers
@ -53,8 +53,10 @@ def _generic_g(prop_name, self):
raise AttributeError("'%s' object has no attribute '%s'" % (self.__class__.__name__, prop_name))
def _generic_g_method(prop_name, self):
method = "_get_attr_%s" % prop_name
try:
if self._squashed:
return self._attributes[prop_name]
method = "_get_attr_%s" % prop_name
return getattr(self, method)()
except KeyError:
raise AttributeError("'%s' object has no attribute '%s'" % (self.__class__.__name__, prop_name))
@ -62,7 +64,7 @@ def _generic_g_method(prop_name, self):
def _generic_g_parent(prop_name, self):
try:
value = self._attributes[prop_name]
if value is None and not self._finalized:
if value is None and not self._squashed and not self._finalized:
try:
value = self._get_parent_attribute(prop_name)
except AttributeError:
@ -106,10 +108,13 @@ class BaseMeta(type):
# its value from a parent object
method = "_get_attr_%s" % attr_name
if method in src_dict or method in dst_dict:
#print("^ assigning generic_g_method to %s" % attr_name)
getter = partial(_generic_g_method, attr_name)
elif ('_get_parent_attribute' in src_dict or '_get_parent_attribute' in dst_dict) and value.inherit:
elif '_get_parent_attribute' in dst_dict and value.inherit:
#print("^ assigning generic_g_parent to %s" % attr_name)
getter = partial(_generic_g_parent, attr_name)
else:
#print("^ assigning generic_g to %s" % attr_name)
getter = partial(_generic_g, attr_name)
setter = partial(_generic_s, attr_name)
@ -135,6 +140,7 @@ class BaseMeta(type):
# now create the attributes based on the FieldAttributes
# available, including from parent (and grandparent) objects
#print("creating class %s" % name)
_create_attrs(dct, dct)
_process_parents(parents, dct)
@ -148,7 +154,7 @@ class Base(with_metaclass(BaseMeta, object)):
_remote_user = FieldAttribute(isa='string')
# variables
_vars = FieldAttribute(isa='dict', priority=100)
_vars = FieldAttribute(isa='dict', priority=100, inherit=False)
# flags and misc. settings
_environment = FieldAttribute(isa='list')
@ -173,6 +179,7 @@ class Base(with_metaclass(BaseMeta, object)):
# other internal params
self._validated = False
self._squashed = False
self._finalized = False
# every object gets a random uuid:
@ -297,6 +304,22 @@ class Base(with_metaclass(BaseMeta, object)):
self._validated = True
def squash(self):
'''
Evaluates all attributes and sets them to the evaluated version,
so that all future accesses of attributes do not need to evaluate
parent attributes.
'''
if not self._squashed:
for name in self._valid_attrs.keys():
getter = partial(_generic_g, name)
setter = partial(_generic_s, name)
deleter = partial(_generic_d, name)
self._attributes[name] = getattr(self, name)
#print("squashed attr %s: %s" % (name, self._attributes[name]))
self._squashed = True
def copy(self):
'''
Create a copy of this object and return it.
@ -305,13 +328,7 @@ class Base(with_metaclass(BaseMeta, object)):
new_me = self.__class__()
for name in self._valid_attrs.keys():
attr_val = getattr(self, name)
if isinstance(attr_val, collections.Sequence):
setattr(new_me, name, attr_val[:])
elif isinstance(attr_val, collections.Mapping):
setattr(new_me, name, attr_val.copy())
else:
setattr(new_me, name, attr_val)
new_me._attributes[name] = shallowcopy(self._attributes[name])
new_me._loader = self._loader
new_me._variable_manager = self._variable_manager

View file

@ -102,29 +102,3 @@ class Become:
if become_user is None:
become_user = C.DEFAULT_BECOME_USER
def _get_attr_become(self):
'''
Override for the 'become' getattr fetcher, used from Base.
'''
if hasattr(self, '_get_parent_attribute'):
return self._get_parent_attribute('become')
else:
return self._attributes['become']
def _get_attr_become_method(self):
'''
Override for the 'become_method' getattr fetcher, used from Base.
'''
if hasattr(self, '_get_parent_attribute'):
return self._get_parent_attribute('become_method')
else:
return self._attributes['become_method']
def _get_attr_become_user(self):
'''
Override for the 'become_user' getattr fetcher, used from Base.
'''
if hasattr(self, '_get_parent_attribute'):
return self._get_parent_attribute('become_user')
else:
return self._attributes['become_user']

View file

@ -278,6 +278,9 @@ class Block(Base, Become, Conditional, Taggable):
for dep in dep_chain:
dep.set_loader(loader)
def _get_attr_environment(self):
return self._get_parent_attribute('environment', extend=True)
def _get_parent_attribute(self, attr, extend=False):
'''
Generic logic to get the attribute or parent attribute for a block value.
@ -288,50 +291,50 @@ class Block(Base, Become, Conditional, Taggable):
value = self._attributes[attr]
if self._parent and (value is None or extend):
parent_value = getattr(self._parent, attr, None)
if extend:
value = self._extend_value(value, parent_value)
else:
value = parent_value
if self._role and (value is None or extend) and hasattr(self._role, attr):
parent_value = getattr(self._role, attr, None)
if extend:
value = self._extend_value(value, parent_value)
else:
value = parent_value
try:
parent_value = getattr(self._parent, attr, None)
if extend:
value = self._extend_value(value, parent_value)
else:
value = parent_value
except AttributeError:
pass
if self._role and (value is None or extend):
try:
parent_value = getattr(self._role, attr, None)
if extend:
value = self._extend_value(value, parent_value)
else:
value = parent_value
dep_chain = self.get_dep_chain()
if dep_chain and (value is None or extend):
dep_chain.reverse()
for dep in dep_chain:
dep_value = getattr(dep, attr, None)
if extend:
value = self._extend_value(value, dep_value)
else:
value = dep_value
dep_chain = self.get_dep_chain()
if dep_chain and (value is None or extend):
dep_chain.reverse()
for dep in dep_chain:
dep_value = getattr(dep, attr, None)
if extend:
value = self._extend_value(value, dep_value)
else:
value = dep_value
if value is not None and not extend:
break
if self._play and (value is None or extend) and hasattr(self._play, attr):
parent_value = getattr(self._play, attr, None)
if extend:
value = self._extend_value(value, parent_value)
else:
value = parent_value
if value is not None and not extend:
break
except AttributeError:
pass
if self._play and (value is None or extend):
try:
parent_value = getattr(self._play, attr, None)
if extend:
value = self._extend_value(value, parent_value)
else:
value = parent_value
except AttributeError:
pass
except KeyError as e:
pass
return value
def _get_attr_environment(self):
return self._get_parent_attribute('environment')
def _get_attr_any_errors_fatal(self):
'''
Override for the 'tags' getattr fetcher, used from Base.
'''
return self._get_parent_attribute('any_errors_fatal')
def filter_tagged_tasks(self, play_context, all_vars):
'''
Creates a new block, with task lists filtered based on the tags contained

View file

@ -79,9 +79,9 @@ class Task(Base, Conditional, Taggable, Become):
_delegate_facts = FieldAttribute(isa='bool', default=False)
_failed_when = FieldAttribute(isa='list', default=[])
_first_available_file = FieldAttribute(isa='list')
_loop = FieldAttribute(isa='string', private=True)
_loop_args = FieldAttribute(isa='list', private=True)
_loop_control = FieldAttribute(isa='class', class_type=LoopControl)
_loop = FieldAttribute(isa='string', private=True, inherit=False)
_loop_args = FieldAttribute(isa='list', private=True, inherit=False)
_loop_control = FieldAttribute(isa='class', class_type=LoopControl, inherit=False)
_name = FieldAttribute(isa='string', default='')
_notify = FieldAttribute(isa='list')
_poll = FieldAttribute(isa='int')
@ -401,10 +401,10 @@ class Task(Base, Conditional, Taggable, Become):
'''
Generic logic to get the attribute or parent attribute for a task value.
'''
value = None
try:
value = self._attributes[attr]
if self._parent and (value is None or extend):
parent_value = getattr(self._parent, attr, None)
if extend:
@ -420,23 +420,7 @@ class Task(Base, Conditional, Taggable, Become):
'''
Override for the 'tags' getattr fetcher, used from Base.
'''
environment = self._attributes['environment']
parent_environment = self._get_parent_attribute('environment', extend=True)
if parent_environment is not None:
environment = self._extend_value(environment, parent_environment)
return environment
def _get_attr_any_errors_fatal(self):
'''
Override for the 'tags' getattr fetcher, used from Base.
'''
return self._get_parent_attribute('any_errors_fatal')
def _get_attr_loop(self):
return self._attributes['loop']
def _get_attr_loop_control(self):
return self._attributes['loop_control']
return self._get_parent_attribute('environment', extend=True)
def get_dep_chain(self):
if self._parent: