Registration
While configurations and components can be instantiated manually, cinnamon provides a
registration-based dependency system that links them together via unique identifiers
called RegistrationKey.
This system allows the Registry to build Configuration and Component
instances on demand, resolve nested dependencies automatically, and enumerate valid
parameter variants — all without the caller knowing which concrete classes are involved.
RegistrationKey
A RegistrationKey is a compound identifier made up of three fields:
name: a general identifier for the registeredConfiguration.namespace: a high-level grouping, useful for distinguishing between user groups, frameworks, or macro categories. Follows the conventionuser/namespace(e.g."huggingface/transformers"), though any string is valid. Defaults to"default"if not provided.tags: an optionalsetof strings that disambiguates keys sharing the samenameandnamespace(e.g. multiple model variants by the same author).
from cinnamon.registry import RegistrationKey
key = RegistrationKey(name='model', tags={'bert', 'large'}, namespace='nlp')
Two keys are equal if and only if their name, tags, and namespace all match:
key_a = RegistrationKey(name='model', tags={'bert'}, namespace='nlp')
key_b = RegistrationKey(name='model', tags={'bert'}, namespace='nlp')
key_c = RegistrationKey(name='model', tags={'gpt'}, namespace='nlp')
key_a == key_b # True
key_a == key_c # False
RegistrationKey has a canonical string representation and can be round-tripped
through it:
key = RegistrationKey(name='model', tags={'bert'}, namespace='nlp')
str(key)
# 'name=model--tags=['bert']--namespace=nlp'
restored = RegistrationKey.from_string(str(key))
restored == key # True
# parse() accepts a key object, its string form, or name/tags/namespace directly
RegistrationKey.parse(name='model', tags={'bert'}, namespace='nlp') == key # True
Registration
Registration is the action of storing a Configuration instance in the Registry,
optionally binding it to a Component class so that component instances can be built
from it later.
There are two ways to register: via a decorated @classmethod, or via an ad-hoc
function.
Class method registration
Decorate a @classmethod of your Configuration with @register_method to
register it automatically when the Registry scans your configurations folder:
from cinnamon.configuration import Configuration, Param
from cinnamon.registry import register_method
class CustomConfig(Configuration):
x: int = Param(5, description='An example parameter')
@classmethod
@register_method(
name='test',
tags={'default'},
namespace='testing',
component='components.CustomComponent'
)
def default(cls) -> 'CustomConfig':
return super().default()
When the Registry processes this file, it:
Stores a
ConfigurationInfoentry keyed byRegistrationKey(name='test', tags={'default'}, namespace='testing').Records that
CustomConfig.default()is the constructor to call.Binds the result to
components.CustomComponent(the component’s module path as a string).
Any @classmethod can be decorated, not just default(). This is useful for
registering multiple templates from the same Configuration class:
class CustomConfig(Configuration):
x: int = Param(5)
y: float = Param(0.01)
@classmethod
@register_method(name='test', tags={'small'}, namespace='testing',
component='components.CustomComponent')
def default(cls) -> 'CustomConfig':
return super().default()
@classmethod
@register_method(name='test', tags={'large'}, namespace='testing',
component='components.CustomComponent')
def large(cls) -> 'CustomConfig':
return cls(x=100, y=0.001)
Ad-hoc registration
When you want to register an existing Configuration without subclassing it,
use the @register decorator and call Registry.register_configuration() directly:
from cinnamon.registry import Registry, register
@register
def custom_registration():
Registry.register_configuration(
config=CustomConfig.default(),
name='test',
tags={'default'},
namespace='testing',
component='components.CustomComponent'
)
This is equivalent to @register_method but keeps the registration logic separate
from the Configuration class — useful when re-using an existing configuration
without modification.
Runnable components
If a Component exposes a method that should be invoked automatically when run via
cmn-run, specify it with the run_method argument:
Registry.register_configuration(
config=CustomConfig.default(),
name='test',
tags={'runnable'},
namespace='testing',
component='components.CustomComponent',
run_method='train' # calls component.train() when run via cmn-run
)
# or via @register_method:
@register_method(name='test', tags={'runnable'}, namespace='testing',
component='components.CustomComponent', run_method='train')
def default(cls) -> 'CustomConfig':
return super().default()
Registered runnable keys carry the internal __runnable special tag and can be
retrieved with Registry.retrieve_runnable_keys().
Retrieving registrations
After Registry.build() (or Registry.dag_resolution() in manual workflows),
you can query the registry by key or by field filters.
Retrieve a configuration instance by its key:
config = Registry.retrieve_configuration(
name='test', tags={'default'}, namespace='testing'
)
Retrieve full registration info (configuration + bound component + run method):
info = Registry.retrieve_configuration_info(
name='test', tags={'default'}, namespace='testing'
)
info.config # the Configuration instance
info.component # 'components.CustomComponent' (string) or None
info.run_method # 'train' or None
Both methods also accept a RegistrationKey instance directly:
key = RegistrationKey(name='test', tags={'default'}, namespace='testing')
config = Registry.retrieve_configuration(registration_key=key)
Search for keys using retrieve_keys(), which supports filtering by name,
namespace, tags, or any combination:
# All keys in a namespace
keys = Registry.retrieve_keys(namespaces='testing')
# All keys matching a name and namespace
keys = Registry.retrieve_keys(names='test', namespaces='testing')
# All keys with specific tags
keys = Registry.retrieve_keys(tags={'default'})
# All runnable keys
keys = Registry.retrieve_runnable_keys()
Configuration also provides a retrieve() classmethod as syntactic sugar that
additionally type-checks the result:
config = CustomConfig.retrieve(name='test', tags={'default'}, namespace='testing')
# raises RuntimeError if the retrieved config is not a CustomConfig instance
Building instances from registrations
Build a Configuration instance via its key:
config = Registry.retrieve_configuration(
name='test', tags={'default'}, namespace='testing'
)
config.x # >>> 5
Build the bound Component instance:
# Via the Registry
component = Registry.instantiate_component(
name='test', tags={'default'}, namespace='testing'
)
# Via the Component class (type-checks the result)
component = CustomComponent.instantiate(
name='test', tags={'default'}, namespace='testing'
)
component.x # >>> 5
See component for the full
details of how component construction works, including nested configurations and
build_args overrides.
Deferred dependency resolution
By default, the Registry resolves all RegistrationKey dependencies in a
configuration — replacing them with the corresponding Configuration instances.
If you need to keep dependencies as RegistrationKey objects after resolution
(for example, to build the component lazily at runtime), pass
resolve_automatically=False at registration time:
Registry.register_configuration(
config=MyConfig.default(),
name='model',
namespace='testing',
resolve_automatically=False
)
# After Registry.dag_resolution():
config = MyConfig.retrieve(name='model', namespace='testing')
isinstance(config.child, RegistrationKey) # True — not resolved to a Configuration
The Registry still validates the key’s existence and checks conditions, but the
dependency field is left as a RegistrationKey for the caller to resolve manually.
Tl;dr
Define your
Component(code logic).Define its corresponding
Configuration(one or more).Register the
Configurationin theRegistryvia aRegistrationKey.A
RegistrationKeyis a(name, tags, namespace)compound identifier.Build
Configurationinstances via theRegistrationKey.Build
Componentinstances via theRegistrationKey.
Congrats! This is 99% of cinnamon.
How to use registration APIs
You do not need to scatter registration calls throughout your codebase. Cinnamon supports a specific code organisation that handles all registration automatically while keeping things readable.
See dependencies for the
recommended project layout and a full walkthrough of how Registry.build() discovers
and executes registrations.