Skip to content

Introduction to plugins

The main way to customize a NOMAD installation is through the use of plugins. A NOMAD plugin is a Python package that an administrator can install into a NOMAD distribution to add custom features. This page helps you develop and publish a NOMAD plugin. For a high-level overview of the plugin mechanism, see the NOMAD plugin system -page.

A single Python plugin package can contain multiple plugin entry points. These entry points represent different types of customizations including:

See the FAIRmat-NFDI GitHub organization page for a list of plugins developed by FAIRmat. You can also see the list of activated plugins and plugin entry points at the bottom of the Information page (about/information) of any NOMAD installation, for example check out the central NOMAD installation.

Plugin anatomy

We provide a template repository which you can use to create the initial plugin repository layout for you. The repository layout as generated by the template looks like this:

├── nomad-example
│   ├── src
|   │   ├── nomad_example
|   |   │   ├── apps
|   |   │   │   ├── __init__.py
|   |   │   ├── normalizers
|   |   │   │   ├── mynormalizer.py
|   |   │   │   ├── __init__.py
|   |   │   ├── schema_packages
|   |   │   │   ├── mypackage.py
|   |   │   │   ├── __init__.py
|   |   │   ├── parsers
|   |   │   │   ├── myparser.py
|   |   │   │   ├── __init__.py
│   ├── docs
│   ├── tests
│   ├── pyproject.toml
│   ├── LICENSE.txt
│   ├── README.md

We suggest using the following convention for naming the repository name and the plugin package:

  • repository name: nomad-<plugin name>
  • package name: nomad_<plugin name>

Controlling loading of plugin entry points

By default, plugin entry points are automatically loaded, and as an administrator you only need to install the Python package. You can, however, control which entry points to load by explicitly including/excluding them in your nomad.yaml. For example, if a plugin has the following pyproject.toml:

[project.entry-points.'nomad.plugin']
myparser = "nomad_example.parsers:myparser"

You could disable the parser entry point in your nomad.yaml with:

plugins:
  entry_points:
    exclude: ["nomad_plugin.parsers:myparser"]

Plugin development guidelines

Linting and formatting

While developing NOMAD plugins, we highly recommend using a Python linter, such as Ruff, to analyze and enforce coding standards in your plugin projects. This also ensures smoother integration and collaboration. If you have used our template repository, you will automatically have ruff defined as a development dependency with suitable defaults set in pyproject.toml together with a GitHub actions that runs the linting and formatting checks on each push to the Git repository.

Testing

For testing, you should use pytest, and a folder structure that mimics the package layout with test modules named after the tested module. For example, if you are developing a parser in myparser.py, the test folder structure should look like this:

├── nomad-example-plugin
│   ├── src
|   │   ├── nomad_example
|   |   │   ├── parsers
|   |   │   │   ├── myparser.py
|   |   │   │   ├── __init__.py
│   ├── tests
|   │   ├── parsers
|   |   │   ├── test_myparser.py
|   |   │   ├── conftest.py
|   │   ├── conftest.py

Any shared test utilities (such as pytest fixtures) should live in conftest.py modules placed at the appropriate level in the folder hierarchy, i.e. utilities dealing with parsers would live in tests/parsers/conftest.py, while root level utilities would live in tests/conftest.py. If you have used our template repository, you will automatically have an initial test folder structure, pytest defined as a development dependency in pyproject.toml and a GitHub action that runs the test suite on each push to the Git repository.

In the pytest framework, test cases are created by defining functions with the test_ prefix, which perform assertions. A typical test case could look like this:

def test_parse_file():
    parser = MyParser()
    archive = EntryArchive()
    parser.parse('tests/data/example.out', archive, logging)

    sim = archive.data
    assert len(sim.model) == 2
    assert len(sim.output) == 2
    assert archive.workflow2.x_example_magic_value == 42

You can run all the tests in the tests/ directory with:

python -m pytest -svx tests

Documentation

As your plugin matures, you should also think about documenting its usage. We recommend using mkdocs to create your documentation as a set of markdown files. If you have used our template repository, you will automatically have an initial documentation folder structure, mkdocs defined as a development dependency in pyproject.toml and a GitHub action that builds the docs to a separate gh-pages branch each push to the Git repository. Note that if you wish to host the documentation using GitHub pages, you need to enable this in the repository settings.

Publishing a plugin

Attention

The standard processes for publishing plugins and using plugins from other developers are still being worked out. The "best" practices mentioned in the following are preliminary. We aim to set up a dedicated plugin registry that allows you to publish your plugin and find plugins from others.

GitHub repository

The simplest way to publish a plugin is to have it live in a publicly shared Git repository. The package can then be installed with:

pip install git+https://<repository_url>

Note

If you develop a plugin in the context of FAIRmat or the NOMAD CoE, put your plugin repositories in the corresponding GitHub organization.

PyPI/pip package

You may additionally publish the plugin package in PyPI. Learn from the PyPI documentation how to create a package for PyPI. We recommend to use the pyproject.toml-based approach.

The PyPI documentation provides further information about how to publish a package to PyPI. If you have access to the MPCDF GitLab and NOMAD's presence there, you can also use the nomad-FAIR package registry:

pip install twine
twine upload \
    -u <username> -p <password> \
    --repository-url https://gitlab.mpcdf.mpg.de/api/v4/projects/2187/packages/pypi \
    dist/nomad-example-plugin-*.tar.gz

Installing a plugin

See our documentation on How to install plugins into a NOMAD Oasis.