Mercurial > piecrust2
comparison piecrust/app.py @ 852:4850f8c21b6e
core: Start of the big refactor for PieCrust 3.0.
* Everything is a `ContentSource`, including assets directories.
* Most content sources are subclasses of the base file-system source.
* A source is processed by a "pipeline", and there are 2 built-in pipelines,
one for assets and one for pages. The asset pipeline is vaguely functional,
but the page pipeline is completely broken right now.
* Rewrite the baking process as just running appropriate pipelines on each
content item. This should allow for better parallelization.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Wed, 17 May 2017 00:11:48 -0700 |
parents | 7d83b9484b98 |
children | f070a4fc033c |
comparison
equal
deleted
inserted
replaced
851:2c7e57d80bba | 852:4850f8c21b6e |
---|---|
1 import time | 1 import time |
2 import os.path | 2 import os.path |
3 import hashlib | |
4 import logging | 3 import logging |
5 import urllib.parse | 4 import urllib.parse |
6 from werkzeug.utils import cached_property | 5 from werkzeug.utils import cached_property |
7 from piecrust import ( | 6 from piecrust import ( |
8 RESOURCES_DIR, | 7 RESOURCES_DIR, |
9 CACHE_DIR, TEMPLATES_DIR, ASSETS_DIR, | 8 CACHE_DIR, TEMPLATES_DIR, ASSETS_DIR, |
10 THEME_DIR, PLUGINS_DIR, | 9 THEME_DIR, PLUGINS_DIR, |
11 CONFIG_PATH, THEME_CONFIG_PATH) | 10 CONFIG_PATH, THEME_CONFIG_PATH) |
12 from piecrust.appconfig import PieCrustConfiguration | 11 from piecrust.appconfig import PieCrustConfiguration |
13 from piecrust.cache import ExtensibleCache, NullExtensibleCache | 12 from piecrust.cache import ExtensibleCache, NullExtensibleCache |
14 from piecrust.configuration import ConfigurationError, merge_dicts | 13 from piecrust.configuration import ConfigurationError |
15 from piecrust.environment import StandardEnvironment | 14 from piecrust.environment import StandardEnvironment |
15 from piecrust.page import Page | |
16 from piecrust.plugins.base import PluginLoader | 16 from piecrust.plugins.base import PluginLoader |
17 from piecrust.routing import Route | 17 from piecrust.routing import Route |
18 from piecrust.sources.base import REALM_THEME | |
19 | 18 |
20 | 19 |
21 logger = logging.getLogger(__name__) | 20 logger = logging.getLogger(__name__) |
22 | 21 |
23 | 22 |
37 | 36 |
38 self.env = env | 37 self.env = env |
39 if self.env is None: | 38 if self.env is None: |
40 self.env = StandardEnvironment() | 39 self.env = StandardEnvironment() |
41 self.env.initialize(self) | 40 self.env.initialize(self) |
42 self.env.registerTimer('SiteConfigLoad') | 41 self.env.stats.registerTimer('SiteConfigLoad') |
43 self.env.registerTimer('PageLoad') | 42 self.env.stats.registerTimer('PageLoad') |
44 self.env.registerTimer("PageDataBuild") | 43 self.env.stats.registerTimer("PageDataBuild") |
45 self.env.registerTimer("BuildRenderData") | 44 self.env.stats.registerTimer("BuildRenderData") |
46 self.env.registerTimer("PageRender") | 45 self.env.stats.registerTimer("PageRender") |
47 self.env.registerTimer("PageRenderSegments") | 46 self.env.stats.registerTimer("PageRenderSegments") |
48 self.env.registerTimer("PageRenderLayout") | 47 self.env.stats.registerTimer("PageRenderLayout") |
49 self.env.registerTimer("PageSerialize") | 48 self.env.stats.registerTimer("PageSerialize") |
50 | 49 |
51 @cached_property | 50 @cached_property |
52 def config(self): | 51 def config(self): |
53 logger.debug("Creating site configuration...") | 52 logger.debug("Creating site configuration...") |
54 start_time = time.perf_counter() | 53 start_time = time.perf_counter() |
62 if not self.theme_site and self.theme_dir: | 61 if not self.theme_site and self.theme_dir: |
63 theme_path = os.path.join(self.theme_dir, THEME_CONFIG_PATH) | 62 theme_path = os.path.join(self.theme_dir, THEME_CONFIG_PATH) |
64 | 63 |
65 config_cache = self.cache.getCache('app') | 64 config_cache = self.cache.getCache('app') |
66 config = PieCrustConfiguration( | 65 config = PieCrustConfiguration( |
67 path=path, theme_path=theme_path, | 66 path=path, theme_path=theme_path, |
68 cache=config_cache, theme_config=self.theme_site) | 67 cache=config_cache, theme_config=self.theme_site) |
69 | 68 |
70 local_path = os.path.join( | 69 local_path = os.path.join( |
71 self.root_dir, 'configs', 'local.yml') | 70 self.root_dir, 'configs', 'local.yml') |
72 config.addVariant(local_path, raise_if_not_found=False) | 71 config.addVariant(local_path, raise_if_not_found=False) |
73 | 72 |
74 if self.theme_site: | 73 if self.theme_site: |
75 variant_path = os.path.join( | 74 variant_path = os.path.join( |
76 self.root_dir, 'configs', 'theme_preview.yml') | 75 self.root_dir, 'configs', 'theme_preview.yml') |
77 config.addVariant(variant_path, raise_if_not_found=False) | 76 config.addVariant(variant_path, raise_if_not_found=False) |
78 | 77 |
79 self.env.stepTimer('SiteConfigLoad', time.perf_counter() - start_time) | 78 self.env.stats.stepTimer('SiteConfigLoad', |
79 time.perf_counter() - start_time) | |
80 return config | 80 return config |
81 | 81 |
82 @cached_property | 82 @cached_property |
83 def assets_dirs(self): | 83 def assets_dirs(self): |
84 assets_dirs = self._get_configurable_dirs( | 84 assets_dirs = self._get_configurable_dirs( |
85 ASSETS_DIR, 'site/assets_dirs') | 85 ASSETS_DIR, 'site/assets_dirs') |
86 | 86 |
87 # Also add the theme directory, if any. | 87 # Also add the theme directory, if any. |
88 if self.theme_dir: | 88 if self.theme_dir: |
89 default_theme_dir = os.path.join(self.theme_dir, ASSETS_DIR) | 89 default_theme_dir = os.path.join(self.theme_dir, ASSETS_DIR) |
90 if os.path.isdir(default_theme_dir): | 90 if os.path.isdir(default_theme_dir): |
93 return assets_dirs | 93 return assets_dirs |
94 | 94 |
95 @cached_property | 95 @cached_property |
96 def templates_dirs(self): | 96 def templates_dirs(self): |
97 templates_dirs = self._get_configurable_dirs( | 97 templates_dirs = self._get_configurable_dirs( |
98 TEMPLATES_DIR, 'site/templates_dirs') | 98 TEMPLATES_DIR, 'site/templates_dirs') |
99 | 99 |
100 # Also, add the theme directory, if any. | 100 # Also, add the theme directory, if any. |
101 if self.theme_dir: | 101 if self.theme_dir: |
102 default_theme_dir = os.path.join(self.theme_dir, TEMPLATES_DIR) | 102 default_theme_dir = os.path.join(self.theme_dir, TEMPLATES_DIR) |
103 if os.path.isdir(default_theme_dir): | 103 if os.path.isdir(default_theme_dir): |
146 if cls is None: | 146 if cls is None: |
147 raise ConfigurationError("No such page source type: %s" % | 147 raise ConfigurationError("No such page source type: %s" % |
148 s['type']) | 148 s['type']) |
149 src = cls(self, n, s) | 149 src = cls(self, n, s) |
150 sources.append(src) | 150 sources.append(src) |
151 | |
151 return sources | 152 return sources |
152 | 153 |
153 @cached_property | 154 @cached_property |
154 def routes(self): | 155 def routes(self): |
155 routes = [] | 156 routes = [] |
156 for r in self.config.get('site/routes'): | 157 for r in self.config.get('site/routes'): |
157 rte = Route(self, r) | 158 rte = Route(self, r) |
158 routes.append(rte) | 159 routes.append(rte) |
159 return routes | 160 return routes |
160 | |
161 @cached_property | |
162 def generators(self): | |
163 defs = {} | |
164 for cls in self.plugin_loader.getPageGenerators(): | |
165 defs[cls.GENERATOR_NAME] = cls | |
166 | |
167 gens = [] | |
168 for n, g in self.config.get('site/generators').items(): | |
169 cls = defs.get(g['type']) | |
170 if cls is None: | |
171 raise ConfigurationError("No such page generator type: %s" % | |
172 g['type']) | |
173 gen = cls(self, n, g) | |
174 gens.append(gen) | |
175 return gens | |
176 | 161 |
177 @cached_property | 162 @cached_property |
178 def publishers(self): | 163 def publishers(self): |
179 defs_by_name = {} | 164 defs_by_name = {} |
180 defs_by_scheme = {} | 165 defs_by_scheme = {} |
195 elif isinstance(t, str): | 180 elif isinstance(t, str): |
196 comps = urllib.parse.urlparse(t) | 181 comps = urllib.parse.urlparse(t) |
197 pub_type = comps.scheme | 182 pub_type = comps.scheme |
198 is_scheme = True | 183 is_scheme = True |
199 cls = (defs_by_scheme.get(pub_type) if is_scheme | 184 cls = (defs_by_scheme.get(pub_type) if is_scheme |
200 else defs_by_name.get(pub_type)) | 185 else defs_by_name.get(pub_type)) |
201 if cls is None: | 186 if cls is None: |
202 raise ConfigurationError("No such publisher: %s" % pub_type) | 187 raise ConfigurationError("No such publisher: %s" % pub_type) |
203 tgt = cls(self, n, t) | 188 tgt = cls(self, n, t) |
204 tgts.append(tgt) | 189 tgts.append(tgt) |
205 return tgts | 190 return tgts |
208 for source in self.sources: | 193 for source in self.sources: |
209 if source.name == source_name: | 194 if source.name == source_name: |
210 return source | 195 return source |
211 return None | 196 return None |
212 | 197 |
213 def getGenerator(self, generator_name): | |
214 for gen in self.generators: | |
215 if gen.name == generator_name: | |
216 return gen | |
217 return None | |
218 | |
219 def getSourceRoutes(self, source_name): | 198 def getSourceRoutes(self, source_name): |
220 for route in self.routes: | 199 for route in self.routes: |
221 if route.source_name == source_name: | 200 if route.source_name == source_name: |
222 yield route | 201 yield route |
223 | 202 |
224 def getSourceRoute(self, source_name, route_metadata): | 203 def getSourceRoute(self, source_name, route_params): |
225 for route in self.getSourceRoutes(source_name): | 204 for route in self.getSourceRoutes(source_name): |
226 if (route_metadata is None or | 205 if (route_params is None or |
227 route.matchesMetadata(route_metadata)): | 206 route.matchesParameters(route_params)): |
228 return route | |
229 return None | |
230 | |
231 def getGeneratorRoute(self, generator_name): | |
232 for route in self.routes: | |
233 if route.generator_name == generator_name: | |
234 return route | 207 return route |
235 return None | 208 return None |
236 | 209 |
237 def getPublisher(self, target_name): | 210 def getPublisher(self, target_name): |
238 for pub in self.publishers: | 211 for pub in self.publishers: |
239 if pub.target == target_name: | 212 if pub.target == target_name: |
240 return pub | 213 return pub |
241 return None | 214 return None |
215 | |
216 def getPage(self, content_item): | |
217 cache_key = content_item.spec | |
218 return self.env.page_repository.get( | |
219 cache_key, | |
220 lambda: Page(content_item)) | |
242 | 221 |
243 def _get_dir(self, default_rel_dir): | 222 def _get_dir(self, default_rel_dir): |
244 abs_dir = os.path.join(self.root_dir, default_rel_dir) | 223 abs_dir = os.path.join(self.root_dir, default_rel_dir) |
245 if os.path.isdir(abs_dir): | 224 if os.path.isdir(abs_dir): |
246 return abs_dir | 225 return abs_dir |
267 | 246 |
268 def apply_variant_and_values(app, config_variant=None, config_values=None): | 247 def apply_variant_and_values(app, config_variant=None, config_values=None): |
269 if config_variant is not None: | 248 if config_variant is not None: |
270 logger.debug("Adding configuration variant '%s'." % config_variant) | 249 logger.debug("Adding configuration variant '%s'." % config_variant) |
271 variant_path = os.path.join( | 250 variant_path = os.path.join( |
272 app.root_dir, 'configs', '%s.yml' % config_variant) | 251 app.root_dir, 'configs', '%s.yml' % config_variant) |
273 app.config.addVariant(variant_path) | 252 app.config.addVariant(variant_path) |
274 | 253 |
275 if config_values is not None: | 254 if config_values is not None: |
276 for name, value in config_values: | 255 for name, value in config_values: |
277 logger.debug("Adding configuration override '%s': %s" % (name, value)) | 256 logger.debug("Adding configuration override '%s': %s" % |
257 (name, value)) | |
278 app.config.addVariantValue(name, value) | 258 app.config.addVariantValue(name, value) |
279 | 259 |
280 | 260 |
281 class PieCrustFactory(object): | 261 class PieCrustFactory(object): |
262 """ A class that builds a PieCrust app instance. | |
263 """ | |
282 def __init__( | 264 def __init__( |
283 self, root_dir, *, | 265 self, root_dir, *, |
284 cache=True, cache_key=None, | 266 cache=True, cache_key=None, |
285 config_variant=None, config_values=None, | 267 config_variant=None, config_values=None, |
286 debug=False, theme_site=False): | 268 debug=False, theme_site=False): |
292 self.debug = debug | 274 self.debug = debug |
293 self.theme_site = theme_site | 275 self.theme_site = theme_site |
294 | 276 |
295 def create(self): | 277 def create(self): |
296 app = PieCrust( | 278 app = PieCrust( |
297 self.root_dir, | 279 self.root_dir, |
298 cache=self.cache, | 280 cache=self.cache, |
299 cache_key=self.cache_key, | 281 cache_key=self.cache_key, |
300 debug=self.debug, | 282 debug=self.debug, |
301 theme_site=self.theme_site) | 283 theme_site=self.theme_site) |
302 apply_variant_and_values( | 284 apply_variant_and_values( |
303 app, self.config_variant, self.config_values) | 285 app, self.config_variant, self.config_values) |
304 return app | 286 return app |
305 | 287 |