A quick and dirty mini-plugin system for Python
Inspired by Pyramid’s and venusian’s scan()
call, I’ve reimplemented an
auto-discovery system for plugins. The problem is simple.
Suppose we want to “register” a series of functions that can run automatically, based on aspects set in the calling environment. The simples and easiest solution is something like:
# in some module, as a global declaration:
from somethingA import runner_A
from somethingB import runner_B
runners = [
runner_A,
runner_B
]
# then, maybe in some function:
def main():
# ...
for runner in runners:
runner()
This is dirty and ugly, but very straight forward. A more elegant, but more complicated solution would be this very simple application of decorators:
runners_registry = []
def register(func):
runners_registry.append(func)
return func
This can be used to decorate a function and add a reference to that function in the registry:
@registry
def clean():
pass
But now, to “activate” the registry call, one needs to make sure that the module containing that function is imported at startup. Ugly.
I prefer a version that makes development easier, as it doesn’t require
updating any code after a new “plugin module” is developed. It’s inspired
by venusian’s scan()
call, but really bare bones and without any feature.
Just very straight forward “import all found modules to activate their
decorators”
def scan(namespace):
""" Scans the namespace for modules and imports them, to activate decorator
"""
import importlib
import pkgutil
name = importlib.util.resolve_name(namespace, package=__package__)
spec = importlib.util.find_spec(name)
if spec is not None:
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
for finder, name, ispkg in pkgutil.iter_modules(module.__path__):
spec = finder.find_spec(name)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
Now I can use it like:
def main():
scan('somepackage.plugins')