File-Based Plugin — Zero Packaging¶
The lightest way to extend functualize: a single .py file in .functualize/plugins/. No package, no entry points — the plugin loader scans the convention directory at boot.
Source¶
examples/plugins/file_based_plugin/
Running¶
cd examples/plugins/file_based_plugin
# The loader discovers .functualize/plugins/run_notifier.py at boot;
# the plugin announces the job's success on the event bus
func greet
How it works¶
- At boot, the composition root resolves plugin directories once, before plugins load: every
[tool.functualize] plugins_directoriesdeclared up the config walk (withroot = trueand the XDG global layer honoured, same asjobs_directories), plus the convention directory.functualize/plugins/at the project root — the directoryfunc builtin inforeports asMode: project, found by walking up rather than by matching the CWD exactly. Declared directories are scanned first. - Each top-level non-underscore
.pyfile is imported; the loader looks for a module-levelpluginobject withname,version, anddescriptionstring attributes that is callable. - The loader invokes
plugin(app)— the registration hook for subscribing to events, registering commands, or adding providers.
Entry-point plugins win on name collisions; file plugins participate in the same dependency ordering and config resolution as packaged plugins.
When to graduate¶
File plugins are per-project. To share across projects or publish, package with an entry point — see Custom Adapter and Custom State Backend, or run func builtin scaffold add plugin.