From 2bc4c5215bf78d2122ad83197ef18b64aa7a6119 Mon Sep 17 00:00:00 2001 From: grabner Date: Sat, 20 May 2017 16:25:49 -0400 Subject: added support for recursive evaluation of "ENV" expansions --- iniherit/interpolation.py | 21 ++++++++++++++++++++- iniherit/test.py | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/iniherit/interpolation.py b/iniherit/interpolation.py index 96b213d..d368f44 100644 --- a/iniherit/interpolation.py +++ b/iniherit/interpolation.py @@ -60,7 +60,26 @@ def interpolate(parser, base_interpolate, section, option, rawval, vars): raise CP.InterpolationDepthError(option, section, rawval) if '%(SUPER)s' in value: raise InterpolationMissingSuperError(option, section, rawval, 'SUPER') - return base_interpolate(parser, section, option, value, vars) + if base_interpolate is None: + return value + vars = dict(vars) + while True: + # ok, this is... uh... "tricky"... basically, we don't want to + # pre-emptively expand SUPER & ENV expressions because then we may + # trip invalid expressions that aren't actually used. thus, we + # only expand keys that are actually requested, and we detect by + # catching InterpolationMissingOptionError's... + try: + return base_interpolate(parser, section, option, value, vars) + except CP.InterpolationMissingOptionError as err: + for key, val in list(vars.items()): + if err.reference.lower() in val.lower(): + newval = interpolate(parser, None, section, key, val, vars) + if newval != val: + vars[key] = newval + break + else: + raise #------------------------------------------------------------------------------ def interpolate_super(parser, src, dst, section, option, value): diff --git a/iniherit/test.py b/iniherit/test.py index 6b2c3ad..d4b93b9 100644 --- a/iniherit/test.py +++ b/iniherit/test.py @@ -453,6 +453,41 @@ class TestIniherit(unittest.TestCase): ) self.assertMultiLineEqual(str(cm.exception), err) + #---------------------------------------------------------------------------- + def test_cascading_env_interpolate(self): + # test that if a key contains an interpolation of another key + # can in turn interpolate an "%(ENV:...)s" style expansion. + files = {k: textwrap.dedent(v) for k, v in { + 'config.ini' : ''' + [DEFAULT] + kw1 = %(kw2)s + kw2 = %(ENV:UNDEFINED:-defval)s + ''', + }.items()} + parser = ConfigParser(loader=ByteLoader(files)) + parser.read('config.ini') + self.assertEqual(parser.get('DEFAULT', 'kw2'), 'defval') + self.assertEqual(parser.get('DEFAULT', 'kw1'), 'defval') + + #---------------------------------------------------------------------------- + def test_subclass_override(self): + # test that subclasses that override `ConfigParser._interpolate`, + # but that still directly call it, works... + class SomeOtherConfigParser(ConfigParser): + def _interpolate(self, section, option, rawval, vars): + return ConfigParser._interpolate(self, section, option, rawval, vars) + files = {k: textwrap.dedent(v) for k, v in { + 'config.ini' : ''' + [DEFAULT] + kw1 = %(kw2)s + kw2 = %(ENV:SOMEVAL:-defval)s + ''', + }.items()} + parser = SomeOtherConfigParser(loader=ByteLoader(files)) + parser.read('config.ini') + self.assertEqual(parser.get('DEFAULT', 'kw2'), 'defval') + self.assertEqual(parser.get('DEFAULT', 'kw1'), 'defval') + #------------------------------------------------------------------------------ # end of $Id$ -- cgit v1.2.1