From cddf1cf98ec3fa3150738b5f3d8edd594c464152 Mon Sep 17 00:00:00 2001 From: James Cammarata Date: Thu, 25 Aug 2016 11:34:08 -0500 Subject: [PATCH] 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 --- lib/ansible/executor/task_executor.py | 2 + lib/ansible/playbook/base.py | 41 ++++++++++---- lib/ansible/playbook/become.py | 26 --------- lib/ansible/playbook/block.py | 81 ++++++++++++++------------- lib/ansible/playbook/task.py | 26 ++------- 5 files changed, 78 insertions(+), 98 deletions(-) diff --git a/lib/ansible/executor/task_executor.py b/lib/ansible/executor/task_executor.py index 85fc9d06b82..d66ab251033 100644 --- a/lib/ansible/executor/task_executor.py +++ b/lib/ansible/executor/task_executor.py @@ -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 diff --git a/lib/ansible/playbook/base.py b/lib/ansible/playbook/base.py index 2447a72824f..e95e16b518a 100644 --- a/lib/ansible/playbook/base.py +++ b/lib/ansible/playbook/base.py @@ -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 diff --git a/lib/ansible/playbook/become.py b/lib/ansible/playbook/become.py index 535eee9d54b..69a87bfe588 100644 --- a/lib/ansible/playbook/become.py +++ b/lib/ansible/playbook/become.py @@ -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'] diff --git a/lib/ansible/playbook/block.py b/lib/ansible/playbook/block.py index cde41172f67..29f87421653 100644 --- a/lib/ansible/playbook/block.py +++ b/lib/ansible/playbook/block.py @@ -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 - - 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 + 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 + + 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 diff --git a/lib/ansible/playbook/task.py b/lib/ansible/playbook/task.py index 9c6e91b15fb..e99f84c108c 100644 --- a/lib/ansible/playbook/task.py +++ b/lib/ansible/playbook/task.py @@ -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: