changeset 848:7d83b9484b98

plugins: Add support for "ad-hoc" local plugins. These are loose `.py` files inside of the `plugins` folder of a website. They're loaded along with other normal (packaged) plugins.
author Ludovic Chabant <ludovic@chabant.com>
date Thu, 27 Apr 2017 20:54:29 -0700
parents 71c4f43d8fc1
children 8f8bbb2e70e1
files piecrust/__init__.py piecrust/app.py piecrust/plugins/base.py
diffstat 3 files changed, 25 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/piecrust/__init__.py	Thu Apr 27 20:53:18 2017 -0700
+++ b/piecrust/__init__.py	Thu Apr 27 20:54:29 2017 -0700
@@ -4,6 +4,7 @@
 TEMPLATES_DIR = 'templates'
 THEME_DIR = 'theme'
 THEMES_DIR = 'themes'
+PLUGINS_DIR = 'plugins'
 
 CONFIG_PATH = 'config.yml'
 THEME_CONFIG_PATH = 'theme_config.yml'
--- a/piecrust/app.py	Thu Apr 27 20:53:18 2017 -0700
+++ b/piecrust/app.py	Thu Apr 27 20:54:29 2017 -0700
@@ -7,7 +7,7 @@
 from piecrust import (
         RESOURCES_DIR,
         CACHE_DIR, TEMPLATES_DIR, ASSETS_DIR,
-        THEME_DIR,
+        THEME_DIR, PLUGINS_DIR,
         CONFIG_PATH, THEME_CONFIG_PATH)
 from piecrust.appconfig import PieCrustConfiguration
 from piecrust.cache import ExtensibleCache, NullExtensibleCache
@@ -127,6 +127,10 @@
         return os.path.join(RESOURCES_DIR, 'theme')
 
     @cached_property
+    def plugins_dir(self):
+        return self._get_dir(PLUGINS_DIR)
+
+    @cached_property
     def cache_dir(self):
         return os.path.join(self.root_dir, CACHE_DIR, self.cache_key)
 
--- a/piecrust/plugins/base.py	Thu Apr 27 20:53:18 2017 -0700
+++ b/piecrust/plugins/base.py	Thu Apr 27 20:54:29 2017 -0700
@@ -1,5 +1,8 @@
+import os.path
+import sys
 import logging
 import importlib
+import importlib.util
 
 
 logger = logging.getLogger(__name__)
@@ -113,9 +116,24 @@
             plugin.initialize(self.app)
 
     def _loadPlugin(self, plugin_name):
+        mod_name = 'piecrust_%s' % plugin_name
         try:
-            mod = importlib.import_module('piecrust_' + plugin_name)
+            # Import from the current environment.
+            mod = importlib.import_module(mod_name)
         except ImportError as ex:
+            mod = None
+
+        if mod is None:
+            # Import as a loose Python file from the plugins dir.
+            pfile = os.path.join(self.app.plugins_dir, plugin_name + '.py')
+            if os.path.isfile(pfile):
+                spec = importlib.util.spec_from_file_location(plugin_name,
+                                                              pfile)
+                mod = importlib.util.module_from_spec(spec)
+                spec.loader.exec_module(mod)
+                sys.modules[mod_name] = mod
+
+        if mod is None:
             logger.error("Failed to load plugin '%s'." % plugin_name)
             logger.error(ex)
             return