API#

The three modules a project imports. Everything here is re-exported from cinnamon itself, so from cinnamon import Configuration and from cinnamon.configuration import Configuration name the same class.

cinnamon.configuration#

class cinnamon.configuration.Configuration(**data)[source]#

Bases: BaseModel

A Configuration specifies the parameters of a Component. Configurations store parameters and allow flow control via conditions.

add_condition(condition, name, description=None, tags=None)[source]#

Adds a condition to be validated.

Parameters:
  • condition (Callable[[Configuration], bool]) – a function that receives as input the current Configuration instance and returns a boolean.

  • name (str) – unique identifier.

  • description (str | None) – a string description for readability purposes.

  • tags (Optional[AbstractSet[str]]) – a set of string tags to mark the condition with metadata.

Warns:

``RuntimeWarning`` – if the provided name already exists in the Configuration instance. The new condition replaces the old one.

classmethod default()[source]#

Returns the default Configuration instance.

Return type:

TypeVar(C, bound= Configuration)

Returns:

Configuration instance.

property dependencies: dict[str, Any]#

Map every dependency field to its value, container shape intact.

Values are whatever the field holds: a single RegistrationKey, a list of them, a dict of them, or None for an unset optional dependency. Use cinnamon.utility.dependencies.iter_dependency_keys to walk the keys without caring which.

dependency_shape(field_name, field)[source]#

Classify field as a scalar, list or dict dependency, or None.

See cinnamon.utility.dependencies for the supported shapes.

Return type:

DependencyShape | None

property expanded: bool#
property fields: dict[str, FieldInfo]#
property has_at_least_two_variants: bool#
property has_variants: bool#
is_dependency(field_name, field)[source]#

Report whether field declares a dependency on another registration.

A dependency is a RegistrationKey (or Configuration) field, a list of them, or a dict of them keyed by string – optionally wrapped in Optional[...]. Nested containers raise TypeError.

Return type:

bool

meta: ClassVar[MetaDescriptor] = <cinnamon.configuration.FieldMetaProxy object>#
model_config: ClassVar[ConfigDict] = {'validate_default': True}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_copy(*, update=None, deep=False)[source]#

Copy this configuration, validating any values supplied via update.

BaseModel.model_copy does not run validators, so an update has to be pushed back through model_validate – that round-trip is what lets Registry.expand_configuration reject invalid variants. With no update there are no unvalidated values, and the round-trip costs roughly 4x a plain deep copy on the hottest call in Registry.build, so it is skipped.

The two paths differ in one visible way: model_validate rebuilds _instance_meta from the class defaults, discarding per-instance metadata, whereas the no-update path keeps pydantic’s copy of it (isolated when deep, shared otherwise). Preserving is the expected behaviour for a copy; the update path keeps the reset because the registry’s DAG construction depends on variant metadata coming from the class.

Return type:

Self

model_post_init(_Configuration__context)[source]#

Runs automatically right after Pydantic instantiates an object.

Return type:

None

classmethod retrieve(registration_key=None, name=None, namespace=None, tags=None)[source]#

Syntactic sugar for retrieving a Configuration from a RegistrationKey in implicit format.

Parameters:
  • registration_key (Union[RegistrationKey, str, None]) – the RegistrationKey used to register the Configuration class.

  • name (str | None) – the name field of RegistrationKey

  • tags (Optional[AbstractSet[str]]) – the tags field of RegistrationKey

  • namespace (str | None) – the namespace field of RegistrationKey

Return type:

TypeVar(C, bound= Configuration)

Returns:

A Configuration instance

