comparison piecrust/templating/pystacheengine.py @ 850:370e74941d32

optimize: Only load some 3rd party packages when needed. This commit only optimizes the Markdown, SmartyPants, and Pystache wrappers.
author Ludovic Chabant <ludovic@chabant.com>
date Sat, 29 Apr 2017 21:27:33 -0700
parents 9d1a89cd8146
children 1bb704434ee2
comparison
equal deleted inserted replaced
849:8f8bbb2e70e1 850:370e74941d32
1 import logging 1 import logging
2 import collections.abc 2 import collections.abc
3 import pystache
4 import pystache.common
5 from piecrust.templating.base import ( 3 from piecrust.templating.base import (
6 TemplateEngine, TemplateNotFoundError, TemplatingError) 4 TemplateEngine, TemplateNotFoundError, TemplatingError)
7 5
8 6
9 logger = logging.getLogger(__name__) 7 logger = logging.getLogger(__name__)
10 8
11 9
13 ENGINE_NAMES = ['mustache'] 11 ENGINE_NAMES = ['mustache']
14 EXTENSIONS = ['mustache'] 12 EXTENSIONS = ['mustache']
15 13
16 def __init__(self): 14 def __init__(self):
17 self.renderer = None 15 self.renderer = None
16 self._not_found_error = None
17 self._pystache_error = None
18 18
19 def renderSegmentPart(self, path, seg_part, data): 19 def renderSegmentPart(self, path, seg_part, data):
20 self._ensureLoaded() 20 self._ensureLoaded()
21 try: 21 try:
22 return self.renderer.render(seg_part.content, data) 22 return self.renderer.render(seg_part.content, data)
23 except pystache.common.TemplateNotFoundError as ex: 23 except self._not_found_error as ex:
24 raise TemplateNotFoundError() from ex 24 raise TemplateNotFoundError() from ex
25 except pystache.common.PystacheError as ex: 25 except self._pystache_error as ex:
26 raise TemplatingError(str(ex), path) from ex 26 raise TemplatingError(str(ex), path) from ex
27 27
28 def renderFile(self, paths, data): 28 def renderFile(self, paths, data):
29 self._ensureLoaded() 29 self._ensureLoaded()
30 tpl = None 30 tpl = None
31 logger.debug("Looking for template: %s" % paths) 31 logger.debug("Looking for template: %s" % paths)
32 for p in paths: 32 for p in paths:
33 if not p.endswith('.mustache'): 33 if not p.endswith('.mustache'):
34 raise TemplatingError( 34 raise TemplatingError(
35 "The Mustache template engine only accepts template " 35 "The Mustache template engine only accepts template "
36 "filenames with a `.mustache` extension. Got: %s" % 36 "filenames with a `.mustache` extension. Got: %s" %
37 p) 37 p)
38 name = p[:-9] # strip `.mustache` 38 name = p[:-9] # strip `.mustache`
39 try: 39 try:
40 tpl = self.renderer.load_template(name) 40 tpl = self.renderer.load_template(name)
41 except Exception as ex: 41 except Exception as ex:
42 logger.debug("Mustache error: %s" % ex) 42 logger.debug("Mustache error: %s" % ex)
45 if tpl is None: 45 if tpl is None:
46 raise TemplateNotFoundError() 46 raise TemplateNotFoundError()
47 47
48 try: 48 try:
49 return self.renderer.render(tpl, data) 49 return self.renderer.render(tpl, data)
50 except pystache.common.PystacheError as ex: 50 except self._pystache_error as ex:
51 raise TemplatingError(str(ex)) from ex 51 raise TemplatingError(str(ex)) from ex
52 52
53 def _ensureLoaded(self): 53 def _ensureLoaded(self):
54 if self.renderer: 54 if self.renderer:
55 return 55 return
56 56
57 import pystache
58 import pystache.common
59
60 self._not_found_error = pystache.common.TemplateNotFoundError
61 self._pystache_error = pystache.common.PystacheError
62
63 class _WorkaroundRenderer(pystache.Renderer):
64 def _make_resolve_context(self):
65 mrc = super(_WorkaroundRenderer, self)._make_resolve_context()
66
67 def _workaround(stack, name):
68 # Pystache will treat anything that's not a string or
69 # a dict as a list. This is just plain wrong, but it will
70 # take a while before the project can get patches on Pypi.
71 res = mrc(stack, name)
72 if res is not None and (
73 res.__class__.__name__ in _knowns or
74 isinstance(res, collections.abc.Mapping)):
75 res = [res]
76 return res
77
78 return _workaround
79
57 self.renderer = _WorkaroundRenderer( 80 self.renderer = _WorkaroundRenderer(
58 search_dirs=self.app.templates_dirs) 81 search_dirs=self.app.templates_dirs)
59 82
60 83
61 _knowns = ['PieCrustData', 'LazyPageConfigData', 'Paginator', 'Assetor', 84 _knowns = ['PieCrustData', 'LazyPageConfigData', 'Paginator', 'Assetor',
62 'PageLinkerData'] 85 'PageLinkerData']
63
64
65 class _WorkaroundRenderer(pystache.Renderer):
66 def _make_resolve_context(self):
67 mrc = super(_WorkaroundRenderer, self)._make_resolve_context()
68
69 def _workaround(stack, name):
70 # Pystache will treat anything that's not a string or a dict as
71 # a list. This is just plain wrong, but it will take a while before
72 # the project can get patches on Pypi.
73 res = mrc(stack, name)
74 if res is not None and (
75 res.__class__.__name__ in _knowns or
76 isinstance(res, collections.abc.Mapping)):
77 res = [res]
78 return res
79
80 return _workaround
81