comparison 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
comparison
equal deleted inserted replaced
-1:000000000000 0:a212a3f2e3ee
1 import json
2 import os.path
3 import types
4 import codecs
5 import hashlib
6 import logging
7 import yaml
8 from cache import SimpleCache
9 from decorators import lazy_property
10 from plugins import PluginLoader
11 from environment import StandardEnvironment
12 from configuration import Configuration, merge_dicts
13
14
15 APP_VERSION = '2.0.0alpha'
16 CACHE_VERSION = '2.0'
17
18 CACHE_DIR = '_cache'
19 TEMPLATES_DIR = '_content/templates'
20 PAGES_DIR = '_content/pages'
21 POSTS_DIR = '_content/posts'
22 PLUGINS_DIR = '_content/plugins'
23 THEME_DIR = '_content/theme'
24
25 CONFIG_PATH = '_content/config.yml'
26 THEME_CONFIG_PATH = '_content/theme_config.yml'
27
28
29 logger = logging.getLogger(__name__)
30
31
32 class VariantNotFoundError(Exception):
33 def __init__(self, variant_path, message=None):
34 super(VariantNotFoundError, self).__init__(
35 message or ("No such configuration variant: %s" % variant_path))
36
37
38 class PieCrustConfiguration(Configuration):
39 def __init__(self, paths=None, cache_dir=False):
40 super(PieCrustConfiguration, self).__init__()
41 self.paths = paths
42 self.cache_dir = cache_dir
43 self.fixups = []
44
45 def applyVariant(self, variant_path, raise_if_not_found=True):
46 variant = self.get(variant_path)
47 if variant is None:
48 if raise_if_not_found:
49 raise VariantNotFoundError(variant_path)
50 return
51 if not isinstance(variant, dict):
52 raise VariantNotFoundError(variant_path,
53 "Configuration variant '%s' is not an array. "
54 "Check your configuration file." % variant_path)
55 self.merge(variant)
56
57 def _load(self):
58 if self.paths is None:
59 self._values = self._validateAll({})
60 return
61
62 path_times = filter(self.paths,
63 lambda p: os.path.getmtime(p))
64 cache_key = hashlib.md5("version=%s&cache=%s" % (
65 APP_VERSION, CACHE_VERSION))
66
67 cache = None
68 if self.cache_dir:
69 cache = SimpleCache(self.cache_dir)
70
71 if cache is not None:
72 if cache.isValid('config.json', path_times):
73 config_text = cache.read('config.json')
74 self._values = json.loads(config_text)
75
76 actual_cache_key = self._values.get('__cache_key')
77 if actual_cache_key == cache_key:
78 return
79
80 values = {}
81 for i, p in enumerate(self.paths):
82 with codecs.open(p, 'r', 'utf-8') as fp:
83 loaded_values = yaml.load(fp.read())
84 for fixup in self.fixups:
85 fixup(i, loaded_values)
86 merge_dicts(values, loaded_values)
87
88 for fixup in self.fixups:
89 fixup(len(self.paths), values)
90
91 self._values = self._validateAll(values)
92
93 if cache is not None:
94 self._values['__cache_key'] = cache_key
95 config_text = json.dumps(self._values)
96 cache.write('config.json', config_text)
97
98
99 class PieCrust(object):
100 def __init__(self, root, cache=True, debug=False, env=None):
101 self.root = root
102 self.debug = debug
103 self.cache = cache
104 self.plugin_loader = PluginLoader(self)
105 self.env = env
106 if self.env is None:
107 self.env = StandardEnvironment()
108 self.env.initialize(self)
109
110 @lazy_property
111 def config(self):
112 logger.debug("Loading site configuration...")
113 paths = []
114 if self.theme_dir:
115 paths.append(os.path.join(self.theme_dir, THEME_CONFIG_PATH))
116 paths.append(os.path.join(self.root, CONFIG_PATH))
117
118 config = PieCrustConfiguration(paths, self.cache_dir)
119 if self.theme_dir:
120 # We'll need to patch the templates directories to be relative
121 # to the site's root, and not the theme root.
122 def _fixupThemeTemplatesDir(index, config):
123 if index == 0:
124 sitec = config.get('site')
125 if sitec:
126 tplc = sitec.get('templates_dirs')
127 if tplc:
128 if isinstance(tplc, types.StringTypes):
129 tplc = [tplc]
130 sitec['templates_dirs'] = filter(tplc,
131 lambda p: os.path.join(self.theme_dir, p))
132
133 config.fixups.append(_fixupThemeTemplatesDir)
134
135 return config
136
137 @lazy_property
138 def templates_dirs(self):
139 templates_dirs = self._get_configurable_dirs(TEMPLATES_DIR,
140 'site/templates_dirs')
141
142 # Also, add the theme directory, if nay.
143 if self.theme_dir:
144 default_theme_dir = os.path.join(self.theme_dir, TEMPLATES_DIR)
145 if os.path.isdir(default_theme_dir):
146 templates_dirs.append(default_theme_dir)
147
148 return templates_dirs
149
150 @lazy_property
151 def pages_dir(self):
152 return self._get_dir(PAGES_DIR)
153
154 @lazy_property
155 def posts_dir(self):
156 return self._get_dir(POSTS_DIR)
157
158 @lazy_property
159 def plugins_dirs(self):
160 return self._get_configurable_dirs(PLUGINS_DIR,
161 'site/plugins_dirs')
162
163 @lazy_property
164 def theme_dir(self):
165 return self._get_dir(THEME_DIR)
166
167 @lazy_property
168 def cache_dir(self):
169 if self.cache:
170 return os.path.join(self.root, CACHE_DIR)
171 return False
172
173 def _get_dir(self, default_rel_dir):
174 abs_dir = os.path.join(self.root, default_rel_dir)
175 if os.path.isdir(abs_dir):
176 return abs_dir
177 return False
178
179 def _get_configurable_dirs(self, default_rel_dir, conf_name):
180 dirs = []
181
182 # Add custom directories from the configuration.
183 conf_dirs = self.config.get(conf_name)
184 if conf_dirs is not None:
185 dirs += filter(conf_dirs,
186 lambda p: os.path.join(self.root, p))
187
188 # Add the default directory if it exists.
189 default_dir = os.path.join(self.root, default_rel_dir)
190 if os.path.isdir(default_dir):
191 dirs.append(default_dir)
192
193 return dirs
194