Raises:
  • InvalidConfigurationTypeException – if there’s a mismatch between the Configuration class used

  • during registration and the type of the built Configuration –

  • instance using the registered –

  • constructor` method (see ConfigurationInfo arguments) –

validate_conditions(strict=True)[source]#

Validates all provided conditions related to the Configuration instance.

Args:
strict: if True, a failed validation process will raise

InvalidConfigurationException

Returns:

A ValidationResult that stores the boolean result of the validation process along with an error message if the result is False.

Raises:
ValidationFailureException: if strict = True and the validation

process failed

Return type:

ValidationResult

validate_variants()[source]#
Return type:

Self

property values: dict[str, Any]#
property variants: list[dict[str, dict[str, Any]]]#

Computes all unique combinations of a configuration’s fields along with their indices. The baseline/default value always gets index 0. Subsequent unique variants get an increasing index (1, 2, …).

cinnamon.configuration.Param(default=PydanticUndefined, *, description=None, tags=None, variants=None, **kwargs)[source]#
Return type:

Any

cinnamon.registry#

cinnamon.registry.Registration#

A registration key, or its canonical string form. Defined after the class so the alias holds the real type: a forward reference here cannot be resolved from other modules’ namespaces, which broke the generated API docs.

alias of RegistrationKey | str

class cinnamon.registry.RegistrationKey(name, namespace=None, tags=None, description=None, metadata=None, special_tags=None)[source]#

Bases: Generic[T]

Compound key used for registration.

ATTRIBUTE_SEPARATOR: str = '--'#
HIERARCHY_SEPARATOR: str = '.'#
KEY_VALUE_SEPARATOR: str = '='#
MAX_TAGS_PER_LINE: int = 6#
check_name(name)[source]#
Return type:

bool

check_namespace(namespace)[source]#
Return type:

bool

check_tags(tags)[source]#
Return type:

bool

property compound_tags#
description: str | None#

Free-form documentation for the key.

classmethod from_dict(data)[source]#

Rebuild a key from to_dict() output.

Return type:

RegistrationKey[Any]

classmethod from_string(string_format)[source]#

Parses a RegistrationKey instance from its string format.

Parameters:

string_format (str) – the string format of a RegistrationKey instance.

Return type:

RegistrationKey[Any]

Returns:

The corresponding parsed RegistrationKey instance

from_tags_simplification(tags)[source]#

Builds a new RegistrationKey from current instance by removing provided tags.

Parameters:

tags (Optional[AbstractSet[str]]) – a Tag set containing tags to remove

Return type:

RegistrationKey[TypeVar(T)]

Returns:

A RegistrationKey instance that is the same as the current instance but with tags removed.

from_variant(variant_kwargs, variant_indexes=None)[source]#
Return type:

RegistrationKey[TypeVar(T)]

property hierarchy_tags#
match(key, tags)[source]#
Return type:

bool

metadata: str | None#

Why a key was rejected, filled in by resolution for invalid keys.

name: str#
namespace: str#
classmethod parse(registration_key=None, name=None, namespace=None, tags=None)[source]#

Parses a given RegistrationKey instance. If the given registration_key is in its string format, it is converted to RegistrationKey instance

Parameters:
  • registration_key (Union[RegistrationKey, str, None]) – a RegistrationKey instance in its class instance or string format

  • name (str | None) – the name field of RegistrationKey

  • namespace (str | None) – the namespace field of RegistrationKey

  • tags (Optional[AbstractSet[str]]) – the tags field of RegistrationKey

Return type:

RegistrationKey[Any]

Returns:

The parsed RegistrationKey instance

sanitize_variant_tag(param_name, param_index, param_value)[source]#
Return type:

str

special_tags: set[str]#

Internal markers such as __runnable; not part of the key’s identity.

tags: frozenset[str]#
to_dict()[source]#

The key as plain JSON-compatible data.

{"name": ..., "namespace": ..., "tags": [...]}, with tags sorted so the result is stable across runs and comparable byte for byte.

Only the three components that make up the key’s identity are included – description and metadata annotate a key rather than identify it, and two keys differing only in those are equal.

Prefer this to the string form for anything that has to survive a round trip: the string form has to be parsed back out of a single line, while this cannot be ambiguous no matter what a tag contains.

Return type:

Dict[str, Any]

to_pretty_string()[source]#
Return type:

str

class cinnamon.registry.Registry[source]#

Bases: object

The registration registry. The registry has three main functionalities: - Storing/Retrieving registered Configuration: via the ConfigurationInfo internal wrapper. - Storing/Retrieving Configuration to Component bindings: the binding operation allows to build a Component instance from its registered Configuration. - Storing/Retrieving registered built Component instances: a Component instance can be registered as well to mimic Singleton behaviors. This functionality is useful is a single Component instance is used multiple times in a program.

All the above functionalities require to specify a RegistrationKey (either directly or indirectly).

REGISTRATION_CONTEXT: RegistrationContext = <cinnamon.registry.RegistrationContext object>#
REGISTRATION_METHODS: Dict[str, Callable | BufferedRegistration | BufferedClassRegistration] = {}#
classmethod build(cls, directory, external_directories=None)[source]#

Main entrypoint of cinnamon. The registry checks provided directories for configurations to populate its internal registry and build the dependency DAG. Eventually, the dependency DAG is expanded to account for variants and invalid configurations.

Parameters:
  • directory (Union[Path, str]) – the main directory of the project containing configurations.

  • external_directories (Optional[List[Union[str, Path]]]) – external directories containing configurations.

Returns:

a ResolutionInfo` containing valid ``RegistrationKey invalid_keys: a ResolutionInfo` containing invalid ``RegistrationKey

Return type:

Tuple[Set[RegistrationKey[Any]], Set[RegistrationKey[Any]]]

Raises:
classmethod check_graph_topology()[source]#

The two properties the graph has to hold, before and after expansion.

Expansion adds a node and an edge per variant, so a graph that was a DAG on the way in is not necessarily one on the way out. Running these only before expansion is what let a variant’s self-loop through.

Raises:
Return type:

None

classmethod check_registration_graph()[source]#

Checks if the dependency DAG is valid.

Raises:
Return type:

bool

classmethod dag_resolution(cls)[source]#

Expands and resolves every dependency in the registration DAG.

Keys are expanded children first, in reverse topological order. expand_configuration recurses into a key’s dependencies, so reaching a parent before its children makes the recursion as deep as the longest chain in the project – and Python’s stack limit then caps that chain at roughly 490 links.

That cap used to depend on the order modules happened to register in: the same graph resolved when children were registered first (each expansion finding its children already done, so nesting stayed shallow) and hit RecursionError when parents came first. Taking the order from the graph rather than from registration removes both the depth limit and the dependence on something no user controls.

Returns:

the set of valid registration keys invalid_keys:the set of invalid registration keys

Return type:

Tuple[Set[RegistrationKey[Any]], Set[RegistrationKey[Any]]]

classmethod expand_configuration(key, valid_key_buffer=None, invalid_key_buffer=None)[source]#

Recursively expand configuration and dependencies.

Return type:

Set[RegistrationKey[Any]]

expanded: bool = False#
classmethod forget_loaded_modules()[source]#

Undo what load_registrations did to the interpreter.

Loading a project puts its root on sys.path and executing its registration scripts leaves their packages in sys.modules. Neither was ever taken back out, so a second project’s configurations resolved to the first one’s – the earlier sys.path entry wins, and an already-imported namespace package keeps the __path__ it was found on. Two projects that both name a folder configurations is not an unusual arrangement; it is the only arrangement.

Only what a load imported is forgotten. Deciding by file location alone drops modules the caller imported before the build, and a module re-imported afterwards is a second class object: a component held from before the reset then fails issubclass against its own class, with a message naming one class twice. A library whose tests scan its own package is exactly that arrangement.

And only configuration modules. A registration script that imports its library puts that library’s modules in sys.modules during the load. A caller that imports a class after the build holds that module’s class, so forgetting the module hands the next build a second copy. Only modules inside a configurations folder collide between projects, so those are the only ones forgotten.

Registrations are re-executed on every load, so nothing here is a cache being thrown away.

Return type:

None

classmethod freeze_runtime()[source]#

The registry reduced to what another process needs in order to build.

A built registry is two things at once: the machinery that produced it – the dependency graph, the registration decorators, the record of which modules were imported – and the answer that machinery arrived at. Only the answer is needed to build a component, and only the answer is portable. This returns it: per key, the component’s import path, the values its configuration resolved to, and the method that runs it.

The component is kept as a path and not as a class, deliberately. A worker importing that path gets the class its own interpreter defines, so an expected_type check compares classes that are genuinely the same – where a pickled class would arrive as a second object that issubclass rejects. It is also what lets this work whatever the registration modules were loaded as: nothing here refers to them.

Every value is pickled here, and one that cannot be raises UnserializableRuntimeException naming the key and the field. A lambda in a configuration is the usual cause. Failing here is the point: the alternative is a worker failing to unpickle something, in a stack that says nothing about which registration it came from.

Nothing is mutated: the registry this is read from is left as it was.

Return type:

Dict[RegistrationKey, Dict[str, Any]]

classmethod from_key(registration_key, **build_args)[source]#

Build component from key.

Return type:

TypeVar(T)

classmethod from_keys(dependency, **build_args)[source]#

Build every component in a dependency, keeping the shape it came in.

A component receives its dependencies as keys, so that it decides when each child is built. When the dependency is a container that usually means a comprehension per field:

self.losses = [Registry.from_key(key) for key in losses]
self.metrics = {name: Registry.from_key(key)
                for name, key in metrics.items()}

which says nothing except “build these”. from_keys says it once:

self.losses = Registry.from_keys(losses)     # list  -> list, in order
self.metrics = Registry.from_keys(metrics)   # dict  -> dict, same labels

A single key builds a single component, so a field typed RegistrationKey | list[RegistrationKey] needs no branch. None returns None, which is what an unset optional dependency should do. Anything that is not a key is passed through untouched.

build_args are forwarded to every component built.

This builds eagerly. Keep the loop when a child should only be built under some condition – the laziness is the reason components are handed keys rather than instances.

Return type:

Any

classmethod in_graph(registration_key=None, name=None, namespace=None, tags=None)[source]#

Return True if key in dependency DAG.

Return type:

bool

classmethod in_registry(registration_key)[source]#

Return True if key is stored.

Return type:

bool

classmethod initialize()[source]#

Reset registry to empty state.

classmethod install_runtime(runtime)[source]#

Make this process able to build from a frozen runtime.

For a worker, which has to resolve keys and has no reason to scan anything: nothing is imported, no directory is walked, no configuration is registered a second time, and the dependency graph is neither rebuilt nor needed, having been resolved by whoever froze this.

from_key() then behaves as it does after a build, nested dependencies included – those are keys held in the values, and they resolve through the same registry.

Return type:

None

classmethod instantiate(registration_key=None, name=None, namespace=None, tags=None, expected_type=None, **build_args)[source]#

Builds a Component instance from its bounded Configuration via the implicit RegistrationKey.

Parameters:
  • registration_key (Union[RegistrationKey, str, None]) – the RegistrationKey used to register the Configuration class.

  • name (str | None) – the name attribute of RegistrationKey

  • tags (Optional[AbstractSet[str]]) – the tags attribute of RegistrationKey

  • namespace (str | None) – the namespace attribute of RegistrationKey

  • expected_type (type | None) – type of the component to be cast

  • build_args – additional custom component constructor args

Return type:

Any

Returns:

The built component instance, carrying a registration_key attribute with the key that built it and a build_args attribute with the overrides it was built with. Components that cannot hold attributes are returned without them.

Raises:
  • InvalidConfigurationTypeException – if there’s a mismatch between the Configuration class used during registration and the type of the built Configuration instance using the registered

  • constructor` method (see ConfigurationInfo arguments) –

  • NotBoundException – if the Configuration is not bound to any component.

