Extending vortex using plugin packages

Extending vortex using plugin packages#

The vortex plugin system makes it easy for users to define and redistribute their own resources and task classes.

The vortex and vortex.nwp packages ship with a number of classes that model data, programs and computational tasks that typically make up most Numerical Weather Prediction pipelines. We expect use-cases, however, for which none of the provided classes apply. These could arise from novel research or using vortex within a domain for which it was not originally designed for.

You can implement the appropriate classes fitting your use case by extending one or several classes present in the vortex or vortex.nwp packages. In order to make these classes available to functions like vortex.input(), vortex.executable() or vortex.task(), the corresponding Python modules must be installed as part of a package that declares the vtx entry point in its metadata.

The vortex.loaded_plugins() can be used to list currently loaded plugin packages.

Example#

Let’s say we wish to define the following ressource class and make it available alongside any other vortex ressources. By make it available, we mean that, given the appropriate collection of arguments, functions of the top-level vortex module such as input or output will be able to instantiate the class.

# new.py

from vortex.data.flow import FlowResource

class NewResource(FlowResource):
    _footprint = [
        {
            "kind": {
                "values": ["observations"]
            },
            "nativefmt": {
                "values": ["json"]
            },
            "model": {
                "values": ["arpege"]
            },
        }
    ]

    @property
    def realkind(self):
        return "my-new-ressource"

Assuming the new.py module is installed, importing it will make the NewResource class available:

>>> from new import NewResource
>>> rh = vortex.input(
...    kind="newressource", model="arpege", cutoff="assim"
...    nativefmt="json", experiment="dummy-xp", local="local-filename"
... )[0]
>>> type(rh.resource)
<class 'new.NewResource'>

Having to manually import modules so that they are available through the top level vortex module functions is error-prone. It can also be awkward to import one or more modules for the sole purpose of making a few classes available, with the modules object themselves not being used in the script. For these reasons, we recommend that you group your modules into an plugin package that will be automatically discovered when importing vortex.

Creating a vortex plugin package#

If you group your module(s) into a Python package that declare the vtx entry point as part of its metadata, a import vortex statement will discover and load the modules it ships.

See also

This guide assumes that you are familiar with writing Python package metadata. If this is not the case, see the Configuring metadata section in the Python Packaging User Guide chapter on Packaging Python Projects.

Continuing on the previous example, here is how it works. We now assume that the new.py module is part of a package with the following directory structure:

new-resource-package/
    pyproject.toml
    src/
        vortex_newresource/
             new.py
             __init__.py

The package’s __init__.py module is responsible for making new available under the vortex_newresource namespace:

# src/newresource/__init__.py

# Use a redundant import alias to instruct linters (e.g. Ruff) that this
# is just a re-export of "new" as part of the package's public interface.
from . import new as new

In other words, importing the vortex_newresource package (import vortex_newresource) will make available the new module as newresource.new.

Next, the package metadata must declare the vtx entry point:

# pyproject.toml

[project.entry-points.vtx]
newresource = "vortex_newresource"

See also

See Entry points specification in the Python Packaging User Guide for more information about declaring entry points.

After installing the plugin package, the vortex_newresource.new module will be automatically discovered and loaded when importing vortex:

>>> import vortex
Loaded plugin newresource
Vortex 2.0.0b1 loaded
>>> rh = vortex.input(
...    kind="newressource", model="arpege", cutoff="assim"
...    nativefmt="json", experiment="dummy-xp", local="local-filename"
... )[0]
>>> type(rh.resource)
<class 'vortex_newresource.new.NewResource'>

Note that importing the vortex_newresource.new explicitly was never required.