summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgrabner <pjg.github@ubergrabner.net>2017-05-20 16:25:49 -0400
committergrabner <pjg.github@ubergrabner.net>2017-05-20 16:25:49 -0400
commit2bc4c5215bf78d2122ad83197ef18b64aa7a6119 (patch)
treee3c71028e68c1500fe1525fe88d07d0a3af6c509
parented787c9c0afa02b732cd26e117f385d17aa4dbfd (diff)
downloadiniherit-2bc4c5215bf78d2122ad83197ef18b64aa7a6119.tar.gz
added support for recursive evaluation of "ENV" expansions
-rw-r--r--iniherit/interpolation.py21
-rw-r--r--iniherit/test.py35
2 files changed, 55 insertions, 1 deletions
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$