Получение AttributeError при печати атрибутов через каталог (obj)

Я пытаюсь распечатать все атрибуты объекта в Python с помощью команды dir().

    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 была в его методе инициализации, подобном этому (публикация только соответствующих строк),

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() и кто-то уже сталкивался с ней. Но если это не так, я думаю, что мне следует использовать какой-то другой подход и не слишком беспокоиться об отладке этого.


person Codebender    schedule 16.08.2015    source источник
comment
не могли бы вы указать, что именно вы получаете как ошибку?   -  person Vivek Anand    schedule 16.08.2015
comment
@TheDeadMan, это просто AttributeError: su. А перед этим идет длинный Traceback. Вы хотите, чтобы я также включил трассировку?   -  person Codebender    schedule 16.08.2015
comment
Попытка некоторого каркасного кода с тем небольшим, что вы показали, не может воспроизвести ошибку. Здесь недостаточно информации.   -  person cdarke    schedule 16.08.2015
comment
@Codebender Лучшим вариантом здесь было бы разместить весь код (если возможно) здесь и позволить другим работать над ним. Это сделает вещи более удобными.   -  person Vivek Anand    schedule 16.08.2015
comment
@Codebender, и да, полная трассировка также была бы полезна.   -  person Anand S Kumar    schedule 16.08.2015


Ответы (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.

person Patrick Maupin    schedule 18.08.2015
comment
Это имеет смысл. Имея опыт работы с Java, я просто удивлен, что атрибуты можно создавать разными способами. - person Codebender; 18.08.2015
comment
Дело в том, что «a» и «b» являются атрибутами класса, а dir() показывает атрибуты класса, а также атрибуты экземпляра. - person Patrick Maupin; 18.08.2015