Transformers are functions for converting Semantic Type objects into other formats that can be consumed by python functions. These transformers are typically defined along with the Semantic Types for which they are designed, and q2-types provides a number of common Types and their transformers. Plugins often define new semantic types and/or transformers that are not covered by these core Types.

How are transformers used by a plugin?

Transformers are not called directly at any time within a plugin. This process is handled by the QIIME 2 framework, as long as the appropriate transformers are registered in that plugin. The framework interprets the input Artifact source format and destination format from a plugin’s registration and functional annotation, respectively. Upon output from a Method, the framework interprets the source format (function output) and the Semantic Type of the desination Artifact from the plugin functional annotation and registration, respectively.

For example, we can see how functional annotations define input and output formats in q2_diversity.beta_phylogenetic:

def beta_phylogenetic(table: biom.Table, phylogeny: skbio.TreeNode,
                      metric: str)-> skbio.DistanceMatrix:

This function requires biom.Table and skbio.TreeNode objects as input, and produces an skbio.DistanceMatrix object as output.

We can examine the first few lines of the Method registration for this function to determine the Semantic Types of these input and output objects:

    inputs={'table': FeatureTable[Frequency],
            'phylogeny': Phylogeny[Rooted]},
    parameters={'metric': Str % Choices(beta.phylogenetic_metrics())},
    outputs=[('distance_matrix', DistanceMatrix % Properties('phylogenetic'))],

So we see that the biom.Table object begins its life as a FeatureTable[Frequency] artifact, the skbio.TreeNode comes from a Phylogeny[Rooted] artifact, and the output skbio.DistanceMatrix must somehow be coerced to become a DistanceMatrix[phylogenetic] artifact. How do we possibly handle this? Before you hyperventilate, remember that the QIIME 2 framework does all conversion for you, provided the appropriate ``transformers` have been registered`.

Registering a Transformer

To give you an idea how this works, let’s take a look at how an example transformer is registered in q2-types:

import skbio

from ..plugin_setup import plugin
from . import LSMatFormat

def _1(data: skbio.DistanceMatrix) -> LSMatFormat:
    ff = LSMatFormat()
    with as fh:
        data.write(fh, format='lsmat')
    return ff

def _2(ff: LSMatFormat) -> skbio.DistanceMatrix:
    return, format='lsmat', verify=False)

These transformers define how an skbio.DistanceMatrix object is transformed into an LSMatFormat object (the underlying format of the data in a DistanceMatrix[phylogenetic] artifact, as defined in q2-types).

So QIIME 2 recognizes (in the function annotation) that it has an incoming skbio.DistanceMatrix, which it transforms (via the registered Transformer) to LSMatFormat and packages into a DistanceMatrix[phylogenetic] artifact (as defined in the Method registration). Easy as 🎂.