Skip to content
Browse files
Save dependencies for template hooks properly
Signed-off-by: Chris Warrick <>
  • Loading branch information
Kwpolska committed May 22, 2017
1 parent e4c5336 commit 581e340fed85fc27a60122a8c81ebb9a1ecd5ddd
Showing 6 changed files with 32 additions and 8 deletions.
@@ -16,6 +16,8 @@ Features

* Save dependencies for template hooks properly (using .__doc__ or
.template_registry_identifier for callables)
* Enable larger panorama thumbnails (Issue #2780)
* Disable ``archive_rss`` link handler, which was useless because no
such RSS was ever generated (Issue #2783)
@@ -537,21 +537,22 @@ HTML:

# In set_site
def generate_html_bit(name, ftype='js'):
"""Generate HTML for an asset."""
return '<script src="/assets/{t}/{n}.{t}">'.format(n=name, t=ftype)

site.template_hooks['extra_head'].append(generate_html_bit, False, 'fancyplugin', type='js')
site.template_hooks['extra_head'].append(generate_html_bit, False, 'fancyplugin', ftype='js')

The second argument to ``append()`` is used to determine whether the function
needs access to the current template context and the site. If it is set to
``True``, the function will also receive ``site`` and ``context`` keyword
arguments. Example use:

.. code-block:: python

# In set_site
def greeting(addr, endswith='', site=None, context=None):
"""Greet someone."""
if context['lang'] == 'en':
greet = u'Hello'
elif context['lang'] == 'es':
@@ -564,6 +565,13 @@ arguments. Example use:

site.template_hooks['page_header'].append(greeting, True, u'Nikola Tesla', endswith=u'!')

Dependencies for template hooks:

* if the input is a string, the string value, alongside arguments to ``append``, is used for calculating dependencies
* if the input is a callable, it attempts ``input.template_registry_identifier``, then ``input.__doc__``, and if neither is available, it uses a static string.

Make sure to provide at least a docstring, or a identifier, to ensure rebuilds work properly.


@@ -579,7 +587,7 @@ So, if you are creating a plugin that generates markup, it may be a good idea
to register it as a shortcode in addition of to restructured text directive or
markdown extension, thus making it available to all markup formats.

To implement your own shortcodes from a plugin, you can create a plugin inheriting ``ShortcodePlugin`` and
To implement your own shortcodes from a plugin, you can create a plugin inheriting ``ShortcodePlugin`` and
from its ``set_site`` method, call

``Nikola.register_shortcode(name, func)`` with the following arguments:
@@ -610,7 +618,7 @@ variable keyword arguments):
between them, otherwise ``None``.

The current language.
The current language.

If the shortcode tag has arguments of the form ``foo=bar`` they will be
passed as named arguments. Everything else will be passed as positional
@@ -2243,7 +2243,7 @@ def generic_renderer(self, lang, output_name, template_name, filters, file_deps=

for k, v in self.GLOBAL_CONTEXT['template_hooks'].items():
deps_dict['||template_hooks|{0}||'.format(k)] = v._items
deps_dict['||template_hooks|{0}||'.format(k)] = v.calculate_deps()

deps_dict[k] = deps_dict['global'][k](lang)
@@ -173,7 +173,7 @@ def gen_tasks(self):
self.image_ext_list.extend('EXTRA_IMAGE_EXTENSIONS', []))

for k, v in['template_hooks'].items():['||template_hooks|{0}||'.format(k)] = v._items['||template_hooks|{0}||'.format(k)] = v.calculate_deps()
yield self.group_task()
@@ -187,7 +187,7 @@ def render_listing(in_name, out_name, input_folder, output_folder, folders=[], f
uptodate = {'c':}

for k, v in['template_hooks'].items():
uptodate['||template_hooks|{0}||'.format(k)] = v._items
uptodate['||template_hooks|{0}||'.format(k)] = v.calculate_deps()

for k in
uptodate[k] =[k](['default_lang'])
@@ -512,9 +512,23 @@ def append(self, inp, wants_site_and_context=False, *args, **kwargs):
c = callable(inp)
self._items.append((c, inp, wants_site_and_context, args, kwargs))

def calculate_deps(self):
"""Calculate dependencies for a registry."""
deps = []
for is_callable, inp, wants_site_and_context, args, kwargs in self._items:
if not is_callable:
name = inp
elif hasattr(inp, 'template_registry_identifier'):
name = inp.template_registry_identifier
elif hasattr(inp, '__doc__'):
name = inp.__doc__
name = '_undefined_callable_'
deps.append((is_callable, name, wants_site_and_context, args, kwargs))

def __hash__(self):
"""Return hash of a registry."""
return hash(config_changed({ self._items})._calc_digest())
return hash(config_changed({ self.calculate_deps()})._calc_digest())

def __str__(self):
"""Stringify a registry."""

0 comments on commit 581e340

Please sign in to comment.