diff --git a/oslo_config/cfg.py b/oslo_config/cfg.py index 6132ed8f..33c68c91 100644 --- a/oslo_config/cfg.py +++ b/oslo_config/cfg.py @@ -240,12 +240,10 @@ Option values may reference other values using PEP 292 string substitution:: Interpolation can be avoided by using `$$`. -.. warning:: +.. note:: - Interpolation using the values of options in groups is not yet - supported. The interpolated option must be in the DEFAULT group - (i.e., ``"$state_path"`` works but ``"$database.state_path"`` does - not). + You can use `.` to delimit option from other groups, e.g. + ${mygroup.myoption}. Special Handling Instructions ----------------------------- @@ -2320,13 +2318,16 @@ class ConfigOpts(collections.Mapping): # a bit more natural for users if '\$' in value: value = value.replace('\$', '$$') - tmpl = string.Template(value) + tmpl = self.Template(value) ret = tmpl.safe_substitute( self.StrSubWrapper(self, group=group, namespace=namespace)) return ret else: return value + class Template(string.Template): + idpattern = r'[_a-z][\._a-z0-9]*' + def _convert_value(self, value, opt): """Perform value type conversion. @@ -2591,10 +2592,16 @@ class ConfigOpts(collections.Mapping): :param key: an opt name :returns: an opt value - :raises: TemplateSubstitutionError if attribute is a group """ try: - value = self.conf._get(key, group=self.group, + group_name, option = key.split(".", 1) + except ValueError: + group = self.group + option = key + else: + group = OptGroup(name=group_name) + try: + value = self.conf._get(option, group=group, namespace=self.namespace) except NoSuchOptError: value = self.conf._get(key, namespace=self.namespace) diff --git a/oslo_config/tests/test_cfg.py b/oslo_config/tests/test_cfg.py index 6aa30de5..1bf0886d 100644 --- a/oslo_config/tests/test_cfg.py +++ b/oslo_config/tests/test_cfg.py @@ -2824,11 +2824,18 @@ class SadPathTestCase(BaseTestCase): self.conf([]) - self.assertFalse(hasattr(self.conf, 'bar')) - self.assertRaises( - AttributeError, getattr, self.conf, 'bar') - self.assertRaises( - cfg.TemplateSubstitutionError, self.conf._get, 'bar') + self.assertTrue(hasattr(self.conf, 'bar')) + self.assertEqual("blaa", self.conf.bar) + + def test_str_sub_from_group_with_brace(self): + self.conf.register_group(cfg.OptGroup('f')) + self.conf.register_cli_opt(cfg.StrOpt('oo', default='blaa'), group='f') + self.conf.register_cli_opt(cfg.StrOpt('bar', default='${f.oo}')) + + self.conf([]) + + self.assertTrue(hasattr(self.conf, 'bar')) + self.assertEqual("blaa", self.conf.bar) def test_set_default_unknown_attr(self): self.conf([]) diff --git a/tests/test_cfg.py b/tests/test_cfg.py index 50965a71..5d85e1ca 100644 --- a/tests/test_cfg.py +++ b/tests/test_cfg.py @@ -2800,11 +2800,8 @@ class SadPathTestCase(BaseTestCase): self.conf([]) - self.assertFalse(hasattr(self.conf, 'bar')) - self.assertRaises( - AttributeError, getattr, self.conf, 'bar') - self.assertRaises( - cfg.TemplateSubstitutionError, self.conf._get, 'bar') + self.assertTrue(hasattr(self.conf, 'bar')) + self.assertEqual("blaa", self.conf.bar) def test_set_default_unknown_attr(self): self.conf([])