7

I'm using ConfigObj in python with Template-style interpolation. Unwrapping my config dictionary via ** doesn't seem to do interpolation. Is this a feature or a bug? Any nice workarounds?

$ cat my.conf
foo = /test
bar = $foo/directory

>>> import configobj
>>> config = configobj.ConfigObj('my.conf', interpolation='Template')
>>> config['bar']
'/test/directory'
>>> '{bar}'.format(**config)
'$foo/directory'

I'd expect the second line to be /test/directory. Why doesn't interpolation work with **kwargs?

Jake Biesinger
  • 5,538
  • 2
  • 23
  • 25
  • The `**` keyword argument unpacking only works for mappings. Most probably, `ConfigObj` instances are not exposing the full [mapping](http://docs.python.org/glossary.html#term-mapping) interface. – Sven Marnach Jul 03 '12 at 16:23
  • ConfigObj *inherits* from dict, so it definitely provides the full mapping interface (and infact the ** unpack has *worked* it has just got the wrong values). I'm not sure how "**"" gets values out, it is obviously bypassing the interpolation though. I'll have to do some experimentation to work out. – fuzzyman Jul 03 '12 at 17:18
  • Isn't there some weirdness associated with inheriting directly from dict? e.g., http://stackoverflow.com/questions/3387691/python-how-to-perfectly-override-a-dict. I'm no expert on this, but perhaps using the [MutableMapping](http://docs.python.org/library/collections.html#collections.MutableMapping) or [DictMixin](http://docs.python.org/library/userdict.html#UserDict.DictMixin) would be better? – Jake Biesinger Jul 03 '12 at 20:51
  • 2
    Yes, inheriting from dict and overriding access methods tends to be a bit fragile, as none of the "extra" functionality (such as the update method) will respect your overridden methods. I would guess the python interpreter is doing the `**kwargs` unpacking directly against the C level implementation of `dict`, since this is one. Unlike `update` et al, I'm not sure this could even be fixed by overriding more methods. :( – Ben Jul 05 '12 at 08:42
  • So essentially there is no way to override the ** operator for the `dict` class. Bummer. I guess the solution is to inherit from `UserDict` or implement all of `MutableMapping`, but not inherit from `dict` which will probably be much slower (pure python functions vs C). – Jake Biesinger Jul 05 '12 at 15:42

2 Answers2

2

When unpacking the keyword argument, then a new object is created: of type dict. This dictionary contains the the raw-values of the configuration (no interpolation)

Demonstration:

>>> id(config)
31143152
>>> def showKeywordArgs(**kwargs):
...     print(kwargs, type(kwargs), id(kwargs))
...
>>> showKeywordArgs(**config)
({'foo': '/test', 'bar': '$foo/directory'}, <type 'dict'>, 35738944)

To resolve your problem you could create an expanded version of your configuration like this:

>>> expandedConfig = {k: config[k] for k in config}
>>> '{bar}'.format(**expandedConfig)
'/test/directory'

Another more elegant way is to simply avoid unpacking: This can be achieved by using the function string.Formatter.vformat:

import string
fmt = string.Formatter()
fmt.vformat("{bar}", None, config)
gecco
  • 17,969
  • 11
  • 51
  • 68
2

I have had a similar problem.

A workaround is to use configobj's function ".dict()". This works because configobj returns a real dictionary, which Python knows how to unpack.

Your example becomes:

>>> import configobj
>>> config = configobj.ConfigObj('my.conf', interpolation='Template')
>>> config['bar']
'/test/directory'
>>> '{bar}'.format(**config.dict())
'/test/directory'
TomK
  • 362
  • 2
  • 10