classmethod is_namespace_covered(registration_key)[source]#

Return True if namespace covered.

Return type:

bool

classmethod is_skipped(path, root)[source]#

Whether path lies inside a directory the scan does not walk.

Judged on the part of the path below root, never on the whole of it: a library installed under ~/.cache/uv/... or a project inside a dotted directory is a perfectly ordinary place to scan from, and testing the absolute path would skip everything in it.

Return type:

bool

classmethod load(cls, directory, external_directories=None)[source]#

Populate the registry and the dependency DAG, without resolving them.

This is the first half of build: after it returns, every registration has been executed and every referenced key is a node in the DAG – including keys that were referenced but never registered. That makes it the point at which the whole set of broken references can be inspected at once, which dag_resolution cannot do because it stops at the first one.

Parameters:
  • directory (Union[Path, str]) – the main directory of the project containing configurations.

  • external_directories (Optional[List[Union[str, Path]]]) – external directories containing configurations.

Return type:

None

classmethod load_registrations(cls, directory)[source]#

Imports a Python’s module for registration. The Registry looks for register() and register_method() decorators. These functions are the entry points for registrations: that is, where the Registry APIs are invoked to issue registrations.

Parameters:

directory (Union[str, Path]) – path of the module

Raises:

InvalidDirectoryException – if the provided directory is not a directory or does not exist.

