diff --git a/docs/settings.rst b/docs/settings.rst index 9ebf85c2..befb6f05 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -101,7 +101,18 @@ Basic settings JINJA_FILTERS = {'urlencode': urlencode_filter} - See `Jinja custom filters documentation`_. + See: `Jinja custom filters documentation`_. + +.. data:: JINJA_GLOBALS = {} + + A dictionary of custom objects to map into the Jinja2 global environment + namespace. The dictionary should map the global name to the global + variable/function. See: `Jinja global namespace documentation`_. + +.. data:: JINJA_TESTS = {} + + A dictionary of custom Jinja2 tests you want to use. The dictionary should + map test names to test functions. See: `Jinja custom tests documentation`_. .. data:: LOG_FILTER = [] @@ -709,6 +720,10 @@ Time and Date .. [#] Default is the system locale. +.. _Jinja custom filters documentation: https://jinja.palletsprojects.com/en/master/api/#custom-filters +.. _Jinja global namespace documentation: https://jinja.palletsprojects.com/en/master/api/#the-global-namespace +.. _Jinja custom tests documentation: https://jinja.palletsprojects.com/en/master/api/#custom-tests + .. _locales on Windows: https://www.microsoft.com/en-us/download/details.aspx?id=55979 .. _locale(1): https://linux.die.net/man/1/locale @@ -1357,6 +1372,5 @@ Example settings :language: python -.. _Jinja custom filters documentation: https://jinja.palletsprojects.com/en/master/api/#custom-filters .. _Jinja Environment documentation: https://jinja.palletsprojects.com/en/master/api/#jinja2.Environment .. _Docutils Configuration: http://docutils.sourceforge.net/docs/user/config.html diff --git a/pelican/generators.py b/pelican/generators.py index 02667cd7..43f6d41a 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -78,6 +78,14 @@ class Generator(object): custom_filters = self.settings['JINJA_FILTERS'] self.env.filters.update(custom_filters) + # get custom Jinja globals from user settings + custom_globals = self.settings['JINJA_GLOBALS'] + self.env.globals.update(custom_globals) + + # get custom Jinja tests from user settings + custom_tests = self.settings['JINJA_TESTS'] + self.env.tests.update(custom_tests) + signals.generator_init.send(self) def get_template(self, name): diff --git a/pelican/settings.py b/pelican/settings.py index 7d80ba90..c3b29ca5 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -120,6 +120,8 @@ DEFAULT_CONFIG = { 'output_format': 'html5', }, 'JINJA_FILTERS': {}, + 'JINJA_GLOBALS': {}, + 'JINJA_TESTS': {}, 'JINJA_ENVIRONMENT': { 'trim_blocks': True, 'lstrip_blocks': True, diff --git a/pelican/tests/test_generators.py b/pelican/tests/test_generators.py index 1f8157a6..fdc1b937 100644 --- a/pelican/tests/test_generators.py +++ b/pelican/tests/test_generators.py @@ -1148,3 +1148,80 @@ class TestStaticGenerator(unittest.TestCase): self.assertTrue( os.path.isdir(os.path.join(self.temp_output, "static"))) self.assertTrue(os.path.isfile(self.endfile)) + + +class TestJinja2Environment(unittest.TestCase): + + def setUp(self): + self.temp_content = mkdtemp(prefix='pelicantests.') + self.temp_output = mkdtemp(prefix='pelicantests.') + self.old_locale = locale.setlocale(locale.LC_ALL) + locale.setlocale(locale.LC_ALL, str('C')) + + def tearDown(self): + rmtree(self.temp_content) + rmtree(self.temp_output) + locale.setlocale(locale.LC_ALL, self.old_locale) + + def _test_jinja2_helper(self, additional_settings, content, expected): + settings = get_settings() + settings['STATIC_PATHS'] = ['static'] + settings['TEMPLATE_PAGES'] = { + 'template/source.html': 'generated/file.html' + } + settings.update(additional_settings) + + generator = TemplatePagesGenerator( + context={'foo': 'foo', 'bar': 'bar'}, settings=settings, + path=self.temp_content, theme='', output_path=self.temp_output) + + # create a dummy template file + template_dir = os.path.join(self.temp_content, 'template') + template_path = os.path.join(template_dir, 'source.html') + os.makedirs(template_dir) + with open(template_path, 'w') as template_file: + template_file.write(content) + + writer = Writer(self.temp_output, settings=settings) + generator.generate_output(writer) + + output_path = os.path.join(self.temp_output, 'generated', 'file.html') + + # output file has been generated + self.assertTrue(os.path.exists(output_path)) + + # output content is correct + with open(output_path, 'r') as output_file: + self.assertEqual(output_file.read(), expected) + + def test_jinja2_filter(self): + """JINJA_FILTERS adds custom filters to Jinja2 environment""" + content = 'foo: {{ foo|custom_filter }}, bar: {{ bar|custom_filter }}' + settings = {'JINJA_FILTERS': {'custom_filter': lambda x: x.upper()}} + expected = 'foo: FOO, bar: BAR' + + self._test_jinja2_helper(settings, content, expected) + + def test_jinja2_test(self): + """JINJA_TESTS adds custom tests to Jinja2 environment""" + content = 'foo {{ foo is custom_test }}, bar {{ bar is custom_test }}' + settings = {'JINJA_TESTS': {'custom_test': lambda x: x == 'bar'}} + expected = 'foo False, bar True' + + self._test_jinja2_helper(settings, content, expected) + + def test_jinja2_global(self): + """JINJA_GLOBALS adds custom globals to Jinja2 environment""" + content = '{{ custom_global }}' + settings = {'JINJA_GLOBALS': {'custom_global': 'foobar'}} + expected = 'foobar' + + self._test_jinja2_helper(settings, content, expected) + + def test_jinja2_extension(self): + """JINJA_ENVIRONMENT adds extensions to Jinja2 environment""" + content = '{% set stuff = [] %}{% do stuff.append(1) %}{{ stuff }}' + settings = {'JINJA_ENVIRONMENT': {'extensions': ['jinja2.ext.do']}} + expected = '[1]' + + self._test_jinja2_helper(settings, content, expected)