What is cinnamon?
Cinnamon separates what your code does from how it is configured.
Instead of scattering parameters across constructors, config files, or command-line
arguments, you define each component's parameters as a typed Configuration class
backed by Pydantic. You then register that configuration
in the Registry and bind it to your component. From that point on, the Registry
handles construction, validation, type-checking, and dependency resolution automatically.
The result is a project where every component is independently swappable, every
parameter is validated and documented, and the full experiment can be reproduced by a
single RegistrationKey.
Features
- Pydantic-backed configurations — field types, constraints (
ge,le,Literal), and cross-field validators via@model_validator. - Registry-based dependency injection — register a
Configuration, bind it to a component by import path, and let cinnamon build the dependency graph automatically. Your component stays a plain class: no base class, no decorator, no import of cinnamon. - Variants — declare alternative parameter values alongside their defaults and enumerate every valid combination.
- Conditions — attach runtime invariants to configurations via
add_condition, validated before any component is built. - Dependencies — compose configurations by pointing fields at
RegistrationKeyinstances, singly or as alist/dictof them; theRegistryresolves the graph children-first, so a child's variants propagate to its parents. - Community-ready — pull components and
Configurationclasses from external projects viaexternal_directoriesand build on top of them. - CLI included —
cmn-checkreports unresolved keys with suggestions and mismatched component signatures without importing your components;cmn-buildresolves and writes the key list;cmn-runandcmn-generaterun experiments and generate scripts without boilerplate.
Installation
pip install cinnamon-core
The distribution is cinnamon-core; the import package is cinnamon:
import cinnamon
They differ because cinnamon on PyPI is an unrelated project. cinnamon-core
is the package these releases have always used, and it supersedes the old
cinnamon-generic, cinnamon-th and cinnamon-tf split, which are no longer
maintained.
Upgrading from 0.2.x? 2.0.0 is a rewrite. Configurations are Pydantic models with typed class annotations, and the
Componentbase class is gone — components are plain classes now, bound by import path. Start from the Quickstart; the 0.2.x API does not carry over.
That covers the library and the two non-interactive commands, cmn-build and
cmn-check.
Optional extras:
| Extra | What it adds | Install |
|---|---|---|
cli |
Interactive prompts for cmn-run and cmn-generate |
pip install "cinnamon-core[cli]" |
examples |
Dependencies for the built-in examples | pip install "cinnamon-core[examples]" |
dev |
pytest, ruff, mypy | pip install "cinnamon-core[dev]" |
Quickstart
1. Define a component — a plain Python class, no base class required:
class DataLoader:
def __init__(self, folder_name: str, batch_size: int):
self.folder_name = folder_name
self.batch_size = batch_size
def load(self):
...
2. Define its configuration — a Pydantic model with typed, documented fields:
from cinnamon.configuration import Configuration, Param
from cinnamon.registry import register_method
class DataLoaderConfig(Configuration):
folder_name: str = Param('data/', description='Root data directory')
batch_size: int = Param(32, ge=1, description='Samples per batch',
variants=[16, 64])
@classmethod
@register_method(name='loader', tags={'default'}, namespace='myproject',
component='components.DataLoader')
def default(cls) -> 'DataLoaderConfig':
return super().default()
3. Build the registry — cinnamon scans your configurations/ folder and resolves dependencies:
from pathlib import Path
from cinnamon.registry import Registry
Registry.build(directory=Path('.'))
4. Instantiate — retrieve and build a component from its registration key:
loader = Registry.instantiate(name='loader', tags={'default'}, namespace='myproject')
loader.load()
The Registry builds the configuration, resolves its dependencies, validates its
conditions, and passes the resulting values to DataLoader.__init__.
5. Enumerate variants — every combination other than the all-defaults one,
which the Registry already registers on its own:
config = DataLoaderConfig.default()
for combo in config.variants:
variant = config.model_copy(update=combo['values'])
loader = DataLoader(**variant.values)
That's it. See the full quickstart for the complete walkthrough.
Performance
Resolution is linear in the size of your project, in all three directions that can grow:
resolution ≈ 0.034 ms × registrations
+ 0.045 ms × dependency edges
+ 0.113 ms × variant configurations
A 500-registration project with 1,000 dependency edges resolves in about 60 ms.
import sklearn on the same machine costs 564 ms — so for any project where
resolution is measurable, importing one component costs more than resolving
everything. That is what binding components by import path buys, and why
cmn-check can validate a project without loading a component at all.
Variant configurations are the expensive term, about three times a plain registration, because each is copied, resolved and validated. If resolution feels slow, the number to look at is how many configurations your sweep expands to, not how many you wrote.
Those constants belong to one machine. Reproduce them on yours:
python benchmarks/dag_scaling.py
Dependency chains are not limited by Python's recursion limit — a 1,500-deep chain resolves under the default of 1,000. See Performance for the graph shapes measured, why it stays linear, and the things that turned out not to be bottlenecks.
Key concepts
| Concept | Description | Docs |
|---|---|---|
Configuration |
A Pydantic BaseModel holding typed, validated parameters |
→ |
Param |
A Field wrapper that adds tags, variants, and cinnamon metadata |
→ |
| Component | Any plain Python class, referenced by its import path (e.g. components.DataLoader) |
→ |
RegistrationKey |
A (name, tags, namespace) identifier that binds a config to a component |
→ |
Registry |
Stores registrations, resolves the dependency DAG, and builds components | → |
| Dependencies | Other registrations referenced by RegistrationKey fields, singly or as a list/dict |
→ |
Learning cinnamon
examples/tutorial/ — seven runnable steps, no dependencies
beyond cinnamon itself. Each one is a single file you can read in a screen and change,
and the test suite runs every one of them on each commit.
pip install cinnamon-core
python examples/tutorial/01_configuration.py
The same steps, with commentary and the code included from these files, are at nlp-unibo.github.io/cinnamon/tutorial.
| Introduces | |
|---|---|
| 1. Configuration | Configuration, Param, validation |
| 2. Registration | components as plain classes, RegistrationKey |
| 3. Variants | one component, many configurations |
| 4. Dependencies | referencing another registration |
| 5. Collections | list and dict of keys |
| 6. Conditions | rejecting combinations that make no sense |
| 7. Project layout | the real directory structure and the CLI |
Step 7 is the one to copy when starting a project of your own.
A full example
The rest of examples/ is a complete ML pipeline: data loading, preprocessing, SVM
classification, and evaluation on the IMDB sentiment dataset. It downloads the dataset
on first run.
pip install -e ".[examples]"
python -m examples.demos.demo_benchmark
See the examples documentation for a full walkthrough.
Documentation
Full documentation is available at nlp-unibo.github.io/cinnamon.
Contributing
Contributions are welcome. CONTRIBUTING.md covers the working
agreement: one branch per change, nox green before pushing, and a pull request into
main.
nox reproduces the whole CI pipeline locally in about a minute:
pip install nox
nox # lint, type-check, and the suite behind a 100% coverage gate
nox -s core # the suite without the CLI extra installed
nox -s examples # the tutorial and the scikit-learn pipeline
nox -s docs # the documentation, warnings treated as errors
For questions, issues, or feature requests, open a GitHub issue or contact:
Federico Ruggeri — federico.ruggeri6@unibo.it
License
Metadata
Release files for cinnamon-core 2.0.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| cinnamon_core-2.0.2.tar.gz | 110.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| cinnamon_core-2.0.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 167.7 kB
Release files / cinnamon_core-2.0.2.tar.gz
| Download URL | cinnamon_core-2.0.2.tar.gz |
|---|---|
| Size | 110.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a94567a214843e8b185cf4253350902de3f19440c826deeff1449f56bf887e84
|
|
BLAKE2b-256 checksum How to use checksums |
e2f9c6a44ef6469e8e3ae2f28703d7a8e4de9a7caafd92231674de11e4c8fc9d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 9, 2026.
Transparency logRelease files / cinnamon_core-2.0.2-py3-none-any.whl
| Download URL | cinnamon_core-2.0.2-py3-none-any.whl |
|---|---|
| Size | 57.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
92df7d547a3af4132bc1f83d9f672debe329b76ca51e430ccb35d9085a6a0799
|
|
BLAKE2b-256 checksum How to use checksums |
97f1a6d92a430dff39eb479b605eaf39741e24a38e8d1e125192ccfba0a81afb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 9, 2026.
Transparency log