Pelican
Docs » Plugins
Plugins
Beginning with version 3.0, Pelican supports plugins. Plugins are a way to add features to Pelican without having to directly modify the Pelican core.
How to use plugins
Starting with version 4.5, Pelican moved to a new plugin structure utilizing namespace packages that can be easily installed via Pip. Plugins supporting this structure will install under the namespace package pelican.plugins and can be automatically discovered by Pelican. To see a list of Pip-installed namespace plugins that are active in your environment, run:
pelican-plugins
If you leave the PLUGINS setting as default (None), Pelican will automatically discover namespace plugins and register them. If, on the other hand, you specify a PLUGINS setting as a list of plugins, this auto-discovery will be disabled. At that point, only the plugins you specify will be registered, and you must explicitly list any namespace plugins as well.
If you are using the PLUGINS setting, you can specify plugins in two ways. The first method specifies plugins as a list of strings. Namespace plugins can be specified either by their full names (pelican.plugins.myplugin) or by their short names (myplugin):
PLUGINS = ['package.myplugin', 'namespace_plugin1', 'pelican.plugins.namespace_plugin2'​]
Alternatively, you can import them in your settings file and pass the modules:
from package import mypluginfrom pelican.plugins import namespace_plugin1, namespace_plugin2​PLUGINS = [myplugin, namespace_plugin1, namespace_plugin2]
Note
When experimenting with different plugins (especially the ones that deal with metadata and content) caching may interfere and the changes may not be visible. In such cases disable caching with
LOAD_CONTENT_CACHE = False
or use the --ignore-cache command-line switch.
If your plugins are not in an importable path, you can specify a list of paths via the PLUGIN_PATHS setting. As shown in the following example, paths in the PLUGIN_PATHS list can be absolute or relative to the settings file:
PLUGIN_PATHS = ["plugins", "/srv/pelican/plugins"​]​PLUGINS = ["assets", "liquid_tags", "sitemap"]
Where to find plugins
Namespace plugins can be found in the pelican-plugins organization as individual repositories. Legacy plugins are located in the pelican-plugins repository and will be gradually phased out in favor of the namespace versions.
Please note that while we do our best to review and maintain these plugins, they are submitted by the Pelican community and thus may have varying levels of support and interoperability.
How to create plugins
Plugins are based on the concept of signals. Pelican sends signals, and plugins subscribe to those signals. The list of available signals is documented in a subsequent section.
The only rule to follow for plugins is to define a register callable, in which you map the signals to your plugin logic. Let’s take a simple example:
import loggingfrom pelican import signalslog = logging​.​getLogger​(​__name__​)​def test(sender): log.debug("%s initialized !!", sender)def register(): signals​.​initialized​.​connect​(​test​)
Note
Signal receivers are weakly-referenced and thus must not be defined within your register callable or they will be garbage-collected before the signal is emitted.
If multiple plugins connect to the same signal, there is no way to guarantee or control in which order the plugins will be executed. This is a limitation inherited from Blinker, the dependency Pelican uses to implement signals.
Namespace plugin structure
Namespace plugins must adhere to a certain structure in order to function properly. They need to be installable (i.e. contain setup.py or equivalent) and have a folder structure as follows:
myplugin ├── pelican │   └── plugins │   └── myplugin │   ├── __init__.py │   └── ... ├── ... └── setup.py
It is crucial that pelican or pelican/plugins folder not contain an __init__.py file. In fact, it is best to have those folders empty besides the listed folders in the above structure and keep your plugin related files contained solely in the pelican/plugins/myplugin folder to avoid any issues.
To easily set up the proper structure, a cookiecutter template for plugins is provided. Refer to that project’s README for instructions on how to use it.
List of signals
Here is the list of currently implemented signals:
SignalArgumentsDescription
initializedpelican object 
finalizedpelican objectinvoked after all the generators are executed and just before pelican exits useful for custom post processing actions, such as: - minifying js/css assets. - notify/ping search engines with an updated sitemap.
generator_initgeneratorinvoked in the Generator.__init__
all_generators_finalizedgeneratorsinvoked after all the generators are executed and before writing output
readers_initreadersinvoked in the Readers.__init__
article_generator_contextarticle_generator, metadata 
article_generator_prereadarticle_generatorinvoked before a article is read in ArticlesGenerator.generate_context; use if code needs to do something before every article is parsed
article_generator_initarticle_generatorinvoked in the ArticlesGenerator.__init__
article_generator_pretaxonomyarticle_generatorinvoked before categories and tags lists are created useful when e.g. modifying the list of articles to be generated so that removed articles are not leaked in categories or tags
article_generator_finalizedarticle_generatorinvoked at the end of ArticlesGenerator.generate_context
article_generator_write_articlearticle_generator, contentinvoked before writing each article, the article is passed as content
article_writer_finalizedarticle_generator, writerinvoked after all articles and related pages have been written, but before the article generator is closed.
get_generatorspelican objectinvoked in Pelican.get_generator_classes, can return a Generator, or several generators in a tuple or in a list.
get_writerpelican objectinvoked in Pelican.get_writer, can return a custom Writer.
page_generator_contextpage_generator, metadata 
page_generator_prereadpage_generatorinvoked before a page is read in PageGenerator.generate_context; use if code needs to do something before every page is parsed.
page_generator_initpage_generatorinvoked in the PagesGenerator.__init__
page_generator_finalizedpage_generatorinvoked at the end of PagesGenerator.generate_context
page_generator_write_pagepage_generator, contentinvoked before writing each page, the page is passed as content
page_writer_finalizedpage_generator, writerinvoked after all pages have been written, but before the page generator is closed.
static_generator_contextstatic_generator, metadata 
static_generator_prereadstatic_generatorinvoked before a static file is read in StaticGenerator.generate_context; use if code needs to do something before every static file is added to the staticfiles list.
static_generator_initstatic_generatorinvoked in the StaticGenerator.__init__
static_generator_finalizedstatic_generatorinvoked at the end of StaticGenerator.generate_context
content_object_initcontent_objectinvoked at the end of Content.__init__
content_writtenpath, contextinvoked each time a content file is written.
feed_generatedcontext, feedinvoked each time a feed gets generated. Can be used to modify a feed object before it gets written.
feed_writtenpath, context, feedinvoked each time a feed file is written.
Warning
Avoid content_object_init signal if you intend to read summary or content properties of the content object. That combination can result in unresolved links when Linking to internal content (see pelican-plugins bug #314). Use _summary and _content properties instead, or, alternatively, run your plugin at a later stage (e.g. all_generators_finalized).
Note
After Pelican 3.2, signal names were standardized. Older plugins may need to be updated to use the new names:
Old nameNew name
article_generate_contextarticle_generator_context
article_generate_finalizedarticle_generator_finalized
article_generate_prereadarticle_generator_preread
pages_generate_contextpage_generator_context
pages_generate_prereadpage_generator_preread
pages_generator_finalizedpage_generator_finalized
pages_generator_initpage_generator_init
static_generate_contextstatic_generator_context
static_generate_prereadstatic_generator_preread
Recipes
We eventually realised some of the recipes to create plugins would be best shared in the documentation somewhere, so here they are!
How to create a new reader
One thing you might want is to add support for your very own input format. While it might make sense to add this feature in Pelican core, we wisely chose to avoid this situation and instead have the different readers defined via plugins.
The rationale behind this choice is mainly that plugins are really easy to write and don’t slow down Pelican itself when they’re not active.
No more talking — here is an example:
from pelican import signalsfrom pelican.readers import BaseReader# Create a new reader class, inheriting from the pelican.reader.BaseReader​class NewReader(BaseReader): enabled = True # Yeah, you probably want that :-) # The list of file extensions you want this reader to match with. # If multiple readers were to use the same extension, the latest will # win (so the one you're defining here, most probably). file_extensions = ['yeah'] # You need to have a read method, which takes a filename and returns # some content and the associated metadata. def read(self, filename): metadata = {'title': 'Oh yeah', 'category': 'Foo', 'date': '2012-12-01'} parsed = {} for key, value in metadata.items(): parsed[key] = self.process_metadata(key, value) return "Some content", parseddef add_reader(readers): readers​.​reader_classes​[​'yeah'​] = NewReader# This is how pelican works.def register(): signals​.​readers_init​.​connect​(​add_reader​)
Adding a new generator
Adding a new generator is also really easy. You might want to have a look at Pelican internals for more information on how to create your own generator.
def get_generators​(​pelican_object​): # define a new generator here if you need to return MyGeneratordef register(): signals​.​get_generators​.​connect​(​get_generators​)
Adding a new writer
Adding a writer will allow you to output additional file formats to disk, or change how the existing formats are written to disk. Note that only one writer will be active at a time, so be sure to either subclass the built-in Writer, or completely re-implement it.
Here is a basic example of how to set up your own writer:
from pelican.writers import Writerfrom pelican import signalsclass MyWriter(Writer): # define new writer functionality passdef add_writer(pelican_object): # use pelican_instance to setup stuff if needed return MyWriterdef register(): signals​.​get_writer​.​connect​(​add_writer​)
Next
Previous
Almost 60% of maintainers have quit or considered quitting. Read the report
Ad by EthicalAds   ·   Monetize your site
© Copyright 2010 – present, Justin Mayer, Alexis Metaireau, and contributors Revision bb10d286.
Built with Sphinx using a theme provided by Read the Docs.
Pelican QuickstartInstalling PelicanWriting contentPublish your siteSettingsHow to use pluginsWhere to find pluginsHow to create pluginsNamespace plugin structureList of signalsRecipesHow to create a new readerAdding a new generatorAdding a new writerThemespelican-themesImporting an existing siteFrequently Asked Questions (FAQ)TipsContributing and feedback guidelinesPelican internalsSome history about PelicanRelease history
Pelican