Skip to main content

cinnamon

A lightweight Python framework for decoupling configuration from code logic.

PyPI version Python 3.10+ License: MIT Tests

Documentation · Quickstart · Tutorial · Examples


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 RegistrationKey instances, singly or as a list/dict of them; the Registry resolves the graph children-first. A scalar dependency's variants propagate to its parents; the members of a list or dict dependency deliberately do not, because the parent would otherwise gain the cross product of every member's variants.
  • Community-ready — pull components and Configuration classes from external projects via external_directories and build on top of them.
  • CLI included — cmn-check reports unresolved keys with suggestions, without importing your components; add --deep and it imports each bound component to check its __init__ against the configuration's fields. cmn-build resolves and writes the key list; cmn-run and cmn-generate run 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 Component base 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_class

@register_class(name='loader', tags={'default'}, namespace='myproject',
                component='components.DataLoader')
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])

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

MIT

Metadata

Release files for cinnamon-core 2.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for cinnamon-core 2.2.1
File Size Uploaded
cinnamon_core-2.2.1.tar.gz 129.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for cinnamon-core 2.2.1
File Interpreter ABI Platform
cinnamon_core-2.2.1-py3-none-any.whl Python 3 none any Details

Total release size: 195.4 kB

Release files / cinnamon_core-2.2.1.tar.gz

Download URL cinnamon_core-2.2.1.tar.gz
Size 129.2 kB
Tags Source
SHA-256 checksum
How to use checksums
9a46c6ddcefd23528db2a51bab6c67bf619d66297a2d55cde32b7516cddd4868
BLAKE2b-256 checksum
How to use checksums
c5352fb9d80703dc5e98fb767ffa374db3d9bdcb2baeb34eebc460d017365186
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 30, 2026.

Transparency log

Release files / cinnamon_core-2.2.1-py3-none-any.whl

Download URL cinnamon_core-2.2.1-py3-none-any.whl
Size 66.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e06a76a9b4bb5e164e9156e76911d7988ef14e8a17e67c6c7a5f89092eaa49f1
BLAKE2b-256 checksum
How to use checksums
20d3a85697902d4e40f9daa8f26f652cc019da6667123abe77dc55e89c1f6a62
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 30, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

2.2.1 This release

2 release files

2.2.0

2 release files

2.1.3

2 release files

2.1.2

2 release files

2.1.1

2 release files

2.1.0

2 release files

2.0.3

2 release files

2.0.2

2 release files

2.0.1

2 release files

2.0.0

2 release files

0.2.2

1 release file

0.2.1

1 release file

0.2

1 release file

0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page