2. Registration#
A component is an ordinary class — no base class, no decorator. cinnamon needs only its import path, as a string, which it resolves when you ask for an instance and not before.
python examples/tutorial/02_registration.py
That is what keeps a build independent of how heavy your components are: nothing imports torch to look at a registry.
class Tokenizer:
"""A component: it does the work, and it is a plain class."""
def __init__(self, lowercase: bool, separator: str):
self.lowercase = lowercase
self.separator = separator
def tokenize(self, text: str) -> list[str]:
return (text.lower() if self.lowercase else text).split(self.separator)
A registration binds (configuration, name, namespace, tags) to that component
path. A RegistrationKey — name, namespace, and a set of tags — is how you name
the binding afterwards, and how you ask for the component.
What to notice#
Registry.dag_resolution()returns two sets, valid and invalid. Step 6 explains why the second one is not an error list.Registry.from_keyis the moment the component class is imported. Before it, the registry knows the path and nothing more.Registry.retrieve_configurationgets the configuration back without building anything at all.
The whole file#
1"""
22. Binding a configuration to a component, and building it.
3
4 python examples/tutorial/02_registration.py
5
6A component is an ordinary class -- no base class, no decorator. cinnamon needs
7only its *import path*, as a string, which it resolves when you ask for an
8instance and not before. That is what keeps a build independent of how heavy
9your components are: nothing imports torch to look at a registry.
10"""
11
12from cinnamon.configuration import Configuration, Param
13from cinnamon.registry import RegistrationKey, Registry
14
15
16class Tokenizer:
17 """A component: it does the work, and it is a plain class."""
18
19 def __init__(self, lowercase: bool, separator: str):
20 self.lowercase = lowercase
21 self.separator = separator
22
23 def tokenize(self, text: str) -> list[str]:
24 return (text.lower() if self.lowercase else text).split(self.separator)
25
26
27class TokenizerConfig(Configuration):
28 lowercase: bool = Param(True, description="Fold text to lower case first")
29 separator: str = Param(" ", description="Token separator")
30
31
32def main() -> None:
33 Registry.initialize()
34
35 # A registration is (configuration, name, namespace, tags) -> component.
36 # In a real project you would put this in a `configurations/` package and
37 # let `Registry.build` discover it; step 7 shows that. Registering by hand
38 # keeps this file readable in one screen.
39 Registry.register_configuration(
40 config=TokenizerConfig(),
41 name="tokenizer",
42 namespace="tutorial",
43 # Normally "mypackage.components.Tokenizer". Inside a script the module
44 # is __main__, so that is the path cinnamon needs.
45 component=f"{__name__}.Tokenizer",
46 )
47
48 # Resolution walks the dependency graph and validates every configuration.
49 valid_keys, invalid_keys = Registry.dag_resolution()
50 print(f"valid: {len(valid_keys)} invalid: {len(invalid_keys)}")
51
52 # A RegistrationKey is how you name a registration: name + namespace + tags.
53 key = RegistrationKey(name="tokenizer", namespace="tutorial")
54 print("key: ", key)
55
56 # ... and how you ask for the component. The class is imported now, at the
57 # moment it is needed.
58 tokenizer = Registry.from_key(key)
59 print("built:", type(tokenizer).__name__)
60 print("work: ", tokenizer.tokenize("The Quick Brown Fox"))
61
62 # The configuration itself is retrievable too, without building anything.
63 config = Registry.retrieve_configuration(registration_key=key)
64 print("config:", config.values)
65
66
67if __name__ == "__main__":
68 main()
Next: 3. Variants — the reason the library exists.