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.plugin4
-rw-r--r--nikola/plugins/template/jinja.py90
-rw-r--r--nikola/plugins/template/mako.plugin4
-rw-r--r--nikola/plugins/template/mako.py61
5 files changed, 94 insertions, 67 deletions
diff --git a/nikola/plugins/template/__init__.py b/nikola/plugins/template/__init__.py
index d416ad7..a530db4 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-2020 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.plugin b/nikola/plugins/template/jinja.plugin
index cfe9fa8..629b20e 100644
--- a/nikola/plugins/template/jinja.plugin
+++ b/nikola/plugins/template/jinja.plugin
@@ -5,9 +5,9 @@ module = jinja
[Documentation]
author = Roberto Alsina
version = 1.0
-website = http://getnikola.com
+website = https://getnikola.com/
description = Support for Jinja2 templates.
[Nikola]
-plugincategory = Template
+PluginCategory = Template
diff --git a/nikola/plugins/template/jinja.py b/nikola/plugins/template/jinja.py
index b02d75c..7795739 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-2020 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -24,47 +24,51 @@
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
"""Jinja template handler."""
-from __future__ import unicode_literals
-import os
+import io
import json
-from collections import deque
+import os
+
+from nikola.plugin_categories import TemplateSystem
+from nikola.utils import makedirs, req_missing, sort_posts, _smartjoin_filter
+
try:
import jinja2
from jinja2 import meta
except ImportError:
- jinja2 = None # NOQA
-
-from nikola.plugin_categories import TemplateSystem
-from nikola.utils import makedirs, req_missing
+ jinja2 = None
class JinjaTemplates(TemplateSystem):
-
"""Support for Jinja2 templates."""
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.filters['sort_posts'] = sort_posts
+ self.lookup.filters['smartjoin'] = _smartjoin_filter
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()
@@ -89,36 +93,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 = [d for d in meta.find_referenced_templates(ast) if d]
+ 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-sig') 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.plugin b/nikola/plugins/template/mako.plugin
index d256faf..2d353bf 100644
--- a/nikola/plugins/template/mako.plugin
+++ b/nikola/plugins/template/mako.plugin
@@ -5,9 +5,9 @@ module = mako
[Documentation]
author = Roberto Alsina
version = 1.0
-website = http://getnikola.com
+website = https://getnikola.com/
description = Support for Mako templates.
[Nikola]
-plugincategory = Template
+PluginCategory = Template
diff --git a/nikola/plugins/template/mako.py b/nikola/plugins/template/mako.py
index aed6596..30e2041 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-2020 Roberto Alsina and others.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
@@ -26,25 +26,22 @@
"""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
from nikola.plugin_categories import TemplateSystem
-from nikola.utils import makedirs, get_logger, STDERR_HANDLER
+from nikola.utils import makedirs, get_logger
-LOGGER = get_logger('mako', STDERR_HANDLER)
+LOGGER = get_logger('mako')
class MakoTemplates(TemplateSystem):
-
"""Support for Mako templates."""
name = "mako"
@@ -55,10 +52,9 @@ class MakoTemplates(TemplateSystem):
directories = []
cache_dir = None
- def get_deps(self, filename):
- """Get dependencies for a template (internal function)."""
- text = util.read_file(filename)
- lex = lexer.Lexer(text=text, filename=filename)
+ def get_string_deps(self, text, filename=None):
+ """Find dependencies for a template string."""
+ lex = lexer.Lexer(text=text, filename=filename, input_encoding='utf-8')
lex.parse()
deps = []
@@ -66,18 +62,25 @@ 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):
+ dep = self.get_template_path(d)
+ if dep:
+ deps[i] = dep
+ else:
+ LOGGER.error("Cannot find template {0} referenced in {1}",
+ d, filename)
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')
- # Workaround for a Mako bug, Issue #825
- if sys.version_info[0] == 2:
- try:
- os.path.abspath(cache_dir).decode('ascii')
- except UnicodeEncodeError:
- cache_dir = tempfile.mkdtemp()
- LOGGER.warning('Because of a Mako bug, setting cache_dir to {0}'.format(cache_dir))
if os.path.exists(cache_dir):
shutil.rmtree(cache_dir)
self.directories = directories
@@ -95,6 +98,7 @@ class MakoTemplates(TemplateSystem):
self.lookup = TemplateLookup(
directories=self.directories,
module_directory=self.cache_dir,
+ input_encoding='utf-8',
output_encoding='utf-8')
def set_site(self, site):
@@ -109,14 +113,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."""
@@ -127,9 +131,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)
- return list(self.cache[template_name])
+ # yes, it uses forward slashes on Windows
+ deps += self.template_deps(fname.split('/')[-1])
+ self.cache[template_name] = list(set(deps))
+ return 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):