Namespace plugin implementation

* Creates pelican.plugins
* Moves plugin related code under pelican.plugins
  * pelican.plugins.signals is now the location for signals, pelican.signals is kept
    for backwards compatibility
  * pelican.plugins._utils contains necessary bits for plugin discovery and loading.
    Logic from Pelican class is moved here. Pelican class now just asks for plugins
    and registers them
* Contains tests for old and new plugin loading
This commit is contained in:
Deniz Turgut 2019-10-27 23:51:08 +03:00
commit a2053c34c3
9 changed files with 307 additions and 73 deletions

77
pelican/plugins/_utils.py Normal file
View file

@ -0,0 +1,77 @@
import importlib
import logging
import pkgutil
import sys
import six
logger = logging.getLogger(__name__)
def iter_namespace(ns_pkg):
# Specifying the second argument (prefix) to iter_modules makes the
# returned name an absolute name instead of a relative one. This allows
# import_module to work without having to do additional modification to
# the name.
return pkgutil.iter_modules(ns_pkg.__path__, ns_pkg.__name__ + ".")
def get_namespace_plugins(ns_pkg=None):
if ns_pkg is None:
import pelican.plugins as ns_pkg
return {
name: importlib.import_module(name)
for finder, name, ispkg
in iter_namespace(ns_pkg)
if ispkg
}
def list_plugins(ns_pkg=None):
from pelican.log import init as init_logging
init_logging(logging.INFO)
ns_plugins = get_namespace_plugins(ns_pkg)
if ns_plugins:
logger.info('Plugins found:\n' + '\n'.join(ns_plugins))
else:
logger.info('No plugins are installed')
def load_plugins(settings):
logger.debug('Finding namespace plugins')
namespace_plugins = get_namespace_plugins()
if namespace_plugins:
logger.debug('Namespace plugins found:\n' +
'\n'.join(namespace_plugins))
plugins = []
if settings.get('PLUGINS') is not None:
_sys_path = sys.path[:]
for path in settings.get('PLUGIN_PATHS', []):
sys.path.insert(0, path)
for plugin in settings['PLUGINS']:
if isinstance(plugin, six.string_types):
logger.debug('Loading plugin `%s`', plugin)
# try to find in namespace plugins
if plugin in namespace_plugins:
plugin = namespace_plugins[plugin]
elif 'pelican.plugins.{}'.format(plugin) in namespace_plugins:
plugin = namespace_plugins['pelican.plugins.{}'.format(
plugin)]
# try to import it
else:
try:
plugin = __import__(plugin, globals(), locals(),
str('module'))
except ImportError as e:
logger.error('Cannot load plugin `%s`\n%s', plugin, e)
continue
plugins.append(plugin)
sys.path = _sys_path
else:
plugins = list(namespace_plugins.values())
return plugins

View file

@ -0,0 +1,52 @@
# -*- coding: utf-8 -*-
from __future__ import print_function, unicode_literals
from blinker import signal
# Run-level signals:
initialized = signal('pelican_initialized')
get_generators = signal('get_generators')
all_generators_finalized = signal('all_generators_finalized')
get_writer = signal('get_writer')
finalized = signal('pelican_finalized')
# Reader-level signals
readers_init = signal('readers_init')
# Generator-level signals
generator_init = signal('generator_init')
article_generator_init = signal('article_generator_init')
article_generator_pretaxonomy = signal('article_generator_pretaxonomy')
article_generator_finalized = signal('article_generator_finalized')
article_generator_write_article = signal('article_generator_write_article')
article_writer_finalized = signal('article_writer_finalized')
page_generator_init = signal('page_generator_init')
page_generator_finalized = signal('page_generator_finalized')
page_generator_write_page = signal('page_generator_write_page')
page_writer_finalized = signal('page_writer_finalized')
static_generator_init = signal('static_generator_init')
static_generator_finalized = signal('static_generator_finalized')
# Page-level signals
article_generator_preread = signal('article_generator_preread')
article_generator_context = signal('article_generator_context')
page_generator_preread = signal('page_generator_preread')
page_generator_context = signal('page_generator_context')
static_generator_preread = signal('static_generator_preread')
static_generator_context = signal('static_generator_context')
content_object_init = signal('content_object_init')
# Writers signals
content_written = signal('content_written')
feed_generated = signal('feed_generated')
feed_written = signal('feed_written')