metadata.py

Alongside items.py you may create another file called metadata.py. It can be used to do advanced processing of the metadata you configured for your nodes and groups. Specifically, it allows each bundle to modify metadata before items.py is evaluated.

This is accomplished through metadata processors. Metadata processors are functions that take the metadata dictionary generated so far as their single argument. You must then return a dictionary with any modifications you need to make plus at least one of several options:

@metadata_processor
def my_metadata_processor(metadata):
    metadata["foo"] = node.name
    return metadata, DONE

You must always return the modified metadata dictionary as the first element. After that, there are a few options you can return. Every metadata processor from every bundle is called repeatedly with the latest metadata dictionary until it indicates that it is done by returning the DONE option or until all remaining metadata processors return RUN_ME_AGAIN. You must always return one of DONE or RUN_ME_AGAIN. Use the latter if your metadata processor depends on metadata that is generated by another metadata processor (which may be called after yours). Here is another example:

@metadata_processor
def first_metadata_processor(metadata):
    metadata["foo"] = node.name
    return metadata, DONE

@metadata_processor
def second_metadata_processor(metadata):
    if "foo" in metadata:
        metadata["bar"] = metadata["foo"]
        return metadata, DONE
    else:
        return metadata, RUN_ME_AGAIN

In this example, "bar" can only be set once "foo" is available and thus the second_metadata_processor has to wait and request to RUN_ME_AGAIN until first_metadata_processor ran. This is necessary because the running order of metadata processors is undefined.

To avoid deadlocks when accessing other nodes' metadata from within a metadata processor, use other_node.partial_metadata instead of other_node.metadata. For the same reason, always use the metadata parameter to access the current node's metadata, never node.metadata.


Available options

OptionDescription
DONEIndicates that this metadata processor has done all it can and need not be called again. Return this whenever possible.
RUN_ME_AGAINIndicates that this metadata processor is still waiting for metadata from another metadata processor to become available.
DEFAULTSThe returned metadata dictionary will only be used to provide default values. The actual metadata generated so far will be recursively merged into the returned dict.
OVERWRITEThe returned metadata dictionary will be recursively merged into the actual metadata generated so far (inverse of DEFAULTS).

Here is an example of how to use DEFAULTS:

@metadata_processor
def my_metadata_processor(metadata):
    return {
        "foo": {
            "bar": 47,
        },
    }, DONE, DEFAULTS

This means node.metadata["foo"]["bar"] will be 47 by default, but can also be overridden in static metadata at the node/group level.


For your convenience, you can access repo, node, metadata_processor and all the options in metadata.py without importing them.