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 394d729
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 34 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
39 changes: 8 additions & 31 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,46 +71,24 @@ 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.
pass
# This would likely mean we don't have module_path set.
# this should not usually happen, so log a warning
LOGGER.warning("Could not find template path for module {0}".format(self.name))

def inject_dependency(self, target, dependency):
"""Add 'dependency' to the target task's task_deps."""
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ mako>=1.0.0
Markdown>=3.0.0
unidecode>=0.04.16
lxml>=3.3.5
Yapsy>=1.11.223
Yapsy>=1.12.0
PyRSS2Gen>=1.1
blinker>=1.3
setuptools>=24.2.0
Expand Down

0 comments on commit 394d729

Please sign in to comment.