diff --git a/docs/themes.rst b/docs/themes.rst index 51b5b0d5..2df717e4 100644 --- a/docs/themes.rst +++ b/docs/themes.rst @@ -140,6 +140,23 @@ your date according to the locale given in your settings:: .. _datetime: https://docs.python.org/3/library/datetime.html#datetime-objects .. _strftime: https://docs.python.org/3/library/datetime.html#strftime-strptime-behavior +Checking Loaded Plugins +----------------------- + +Pelican provides a ``plugin_enabled`` Jinja test for checking if a certain plugin +is enabled. This test accepts a plugin name as a string and will return a boolean. +Namespace Plugins can be specified with their full name (``pelican.plugins.plugin_name``) +or their short name (``plugin_name``). The following example uses ``webassets`` plugin +to minify CSS if it is enabled and falls back to regular CSS otherwise:: + + {% if "webassets" is plugin_enabled %} + {% assets filters="cssmin", output="css/style.min.css", "css/style.scss" %} + + {% endassets %} + {% else %} + + {% endif %} + index.html ---------- diff --git a/pelican/generators.py b/pelican/generators.py index 0bbb7268..3b5ca9e4 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -20,6 +20,7 @@ from jinja2 import ( from pelican.cache import FileStampDataCacher from pelican.contents import Article, Page, Static from pelican.plugins import signals +from pelican.plugins._utils import plugin_enabled from pelican.readers import Readers from pelican.utils import ( DateFormatter, @@ -102,6 +103,9 @@ class Generator: # get custom Jinja tests from user settings custom_tests = self.settings["JINJA_TESTS"] + self.env.tests["plugin_enabled"] = partial( + plugin_enabled, plugin_list=self.settings["PLUGINS"] + ) self.env.tests.update(custom_tests) signals.generator_init.send(self) diff --git a/pelican/plugins/_utils.py b/pelican/plugins/_utils.py index f0c18f5c..c25f8114 100644 --- a/pelican/plugins/_utils.py +++ b/pelican/plugins/_utils.py @@ -40,6 +40,22 @@ def list_plugins(ns_pkg=None): logger.info("No plugins are installed") +def plugin_enabled(name, plugin_list=None): + if plugin_list is None or not plugin_list: + # no plugins are loaded + return False + + if name in plugin_list: + # search name as is + return True + + if "pelican.plugins.{}".format(name) in plugin_list: + # check if short name is a namespace plugin + return True + + return False + + def load_legacy_plugin(plugin, plugin_paths): if "." in plugin: # it is in a package, try to resolve package first diff --git a/pelican/tests/test_generators.py b/pelican/tests/test_generators.py index 52adb2c9..af6f5b1a 100644 --- a/pelican/tests/test_generators.py +++ b/pelican/tests/test_generators.py @@ -1581,6 +1581,31 @@ class TestJinja2Environment(TestCaseWithCLocale): self._test_jinja2_helper(settings, content, expected) + def test_jinja2_filter_plugin_enabled(self): + """JINJA_FILTERS adds custom filters to Jinja2 environment""" + settings = {"PLUGINS": ["legacy_plugin", "pelican.plugins.ns_plugin"]} + jinja_template = ( + "{plugin}: " + "{{% if '{plugin}' is plugin_enabled %}}yes" + "{{% else %}}no{{% endif %}}" + ) + content = " / ".join( + ( + jinja_template.format(plugin="ns_plugin"), + jinja_template.format(plugin="pelican.plugins.ns_plugin"), + jinja_template.format(plugin="legacy_plugin"), + jinja_template.format(plugin="unknown"), + ) + ) + expected = ( + "ns_plugin: yes / " + "pelican.plugins.ns_plugin: yes / " + "legacy_plugin: yes / " + "unknown: no" + ) + + 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 }}" diff --git a/pelican/tests/test_plugins.py b/pelican/tests/test_plugins.py index 4f02022c..ccce684e 100644 --- a/pelican/tests/test_plugins.py +++ b/pelican/tests/test_plugins.py @@ -2,7 +2,12 @@ import os from contextlib import contextmanager import pelican.tests.dummy_plugins.normal_plugin.normal_plugin as normal_plugin -from pelican.plugins._utils import get_namespace_plugins, get_plugin_name, load_plugins +from pelican.plugins._utils import ( + get_namespace_plugins, + get_plugin_name, + load_plugins, + plugin_enabled, +) from pelican.tests.support import unittest @@ -183,3 +188,78 @@ class PluginTest(unittest.TestCase): get_plugin_name(NoopPlugin()), "PluginTest.test_get_plugin_name..NoopPlugin", ) + + def test_plugin_enabled(self): + def get_plugin_names(plugins): + return [get_plugin_name(p) for p in plugins] + + with tmp_namespace_path(self._NS_PLUGIN_FOLDER): + # with no `PLUGINS` setting, load namespace plugins + SETTINGS = {} + plugins = get_plugin_names(load_plugins(SETTINGS)) + self.assertTrue(plugin_enabled("ns_plugin", plugins)) + self.assertTrue(plugin_enabled("pelican.plugins.ns_plugin", plugins)) + self.assertFalse(plugin_enabled("normal_plugin", plugins)) + self.assertFalse(plugin_enabled("unknown", plugins)) + + # disable namespace plugins with `PLUGINS = []` + SETTINGS = {"PLUGINS": []} + plugins = get_plugin_names(load_plugins(SETTINGS)) + self.assertFalse(plugin_enabled("ns_plugin", plugins)) + self.assertFalse(plugin_enabled("pelican.plugins.ns_plugin", plugins)) + self.assertFalse(plugin_enabled("normal_plugin", plugins)) + self.assertFalse(plugin_enabled("unknown", plugins)) + + # with `PLUGINS`, load only specified plugins + + # normal plugin + SETTINGS = { + "PLUGINS": ["normal_plugin"], + "PLUGIN_PATHS": [self._NORMAL_PLUGIN_FOLDER], + } + plugins = get_plugin_names(load_plugins(SETTINGS)) + self.assertFalse(plugin_enabled("ns_plugin", plugins)) + self.assertFalse(plugin_enabled("pelican.plugins.ns_plugin", plugins)) + self.assertTrue(plugin_enabled("normal_plugin", plugins)) + self.assertFalse(plugin_enabled("unknown", plugins)) + + # normal submodule/subpackage plugins + SETTINGS = { + "PLUGINS": [ + "normal_submodule_plugin.subplugin", + "normal_submodule_plugin.subpackage.subpackage", + ], + "PLUGIN_PATHS": [self._NORMAL_PLUGIN_FOLDER], + } + plugins = get_plugin_names(load_plugins(SETTINGS)) + self.assertFalse(plugin_enabled("ns_plugin", plugins)) + self.assertFalse(plugin_enabled("pelican.plugins.ns_plugin", plugins)) + self.assertFalse(plugin_enabled("normal_plugin", plugins)) + self.assertFalse(plugin_enabled("unknown", plugins)) + + # namespace plugin short + SETTINGS = {"PLUGINS": ["ns_plugin"]} + plugins = get_plugin_names(load_plugins(SETTINGS)) + self.assertTrue(plugin_enabled("ns_plugin", plugins)) + self.assertTrue(plugin_enabled("pelican.plugins.ns_plugin", plugins)) + self.assertFalse(plugin_enabled("normal_plugin", plugins)) + self.assertFalse(plugin_enabled("unknown", plugins)) + + # namespace plugin long + SETTINGS = {"PLUGINS": ["pelican.plugins.ns_plugin"]} + plugins = get_plugin_names(load_plugins(SETTINGS)) + self.assertTrue(plugin_enabled("ns_plugin", plugins)) + self.assertTrue(plugin_enabled("pelican.plugins.ns_plugin", plugins)) + self.assertFalse(plugin_enabled("normal_plugin", plugins)) + self.assertFalse(plugin_enabled("unknown", plugins)) + + # normal and namespace plugin + SETTINGS = { + "PLUGINS": ["normal_plugin", "ns_plugin"], + "PLUGIN_PATHS": [self._NORMAL_PLUGIN_FOLDER], + } + plugins = get_plugin_names(load_plugins(SETTINGS)) + self.assertTrue(plugin_enabled("ns_plugin", plugins)) + self.assertTrue(plugin_enabled("pelican.plugins.ns_plugin", plugins)) + self.assertTrue(plugin_enabled("normal_plugin", plugins)) + self.assertFalse(plugin_enabled("unknown", plugins))