Basic Notes on Pelican Plugin Architecture ########################################## :title: Basic Notes on Pelican Plugin Architecture :date: 2016-11-04 :category: Software :tags: programming, pelican :slug: basic-notes-on-pelican-plugin-architecture :series: Pelican Plugins :author: Chris Ramsay :status: published :language: en :show_source: True .. role:: bash(code) :language: bash .. contents:: Rationale --------- .. PELICAN_BEGIN_SUMMARY I have always been a fan of the `Sphinx`_ Python documentation generator and it has, I think, a nice feature where you can check out the raw source of a generated piece of documentation - a *Show Source* link. I decided that developing a `Pelican plugin`_ to imitate that feature would be a great way of getting a little deeper into the Pelican code itself. This first post explores the Pelican plugin architecture and the basics of building a plugin. .. PELICAN_END_SUMMARY The Pelican Plugin Architecture ------------------------------- Firstly, and mostly for my own benefit, I would like to make a few notes about the Pelican plugin architecture. Now that I have delved into it a little I have found that it is quite simple and gives you great ease of access to the entire build process. Pretty much all the important objects in the process can be accessed and modified. The Pelican blog system uses the `blinker`_ Python library to emit a series of signals during build time, that a series of interested parties can subscribe to. Any plugin developed for Pelican is designed to hook into this series of signals, and there are a lot! Here is a list of signals and the objects that are available (as from :bash:`pelican/pelican/signals.py`) at commit `8993c55`_: .. code-block:: bash Signals |-Run-level signals | |-initialized (pelican object) | |-get_generators (pelican object) | |-all_generators_finalized (generators) | |-get_writer (pelican object) | `-finalized (pelican object) |-Reader-level signals | `-readers_init (readers) |-Generator-level signals | |-generator_init (generator) | |-article_generator_init (article_generator) | |-article_generator_pretaxonomy (article_generator) | |-article_generator_finalized (article_generator) | |-article_generator_write_article (article_generator, content) | |-article_writer_finalized (article_generator, writer) | |-page_generator_init (page_generator) | |-page_generator_finalized (page_generator) | |-page_writer_finalized (page_generator, writer) | |-static_generator_init (static_generator) | `-static_generator_finalized (static_generator) |-Page-level signals | |-article_generator_preread (article_generator) | |-article_generator_context (article_generator, metadata) | |-page_generator_preread (page_generator) | |-page_generator_context (page_generator, metadata) | |-static_generator_preread (static_generator) | |-static_generator_context (static_generator, metadata) | `-content_object_init (content_object) `-Writers signals |-content_written (path, context) `-feed_written (path, context, feed) Plugin File Layout ------------------ In order to build a plugin, you need at least a couple of files in your plugin directory: an :bash:`__init__.py` and a plugin file, say, :bash:`myplugin.py`, looking something like: .. code-block:: bash myplugin |-__init__.py `-myplugin.py The contents of :bash:`__init__.py` should be the following: .. code-block:: python from from .myplugin import * The contents of :bash:`myplugin.py` could be something like: .. code-block:: python from pelican import signals def plugin_function(article_generator, content): """Your plugin function for article_generator_write_article""" # You now have access to the article_generator & content objects pass def plugin_function_2(path, context): """Your plugin function for content_written""" # You now have access to the path & context objects pass def register(): """ The register function is a requirement for the plugin to work. You have to call your plugin functions by `signals.x.connect(function)` with x being the required signal(s) and main_function being the function for that signal. You can have as many signal.x.connect() as you want here. """ # When the signals below are emitted, functions above will be called signals.article_generator_write_article.connect(plugin_function) signals.content_written.connect(plugin_function_2) That's It! ---------- So you can see that developing a simple plugin is really not a lot of work if you understand the basics. In order to gain a deeper knowledge of what you can do with plugins, it is definitely worth spending some time having a look through the `Pelican plugin repo`_ to see what others have done. Whilst you are there, have a look at how the objects that are made available in the functions are used. There are more helpful hints regarding plugin development `here in the Pelican docs`_, as well as a handy page on `contributing a plugin`_ once again in the `Pelican plugin repo`_ Have fun! .. footnotes .. links .. _`Pelican plugin`: https://github.com/getpelican/pelican-plugins .. _`Pelican plugin repo`: https://github.com/getpelican/pelican-plugins .. _`Sphinx`: http://www.sphinx-doc.org/ .. _`blinker`: https://pythonhosted.org/blinker/ .. _`8993c55`: https://github.com/getpelican/pelican/blob/8993c55e6edc4993790e2aef182b924ff60b5239/pelican/signals.py .. _`here in the Pelican docs`: http://docs.getpelican.com/en/latest/plugins.html .. _`contributing a plugin`: https://github.com/getpelican/pelican-plugins/blob/master/Contributing.rst