Launcher Plugins¶
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
BaseLauncher
andBaseLauncherSerialized
insidemake sure its parent location is registred in the PYTHONPATH
append the module name to the
launcher_plugins
configuration key
Which could be illustrated by the following commands:
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 the content of:
{root}/tests/data/plugins-behr
{root}/tests/data/plugins-tyfa
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 would need to create a dataclass
object from
it which subclass BaseLauncher
, and it’s corresponding BaseLauncherSerialized
subclass which describe how that dataclass is serialized.
Here is an example which subclass both of those class, in which we create a launcher for “git cloning”:
import dataclasses
import http.client
import subprocess
from pathlib import Path
from typing import List
from typing import Optional
from kloch.launchers import BaseLauncher
from kloch.launchers import BaseLauncherSerialized
from kloch.launchers import BaseLauncherFields
@dataclasses.dataclass
class GitCloneLauncher(BaseLauncher):
name = "git-clone"
# all field must have a default value
# but we make it required using the `required_fields` attribute
remote_url: str = ""
required_fields = ["remote_url"]
def execute(self, tmpdir: Path, command: Optional[List[str]] = None):
# we consider `command` are just extra args to git clone command
_command = ["git", "clone", self.remote_url] + command
subprocess.run(_command, env=self.environ, cwd=self.cwd)
@dataclasses.dataclass(frozen=True)
class GitCloneLauncherFields(BaseLauncherFields):
remote_url: str = dataclasses.field(
# this is the expected key name in the serialized representation
default="remote-url",
# this if for automated documentation generation
metadata={
"description": "An URL to a valid remote git repository.",
"required": True,
},
)
def does_url_exists(url: str) -> bool:
connection = http.client.HTTPConnection(url)
connection.request("HEAD", "")
return connection.getresponse().status < 400
class GitCloneLauncherSerialized(BaseLauncherSerialized):
# the class it serialize
source = GitCloneLauncher
# we can pick a different name but we keep it similar for simplicity
identifier = GitCloneLauncher.name
fields = GitCloneLauncherFields
# short one line description of the launcher
summary = "Just clone a repository from a git remote."
# full documentation of an arbitrary length for the launcher
description = "From a git remote repository url, git clone it in the current working directory."
def validate(self):
super().validate()
remote_url = self.fields.remote_url
assert remote_url in self, f"'{remote_url}': missing or empty attribute."
assert does_url_exists(
self[remote_url]
), f"'{remote_url}': url provided doesn't exists: {self[remote_url]}."
# we override for type-hint
def unserialize(self) -> GitCloneLauncher:
# noinspection PyTypeChecker
return super().unserialize()
Registering¶
Then add the kloch_gitclone.py
parent location in your PYTHONPATH
so the plugin system can do an import kloch_gitclone
.
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 Configuration
A quick way to do it is just to set the corresponding environment variable before starting kloch.
export KLOCH_CONFIG_LAUNCHER_PLUGINS=kloch_gitclone