New issue 25 by chris.morganiser: config.section.key fails rather than
returning Undefined for undefined section
http://code.google.com/p/iniparse/issues/detail?id=25
Long summary: An INIConfig provides a nice Undefined object for undefined
sections so that you can set values directly. Unfortunately, this Undefined
does not provide magic __getitem__ or __getattr__ methods, so undefined
values raise an AttributeError.
I use a simple function, ``ini_defined``, for some things on INI files for
setting and validation of some INI files in a set format, where the
sections and keys may or may not exist::
from iniparse.config import Undefined
def ini_defined(val):
return not isinstance(val, Undefined)
I may add I think this would be helpful in ``iniparse.utils``.
On to the issue. Here is the current behaviour::
>>> import iniparse
>>> config = iniparse.INIConfig()
>>> config.section
<iniparse.config.Undefined object at 0x7fcfaeff9190>
>>> config.section.key
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Undefined' object has no attribute 'key'
>>> config.section.key = 'value'
>>> config.section.key2
<iniparse.config.Undefined object at 0x7fcfaeff9350>
When the section is defined, an undefined key works, so that I can do
``ini_defined(config.section.key2)``. However,
``ini_defined(config.section2.key)`` will raise an AttributeError, so I've
taken to doing ``ini_defined(config.section2) and
ini_defined(config.section2.key)`` when the section may not exist. Not neat.
Here is a patch which provides __getitem__ and __getattr__ for the
Undefined object, returning another Undefined object. There is one
side-effect to it which I've identified; the getter can then go to any
number of levels. But the setter will fail, so I think it's alright::
>>> # New behaviour; foo.section2 is not a defined section
>>> foo.section2.key
<iniparse.config.Undefined object at 0x7f3d7ab63a50>
>>> foo.section2.key.subkey
<iniparse.config.Undefined object at 0x7f3d7ab63bd0>
>>> foo.section2.key.subkey = 'value'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "iniparse/config.py", line 108, in __setattr__
obj =
object.__getattribute__(self, 'namespace')._new_namespace(self.name)
TypeError: 'Undefined' object is not callable
>>> foo.section2.key.subkey.subkey
<iniparse.config.Undefined object at 0x7f3d7ab63b50>
>>> foo.section2.key.subkey.subkey = 'value'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "iniparse/config.py", line 108, in __setattr__
obj =
object.__getattribute__(self, 'namespace')._new_namespace(self.name)
TypeError: 'Undefined' object is not callable
I don't think it would be worth while "fixing" this getter behaviour with
another Undefined subclass. Note that this is a different exception from
the Exception: ('No sub-sections allowed', 'key3') you get if the section
is defined but the key is not - e.g. ``foo.section.key3.subkey``. If it
matters I can fix it up.
Side note: defining __getattribute__ instead of __getattr__ and using
object.__getattribute__ locally (a decorator could switch self for a magic
thing which would use object.__getattribute__ instead of
self.__getattribute__) would remove the possibility of name clashes
entirely. But I think I'll create another bug and patch for that. May take
a while, though.
Attachments:
get-undefined.patch 975 bytes
I discovered an issue with this; rather than having ``'key' in
iniconfig.UndefinedSection`` or ``list(iniconfig.UndefinedSection)`` raise
an error as they currently do, it triggers a race condition, getting items
0, 1, 2, etc.
I discussed this at
http://stackoverflow.com/questions/4256357/how-to-override-python-listiterator-behaviour
and came up with the following solution.
The solution is overriding __iter__ in the Undefined class::
def __iter__(self):
"""Simple iterator which produces no output."""
return
yield
This makes ``list(iniconfig.UndefinedSection)`` return [] and makes ``'key'
in iniconfig.UndefinedSection`` return False as expected.