Launcher Plugins
================
Kloch comes with a basic plugin system that allow you to add new launchers.
The workflow can be simply described as:
- create a new python module at an arbitrary location
- subclass :any:`BaseLauncher` and :any:`BaseLauncherSerialized` inside
- make sure its parent location is registred in the PYTHONPATH so they can be imported
- append the module name to the ``launcher_plugins`` configuration key
Which could be illustrated by the following bash commands:
.. code-block:: shell
cd /d/dev/my-kloch-plugins/
touch kloch_mylauncher.py
# edit its content ^
export PYTHONPATH=$PYTHONPATH:/d/dev/my-kloch-plugins/
export KLOCH_CONFIG_LAUNCHER_PLUGINS=kloch_mylauncher
# check if we succesfully registred our plugin
kloch plugins
As example you can check:
- ``{root}/tests/data/plugins-behr``
- ``{root}/tests/data/plugins-tyfa``
- https://github.com/knotsanimation/kloch-launcher-rezenv
Creating the module
-------------------
A plugin for kloch is a regular python module that will be imported and parsed
to find specific object.
So you can create a regular python file at any location, or if you prefer
you can also ship your plugin as a python package by creating a directory
and an ``__init__.py`` file.
.. tip::
As kloch plugins need to be in the PYTHONPATH, every python code will be
able to import them so make sure to pick a name that will not conflict
with other modules. Prefixing them with ``kloch_`` is recommended.
Creating the subclasses
-----------------------
When creating the a new launcher you start by creating a ``dataclass`` object
that declare what are the user-configurable options and how are they "launched".
This is done by subclassing :any:`BaseLauncher`.
Next you need to declare how that dataclass must be serialized, by creating
a subclass of :any:`BaseLauncherSerialized`. This act as a high-level
controller for serialization.
For the granular control over how each field of the dataclass is serialized you
must created another dataclass subclass, but of :any:`BaseLauncherFields`.
Which will just miror the ``BaseLauncher`` field structure, but where each of
its field provide more metadata to unserialize the field.
Here is an example which subclass both of those class, in which we create
a launcher for "git cloning":
.. literalinclude:: _injected/snip-launcher-plugin-basic.py
:language: python
:caption: kloch_gitclone.py
.. tip::
When implementing a field for the user to have control on the launcher,
it's best to implement it as ``dict`` type over ``list`` because it
allow users to override one item in particular using the token system.
Registering
-----------
Then add the ``kloch_gitclone.py`` parent location in your PYTHONPATH
so the plugin system can do an ``import kloch_gitclone``.
.. tip::
PYTHONPATH is the standard mechanism by python to discover and import modules
but nothing prevent you to use other tools or methods.
You could for example create a ``pyproject.toml`` which declare ``kloch``
and your plugin as dependency and let a tool like `uv `_
or `poetry `_ create the venv.
As long as it can be ``import my_plugin_module_name`` it will work !
The last step is to add the module name in the list of launcher plugins to use.
You do this by modifying the kloch configuration, which is explained in :doc:`config`
A quick way to do it is just to set the corresponding environment variable before
starting `kloch`.
.. code-block:: shell
export KLOCH_CONFIG_LAUNCHER_PLUGINS=kloch_gitclone
You can check your plugin is registred by calling:
.. code-block:: shell
kloch plugins