Skip to content

Commit

Permalink
Use callback to set plugin module path for template location
Browse files Browse the repository at this point in the history
This is a much cleaner (I think) approach to the problem of
figuring out the plugin's template path (see #3701). We use
yaspy's ability to provide a callback when loading the plugins.
The callback is passed a `yaspy.PluginInfo` instance, which
conveniently contains a reference to the plugin object itself,
and its filesystem path. So we can just add a method on our
plugin class to set the filesystem path as an attribute, and
have the callback function call that method.

Signed-off-by: Adam Williamson <[email protected]>
  • Loading branch information
AdamWill committed Jul 15, 2023
1 parent 4f51e2e commit 9fcde54
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 32 deletions.
17 changes: 15 additions & 2 deletions nikola/nikola.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,19 @@ def _enclosure(post, lang):
return url, length, mime


def _plugin_load_callback(plugin_info):
"""Set the plugin's module path.
So we can find its template path later.
"""
try:
plugin_info.plugin_object.set_module_path(plugin_info.path)
except AttributeError:
# this is just for safety in case plugin_object somehow
# isn't set
pass


class Nikola(object):
"""Class that handles site generation.
Expand Down Expand Up @@ -1103,7 +1116,7 @@ def init_plugins(self, commands_only=False, load_all=False):
self.plugin_manager._candidates = list(set(self.plugin_manager._candidates) - bad_candidates)

self.plugin_manager._candidates = self._filter_duplicate_plugins(self.plugin_manager._candidates)
self.plugin_manager.loadPlugins()
self.plugin_manager.loadPlugins(callback_after=_plugin_load_callback)

# Search for compiler plugins which we disabled but shouldn't have
self._activate_plugins_of_category("PostScanner")
Expand Down Expand Up @@ -1134,7 +1147,7 @@ def init_plugins(self, commands_only=False, load_all=False):
utils.LOGGER.debug('Not loading compiler extension {}', p[-1].name)
if to_add:
self.plugin_manager._candidates = self._filter_duplicate_plugins(to_add)
self.plugin_manager.loadPlugins()
self.plugin_manager.loadPlugins(callback_after=_plugin_load_callback)

# Jupyter theme configuration. If a website has ipynb enabled in post_pages
# we should enable the Jupyter CSS (leaving that up to the theme itself).
Expand Down
37 changes: 7 additions & 30 deletions nikola/plugin_categories.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
import io
import logging
import os
import sys
import typing

import doit
Expand Down Expand Up @@ -72,45 +71,23 @@ def set_site(self, site):
if not site.debug:
self.logger.level = logging.INFO

def set_module_path(self, module_path):
"""Set the plugin's module path."""
self.module_path = module_path

def inject_templates(self):
"""Inject 'templates/<engine>' (if exists) very early in the theme chain."""
try:
mod_candidate = None
# since https://github.com/tibonihoo/yapsy/pull/11 ,
# yapsy only adds each imported plugin to sys.modules
# under its modified, "unique" name (see early in
# PluginManager.loadPlugins), so we recreate the
# modified name here to find it. we fudge the serial
# number here, assuming that if a plugin is loaded
# under the same name multiple times, the location
# will also be the same, so we can just use 0.
possible_names = (
self.__class__.__module__,
"yapsy_loaded_plugin_" + self.__class__.__module__ + "_0",
"yapsy_loaded_plugin_" + self.name + "_0",
)
for possible_name in possible_names:
mod_candidate = sys.modules.get(possible_name)
if mod_candidate:
break
if not mod_candidate:
# well, we tried. we wind up here for the dummy
# plugins; honestly I'm not sure exactly why/how,
# but they don't have templates, so it's okay
return
# Sorry, found no other way to get this
mod_path = mod_candidate.__file__
mod_dir = os.path.dirname(mod_path)
mod_dir = os.path.dirname(self.module_path)
tmpl_dir = os.path.join(
mod_dir, 'templates', self.site.template_system.name
)
if os.path.isdir(tmpl_dir):
# Inject tmpl_dir low in the theme chain
self.site.template_system.inject_directory(tmpl_dir)
except AttributeError:
# In some cases, __builtin__ becomes the module of a plugin.
# We couldn’t reproduce that, and really find the reason for this,
# so let’s just ignore it and be done with it.
# Just in case something went wrong and we don't have
# self.module_path set
pass

def inject_dependency(self, target, dependency):
Expand Down

0 comments on commit 9fcde54

Please sign in to comment.