Writing an OTIO SchemaDef Plugin

OpenTimelineIO SchemaDef plugins are plugins that define new schemas within the otio type registry system. You might want to do this to add new schemas that are specific to your own internal studio workflow and shouldn’t be part of the generic OpenTimelineIO package.

To write a new SchemaDef plugin, you create a Python source file that defines and registers one or more new classes subclassed from otio.core.SerializeableObject. Multiple schema classes can be defined and registered in one plugin, or you can use a separate plugin for each of them.

Here’s an example of defining a very simple class called MyThing:

import opentimelineio as otio

@otio.core.register_type
class MyThing(otio.core.SerializableObject):
    """A schema for my thing."""

    _serializable_label = "MyThing.1"
    _name = "MyThing"

    def __init__(
        self,
        arg1=None,
        argN=None
    ):
        otio.core.SerializableObject.__init__(self)
        self.arg1 = arg1
        self.argN = argN

    arg1 = otio.core.serializable_field(
        "arg1",
        doc = ( "arg1's doc string")
    )

    argN = otio.core.serializable_field(
        "argN",
        doc = ( "argN's doc string")
    )

    def __str__(self):
        return "MyThing({}, {})".format(
            repr(self.arg1),
            repr(self.argN)
        )

    def __repr__(self):
        return "otio.schema.MyThing(arg1={}, argN={})".format(
            repr(self.arg1),
            repr(self.argN)
        )

In the example, the MyThing class has two parameters arg1 and argN, but your schema class could have any number of parameters as needed to contain the data fields you want to have in your class.

One or more class definitions like this one can be included in a plugin source file, which must then be added to the plugin manifest as shown below:

Registering Your SchemaDef Plugin

To create a new SchemaDef plugin, you need to create a Python source file as shown in the example above. Let’s call it mything.py. Then you must add it to a plugin manifest:

{
    "OTIO_SCHEMA" : "PluginManifest.1",
    "schemadefs" : [
        {
            "OTIO_SCHEMA" : "SchemaDef.1",
            "name" : "mything",
            "execution_scope" : "in process",
            "filepath" : "mything.py"
         }
    ]
}

The same plugin manifest may also include adapters and media linkers, if desired.

Then you need to add this manifest to your $OTIO_PLUGIN_MANIFEST_PATH environment variable (which is “:” separated).

Using the New Schema in Your Code

Now that we’ve defined a new otio schema, how can we create an instance of the schema class in our code (for instance, in an adapter or media linker)?

SchemaDef plugins are loaded in a deferred way. The load is triggered either by reading a file that contains the schema or by manually asking the plugin for its module object. For example, if you have a my_thing schemadef module:

import opentimelineio as otio

my_thing = otio.schema.schemadef.module_from_name('my_thing')

Once the plugin has been loaded, SchemaDef plugin modules are magically inserted into a namespace called otio.schemadef, so you can create a class instance just like this:

import opentimelineio as otio

mine = otio.schemadef.my_thing.MyThing(arg1, argN)

An alternative approach is to use the instance_from_schema mechanism, which requires that you create and provide a dict of the parameters:

    mything = otio.core.instance_from_schema("MyThing", 1, {
        "arg1": arg1,
        "argN": argN
    })

This instance_from_schema approach has the added benefit of calling the schema upgrade function to upgrade the parameters in the case where the requested schema version is earlier than the current version defined by the schemadef plugin. This seems rather unlikely to occur in practice if you keep your code up-to-date, so the first technique of creating the class instance directly from otio.schemadef is usually preferred.