This unfortunately won’t do much good as a PR because the project seems to be abandoned but you can get really big speedups in pystache by caching templates and (particularly) parsed templates. Especially if you are using a lot of partials.
It looks something like this:
# Subclass pystache.Renderer to provide our custom caching versions of pystache classes for performance reasons.
class CachedRenderer(pystache.Renderer):
def _make_loader(self) -> pystache.loader.Loader:
return CachedLoader(file_encoding=self.file_encoding, extension=self.file_extension, to_unicode=self.str, search_dirs=self.search_dirs)
def _make_render_engine(self) -> pystache.renderengine.RenderEngine:
resolve_context = self._make_resolve_context()
resolve_partial = self._make_resolve_partial()
engine = CachedRenderEngine(literal=self._to_unicode_hard, escape=self._escape_to_unicode, resolve_context=resolve_context, resolve_partial=resolve_partial, to_str=self.str_coerce)
return engine
# A custom loader that acts exactly as the default loader but only loads a given file once to speed up repeated use of partials.
# This will stop us loading record.mustache from disk 16,000 times on /cards/ for example.
class CachedLoader(pystache.loader.Loader):
def __init__(self, file_encoding: Optional[str] = None, extension: Optional[Union[str, bool]] = None, to_unicode: Optional[StringConverterFunction] = None, search_dirs: Optional[List[str]] = None) -> None:
super().__init__(file_encoding, extension, to_unicode, search_dirs)
self.templates: Dict[str, str] = {}
def read(self, path: str, encoding: Optional[str] = None) -> str:
if self.templates.get(path) is None:
# look in redis using modified date on filesystem of path
self.templates[path] = super().read(path, encoding)
# write to redis
return self.templates[path]
# If you have already parsed a template, don't parse it again.
class CachedRenderEngine(pystache.renderengine.RenderEngine):
# pylint: disable=too-many-arguments
def __init__(self, literal: StringConverterFunction = None, escape: StringConverterFunction = None, resolve_context: Optional[Callable[[ContextStack, str], str]] = None, resolve_partial: Optional[StringConverterFunction] = None, to_str: Optional[Callable[[object], str]] = None) -> None:
super().__init__(literal, escape, resolve_context, resolve_partial, to_str)
self.parsed_templates: Dict[str, pystache.parsed.ParsedTemplate] = {}
def render(self, template: str, context_stack: ContextStack, delimiters: Optional[Tuple[str, str]] = None) -> str:
if self.parsed_templates.get(template) is None:
# look in redis
self.parsed_templates[template] = pystache.parser.parse(template, delimiters)
# store in redis
return self.parsed_templates[template].render(self, context_stack)