classmethod parse_configuration_files(cls, directories)[source]#

Runs a static code analyzer to inspect code scripts containing cinnamon registrations with the goal of determining unique namespaces.

Parameters:

directories (List[Path]) – list of directories containing cinnamon registrations.

Returns:

unique list of namespaces mapping: mapping from namespace to pathlib.Path directories.

Return type:

Tuple[List[str], Dict[str, Path]]

classmethod register_configuration(config, name, namespace, tags=None, component=None, run_method=None)[source]#

Registers a Configuration in the registry. In particular, a ConfigurationInfo wrapper is stored in the Registry.

Parameters:
  • config (Configuration) – Configuration` instance

  • name (str) – the name field of RegistrationKey

  • namespace (str) – the namespace field of RegistrationKey

  • tags (Optional[AbstractSet[str]]) – the tags field of RegistrationKey,

  • component (str | None) – Component module path as string

  • run_method (str | None) – Component method to run when instantiating the Component as runnable

Returns:

The built RegistrationKey instance that can be used to retrieve the registered ConfigurationInfo.

Raises:
classmethod register_configuration_from_key(config, registration_key, component=None, run_method=None)[source]#

Register a Configuration under a key you already hold.

register_configuration takes name, namespace and tags and assembles the key itself, which is right when that is what you have. Resolution is the other case: expand_configuration derives a variant key with key.from_variant(...) and then had to take it apart into three fields for a callee that put them straight back together.

The key is used as given, not copied. It therefore keeps whatever description and special_tags it arrived with, where the rebuilt key lost them and started from empty.

Parameters:
  • config (Configuration) – the Configuration instance to register.

  • registration_key (RegistrationKey[Any]) – the key to register it under.

  • component (str | None) – component module path, as a string.

  • run_method (str | None) – component method to run when it is instantiated as runnable.

Return type:

RegistrationKey[Any]

Returns:

The key it registered, which is the one passed in.

Raises:
classmethod registered_items()[source]#

Return a read-only view over (key, ConfigurationInfo) pairs.

Public counterpart to _REGISTRY for consumers that need to walk the whole registry (the static analyzer, reporting tools) without depending on the internal container.

Return type:

ItemsView[RegistrationKey[Any], ConfigurationInfo]

classmethod resolve_configuration(config)[source]#

Replace every dependency key with the Configuration it names.

Container shapes survive: a list[RegistrationKey] field becomes a list of configurations in the same order, a dict[str, RegistrationKey] keeps its labels. Members that are already resolved are left alone, so the call is idempotent.

This runs on throwaway copies during validation. The registered configuration keeps its raw keys, which is what components receive – they call Registry.from_key on them to build their own children.

Return type:

Configuration

classmethod resolve_external_directories(cls, external_directories)[source]#

Checks if provided directories are valid directories and exist.

Parameters:

external_directories (List[Union[str, Path]]) – directories to validate.

Returns:

validated directories as pathlib.Path instances

Return type:

List[Path]

Raises:

InvalidDirectoryException – if any of the provided directories is not a directory or does not exist.

classmethod restore(state)[source]#

Put back what snapshot took, sys.path included.

Return type:

None

classmethod retrieve_configuration(registration_key=None, name=None, namespace=None, tags=None)[source]#

Retrieves a Configuration instance from the registry via its RegistrationKey.

Parameters:
  • registration_key (Union[RegistrationKey, str, None]) – key used to register the configuration

  • name (str | None) – the name field of RegistrationKey

  • namespace (str | None) – the namespace field of RegistrationKey

  • tags (Optional[AbstractSet[str]]) – the tags field of RegistrationKey

Returns:

the built configuration instance

Return type:

Configuration

classmethod retrieve_configuration_info(registration_key=None, name=None, namespace=None, tags=None)[source]#

Retrieves a Configuration instance from the registry via its RegistrationKey.

Parameters:
  • registration_key (Union[RegistrationKey, str, None]) – key used to register the configuration

  • name (str | None) – the name field of RegistrationKey

  • namespace (str | None) – the namespace field of RegistrationKey

  • tags (Optional[AbstractSet[str]]) – the tags field of RegistrationKey

Return type:

ConfigurationInfo

Returns:

The ConfigurationInfo stored under the parsed key.

classmethod retrieve_keys(names=None, namespaces=None, tags=None, special_tags=None, keys=None)[source]#

Retrieves RegistrationKey via given name, tags, namespaces filters. The search can be limited to a fixed set of keys, optionally given in input.

Parameters:
Return type:

List[RegistrationKey[Any]]

Returns:

Matching RegistrationKey instances.

classmethod retrieve_runnable_keys()[source]#

Return keys marked runnable (run_method set).

Return type:

List[RegistrationKey[Any]]

classmethod snapshot()[source]#

The registry’s whole state, shallow, plus the paths it put on sys.path.

Shallow on purpose. The point is to restore the objects a failed build replaced, not to defend against something mutating them: nothing mutates a registry while a build of another directory is running, and deep-copying a registration graph on every build would cost more than the build.

Return type:

Dict[str, Any]

classmethod unresolved_keys()[source]#

Keys that something depends on but nothing registered.

Meaningful between load and dag_resolution: registration adds a node for every referenced key, so anything in the graph without a registry entry is a broken reference. After a successful build the set is empty, since resolution would have failed.

Return type:

Set[RegistrationKey[Any]]

classmethod update_namespaces(cls, namespaces, module_mapping)[source]#

Merge namespaces into registry mappings.

Raises:

RuntimeWarning – if a namespace is already mapped to a directory. Two directories claiming one namespace makes resolution ambiguous, so the merge is refused rather than silently resolved.

cinnamon.registry.json_default(value)[source]#

default= hook that teaches json about registration keys.

A class cannot make itself serializable to json.dumps() – the encoder dispatches on a fixed set of types and consults default only for what it does not recognise. So this is the hook rather than a method:

json.dumps({"losses": [key_a, key_b]}, default=json_default)

Keys become the mapping from RegistrationKey.to_dict(), whatever they are nested inside. Anything else is passed on to the normal TypeError, so unrelated unserializable objects still fail where they should.

Inside a Configuration none of this is needed: pydantic already knows how to serialize a key, and config.model_dump_json() writes its string form.

Return type:

Any

cinnamon.registry.register(func)[source]#
Return type:

Callable

cinnamon.registry.register_class(name, namespace, tags=None, component=None, run_method=None)[source]#

Register a Configuration subclass, as a decorator on the class itself.

register_method() needs a method to hang on, so a configuration that changes nothing but its parameters still has to write one out:

class TransformerFRConfig(GRUFRConfig):
    backbone: RegistrationKey = Param(TRANSFORMER)

    @classmethod
    @register_method(
        name="model",
        tags={"fr", "transformer"},
        namespace=NAMESPACE,
        component="myproject.models.FR",
    )
    def default(cls):
        return super().default()

That method says nothing the class does not already say. Decorating the class says the same thing and stops there:

@register_class(
    name="model",
    tags={"fr", "transformer"},
    namespace=NAMESPACE,
    component="myproject.models.FR",
)
class TransformerFRConfig(GRUFRConfig):
    backbone: RegistrationKey = Param(TRANSFORMER)

A configuration whose default does real work – adding a condition, say – writes it as an ordinary classmethod; that is the one the registration builds from, since it is the one the class has.

Parameters:
  • name (str) – name of the registration key.

  • namespace (str) – namespace of the registration key. Pass it as a keyword, named by a literal or a module-level constant: Registry.build reads namespaces out of the source before importing anything, so a namespace it cannot see statically leaves the directory looking as though it registers nothing.

  • tags (Optional[AbstractSet[str]]) – tags of the registration key.

  • component (str | None) – import path of the component the configuration describes.

  • run_method (str | None) – name of the component method a runner invokes.

Return type:

Callable

Returns:

The class, unchanged.

cinnamon.registry.register_method(name, namespace, tags=None, component=None, run_method=None)[source]#
Return type:

Callable

cinnamon.registry.setup(directory=None, external_directories=None, logging_level=20)[source]#

Build the registry around an entry point, and run it.

Every script that uses cinnamon opens the same four lines:

if __name__ == '__main__':
    directory = Path(__file__).parent.parent.resolve()
    Registry.build(directory=directory)
    logging.basicConfig(level=logging.INFO)
    logger = getLogger(__name__)

which this replaces with:

@setup(directory=Path(__file__).parent.parent)
def main():
    benchmark = Registry.instantiate(name='benchmark', namespace='examples')
    benchmark.run()

The decorated function runs immediately when its module is __main__, which is what removes the if __name__ guard rather than merely moving the build call into a decorator. Imported from anywhere else it does not run, so a module keeps its ordinary behaviour under import and under test; the decorated function stays callable, and calling it rebuilds the registry.

Because it runs at decoration time, put it where you would have put the if __name__ block: after everything it refers to. That is the same discipline, not a new one.

Parameters:
  • directory (Union[Path, str, None]) – the project directory to build from. Defaults to the working directory, matching the -dir flag of the cmn-* commands.

  • external_directories (Optional[List[Union[str, Path]]]) – external directories containing configurations.

  • logging_level (int | None) – level to pass to logging.basicConfig. Pass None to leave logging alone, for a script that configures its own.

Return type:

Callable

Returns:

The decorator, which returns the function unchanged apart from the build that precedes each call.

cinnamon.cli#

cinnamon.cli.build()[source]#
cinnamon.cli.check()[source]#

Report registration problems without running anything.

Two passes, in the order the problems occur:

1. Keys – run after Registry.load so that every broken reference is visible. dag_resolution stops at the first one, which is why a project with three typos otherwise takes three runs to fix. 2. Bindings – only once the keys resolve, since the component analyzer needs an expanded registry.

The binding pass resolves component paths on the filesystem without importing them, so the command stays fast whatever the components weigh. --deep imports each one to check its __init__ against the configuration’s fields, at the cost of that import.

Exits non-zero when errors are found, so it can gate a commit or a CI job.

Return type:

None

cinnamon.cli.generate()[source]#
cinnamon.cli.run()[source]#

cinnamon#