Developing a Betty extension

Extensions are Betty plugins that integrate deeply with the Betty application and have the most power to change or add functionality to your sites.

Getting started

  1. Determine where in your package you want the extension to be located. Its fully qualified name will be used as its extension name, e.g. an extension my_package.my_module.MyExtension will be named "my_package.my_module.MyExtension". This name is also used to enable the extension in project configuration files.

  2. Create a new class that extends betty.app.extension.Extension, for example:

from betty.app.extension import Extension

class MyExtension(Extension):
  pass

Congratulations! You have created your very first Betty extension. Keep reading to learn how to add functionality.

Making your extension discoverable

Making your extension discoverable means that Betty knows it’s available and can present users with the option to enable and configure your extension for their project.

Given an extension my_package.my_module.MyExtension, add the following to your extension’s Python package:

[project.entry-points.'betty.extensions']
'my_package.my_module.MyExtension' = 'my_package.my_module.MyExtension'
SETUP = {
    'entry_points': {
        'betty.extensions': [
            'my_package.my_module.MyExtension=my_package.my_module.MyExtension',
        ],
    },
}
if __name__ == '__main__':
    setup(**SETUP)

Asset management

Extensions can enable asset management to provide translations, templates, and more, by overriding betty.app.extension.Extension.assets_directory_path() to return the path on disk where the extension’s assets are located. This may be anywhere in your Python package.

from betty.app.extension import Extension

class MyExtension(Extension):
    @classmethod
    def assets_directory_path(cls) -> Path | None:
        # A directory named "assets" in the same parent directory as the current Python file.
        return Path(__file__).parent / 'assets'

Dependencies

Important

Any dependencies on other Python packages must be declared by your extension’s Python package.

Extensions have fine-grained control over which other extensions they require, and the order in which they appear in the extension dependency tree:

betty.app.extension.Extension.depends_on()

Declare required other extensions. This ensures those extensions are enabled and appear before your extension in the extension dependency tree.

betty.app.extension.Extension.comes_after()

Declare other extensions that are not required, but if they are enabled, then your extension will appear after them in the extension dependency tree.

betty.app.extension.Extension.comes_before()

Declare other extensions that are not required, but if they are enabled, then your extension will appear before them in the extension dependency tree.

Dispatching

Extensions can handle dispatched events by extending from any of the following classes:

betty.app.extension.ConfigurableExtension

Enable configuration management for the extension.

betty.app.extension.Theme

Mark the extension as being a theme, e.g. an extension that determines the overall look and feel of a site.

betty.app.extension.UserFacingExtension

Mark the extension as being suitable for end user interaction, e.g. it is not internal.

betty.generate.Generator

Dispatched when the site is being generated. This is used to tell extensions when to generate their parts of the site.

betty.gui.GuiBuilder

Provide a Graphical User Interface to manage the extension in the Betty Desktop application.

betty.html.CssProvider

Add additional CSS files to generated pages.

betty.html.JsProvider

Add additional JavaScript files to generated pages.

betty.jinja2.Jinja2Provider

Integrate the extension with Jinja2.

betty.load.Loader

Dispatched when data is loaded into an ancestry. This is used to import data.

betty.load.PostLoader

Dispatched after data is loaded into an ancestry. This is used to modify loaded data.