From fa76eb6aa962dbd1313079c39d612b53853049ec Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Wed, 18 Dec 2024 13:43:19 -0600 Subject: [PATCH] fix: force interpolation of `%(here)s`, `%(__file__)s` in config files we were previously doing this only for the `wutta.config.include` and `wutta.config.require` settings, and pyramid (or paste?) has been handling certain other ones, e.g. for beaker session cache paths. but we really need to be able to rely on this being available "everywhere" or else it's just confusing. --- src/wuttjamaican/conf.py | 38 ++++++++++++++++++++++++-------------- tests/test_conf.py | 11 +++++++++++ 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/src/wuttjamaican/conf.py b/src/wuttjamaican/conf.py index 2c71e28..fd23388 100644 --- a/src/wuttjamaican/conf.py +++ b/src/wuttjamaican/conf.py @@ -252,34 +252,44 @@ class WuttaConfig: if path in self.files_read: return - # try to load config from the given path - try: - config = configuration.config_from_ini(path, read_from_file=True) - except FileNotFoundError: - if not require: - log.warning("INI config file not found: %s", path) - return - raise + # try to load config with standard parser, and default vars + here = os.path.dirname(path) + config = configparser.ConfigParser(defaults={'here': here, '__file__': path}) + if not config.read(path): + if require: + raise FileNotFoundError(f"could not read required config file: {path}") + return - # ok add that one to the mix + # load all values into (yet another) temp config + temp_config = configparser.RawConfigParser() + for section in config.sections(): + temp_config.add_section(section) + # nb. must interpolate most values but *not* for logging formatters + raw = section.startswith('formatter_') + for option in config.options(section): + temp_config.set(section, option, config.get(section, option, raw=raw)) + + # re-write as temp file with "final" values + fd, temp_path = tempfile.mkstemp(suffix='.ini') + os.close(fd) + with open(temp_path, 'wt') as f: + temp_config.write(f) + + # and finally, load that into our main config + config = configuration.config_from_ini(temp_path, read_from_file=True) configs.append(config) self.files_read.append(path) - # need parent folder of that path, for %(here)s interpolation - here = os.path.dirname(path) - # bring in any "required" files requires = config.get(f'{self.appname}.config.require') if requires: for path in self.parse_list(requires): - path = path % {'here': here} self._load_ini_configs(path, configs, require=True) # bring in any "included" files includes = config.get(f'{self.appname}.config.include') if includes: for path in self.parse_list(includes): - path = path % {'here': here} self._load_ini_configs(path, configs, require=False) def get_prioritized_files(self): diff --git a/tests/test_conf.py b/tests/test_conf.py index b5b8771..5b46539 100644 --- a/tests/test_conf.py +++ b/tests/test_conf.py @@ -159,6 +159,17 @@ require = %(here)s/first.conf self.assertEqual(files[0], second) self.assertEqual(files[1], first) + def test_default_vars_interpolated(self): + myconf = self.write_file('my.conf', """ +[foo] +bar = %(here)s/bar.txt +baz = %(__file__)s +""") + + config = conf.WuttaConfig(files=[myconf]) + self.assertEqual(config.get('foo.bar'), f'{self.tempdir}/bar.txt') + self.assertEqual(config.get('foo.baz'), myconf) + def test_constructor_defaults(self): config = conf.WuttaConfig() self.assertEqual(config.defaults, {})