MultiQC Plugins
MultiQC is written around a system designed for extensibility and plugins. These features allow custom code to be written without polluting the central code base.
Please note that we want MultiQC to grow as a community tool! So if you're writing a module or theme that can be used by others, please keep it within the main MultiQC framework and submit a pull request.
Entry Points
The plugin system works using setuptools entry points.
In pyproject.toml
you will see a section of code that looks like this (truncated):
[project.entry-points."multiqc.modules.v1"]
qualimap = "multiqc.modules.qualimap:MultiqcModule"
[project.entry-points."multiqc.templates.v1"]
default = "multiqc.templates.default"
# [project.entry-points."multiqc.cli_options.v1"]
# my-new-option = "myplugin.cli:new_option"
# [project.entry-points."multiqc.hooks.v1"]
# before_config = "myplugin.hooks:before_config"
These sets of entry points can each be extended to add functionality to MultiQC:
multiqc.modules.v1
- Defines the module classes. Used to add new modules.
multiqc.templates.v1
- Defines the templates. Can be used for new templates.
multiqc.cli_options.v1
- Allows plugins to add new custom command line options
multiqc.hooks.v1
- Code hooks for plugins to add new functionality
Any python program can create entry points with the same name, once installed MultiQC will find these and run them accordingly.
If your Python project uses setup.py
instead you can still tie into the entry points.
For an example of this in action, see the
MultiQC_NGI setup file:
entry_points = {
'multiqc.templates.v1': [
'ngi = multiqc_ngi.templates.ngi',
'genstat = multiqc_ngi.templates.genstat',
],
'multiqc.cli_options.v1': [
'project = multiqc_ngi.cli:pid_option'
],
'multiqc.hooks.v1': [
'after_modules = multiqc_ngi.hooks:ngi_metadata',
]
},
Here, two new templates are added, a new command line option and a new code hook.
Modules
List items added to multiqc.modules.v1
specify new modules. They should
be described as follows:
modname = "python_mod.dirname.submodname:classname"
Once this is done, everything else should be the same as described in the writing modules documentation.
Templates
As above, though no need to specify a class name at the end. See the writing templates documentation for further instructions.
Command line options
MultiQC handles command line interaction using the click
framework. You can use the multiqc.cli_options.v1
entry point to add new
click decorators for command line options. For example, the MultiQC_NGI
plugin uses the entry point above with the following code in cli.py
:
import click
pid_option = click.option('--project', type=str)
The values given from additional command line arguments are parsed by
MultiQC and put into config.kwargs
. The above plugin later reads
the value given by the user with the --project
flag in a hook:
from multiqc import config
if config.kwargs['project'] is not None:
# do some stuff
See the click documentation or the main MultiQC script for more information and examples of adding command line options.
Hooks
Hooks are a little more complicated - these define points in the core
MultiQC code where you can run custom functions. This can be useful as
your code is able to access data generated by other parts of the program.
For example, you could tie into the after_modules
hook to insert data
processed by MultiQC modules into a database automatically.
Here, the entry point names are the hook titles, described as commented out
lines in the core MultiQC setup.py
: execution_start
, config_loaded
,
before_modules
, after_modules
and execution_finish
.
These should point to a function in your code which will be executed when that hook fires. Your custom code can import the core MultiQC modules to access configuration and loggers. For example:
"""
MultiQC hook functions - we tie into the MultiQC
core here to add in extra functionality.
"""
import logging
from multiqc.utils import report
log = logging.getLogger('multiqc')
def after_modules():
""" Plugin code to run when MultiQC modules have completed """
num_modules = len(report.modules)
status_string = f"MultiQC hook - {num_modules} modules reported!"
log.critical(status_string)