summaryrefslogtreecommitdiffstats
path: root/nikola/plugins/template
diff options
context:
space:
mode:
Diffstat (limited to 'nikola/plugins/template')
-rw-r--r--nikola/plugins/template/__init__.py2
-rw-r--r--nikola/plugins/template/jinja.py74
-rw-r--r--nikola/plugins/template/mako.py35
3 files changed, 71 insertions, 40 deletions
diff --git a/nikola/plugins/template/__init__.py b/nikola/plugins/template/__init__.py
index d416ad7..d5efd61 100644
--- a/nikola/plugins/template/__init__.py
+++ b/nikola/plugins/template/__init__.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2015 Roberto Alsina and others.
+# Copyright © 2012-2016 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
diff --git a/nikola/plugins/template/jinja.py b/nikola/plugins/template/jinja.py
index e7df102..5a2135f 100644
--- a/nikola/plugins/template/jinja.py
+++ b/nikola/plugins/template/jinja.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2015 Roberto Alsina and others.
+# Copyright © 2012-2016 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -29,8 +29,8 @@
from __future__ import unicode_literals
import os
+import io
import json
-from collections import deque
try:
import jinja2
from jinja2 import meta
@@ -47,23 +47,27 @@ class JinjaTemplates(TemplateSystem):
name = "jinja"
lookup = None
dependency_cache = {}
+ per_file_cache = {}
def __init__(self):
"""Initialize Jinja2 environment with extended set of filters."""
if jinja2 is None:
return
- self.lookup = jinja2.Environment()
+
+ def set_directories(self, directories, cache_folder):
+ """Create a new template lookup with set directories."""
+ if jinja2 is None:
+ req_missing(['jinja2'], 'use this theme')
+ cache_folder = os.path.join(cache_folder, 'jinja')
+ makedirs(cache_folder)
+ cache = jinja2.FileSystemBytecodeCache(cache_folder)
+ self.lookup = jinja2.Environment(bytecode_cache=cache)
self.lookup.trim_blocks = True
self.lookup.lstrip_blocks = True
self.lookup.filters['tojson'] = json.dumps
self.lookup.globals['enumerate'] = enumerate
self.lookup.globals['isinstance'] = isinstance
self.lookup.globals['tuple'] = tuple
-
- def set_directories(self, directories, cache_folder):
- """Create a new template lookup with set directories."""
- if jinja2 is None:
- req_missing(['jinja2'], 'use this theme')
self.directories = directories
self.create_lookup()
@@ -88,36 +92,46 @@ class JinjaTemplates(TemplateSystem):
if jinja2 is None:
req_missing(['jinja2'], 'use this theme')
template = self.lookup.get_template(template_name)
- output = template.render(**context)
+ data = template.render(**context)
if output_name is not None:
makedirs(os.path.dirname(output_name))
- with open(output_name, 'w+') as output:
- output.write(output.encode('utf8'))
- return output
+ with io.open(output_name, 'w', encoding='utf-8') as output:
+ output.write(data)
+ return data
def render_template_to_string(self, template, context):
"""Render template to a string using context."""
return self.lookup.from_string(template).render(**context)
+ def get_string_deps(self, text):
+ """Find dependencies for a template string."""
+ deps = set([])
+ ast = self.lookup.parse(text)
+ dep_names = meta.find_referenced_templates(ast)
+ for dep_name in dep_names:
+ filename = self.lookup.loader.get_source(self.lookup, dep_name)[1]
+ sub_deps = [filename] + self.get_deps(filename)
+ self.dependency_cache[dep_name] = sub_deps
+ deps |= set(sub_deps)
+ return list(deps)
+
+ def get_deps(self, filename):
+ """Return paths to dependencies for the template loaded from filename."""
+ with io.open(filename, 'r', encoding='utf-8') as fd:
+ text = fd.read()
+ return self.get_string_deps(text)
+
def template_deps(self, template_name):
"""Generate list of dependencies for a template."""
- # Cache the lists of dependencies for each template name.
if self.dependency_cache.get(template_name) is None:
- # Use a breadth-first search to find all templates this one
- # depends on.
- queue = deque([template_name])
- visited_templates = set([template_name])
- deps = []
- while len(queue) > 0:
- curr = queue.popleft()
- source, filename = self.lookup.loader.get_source(self.lookup,
- curr)[:2]
- deps.append(filename)
- ast = self.lookup.parse(source)
- dep_names = meta.find_referenced_templates(ast)
- for dep_name in dep_names:
- if (dep_name not in visited_templates and dep_name is not None):
- visited_templates.add(dep_name)
- queue.append(dep_name)
- self.dependency_cache[template_name] = deps
+ filename = self.lookup.loader.get_source(self.lookup, template_name)[1]
+ self.dependency_cache[template_name] = [filename] + self.get_deps(filename)
return self.dependency_cache[template_name]
+
+ def get_template_path(self, template_name):
+ """Get the path to a template or return None."""
+ try:
+ t = self.lookup.get_template(template_name)
+ return t.filename
+ except jinja2.TemplateNotFound:
+ return None
diff --git a/nikola/plugins/template/mako.py b/nikola/plugins/template/mako.py
index 6da21db..0c9bb64 100644
--- a/nikola/plugins/template/mako.py
+++ b/nikola/plugins/template/mako.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-# Copyright © 2012-2015 Roberto Alsina and others.
+# Copyright © 2012-2016 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -27,12 +27,13 @@
"""Mako template handler."""
from __future__ import unicode_literals, print_function, absolute_import
+import io
import os
import shutil
import sys
import tempfile
-from mako import util, lexer, parsetree
+from mako import exceptions, util, lexer, parsetree
from mako.lookup import TemplateLookup
from mako.template import Template
from markupsafe import Markup # It's ok, Mako requires it
@@ -54,9 +55,8 @@ class MakoTemplates(TemplateSystem):
directories = []
cache_dir = None
- def get_deps(self, filename):
- """Get dependencies for a template (internal function)."""
- text = util.read_file(filename)
+ def get_string_deps(self, text, filename=None):
+ """Find dependencies for a template string."""
lex = lexer.Lexer(text=text, filename=filename)
lex.parse()
@@ -65,8 +65,17 @@ class MakoTemplates(TemplateSystem):
keyword = getattr(n, 'keyword', None)
if keyword in ["inherit", "namespace"] or isinstance(n, parsetree.IncludeTag):
deps.append(n.attributes['file'])
+ # Some templates will include "foo.tmpl" and we need paths, so normalize them
+ # using the template lookup
+ for i, d in enumerate(deps):
+ deps[i] = self.get_template_path(d)
return deps
+ def get_deps(self, filename):
+ """Get paths to dependencies for a template."""
+ text = util.read_file(filename)
+ return self.get_string_deps(text, filename)
+
def set_directories(self, directories, cache_folder):
"""Create a new template lookup with set directories."""
cache_dir = os.path.join(cache_folder, '.mako.tmp')
@@ -108,14 +117,14 @@ class MakoTemplates(TemplateSystem):
data = template.render_unicode(**context)
if output_name is not None:
makedirs(os.path.dirname(output_name))
- with open(output_name, 'w+') as output:
+ with io.open(output_name, 'w', encoding='utf-8') as output:
output.write(data)
return data
def render_template_to_string(self, template, context):
"""Render template to a string using context."""
context.update(self.filters)
- return Template(template).render(**context)
+ return Template(template, lookup=self.lookup).render(**context)
def template_deps(self, template_name):
"""Generate list of dependencies for a template."""
@@ -126,10 +135,18 @@ class MakoTemplates(TemplateSystem):
dep_filenames = self.get_deps(template.filename)
deps = [template.filename]
for fname in dep_filenames:
- deps += self.template_deps(fname)
- self.cache[template_name] = tuple(deps)
+ deps += [fname] + self.get_deps(fname)
+ self.cache[template_name] = deps
return list(self.cache[template_name])
+ def get_template_path(self, template_name):
+ """Get the path to a template or return None."""
+ try:
+ t = self.lookup.get_template(template_name)
+ return t.filename
+ except exceptions.TopLevelLookupException:
+ return None
+
def striphtml(text):
"""Strip HTML tags from text."""