Snakemake Report Plugins Interface
This library provides the abstract base classes, decorators, and type hints required to develop custom report plugins for Snakemake workflows. It defines the standardized interface that Snakemake uses to discover and interact with report plugins. As an interface package, it aims for API stability for plugin developers and generally follows Snakemake's major release cadence. The current version is 2.0.1 and requires Python 3.11 or newer.
Common errors
-
ModuleNotFoundError: No module named 'snakemake_interface_report_plugins.api'
cause The `snakemake-interface-report-plugins` package is either not installed, or there's a typo in the import path.fixEnsure the package is installed: `pip install snakemake-interface-report-plugins`. Verify the import path is `from snakemake_interface_report_plugins.api import ...`. -
TypeError: Can't instantiate abstract class MyPlugin with abstract methods get_report_name
cause Your plugin class inherits from `ReportPluginBase` but has not implemented all of its abstract methods (e.g., `get_report_name`, `get_report_css`, `get_report_js`, `get_report_html`).fixImplement all methods declared as abstract in `ReportPluginBase` in your plugin class. Refer to the quickstart example for required methods. -
snakemake.exceptions.PluginError: Plugin 'my-plugin' could not be registered: Class must inherit from ReportPluginBase
cause Your plugin class is decorated with `@register_report_plugin` but does not actually inherit from `snakemake_interface_report_plugins.api.ReportPluginBase`.fixEnsure your plugin class explicitly inherits `ReportPluginBase`: `class MyPlugin(ReportPluginBase):`. -
ImportError: cannot import name 'ReportPluginBase' from 'snakemake_interface_report_plugins'
cause You are trying to import `ReportPluginBase` directly from the top-level package instead of its specific `api` submodule.fixChange your import statement to `from snakemake_interface_report_plugins.api import ReportPluginBase`.
Warnings
- breaking Version 2.0.0 introduced significant breaking changes for plugin developers. The plugin registration mechanism was refactored, requiring plugins to explicitly inherit from `ReportPluginBase` and use the `@register_report_plugin` decorator. Older plugins designed for `snakemake-interface-report-plugins` v1.x will not be compatible.
- gotcha This library defines the *interface* for plugins, not the core logic for running Snakemake reports. While you implement the plugin using this interface, the plugin itself must be discovered and invoked by a compatible Snakemake core version (typically Snakemake >=8.0.0 for v2 plugins).
- gotcha Report plugins must implement all abstract methods defined in `ReportPluginBase`, such as `get_report_name`, `get_report_css`, `get_report_js`, and `get_report_html`. Failure to do so will result in `TypeError` when the plugin class is instantiated.
- gotcha This library specifically requires Python 3.11 or newer (and less than 4.0). Using an older Python version will lead to installation failures or runtime errors.
Install
-
pip install snakemake-interface-report-plugins
Imports
- ReportPluginBase
from snakemake_interface_report_plugins import ReportPluginBase
from snakemake_interface_report_plugins.api import ReportPluginBase
- register_report_plugin
from snakemake_interface_report_plugins import register_report_plugin
from snakemake_interface_report_plugins.api import register_report_plugin
Quickstart
import os
from pathlib import Path
from snakemake_interface_report_plugins.api import ReportPluginBase, register_report_plugin
@register_report_plugin
class MyCustomReportPlugin(ReportPluginBase):
"""
A minimal Snakemake report plugin demonstrating the required interface.
"""
def __init__(
self,
report_dir: Path,
report_paths: dict[str, Path],
log_file: Path,
**kwargs,
):
super().__init__(report_dir, report_paths, log_file, **kwargs)
self.plugin_id = kwargs.get("name", "my-custom-report")
# Custom initialization logic here
def get_report_name(self) -> str:
"""Returns the unique name for this report plugin."""
return self.plugin_id
def get_report_css(self) -> list[Path]:
"""Returns a list of paths to CSS files for the report."""
# In a real plugin, these would typically be relative paths to files
# within the plugin's package, resolved by pkg_resources or importlib.resources.
return []
def get_report_js(self) -> list[Path]:
"""Returns a list of paths to JS files for the report."""
return []
def get_report_html(self, jobid: str) -> str:
"""
Generates the HTML content for a specific job ID.
This content will be embedded into the final Snakemake report.
"""
return f"""
<div class="custom-report-section">
<h3>Report for job: <code>{jobid}</code> from {self.plugin_id}</h3>
<p>Report directory: {self.report_dir}</p>
<p>Log file: {self.log_file}</p>
<p>Timestamp: {os.path.getmtime(self.log_file) if self.log_file.exists() else 'N/A'}</p>
</div>
"""
# To demonstrate, a plugin needs to be discovered by Snakemake.
# This example code does not run a Snakemake workflow, but shows
# the structure of a plugin Python file.
# In a real scenario, Snakemake would import and instantiate this class.
if __name__ == "__main__":
# Simulate Snakemake's environment for instantiation
temp_dir = Path("./temp_plugin_test").resolve()
temp_dir.mkdir(exist_ok=True)
sample_log_file = temp_dir / "sample_log.txt"
sample_log_file.write_text("Log content here.")
# Snakemake would instantiate your plugin like this:
mock_report_paths = {"summary": temp_dir / "summary.html"}
plugin_instance = MyCustomReportPlugin(
report_dir=temp_dir,
report_paths=mock_report_paths,
log_file=sample_log_file,
name="demo-plugin-instance"
)
print(f"Plugin registered: {plugin_instance.get_report_name()}")
print(f"Example HTML output for 'job_A':\n{plugin_instance.get_report_html('job_A')}")
# Cleanup
sample_log_file.unlink()
temp_dir.rmdir()