diff piecrust/app.py @ 0:a212a3f2e3ee

Initial commit.
author Ludovic Chabant <ludovic@chabant.com>
date Sat, 21 Dec 2013 14:44:02 -0800
parents
children aaa8fb7c8918
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/piecrust/app.py	Sat Dec 21 14:44:02 2013 -0800
@@ -0,0 +1,194 @@
+import json
+import os.path
+import types
+import codecs
+import hashlib
+import logging
+import yaml
+from cache import SimpleCache
+from decorators import lazy_property
+from plugins import PluginLoader
+from environment import StandardEnvironment
+from configuration import Configuration, merge_dicts
+
+
+APP_VERSION = '2.0.0alpha'
+CACHE_VERSION = '2.0'
+
+CACHE_DIR = '_cache'
+TEMPLATES_DIR = '_content/templates'
+PAGES_DIR = '_content/pages'
+POSTS_DIR = '_content/posts'
+PLUGINS_DIR = '_content/plugins'
+THEME_DIR = '_content/theme'
+
+CONFIG_PATH = '_content/config.yml'
+THEME_CONFIG_PATH = '_content/theme_config.yml'
+
+
+logger = logging.getLogger(__name__)
+
+
+class VariantNotFoundError(Exception):
+    def __init__(self, variant_path, message=None):
+        super(VariantNotFoundError, self).__init__(
+                message or ("No such configuration variant: %s" % variant_path))
+
+
+class PieCrustConfiguration(Configuration):
+    def __init__(self, paths=None, cache_dir=False):
+        super(PieCrustConfiguration, self).__init__()
+        self.paths = paths
+        self.cache_dir = cache_dir
+        self.fixups = []
+
+    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 _load(self):
+        if self.paths is None:
+            self._values = self._validateAll({})
+            return
+        
+        path_times = filter(self.paths,
+                lambda p: os.path.getmtime(p))
+        cache_key = hashlib.md5("version=%s&cache=%s" % (
+                APP_VERSION, CACHE_VERSION))
+        
+        cache = None
+        if self.cache_dir:
+            cache = SimpleCache(self.cache_dir)
+
+        if cache is not None:
+            if cache.isValid('config.json', path_times):
+                config_text = cache.read('config.json')
+                self._values = json.loads(config_text)
+                
+                actual_cache_key = self._values.get('__cache_key')
+                if actual_cache_key == cache_key:
+                    return
+
+        values = {}
+        for i, p in enumerate(self.paths):
+            with codecs.open(p, 'r', 'utf-8') as fp:
+                loaded_values = yaml.load(fp.read())
+            for fixup in self.fixups:
+                fixup(i, loaded_values)
+            merge_dicts(values, loaded_values)
+
+        for fixup in self.fixups:
+            fixup(len(self.paths), values)
+
+        self._values = self._validateAll(values)
+
+        if cache is not None:
+            self._values['__cache_key'] = cache_key
+            config_text = json.dumps(self._values)
+            cache.write('config.json', config_text)
+
+
+class PieCrust(object):
+    def __init__(self, root, cache=True, debug=False, env=None):
+        self.root = root
+        self.debug = debug
+        self.cache = cache
+        self.plugin_loader = PluginLoader(self)
+        self.env = env
+        if self.env is None:
+            self.env = StandardEnvironment()
+        self.env.initialize(self)
+
+    @lazy_property
+    def config(self):
+        logger.debug("Loading site configuration...")
+        paths = []
+        if self.theme_dir:
+            paths.append(os.path.join(self.theme_dir, THEME_CONFIG_PATH))
+        paths.append(os.path.join(self.root, CONFIG_PATH))
+
+        config = PieCrustConfiguration(paths, self.cache_dir)
+        if self.theme_dir:
+            # We'll need to patch the templates directories to be relative
+            # to the site's root, and not the theme root.
+            def _fixupThemeTemplatesDir(index, config):
+                if index == 0:
+                    sitec = config.get('site')
+                    if sitec:
+                        tplc = sitec.get('templates_dirs')
+                        if tplc:
+                            if isinstance(tplc, types.StringTypes):
+                                tplc = [tplc]
+                            sitec['templates_dirs'] = filter(tplc,
+                                    lambda p: os.path.join(self.theme_dir, p))
+
+            config.fixups.append(_fixupThemeTemplatesDir)
+
+        return config
+
+    @lazy_property
+    def templates_dirs(self):
+        templates_dirs = self._get_configurable_dirs(TEMPLATES_DIR,
+                'site/templates_dirs')
+
+        # Also, add the theme directory, if nay.
+        if self.theme_dir:
+            default_theme_dir = os.path.join(self.theme_dir, TEMPLATES_DIR)
+            if os.path.isdir(default_theme_dir):
+                templates_dirs.append(default_theme_dir)
+
+        return templates_dirs
+
+    @lazy_property
+    def pages_dir(self):
+        return self._get_dir(PAGES_DIR)
+
+    @lazy_property
+    def posts_dir(self):
+        return self._get_dir(POSTS_DIR)
+
+    @lazy_property
+    def plugins_dirs(self):
+        return self._get_configurable_dirs(PLUGINS_DIR,
+                'site/plugins_dirs')
+
+    @lazy_property
+    def theme_dir(self):
+        return self._get_dir(THEME_DIR)
+
+    @lazy_property
+    def cache_dir(self):
+        if self.cache:
+            return os.path.join(self.root, CACHE_DIR)
+        return False
+
+    def _get_dir(self, default_rel_dir):
+        abs_dir = os.path.join(self.root, default_rel_dir)
+        if os.path.isdir(abs_dir):
+            return abs_dir
+        return False
+
+    def _get_configurable_dirs(self, default_rel_dir, conf_name):
+        dirs = []
+
+        # Add custom directories from the configuration.
+        conf_dirs = self.config.get(conf_name)
+        if conf_dirs is not None:
+            dirs += filter(conf_dirs,
+                    lambda p: os.path.join(self.root, p))
+
+        # Add the default directory if it exists.
+        default_dir = os.path.join(self.root, default_rel_dir)
+        if os.path.isdir(default_dir):
+            dirs.append(default_dir)
+
+        return dirs
+