Skip to content
Browse files
Fix #3025 part 2 -- add smartjoin function/filter
Signed-off-by: Chris Warrick <>
  • Loading branch information
Kwpolska committed Apr 15, 2018
1 parent f4ee382 commit 60bee0074d3d96e3dda41ad12019ebe1f47b666c
@@ -6,6 +6,8 @@ Features

* New ``METADATA_VALUE_MAPPING`` setting to allow for flexible global
modification of metadata (Issue #3025)
* New ``smartjoin`` template function/filter that joins lists and
leaves strings as-is (Issue #3025)
* Recognize both TEASER_END and (new) END_TEASER (Issue #3010)
* New MARKDOWN_EXTENSION_CONFIGS setting (Issue #2970)
* Replace ``flowr.js`` with ``justified-layout.js`` by Flickr
@@ -98,6 +98,7 @@ Name Type Descript
``SLUG_TAG_PATH`` bool ``SLUG_TAG_PATH`` setting
``social_buttons_code`` TranslatableSetting<str> ``SOCIAL_BUTTONS_CODE`` setting
``sort_posts`` function ``utils.sort_posts`` function
``smartjoin`` function ``utils.smartjoin`` function
``template_hooks`` dict<str, TemplateHookRegistry> Template hooks registered by plugins
``theme_color`` str ``THEME_COLOR`` setting
``timezone`` tzinfo Timezone object (represents the configured timezone)
@@ -8,7 +8,7 @@
<%block name="extra_head">
% if post.meta('keywords'):
<meta name="keywords" content="${post.meta('keywords')|h}">
<meta name="keywords" content="${smartjoin(', ', post.meta('keywords'))|h}">
% endif
<meta name="author" content="${|h}">
%if post.prev_post:
@@ -1145,6 +1145,7 @@ def _set_global_context_from_config(self):
self._GLOBAL_CONTEXT['posts_section_name'] = self.config.get('POSTS_SECTION_NAME')
self._GLOBAL_CONTEXT['posts_section_title'] = self.config.get('POSTS_SECTION_TITLE')
self._GLOBAL_CONTEXT['sort_posts'] = utils.sort_posts
self._GLOBAL_CONTEXT['smartjoin'] = utils.smartjoin
self._GLOBAL_CONTEXT['meta_generator_tag'] = self.config.get('META_GENERATOR_TAG')

self._GLOBAL_CONTEXT.update(self.config.get('GLOBAL_CONTEXT', {}))
@@ -37,7 +37,7 @@
jinja2 = None # NOQA

from nikola.plugin_categories import TemplateSystem
from nikola.utils import makedirs, req_missing, sort_posts
from nikola.utils import makedirs, req_missing, sort_posts, _smartjoin_filter

class JinjaTemplates(TemplateSystem):
@@ -65,6 +65,7 @@ def set_directories(self, directories, cache_folder):
self.lookup.lstrip_blocks = True
self.lookup.filters['tojson'] = json.dumps
self.lookup.filters['sort_posts'] = sort_posts
self.lookup.filters['smartjoin'] = _smartjoin_filter

This comment has been minimized.

Copy link

felixfontein Apr 15, 2018


Do I understand it correctly that for Jinja2, smartjoin is both availbale as a filter and as a regular function?

This comment has been minimized.

Copy link

Kwpolska Apr 15, 2018

Author Member

Yes. Mako filters can’t take arguments. In Jinja2, the following two statements are valid and equivalent:

{{ foo|smartjoin(', ') }}
{{ smartjoin(', ', foo) }}
self.lookup.globals['enumerate'] = enumerate
self.lookup.globals['isinstance'] = isinstance
self.lookup.globals['tuple'] = tuple
@@ -95,8 +95,8 @@
'get_displayed_page_number', 'adjust_name_for_index_path_list',
'adjust_name_for_index_path', 'adjust_name_for_index_link',
'NikolaPygmentsHTML', 'create_redirect', 'clean_before_deployment',
'sort_posts', 'indent', 'load_data', 'html_unescape', 'rss_writer',
'sort_posts', 'smartjoin', 'indent', 'load_data', 'html_unescape',
'rss_writer', 'map_metadata', 'req_missing',
# Deprecated, moved to hierarchy_utils:
'TreeNode', 'clone_treenode', 'flatten_tree_structure',
'sort_classifications', 'join_hierarchical_category_path',
@@ -1862,6 +1862,26 @@ def sort_posts(posts, *keys):
return posts

def smartjoin(join_char: str, string_or_iterable) -> str:
"""Join string_or_iterable with join_char if it is not a string already.
>>> smartjoin('; ', 'foo, bar')
'foo, bar'
>>> smartjoin('; ', ['foo', 'bar'])
'foo; bar'
if isinstance(string_or_iterable, (unicode_str, bytes_str)):
return string_or_iterable
return join_char.join(string_or_iterable)

def _smartjoin_filter(string_or_iterable, join_char: str) -> str:
"""A version of smartjoin that works as a Jinja filter (with reversed arguments)."""
return smartjoin(join_char, string_or_iterable)

# Stolen from textwrap in Python 3.4.3.
def indent(text, prefix, predicate=None):
"""Add 'prefix' to the beginning of selected lines in 'text'.

0 comments on commit 60bee00

Please sign in to comment.