Plugin Architecture
Each command provided by KIWI NG is written as a task plugin under the kiwi.tasks namespace. As a developer, you can extend the KIWI NG system command space with custom task plugins, following the conventions below.
Naming conventions
- Task plugin file name
The file name of a task plugin must follow the pattern
system_<command>.py. This allows you to invoke the task with kiwi-ng system <command> ...- Task plugin option handling
KIWI NG uses the typer module to handle options. Each task plugin must use typer to allow option handling. The typer definition must be provided in a file named
cli.pyand must live in the toplevel of the plugin python namespace. Thecli.pyfile must provide a dictionary namedtypersthat maps the command name to itstyper.Typerinstance.- Task plugin class
The implementation of the plugin must be a class that matches the naming convention
System<Command>Task. The class must inherit from theCliTaskbase class. On the plugin startup, KIWI NG expects an implementation of theprocessmethod.- Task plugin entry point
Registration of the plugin must be done in
pyproject.tomlusing thetool.poetry.pluginsconcept. The entry point group must bekiwi.tasks, and the name of the toplevel plugin python namespace must contain the_pluginsubstring. Otherwise, KIWI NG does not load thecli.pyof the plugin. The namespaceskiwi_boxed_pluginandkiwi_stackbuild_pluginare ignored because their commands are now part of KIWI NG itself.[tool.poetry] name = "kiwi_<pluginname>_plugin" packages = [ { include = "kiwi_<pluginname>_plugin"}, ] [tool.poetry.plugins] [tool.poetry.plugins."kiwi.tasks"] system_<command> = "kiwi_<pluginname>_plugin.tasks.system_<command>"
Example plugin
Note
The following example assumes an existing Python project which was set up using poetry and pyproject.toml.
Assuming the project namespace is kiwi_relax_plugin, create the task plugin directory
kiwi_relax_plugin/tasks.Create the entry point in
pyproject.toml.Assuming we want to create the system command justdoit, this is the following entry point definition in
pyproject.toml:[tool.poetry] name = "kiwi_relax_plugin" packages = [ { include = "kiwi_relax_plugin"}, ] [tool.poetry.plugins] [tool.poetry.plugins."kiwi.tasks"] system_justdoit = "kiwi_relax_plugin.tasks.system_justdoit"
Create the typer cli interface in the file
kiwi_relax_plugin/cli.pywith the following content:import typer from typing import Annotated # typers variable must be provided for kiwi plugins typers = { 'justdoit': typer.Typer(add_completion=False) } system = typers['justdoit'] @system.callback( help='What is it good for', invoke_without_command=True, subcommand_metavar='' ) def justdoit( ctx: typer.Context, now: Annotated[bool, typer.Option(help='For --now option')] = False ): Cli=ctx.obj Cli.subcommand_args['justdoit'] = { '--now': now, 'help': False } Cli.global_args['command'] = 'justdoit' Cli.global_args['system'] = True Cli.cli_ok = True
Create the plugin code in the file
kiwi_relax_plugin/tasks/system_justdoit.pywith the following content:# These imports requires kiwi to be part of your environment # It can be either installed from pip into a virtual development # environment or from the distribution package manager. from kiwi.tasks.base import CliTask from kiwi.help import Help class SystemJustdoitTask(CliTask): def process(self): self.manual = Help() if self.command_args.get('help') is True: # The following will invoke man to show the man page # for the requested command. Thus, for the call to # succeed, a man page needs to be written and # installed by the plugin. return self.manual.show('kiwi::relax::justdoit') if self.command_args.get('--now'): print( 'https://genius.com/Frankie-goes-to-hollywood-relax-lyrics' )
Test the plugin
$ poetry run kiwi-ng system justdoit --now