I am trying to print all the attributes of an Object in Python using the dir()
command.
if self.play:
for props in dir(self.play):
f.write('\n%s:::%s'%(props, getattr(self.play, props)))
But when I execute this command I get,
AttributeError: su
(There's no other information except the traceback) on the f.write(...)
line.
So, I searched the Play
class for the attribute named su
, but I was not able to find it at all.
The only reference to su
it had was on it's init method like this (Posting only the relevant lines),
class Play(object):
def __init__(self, ds):
self._ds = ds
self.become = ds['su']
Play
has NOT overridden __dir__()
method.
So, why am I getting this error and how to avoid it?
I am using Python 2.7 if that's relevant, and I am very new to Python programming, so please answer in a way for newbies to understand.
Attaching the stack trace and full code of Play
class.
Error:
Traceback (most recent call last):
File "/usr/local/bin/ansible-playbook", line 5, in <module>
pkg_resources.run_script('ansible==1.9.2', 'ansible-playbook')
File "/usr/lib/python2.7/dist-packages/pkg_resources.py", line 528, in run_script
self.require(requires)[0].run_script(script_name, ns)
File "/usr/lib/python2.7/dist-packages/pkg_resources.py", line 1394, in run_script
execfile(script_filename, namespace, namespace)
File "/usr/local/lib/python2.7/dist-packages/ansible-1.9.2-py2.7.egg/EGG-INFO/scripts/ansible-playbook", line 324, in <module>
sys.exit(main(sys.argv[1:]))
File "/usr/local/lib/python2.7/dist-packages/ansible-1.9.2-py2.7.egg/EGG-INFO/scripts/ansible-playbook", line 264, in main
pb.run()
File "/usr/local/lib/python2.7/dist-packages/ansible-1.9.2-py2.7.egg/ansible/playbook/__init__.py", line 348, in run
if not self._run_play(play):
File "/usr/local/lib/python2.7/dist-packages/ansible-1.9.2-py2.7.egg/ansible/playbook/__init__.py", line 730, in _run_play
self.callbacks.on_play_start(play.name)
File "/usr/local/lib/python2.7/dist-packages/ansible-1.9.2-py2.7.egg/ansible/callbacks.py", line 720, in on_play_start
call_callback_module('playbook_on_play_start', name)
File "/usr/local/lib/python2.7/dist-packages/ansible-1.9.2-py2.7.egg/ansible/callbacks.py", line 179, in call_callback_module
method(*args, **kwargs)
File "/home/mobins/ansibletemp/callback_plugins/email_reporter.py", line 210, in playbook_on_play_start
self.writeProp(f)
File "/home/mobins/ansibletemp/callback_plugins/email_reporter.py", line 18, in writeProp
f.write('\n%s:::%s'%(props, getattr(self.play, props)))
AttributeError: su
Play Class:
class Play(object):
_pb_common = [
'accelerate', 'accelerate_ipv6', 'accelerate_port', 'any_errors_fatal', 'become',
'become_method', 'become_user', 'environment', 'force_handlers', 'gather_facts',
'handlers', 'hosts', 'name', 'no_log', 'remote_user', 'roles', 'serial', 'su',
'su_user', 'sudo', 'sudo_user', 'tags', 'vars', 'vars_files', 'vars_prompt',
'vault_password',
]
__slots__ = _pb_common + [
'_ds', '_handlers', '_play_hosts', '_tasks', 'any_errors_fatal', 'basedir',
'default_vars', 'included_roles', 'max_fail_pct', 'playbook', 'remote_port',
'role_vars', 'transport', 'vars_file_vars',
]
# to catch typos and so forth -- these are userland names
# and don't line up 1:1 with how they are stored
VALID_KEYS = frozenset(_pb_common + [
'connection', 'include', 'max_fail_percentage', 'port', 'post_tasks',
'pre_tasks', 'role_names', 'tasks', 'user',
])
# *************************************************
def __init__(self, playbook, ds, basedir, vault_password=None):
''' constructor loads from a play datastructure '''
for x in ds.keys():
if not x in Play.VALID_KEYS:
raise errors.AnsibleError("%s is not a legal parameter of an Ansible Play" % x)
# allow all playbook keys to be set by --extra-vars
self.vars = ds.get('vars', {})
self.vars_prompt = ds.get('vars_prompt', {})
self.playbook = playbook
self.vars = self._get_vars()
self.vars_file_vars = dict() # these are vars read in from vars_files:
self.role_vars = dict() # these are vars read in from vars/main.yml files in roles
self.basedir = basedir
self.roles = ds.get('roles', None)
self.tags = ds.get('tags', None)
self.vault_password = vault_password
self.environment = ds.get('environment', {})
if self.tags is None:
self.tags = []
elif type(self.tags) in [ str, unicode ]:
self.tags = self.tags.split(",")
elif type(self.tags) != list:
self.tags = []
# make sure we have some special internal variables set, which
# we use later when loading tasks and handlers
load_vars = dict()
load_vars['playbook_dir'] = os.path.abspath(self.basedir)
if self.playbook.inventory.basedir() is not None:
load_vars['inventory_dir'] = self.playbook.inventory.basedir()
if self.playbook.inventory.src() is not None:
load_vars['inventory_file'] = self.playbook.inventory.src()
# We first load the vars files from the datastructure
# so we have the default variables to pass into the roles
self.vars_files = ds.get('vars_files', [])
if not isinstance(self.vars_files, list):
raise errors.AnsibleError('vars_files must be a list')
processed_vars_files = self._update_vars_files_for_host(None)
# now we load the roles into the datastructure
self.included_roles = []
ds = self._load_roles(self.roles, ds)
# and finally re-process the vars files as they may have been updated
# by the included roles, but exclude any which have been processed
self.vars_files = utils.list_difference(ds.get('vars_files', []), processed_vars_files)
if not isinstance(self.vars_files, list):
raise errors.AnsibleError('vars_files must be a list')
self._update_vars_files_for_host(None)
# template everything to be efficient, but do not pre-mature template
# tasks/handlers as they may have inventory scope overrides. We also
# create a set of temporary variables for templating, so we don't
# trample on the existing vars structures
_tasks = ds.pop('tasks', [])
_handlers = ds.pop('handlers', [])
temp_vars = utils.combine_vars(self.vars, self.vars_file_vars)
temp_vars = utils.combine_vars(temp_vars, self.playbook.extra_vars)
try:
ds = template(basedir, ds, temp_vars)
except errors.AnsibleError, e:
utils.warning("non fatal error while trying to template play variables: %s" % (str(e)))
ds['tasks'] = _tasks
ds['handlers'] = _handlers
self._ds = ds
hosts = ds.get('hosts')
if hosts is None:
raise errors.AnsibleError('hosts declaration is required')
elif isinstance(hosts, list):
try:
hosts = ';'.join(hosts)
except TypeError,e:
raise errors.AnsibleError('improper host declaration: %s' % str(e))
self.serial = str(ds.get('serial', 0))
self.hosts = hosts
self.name = ds.get('name', self.hosts)
self._tasks = ds.get('tasks', [])
self._handlers = ds.get('handlers', [])
self.remote_user = ds.get('remote_user', ds.get('user', self.playbook.remote_user))
self.remote_port = ds.get('port', self.playbook.remote_port)
self.transport = ds.get('connection', self.playbook.transport)
self.remote_port = self.remote_port
self.any_errors_fatal = utils.boolean(ds.get('any_errors_fatal', 'false'))
self.accelerate = utils.boolean(ds.get('accelerate', 'false'))
self.accelerate_port = ds.get('accelerate_port', None)
self.accelerate_ipv6 = ds.get('accelerate_ipv6', False)
self.max_fail_pct = int(ds.get('max_fail_percentage', 100))
self.no_log = utils.boolean(ds.get('no_log', 'false'))
self.force_handlers = utils.boolean(ds.get('force_handlers', self.playbook.force_handlers))
# Fail out if user specifies conflicting privelege escalations
if (ds.get('become') or ds.get('become_user')) and (ds.get('sudo') or ds.get('sudo_user')):
raise errors.AnsibleError('sudo params ("become", "become_user") and su params ("sudo", "sudo_user") cannot be used together')
if (ds.get('become') or ds.get('become_user')) and (ds.get('su') or ds.get('su_user')):
raise errors.AnsibleError('sudo params ("become", "become_user") and su params ("su", "su_user") cannot be used together')
if (ds.get('sudo') or ds.get('sudo_user')) and (ds.get('su') or ds.get('su_user')):
raise errors.AnsibleError('sudo params ("sudo", "sudo_user") and su params ("su", "su_user") cannot be used together')
# become settings are inherited and updated normally
self.become = ds.get('become', self.playbook.become)
self.become_method = ds.get('become_method', self.playbook.become_method)
self.become_user = ds.get('become_user', self.playbook.become_user)
# Make sure current play settings are reflected in become fields
if 'sudo' in ds:
self.become=ds['sudo']
self.become_method='sudo'
if 'sudo_user' in ds:
self.become_user=ds['sudo_user']
elif 'su' in ds:
self.become=True
self.become=ds['su']
self.become_method='su'
if 'su_user' in ds:
self.become_user=ds['su_user']
# gather_facts is not a simple boolean, as None means that a 'smart'
# fact gathering mode will be used, so we need to be careful here as
# calling utils.boolean(None) returns False
self.gather_facts = ds.get('gather_facts', None)
if self.gather_facts is not None:
self.gather_facts = utils.boolean(self.gather_facts)
load_vars['role_names'] = ds.get('role_names', [])
self._tasks = self._load_tasks(self._ds.get('tasks', []), load_vars)
self._handlers = self._load_tasks(self._ds.get('handlers', []), load_vars)
# apply any missing tags to role tasks
self._late_merge_role_tags()
# place holder for the discovered hosts to be used in this play
self._play_hosts = None
As you could have guessed, the Play
class is from a tool called ansible and since I didn't know well about it's code and it's call structure, I was having a difficult time in debugging that.
I just hoped this error was common while using dir()
and someone would have already faced it. But if that's not the case, I think I should use some other approach probably and not worry too much about debugging this.
When you define __slots__
the attributes will show up, even if you haven't assigned any value to them:
>>> class foo(object):
... __slots__ = 'a', 'b'
...
>>> x = foo()
>>> dir(x)
[..., 'a', 'b']
The easy way to ignore unset attributes it to use the default parameter to getattr()
. If None
is a valid value for any of your variables (likely), then you can create a special Undefined
class that you only use to show that a variable has not been set:
>>> class Undefined:
... pass
...
>>> for name in dir(x):
... value = getattr(x, name, Undefined) # Default to Undefined
... print name, value
...
__class__ <class '__main__.foo'>
__delattr__ <method-wrapper '__delattr__' of foo object at 0xb6a5e1cc>
__doc__ None
__format__ <built-in method __format__ of foo object at 0xb6a5e1cc>
__getattribute__ <method-wrapper '__getattribute__' of foo object at 0xb6a5e1cc>
__hash__ <method-wrapper '__hash__' of foo object at 0xb6a5e1cc>
__init__ <method-wrapper '__init__' of foo object at 0xb6a5e1cc>
__module__ __main__
__new__ <built-in method __new__ of type object at 0x8335200>
__reduce__ <built-in method __reduce__ of foo object at 0xb6a5e1cc>
__reduce_ex__ <built-in method __reduce_ex__ of foo object at 0xb6a5e1cc>
__repr__ <method-wrapper '__repr__' of foo object at 0xb6a5e1cc>
__setattr__ <method-wrapper '__setattr__' of foo object at 0xb6a5e1cc>
__sizeof__ <built-in method __sizeof__ of foo object at 0xb6a5e1cc>
__slots__ ('a', 'b')
__str__ <method-wrapper '__str__' of foo object at 0xb6a5e1cc>
__subclasshook__ <built-in method __subclasshook__ of type object at 0xb6d9d82c>
a __main__.Undefined
b __main__.Undefined
You will then easily be able to do a test in your loop to ignore any results if value is Undefined
.