Question

With Python ConfigParser, is it possible to use interpolation across foreign sections? My mind seems to tell me I've seen that it's possible somewhere, but I can't find it when searching.

This example doesn't work, but it's to give an idea of what I'm trying to do.

[section1]
root = /usr

[section2]
root = /usr/local

[section3]
dir1 = $(section1:root)/bin
dir2 = $(section2:root)/bin

Note that I'm using Python 2.4.

Was it helpful?

Solution

In python 3.2 and up this is perfectly valid:

[Common]
home_dir: /Users
library_dir: /Library
system_dir: /System
macports_dir: /opt/local

[Frameworks]
Python: 3.2
path: ${Common:system_dir}/Library/Frameworks/

[Arthur]
nickname: Two Sheds
last_name: Jackson
my_dir: ${Common:home_dir}/twosheds
my_pictures: ${my_dir}/Pictures
python_dir: ${Frameworks:path}/Python/Versions/${Frameworks:Python}

Edit:

I just saw that you are using python 2.4, so no, section interpolation cannot be done in python 2.4. It was introduced in python 3.2 - See section 13.2.5 - ConfigParser Interpolation of values.

class configparser.ExtendedInterpolation

An alternative handler for interpolation which implements a more advanced syntax, used for instance in zc.buildout. Extended interpolation is using ${section:option} to denote a value from a foreign section. Interpolation can span multiple levels. For convenience, if the section: part is omitted, interpolation defaults to the current section (and possibly the default values from the special section). For example, the configuration specified above with basic interpolation, would look like this with extended interpolation:

   [Paths]
   home_dir: /Users
   my_dir: ${home_dir}/lumberjack
   my_pictures: ${my_dir}/Pictures

Values from other sections can be fetched as well:

   [Common]
   home_dir: /Users
   library_dir: /Library
   system_dir: /System
   macports_dir: /opt/local

   [Frameworks]
   Python: 3.2
   path: ${Common:system_dir}/Library/Frameworks/

   [Arthur]
   nickname: Two Sheds
   last_name: Jackson
   my_dir: ${Common:home_dir}/twosheds
   my_pictures: ${my_dir}/Pictures
   python_dir: ${Frameworks:path}/Python/Versions/${Frameworks:Python}

OTHER TIPS

You do have access to the special-case [DEFAULT] section. Values defined here can be accessed via interpolation from other sections even for older versions of Python.

If you're stuck with python 2.7 and you need to do cross-section interpolation it is easy enough to do this by hand using regexps.

Here is the code:

INTERPOLATION_RE = re.compile(r"\$\{(?:(?P<section>[^:]+):)?(?P<key>[^}]+)\}")

def load_something_from_cp(cp, section="section"):
    result = []
    def interpolate_func(match):
        d = match.groupdict()
        section = d.get('section', section)
        key = d.get('key')
        return cp.get(section, key)
    for k, v in cp.items(section):
        v = re.sub(INTERPOLATION_RE, interpolate_func, v)
        result.append(
            (v, k)
        )
    return result

Caveeats:

  • There is no recursion in interpolation
  • When parsing many sections, youll need to somehow guess current section.

I have run into this in the project I'm working on right now, and I implemented a quick extension to the ConfigParser.SafeConfigParser class in which I have overwritten the get() function. I thought some may find it useful.

import re
import ConfigParser

class ExtParser(ConfigParser.SafeConfigParser):
     #implementing extended interpolation
     def __init__(self, *args, **kwargs):
         self.cur_depth = 0 
         ConfigParser.SafeConfigParser.__init__(self, *args, **kwargs)


     def get(self, section, option, raw=False, vars=None):
         r_opt = ConfigParser.SafeConfigParser.get(self, section, option, raw=True, vars=vars)
         if raw:
             return r_opt

         ret = r_opt
         re_oldintp = r'%\((\w*)\)s'
         re_newintp = r'\$\{(\w*):(\w*)\}'

         m_new = re.findall(re_newintp, r_opt)
         if m_new:
             for f_section, f_option in m_new:
                 self.cur_depth = self.cur_depth + 1 
                 if self.cur_depth < ConfigParser.MAX_INTERPOLATION_DEPTH:
                     sub = self.get(f_section, f_option, vars=vars)
                     ret = ret.replace('${{{0}:{1}}}'.format(f_section, f_option), sub)
                 else:
                     raise ConfigParser.InterpolationDepthError, (option, section, r_opt)



         m_old = re.findall(re_oldintp, r_opt)
         if m_old:
             for l_option in m_old:
                 self.cur_depth = self.cur_depth + 1 
                 if self.cur_depth < ConfigParser.MAX_INTERPOLATION_DEPTH:
                     sub = self.get(section, l_option, vars=vars)
                     ret = ret.replace('%({0})s'.format(l_option), sub)
                 else:
                     raise ConfigParser.InterpolationDepthError, (option, section, r_opt)

         self.cur_depth = self.cur_depth - 1 
         return ret 
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top