More informative playbook attribute errors (#77082)

* More informative playbook attribute errors

Co-authored-by: Sloane Hertel <19572925+s-hertel@users.noreply.github.com>
pull/77127/head
Brian Coca 4 years ago committed by GitHub
parent 8cbe1435c2
commit 143904f49b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,2 @@
bugfixes:
- playbook/strategy have more informative 'attribute' based errors for playbook objects and handlers.

@ -16,10 +16,9 @@ from jinja2.exceptions import UndefinedError
from ansible import constants as C from ansible import constants as C
from ansible import context from ansible import context
from ansible.errors import AnsibleError from ansible.errors import AnsibleError, AnsibleParserError, AnsibleUndefinedVariable, AnsibleAssertionError
from ansible.module_utils.six import string_types from ansible.module_utils.six import string_types
from ansible.module_utils.parsing.convert_bool import boolean from ansible.module_utils.parsing.convert_bool import boolean
from ansible.errors import AnsibleParserError, AnsibleUndefinedVariable, AnsibleAssertionError
from ansible.module_utils._text import to_text, to_native from ansible.module_utils._text import to_text, to_native
from ansible.parsing.dataloader import DataLoader from ansible.parsing.dataloader import DataLoader
from ansible.playbook.attribute import Attribute, FieldAttribute from ansible.playbook.attribute import Attribute, FieldAttribute
@ -36,7 +35,7 @@ def _generic_g(prop_name, self):
try: try:
value = self._attributes[prop_name] value = self._attributes[prop_name]
except KeyError: except KeyError:
raise AttributeError("'%s' object has no attribute '%s'" % (self.__class__.__name__, prop_name)) raise AttributeError("'%s' does not have the keyword '%s'" % (self.__class__.__name__, prop_name))
if value is Sentinel: if value is Sentinel:
value = self._attr_defaults[prop_name] value = self._attr_defaults[prop_name]
@ -51,7 +50,7 @@ def _generic_g_method(prop_name, self):
method = "_get_attr_%s" % prop_name method = "_get_attr_%s" % prop_name
return getattr(self, method)() return getattr(self, method)()
except KeyError: except KeyError:
raise AttributeError("'%s' object has no attribute '%s'" % (self.__class__.__name__, prop_name)) raise AttributeError("'%s' does not support the keyword '%s'" % (self.__class__.__name__, prop_name))
def _generic_g_parent(prop_name, self): def _generic_g_parent(prop_name, self):
@ -64,7 +63,7 @@ def _generic_g_parent(prop_name, self):
except AttributeError: except AttributeError:
value = self._attributes[prop_name] value = self._attributes[prop_name]
except KeyError: except KeyError:
raise AttributeError("'%s' object has no attribute '%s'" % (self.__class__.__name__, prop_name)) raise AttributeError("'%s' nor it's parents support the keyword '%s'" % (self.__class__.__name__, prop_name))
if value is Sentinel: if value is Sentinel:
value = self._attr_defaults[prop_name] value = self._attr_defaults[prop_name]
@ -146,12 +145,15 @@ class BaseMeta(type):
# method, or if the attribute is marked as not inheriting # method, or if the attribute is marked as not inheriting
# its value from a parent object # its value from a parent object
method = "_get_attr_%s" % attr_name method = "_get_attr_%s" % attr_name
if method in src_dict or method in dst_dict: try:
getter = partial(_generic_g_method, attr_name) if method in src_dict or method in dst_dict:
elif ('_get_parent_attribute' in dst_dict or '_get_parent_attribute' in src_dict) and value.inherit: getter = partial(_generic_g_method, attr_name)
getter = partial(_generic_g_parent, attr_name) elif ('_get_parent_attribute' in dst_dict or '_get_parent_attribute' in src_dict) and value.inherit:
else: getter = partial(_generic_g_parent, attr_name)
getter = partial(_generic_g, attr_name) else:
getter = partial(_generic_g, attr_name)
except AttributeError as e:
raise AnsibleParserError("Invalid playbook definition: %s" % to_native(e), orig_exc=e)
setter = partial(_generic_s, attr_name) setter = partial(_generic_s, attr_name)
deleter = partial(_generic_d, attr_name) deleter = partial(_generic_d, attr_name)

@ -26,6 +26,7 @@ import pprint
import sys import sys
import threading import threading
import time import time
import traceback
from collections import deque from collections import deque
from multiprocessing import Lock from multiprocessing import Lock
@ -42,7 +43,7 @@ from ansible.executor.process.worker import WorkerProcess
from ansible.executor.task_result import TaskResult from ansible.executor.task_result import TaskResult
from ansible.executor.task_queue_manager import CallbackSend from ansible.executor.task_queue_manager import CallbackSend
from ansible.module_utils.six import string_types from ansible.module_utils.six import string_types
from ansible.module_utils._text import to_text from ansible.module_utils._text import to_text, to_native
from ansible.module_utils.connection import Connection, ConnectionError from ansible.module_utils.connection import Connection, ConnectionError
from ansible.playbook.conditional import Conditional from ansible.playbook.conditional import Conditional
from ansible.playbook.handler import Handler from ansible.playbook.handler import Handler
@ -1037,10 +1038,14 @@ class StrategyBase:
# but this may take some work in the iterator and gets tricky when # but this may take some work in the iterator and gets tricky when
# we consider the ability of meta tasks to flush handlers # we consider the ability of meta tasks to flush handlers
for handler in handler_block.block: for handler in handler_block.block:
if handler.notified_hosts: try:
result = self._do_handler_run(handler, handler.get_name(), iterator=iterator, play_context=play_context) if handler.notified_hosts:
if not result: result = self._do_handler_run(handler, handler.get_name(), iterator=iterator, play_context=play_context)
break if not result:
break
except AttributeError as e:
display.vvv(traceback.format_exc())
raise AnsibleParserError("Invalid handler definition for '%s'" % (handler.get_name()), orig_exc=e)
return result return result
def _do_handler_run(self, handler, handler_name, iterator, play_context, notified_hosts=None): def _do_handler_run(self, handler, handler_name, iterator, play_context, notified_hosts=None):

Loading…
Cancel
Save