Mercurial > piecrust2
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 |
