forked from getpelican/pelican
-
Notifications
You must be signed in to change notification settings - Fork 0
Plugin Development
Egbert edited this page Jul 1, 2024
·
24 revisions
Create the __init__.py
file
$ cd ~/work/my_plugin/pelican/plugins/my_plugin
$ vi __init__.py
and fill it with:
# coding: utf-8
""" Tableize adapt to a Pelican's plugin """
__title__ = 'my_plugin'
__version__ = '0.0.0'
__author__ = 'John Doe'
__email__ = "[email protected]"
__credits__ = ["John Doe, Jane Doe, Don Doe"]
__maintainer__ = "Jill Doe"
__status__ = "Stable"
__license__ = 'GPLv2'
__copyright__ = 'Copyright 2024'
from .my_plugin import register # NOQA
Create a primary plugin Python source file in the same ~/work/my_plugin/pelican/plugins/my_plugin
directory:
vi my_plugin.py
And fill it with the following code template:
from copy import copy
import pprint
import logging
from pelican import signals, logger
from pelican.readers import BaseReader
from pelican.contents import Article, Page
from pelican.settings import DEFAULT_CONFIG
logger = logging.getLogger('my_plugin') # log with my plugin name
# if you used `__name__` instead, you will get a much longer string
# like `pelican.plugins.my_plugin.my_plugin`, which is not desirable.
# These fills in settings but never overwrite any pre-exist settings
# In short, supply any missing settings.
def set_default_settings(settings):
""" If the pelican.settings is missing any of our plugin settings,
fill those settings as well."""
settings.setdefault(
'MY_PLUGIN_SETTINGS,
{
'max_line': 80,
'header': False,
'separator': ',',
'auto_index': 1
}
)
def my_plugin_pelican_initialized_all(pelican):
mp_pelican = pelican # NOQA
if mp_pelican is None:
logger.fatal('No pelican access; my_plugin plugin is disabled')
return
mp_settings = pelican.settings.get('MY_PLUGIN_SETTINGS')
if mp_settings is None:
logger.warning('%s does not exist in pelican.settings; ' +
'plugin\'s built-in defaults used',
'MY_PLUGIN_SETTINGS')
# this is the part where we must implicitly declare our defaults
set_default_settings(pelican.settings)
else:
# explicit settings for this plugin are found in
# pelican.settings['MY_PLUGIN_SETTINGS']
logger.debug('{0!s} exists in pelican.settings'.format(
'MY_PLUGIN_SETTINGS'))
mp_settings = copy(
mp_pelican_settings['MY_PLUGIN_SETTINGS'])
check_plugin_settings(mp_pelican)
def mp_article_init(articles_generator):
# arg1 : articles_generator:ArticlesGenerator
#
# articles_generator provides the following variable member items:
# articles:list, authors:dict, categories:dict, context:dict, dates:dict,
# drafts:list, drafts_translation:list, env:Environment, hidden_articles:list,
# hidden_translations:list, output_path:str, path:str, period_archives_dict,
# readers:Readers, related_posts:list, settings:dict, tags:dict, theme:str,
# translations:list.
#
# 1st article-related signal.
# generator processing Class (i.e., ArticlesGenerator, PagesGenerator,
# StaticGenerator).
# no content, no articles, but lots of other stuffs.
# first appearance of article_generator.settings.
# Callstack:
# ArticlesGenerator.__init__()
#
#
# Hooked by signals.article_generator_init.connect(mp_article_init)
logger.info('mp_article_init called: path is ' + articles_generator.path)
return
def mp_article_preread(articles_generator):
# Description:
# useful for modifying file-related variables before reading
# this is the only article-related signal BEFORE any Markdown pre-processing
#
# arg1 : articles_generator:ArticlesGenerator
#
# articles_generator:Articles_generator provides the following
# variable member items:
# articles:list, authors:dict, categories:dict, context:dict, dates:dict,
# drafts:list, drafts_translations:list, env:Environment, hidden_articles:list,
# hidden_translations:list, output_path:str, path:str, period_archives:dict,
# readers:Readers, related_posts:list, settings:dict, tags:dict, theme:str,
# and translations:list. (No content here, yet)
#
# 2nd article-related signal
# First signal for entire ArticleGenerator.generate_context()
# First signal within Readers.read_file().
# Callstack:
# signals.article_generator_preread.send()
# ArticlesGenerator.generate_context()
# Pelican.run()
#
# Hooked by signals.article_generator_preread.connect(mp_article_preread).
#
print('mp_article_preread called')
return
def mp_article_context(articles_generator, metadata=[]):
# Description:
# context and metadata are added here the first time for
# ArticlesGenerator but still no content yet.
#
# arg1 : articles_generator:ArticlesGenerator
# arg2 : metadata:dict
#
# articles_generator of ArticlesGenerator class provides the
# following variable member items:
# articles:list, authors:dict, categories:dict, context:dict,
# dates:dict, drafts:list,
# drafts_translations:list, env:Environment, hidden_articles:list,
# hidden_translations:list, output_path:str, path:str, period_archives:dict,
# readers:Readers, related_posts:list, settings:dict, tags:dict, theme:str,
# and translations:list. (No content here, yet)
#
# metadata of dict type provides the following variable member items:
# status:str, category:Category, reader:str, title:str, data:SafeDatetime,
# tags:list, summary:str, lang:str, private:str, and __len__:int.
#
# 3rd article-related signal.
# 2nd signal for entire ArticleGenerator.generate_context().
# 1st param is Generator type (ArticlesGenerator/PagesGenerator/StaticGenerator).
# 2nd param is metadata dict object (what you see in first few lines of an
# article/page/static file).
#
# Hooked by signals.article_generator_context.connect(mp_article_context).
#
print('mp_article_context called')
return
def mp_content_object_init(content_class: object):
# Description:
# First signal handler to provide the actual content of any article/page/static
# file.
#
# arg1 : content_class:Content, could be article:Article, page:Page, static:Static
#
# article of Article(Content) subclass provides the following variable member items:
# allowed_statuses:tuple, author:Author, authors:list, category:Category,
# content:str, date:SafeDatetime, date_format:str, default_status:str,
# default_template:str, filename:str, get_content:partial, get_summary:partial,
# in_default_lang:bool, lang:str, locale_date:str, mandatory_properties:tuple,
# metadata:dict, private:str, reader:str, relative_dir:str,
# relative_source_path:str, save_as:str, settings:dict, slug:str,
# source_path:str, status:str, summary:str, tags:list, template:str,
# timezone:Zoneinfo, title:str, translations:list, url:str, url_format:dict
#
# Callstack:
# signals.content_object_init.send()
# Content.__init__()
# Article.__init__()
# Readers.read_file()
# ArticlesGenerator.generate_context()
# Pelican.run()
#
# 4th article-related signal.
# 3rd signal in ArticlesGenerator.generate_context().
# Still inside read_file().
# First signal appearance having a content provided by Markdown.read_file().
#
# Hooked using signals.content_object_init.connect(mp_content_object_init).
#
print('mp_content_object_init called')
if content_class is None:
return
content = content_class.content
print('mp_content_object_init: content: {0!s}'.format(content))
# Only process Article or Page subclass contents
if not (isinstance(content_class, Article) or isinstance(content_class, Page)):
return
return
def mp_article_pretaxonomy(articles_generator):
# Description:
# Now you can first tell if it came from ArticlesGenerator or StaticGenerator
# using self variable.
# But the content is now gone.
# After all translations are done, after a complete list of all document file
# is made for each generator type.
# All about indexing, cross-linking and translation, plugins who want the
# metadata after such translation/index/lists updating;
# absolutely no content (anymore).
#
# arg1: articles_generator:ArticlesGenerator
#
# articles_generator of ArticlesGenerator class provides the
# following variable member items:
# articles:list, authors:dict, categories:dict, content:dict,
# dates:dict, drafts:list,
# drafts_translations:list, env:Environment, hidden_articles:list,
# hidden_translations:list, output_path:str, path:str, period_archives:dict,
# readers:Readers, related_posts:list, settings:dict, tags:dict, theme:str,
# and translations:list.
#
# 5th article-related signal.
# 4th signal in ArticlesGenerator.generate_context().
#
# Hooked by signals.article_generator_pretaxonomy.connect(mp_article_pretaxonomy).
#
print('mp_article_pretaxonomy called')
return
def mp_article_finalized(articles_generator):
# Description:
#
# arg1 : articles_generators:ArticlesGenerator
#
# articles_generator of ArticlesGenerator class provides the
# following variable member items:
# articles:list, authors:dict, categories:dict, context:dict
# dates:dict, drafts:list,
# drafts_translations:list, env:Environment, hidden_articles:list,
# hidden_translations:list, output_path:str, path:str, period_archives:dict,
# readers:Readers, related_posts:list, settings:dict, tags:dict, theme:str,
# and translations:list.
#
# 5th article-related signal.
# The last signal in ArticlesGenerator.generate_context().
# all the caches are saved.
#
# Hooked by signals.article_generator_finalized.connect(tp_article_finalized).
#
print('mp_article_finalized called')
return
def mp_article_write(articles_generator, content=[]):
#
# arg1 : articles_generator:ArticlesGenerator
# arg2 : content:Article
#
# articles_generator of ArticlesGenerator class provides the
# following variable member items:
# articles:list, authors:dict, categories:dict, context:dict
# dates:dict, drafts:list,
# drafts_translations:list, env:Environment, hidden_articles:list,
# hidden_translations:list, output_path:str, path:str, period_archives:dict,
# readers:Readers, related_posts:list, settings:dict, tags:dict, theme:str,
# and translations:list.
#
# content of Article class provides the following variable member items:
# allowed_statuses:tuple, author:Author, authors:list, category:Category,
# content:str, date:SafeDatetime, date_format:str, default_status:str,
# default_template:str, filename:str, get_content:partial, get_summary:partial,
# in_default_lang:bool, lang:str, locale_date:str, mandatory_properties:tuple,
# metadata:dict, private:str, reader:str, relative_dir:str,
# relative_source_path:str, save_as:str, settings:dict, slug:str,
# source_path:str, status:str, summary:str, tags:list, template:str,
# timezone:Zoneinfo, title:str, translations:list, url:str, url_format:dict
#
# 6th article-related signal.
# 5th signal in ArticlesGenerator.generate_context().
# Article.content is back.
# Article.metadata is back.
#
# Hooked by signals.article_generator_write_article.connect(mp_article_write).
#
print('mp_article_write called')
return
# This is how pelican plugin works.
# register() is a well-established function name used by Pelican plugin
# handler for this plugin to get recognized, inserted, initialized, and
# its processors added into and by the Pelican app.
def register():
signals.initialized.connect(my_plugin_pelican_initialized_all)
# Different version of Pelican behave differently.
# By using 'try', we ensure that all signals are available before
# our plugin processing.
try:
# All signals are listed here as of Pelican v4.9.1
# signals.get_generators.connect()
# signals.readers_init()
# signals.generator_init()
signals.article_generator_init.connect(mp_article_init)
# signals.readers_init()
# signals.readers_init()
# signals.generator_init()
# signals.page_generator_init()
# signals.readers_init()
# signals.generator_init()
# signals.readers_init()
# signals.generator_init()
# signals.static_generator_init()
signals.article_generator_preread.connect(mp_article_preread)
signals.article_generator_context.connect(mp_article_context)
signals.content_object_init.connect(mp_content_object_init)
signals.article_generator_pretaxonomy.connect(mp_article_pretaxonomy)
signals.article_generator_finalized.connect(mp_article_finalized)
# signals.page_generator_preread.connect(mp_page_preread)
# signals.page_generator_context.connect(mp_page_context)
# signals.content_object_init.connect(mp_content_object_init)
# signals.page_generator_finalized.connect(mp_page_finalized)
# signals.static_generator_preread.connect(mp_static_preread)
# signals.static_generator_context.connect(mp_static_context)
# signals.content_object_init.connect(mp_content_object_init)
# signals.static_generator_finalized.connect(mp_static_finalized)
# signals.all_generators_finalized.connect(mp_all_generators_finalized)
# signals.get_writers()
# signals.feed_generated()
# signals.feed_written()
signals.article_generator_write_article.connect(mp_article_write)
# signals.content_written()
# signals.article_writer_finalized.connect(mp_article_write)
# signals.page_generator_write_page.connect(mp_article_write)
# signals.content_written()
# signals.page_writer_finalized()
# signals.content_written()
# signals.pelican_finalized()
except Exception as e:
logger.exception('Plugin failed to execute: {}'.format(pprint.pformat(e)))
logger.info(
'my_plug plugin registered for Pelican, using new 4.0 plugin variant')