Mercurial > piecrust2
diff piecrust/appconfig.py @ 666:81d9c3a3a0b5
internal: Get rid of the whole "sub cache" business.
* Compute cache keys up front, so the cache directory is only chosen once.
* Buffer up config variants to apply before loading the config. Makes it
possible to cache variant-resulting configs, too.
* Make a factory class to reuse the logic that creates the `PieCrust` object
correctly for multi-process workers and such.
* Add a test.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Thu, 03 Mar 2016 08:22:41 -0800 |
parents | 3ceeca7bb71c |
children | 3df808b133f8 |
line wrap: on
line diff
--- a/piecrust/appconfig.py Thu Mar 03 08:19:28 2016 -0800 +++ b/piecrust/appconfig.py Thu Mar 03 08:22:41 2016 -0800 @@ -14,7 +14,7 @@ from piecrust.cache import NullCache from piecrust.configuration import ( Configuration, ConfigurationError, ConfigurationLoader, - merge_dicts, visit_dict) + get_dict_value, set_dict_value, merge_dicts, visit_dict) from piecrust.sources.base import REALM_USER, REALM_THEME @@ -22,55 +22,100 @@ class VariantNotFoundError(Exception): - def __init__(self, variant_path, message=None): + def __init__(self, variant_name, message=None): super(VariantNotFoundError, self).__init__( message or ("No such configuration variant: %s" % - variant_path)) + variant_name)) + + +def _make_variant_fixup(variant_name, raise_if_not_found): + def _variant_fixup(index, config): + if index != -1: + return + try: + try: + v = get_dict_value(config, 'variants/%s' % variant_name) + except KeyError: + raise VariantNotFoundError(variant_name) + if not isinstance(v, dict): + raise VariantNotFoundError( + variant_name, + "Configuration variant '%s' is not an array. " + "Check your configuration file." % variant_name) + merge_dicts(config, v) + except VariantNotFoundError: + if raise_if_not_found: + raise + + return _variant_fixup class PieCrustConfiguration(Configuration): def __init__(self, paths=None, cache=None, values=None, validate=True, theme_config=False): super(PieCrustConfiguration, self).__init__() - self.paths = paths - self.cache = cache or NullCache() - self.fixups = [] + self._paths = paths + self._cache = cache or NullCache() + self._fixups = [] self.theme_config = theme_config # Set the values after we set the rest, since our validation needs # our attributes. if values: self.setAll(values, validate=validate) - def applyVariant(self, variant_path, raise_if_not_found=True): - variant = self.get(variant_path) - if variant is None: - if raise_if_not_found: - raise VariantNotFoundError(variant_path) - return - if not isinstance(variant, dict): - raise VariantNotFoundError( - variant_path, - "Configuration variant '%s' is not an array. " - "Check your configuration file." % variant_path) - self.merge(variant) + def addFixup(self, f): + self._ensureNotLoaded() + self._fixups.append(f) + + def addPath(self, p, first=False): + self._ensureNotLoaded() + if not first: + self._paths.append(p) + else: + self._paths.insert(0, p) + + def addVariant(self, variant_path, raise_if_not_found=True): + self._ensureNotLoaded() + if os.path.isfile(variant_path): + self.addPath(variant_path) + else: + name, _ = os.path.splitext(os.path.basename(variant_path)) + fixup = _make_variant_fixup(name, raise_if_not_found) + self.addFixup(fixup) + + logger.warning( + "Configuration variants should now be `.yml` files located " + "in the `configs/` directory of your website.") + logger.warning( + "Variants defined in the site configuration will be " + "deprecated in a future version of PieCrust.") + + def addVariantValue(self, path, value): + def _fixup(index, config): + set_dict_value(config, path, value) + self.addFixup(_fixup) + + def _ensureNotLoaded(self): + if self._values is not None: + raise Exception("The configurations has been loaded.") def _load(self): - if self.paths is None: + if self._paths is None: self._values = self._validateAll({}) return - path_times = [os.path.getmtime(p) for p in self.paths] + path_times = [os.path.getmtime(p) for p in self._paths] cache_key_hash = hashlib.md5( ("version=%s&cache=%d" % ( APP_VERSION, CACHE_VERSION)).encode('utf8')) - for p in self.paths: + for p in self._paths: cache_key_hash.update(("&path=%s" % p).encode('utf8')) cache_key = cache_key_hash.hexdigest() - if self.cache.isValid('config.json', path_times): + if self._cache.isValid('config.json', path_times): logger.debug("Loading configuration from cache...") - config_text = self.cache.read('config.json') + config_text = self._cache.read('config.json') self._values = json.loads( config_text, object_pairs_hook=collections.OrderedDict) @@ -82,32 +127,32 @@ logger.debug("Outdated cache key '%s' (expected '%s')." % ( actual_cache_key, cache_key)) - logger.debug("Loading configuration from: %s" % self.paths) + logger.debug("Loading configuration from: %s" % self._paths) values = {} try: - for i, p in enumerate(self.paths): + for i, p in enumerate(self._paths): with open(p, 'r', encoding='utf-8') as fp: loaded_values = yaml.load( fp.read(), Loader=ConfigurationLoader) if loaded_values is None: loaded_values = {} - for fixup in self.fixups: + for fixup in self._fixups: fixup(i, loaded_values) merge_dicts(values, loaded_values) - for fixup in self.fixups: - fixup(len(self.paths), values) + for fixup in self._fixups: + fixup(-1, values) self._values = self._validateAll(values) except Exception as ex: raise Exception("Error loading configuration from: %s" % - ', '.join(self.paths)) from ex + ', '.join(self._paths)) from ex logger.debug("Caching configuration...") self._values['__cache_key'] = cache_key config_text = json.dumps(self._values) - self.cache.write('config.json', config_text) + self._cache.write('config.json', config_text) self._values['__cache_valid'] = False