Ivy

A static website generator for people who enjoy the simpler things in life.

Version 6.5.0

Extensions


Installing Extensions

An extension (also known as a plugin) is a Python module or package that extends Ivy's functionality. You can install extensions for a site in one of two ways.

extensions = [
    'extension_one',
    'extension_two',
]

This second method can be used to enable extensions installed from the Python package index using pip.

Event & Filter Hooks

Ivy exports a flexible framework of event and filter hooks. Plugins can extend Ivy by registering callback functions on these hooks.

Most of Ivy's default functionality — e.g. support for Jinja templates or Markdown files — is implemented by a set of bundled plugins which make use of this hook system. If you want to extend Ivy yourself you'll probably want to start by taking at look at how they work.

You can find these bundled plugins in the ivy/ext/ directory which you can view on Github.

Events

Event callbacks accept zero or more arguments depending on the specific hook. They may modify their arguments in place but have no return value.

Here's a simple event callback that prints a count of the number of pages that have been written to disk:

from ivy.events import Event

@ivy.events.register(Event.EXIT)
def print_page_count():
    print(ivy.site.pages_written())

This callback is registered on the EXIT event hook which fires just before the application exits. (The EXIT event hook can be found in the ivy/__init__.py file.)

Filters

Filter callbacks accept at least one argument — the value to be filtered. They may accept additional arguments depending on the specific hook. Filter callbacks modify and return the value of their first argument.

Here's a simple filter callback that changes every instance of the word foo in node content to bar:

from ivy.filters import Filter

@ivy.filters.register(Filter.NODE_TEXT)
def foo_to_bar(text, node):
    return text.replace('foo', 'bar')

This callback is registered on the NODE_TEXT filter hook which fires just before a node's text is rendered into HTML. (The NODE_TEXT filter hook can be found in the ivy/nodes.py file).

Note that this hook supplies us with the Node instance itself as an additional argument which in this case we ignore.

Rendering & Parsing Engines

Ivy relies for most of its functionality on a suite of pluggable rendering and parsing engines, e.g. the Jinja template-engine for handling .jinja template files. Extensions can register support for additional rendering and parsing engines using a system of @register decorators.

Template Engines

Template-engines produce the output HTML for finished .html pages in the site.

Ivy has builtin support for Jinja and Ibis templates. Extensions can register support for additional template-engines using the @ivy.templates.register() decorator. Template-engine callbacks are registered per template-file-extension, e.g.

@ivy.templates.register('jinja')
def jinja_callback(page_data, template_filename):
    ...
    return html

A template-engine callback should accept a dictionary of page data and a template filename and return a string of HTML.

Rendering Engines

Rendering-engines convert node content into HTML which can then be poured into a template to produce the finished .html output page.

Ivy has builtin support for node files written in Markdown and Syntext. Extensions can register support for additional input formats using the @ivy.renderers.register() decorator. Rendering-engine callbacks are registered per file-extension, e.g.

@ivy.renderers.register('md')
def markdown_callback(text):
    ...
    return html

A rendering-engine callback should accept a single string argument containing plain text and return a string of HTML.

Note that if you register a custom callback for .md files, this will override the default Markdown renderer.

Node Metadata

Ivy has builtin support for YAML file headers. Extensions can add support for additional metadata formats by preprocessing file content on the file_text filter hook.

from ivy.filters import Filter

@ivy.filters.register(Filter.FILE_TEXT)
def parse_toml_header(raw_text, meta_dict):
    ...
    return filtered_text

This filter fires each time a node file is loaded from disk; it passes the raw file text along with a metadata dictionary. Callbacks can check the text for an appropriate header marker, process the header if found, and update the dictionary. They should return the text with the header stripped.

The FILE_TEXT filter hook can be found in the ivy/utils.py file.

Bundled Extension Settings

Markdown

Ivy uses the Markdown package to render node files with a .md extension. You can add a dictionary of keyword arguments for the Markdown renderer to your site configuration file via a markdown_settings attribute, e.g.

markdown_settings = {
    'extensions': ['markdown.extensions.extra']
}

See the Markdown package's documentation for details of the available options.

Note that you can register a custom handler for .md files to use an alternative Markdown library of your choice.

Syntext

Ivy uses the Syntext package to render node files with a .stx extension. You can add a dictionary of keyword arguments for the Syntext renderer to your site configuration file via a syntext_settings attribute, e.g.

syntext_settings = {
    'pygmentize': False,
}

Jinja

Ivy uses the Jinja package to render template files with a .jinja extension. You can add a dictionary of keyword arguments for the Jinja environment to your site configuration file via a jinja_settings attribute.

Shortcodes

Ivy uses the Shortcodes package to process shortcodes in node files. You can add a dictionary of keyword arguments for the shortcode parser to your site configuration file via a shortcode_settings attribute.

Automenu

The bundled Automenu extension automatically generates a menu containing links to every node in the site. The menu can be accessed in templates via the automenu attribute. This menu can be customized in three ways:

Only nodes which have a menu_title or title attribute are included in the menu.