Skip to content

Plugins¤

Here is how you create or extend tools with plugins, callable like <toolname> <pluginname> [plugin flags].

Git Analogy

In this terminology, "git" would be the name of the tool, "checkout" would be the name of the plugin.

Defining a New Tool¤

In your pyproject.toml add the name of the tool within the scripts section like so:

[tool.poetry.scripts]
myapp = "devapp.tools.plugin:main"

This makes the tool available, with no plugins yet:

$ poetry install
Installing dependencies from lock file

No dependencies to install or update

Installing the current project: devapps (2023.06.20)
$ myapp -h || true
No plugins found. Create <namespace>/plugins/myapp_<namespace>/ folder(s), containing importable python modules.

We are ready to create plugins:

Plugins must reside within <package>/plugins/<tool_name>_<package_name>/ subdirectory of packages of the current repo.

The package name at the end is to allow "higher order repos" to supply plugins with same name but changed behaviour.

For the demo we pick the devapp package within this repo, devapps:

$ mkdir -p "src/devapp/plugins/myapp_devapp" && ls "src/devapp/plugins" && pwd
dev_devapp
myapp_devapp
ops_devapp
/home/runner/work/devapps/devapps

Then we create a demo plugin:

$ cat src/devapp/plugins/myapp_devapp/say_hello.py
"""
Saying Hello
"""

from functools import partial

from devapp.app import run_app, do, app
from devapp.tools import FLG


class Flags:
    'Simple Hello World'

    autoshort = 'g'  # all short forms for our flags prefixed with this

    class name:
        n = 'Who shall be greeted'
        d = 'User'


# --------------------------------------------------------------------------- app
def greet(name):
    print('Hey, %s!' % name)


def run():
    do(greet, name=FLG.name)


main = partial(run_app, run, flags=Flags)

The plugin is now available:

$ myapp -h

Usage: myapp ACTION [--flag[=value] ...]

Available Action Plugins:
    - say_hello

Help:
    myapp <action> <-h|--helpfull> [match]

Note:
    - Action shortcuts understood, e.g. action "foo_bar_baz" = fbb
    - Plugins are taken on first found basis
    - Flags also have shortcut versions (e.g. -hf for --helpfull)

Example:
    myapp say_hello -hf log # all flags about logging
$ myapp sh -lf 2 -gn Joe
07-25 16:06:14 [info     ] greet                          [say_hello] name=Joe store_log=None
Hey, Joe!
  • Further plugins for our myapp tool are now simply added into this directory

Extending a Given Tool¤

Higher order repos (dependend/derived on devapp) can add their own plugins for myapp, following the directory convention given above.

Means: A package "foo" depending on devapp may add a

/src(of_foo package)/bar/plugins/myapp_bar/bettergreeter.py

so that the myapp tool has a better/more specialized greeter plugin.

Derived package foo may also change the behaviour of the "say_hello" plugin of "myapp" by providing this module as well.

Here is how you "patch" a given module, e.g. the project plugin of the ops tool, from a devapps derived package (here lc-python):

~/repos/lc-python/sr/o/p/ops_operators master !3 ?1  pwd
/home/gk/repos/lc-python/src/operators/plugins/ops_operators
~/repos/lc-python/sr/o/p/ops_operators master !3 ?1  cat project.py
from devapp.plugins.ops_devapp.project import *


class Flags(Flags):
    'defined here, so that ops project -h works correctly'

# (overwrite your stuff here)

main = lambda: run_app(run, flags=Flags)

.

Back to top