jncramp / iniparse

Automatically exported from code.google.com/p/iniparse
Other
0 stars 0 forks source link

config.section.key fails rather than returning Undefined for undefined section #25

Open GoogleCodeExporter opened 8 years ago

GoogleCodeExporter commented 8 years ago
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.

Original issue reported on code.google.com by chris.morganiser on 13 Nov 2010 at 12:30

Attachments:

GoogleCodeExporter commented 8 years ago
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-b
ehaviour 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.

Original comment by chris.morganiser on 23 Nov 2010 at 9:39