MultiQC logo

MultiQC Plugins

Docs / Development

Extending core functionality with plugins

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 setup.py you will see a section of code that looks like this (truncated):

entry_points = {
    'multiqc.modules.v1': [
        'qualimap = multiqc.modules.qualimap:MultiqcModule',
    ],
    'multiqc.templates.v1': [
        'default = multiqc.templates.default',
    ],
    # 'multiqc.cli_options.v1': [
        # 'my-new-option = myplugin.cli:new_option'
    # ],
    # 'multiqc.hooks.v1': [
        # 'before_config = myplugin.hooks:before_config',
        # 'config_loaded = myplugin.hooks:config_loaded',
        # 'execution_start = myplugin.hooks:execution_start',
        # 'before_modules = myplugin.hooks:before_modules',
        # 'after_modules = myplugin.hooks:after_modules',
        # 'execution_finish = myplugin.hooks:execution_finish',
    # ]
},

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. 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)