通过“dir(obj)”打印属性时获取“AttributeError”

时间:2015-08-16 13:18:22

标签: python attributeerror

我正在尝试使用dir()命令在Python中打印Object的所有属性。

    if self.play:
        for props in dir(self.play):
            f.write('\n%s:::%s'%(props, getattr(self.play, props)))

但是当我执行这个命令时,我得到了,

AttributeError: su f.write(...)行上没有除追踪之外的其他信息。

所以,我在Play类中搜索了名为su的属性,但我根本找不到它。

它对su的唯一引用就是这样的init方法(只发布相关的行),

class Play(object):
    def __init__(self, ds):
        self._ds = ds
        self.become = ds['su']

Play未覆盖__dir__()方法。

那么,为什么我会收到此错误以及如何避免错误?

我正在使用 Python 2.7 ,如果这是相关的,我对Python编程很新,所以请以新手理解的方式回答。

编辑:

附加堆栈跟踪和Play类的完整代码。

错误:

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

玩类:

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

正如您可能已经猜到的那样,Play类来自一个名为 ansible 的工具,因为我不太清楚它的代码和它的调用结构,所以我遇到了困难调试时间。

我只是希望这个错误在使用dir()时很常见,有人会遇到它。但如果情况并非如此,我认为我应该使用其他一些方法,而不是过于担心调试它。

1 个答案:

答案 0 :(得分:1)

当您定义__slots__时,即使您没有为其分配任何值,也会显示属性:

>>> class foo(object):
...     __slots__ = 'a', 'b'
... 
>>> x = foo()
>>> dir(x)
[..., 'a', 'b']

忽略未设置的简单方法是将其用于getattr()的默认参数。如果None是您的任何变量的有效值(可能),那么您可以创建一个特殊的Undefined类,仅用于显示尚未设置变量:

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

如果value is Undefined,您将可以轻松地在循环中进行测试,以忽略任何结果。