Skip to content

Components

The model every kind shares: the base class, its definition, the identity a component is resolved by, and the relations that bind components to one another.

Component

Component(**data: Any)

Bases: interloper.serializable.base.Serializable

Fundamental building block: identifiable, composable, serializable.

Every catalog citizen extends Component. On top of Serializable it provides:

  • Identity: kind (class-level category), id (instance-level, overridable) and identity, the pair relation matching reads.
  • Relations: one relations map, name to Relation, collected from the class body and bound per instance.
  • Definition: definition() exposes class metadata for API consumers.

A relation written in a class body is a Relation value, so Relation joins IgnoredDescriptor in ignored_types: Pydantic never mistakes a declared relation for a field.

Bind the relation kwargs, validate everything else as fields.

A kwarg named after a declared relation binds that relation instead of reaching Pydantic, which knows nothing about relations: a list or tuple binds every element, None binds nothing. Each binding is checked as it is made (see bind); whether every relation that must be bound is bound is a question for whoever assembles the graph, since components are wired piecewise (a source's assets read its bindings through it, reconstruction binds references once the document is whole), so validate_relations is called at those boundaries, not here.

Unknown kwargs are a loud error rather than pydantic's silent extra="ignore" drop: a misnamed field would otherwise vanish, and a stale persisted config key surfaces as drift at load time, consistent with the fail-closed drift checks.

Parameters:

Name Type Description Default
**data Any

Field values and relation targets, keyed by field or relation name.

required

Raises:

Type Description
TypeError

If a kwarg names neither a field nor a declared relation.

discriminator

discriminator

This instance's discriminator value, if declared and set.

The value of the config field marked discriminator=True: what distinguishes instances of the same component class (an ad account id, a site URL, ...). Drives the derived instance_name and, for sources, the per-instance asset table names.

icon

icon = ''

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

identity

identity

What this component is for relation matching: its owner's key and its own.

key

key = 'component'

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

kind

kind = ''

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.

maturity

maturity = <Maturity.STABLE: 'stable'>

How far a component is from being relied on in production.

Informational: it is shown beside the component wherever it is offered and never changes what the component does. ALPHA is early work whose behaviour, configuration and output may still change and that has not been proven against a live service; BETA works and is being proven, with changes still possible; STABLE, the default, is relied on; DEPRECATED still works but is being phased out, so new setups should avoid it.

A composite, such as an asset inside its source, shows the least mature of its parts, DEPRECATED counting as the least: a deprecated source deprecates its assets, and a deprecated asset stays deprecated whatever its source.

model_config

model_config = {'arbitrary_types_allowed': True, 'ignored_types': (<class 'interloper.serializable.base.IgnoredDescriptor'>, <class 'interloper.component.relation.Relation'>)}

dict() -> new empty dictionary dict(mapping) -> new dictionary initialized from a mapping object's (key, value) pairs dict(iterable) -> new dictionary initialized as if via: d = {} for k, v in iterable: d[k] = v dict(**kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list. For example: dict(one=1, two=2)

owned

owned

The components this one owns (a source's assets), which travel inside its own spec.

Returns:

Type Description

The owned components, in field order; empty for a component that

owns none.

parent

parent

The component that owns this one, None when it stands alone.

A source owns its assets; a connection, a config or a standalone asset has no owner. Ownership scopes the bare keys a relation declares, so it is what identity reads.

qualified_key

qualified_key

This component's key, qualified by its owner's key when it has an owner.

relations

relations = {}

dict() -> new empty dictionary dict(mapping) -> new dictionary initialized from a mapping object's (key, value) pairs dict(iterable) -> new dictionary initialized as if via: d = {} for k, v in iterable: d[k] = v dict(**kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list. For example: dict(one=1, two=2)

sensitive

sensitive = False

bool(x) -> bool

Returns True when the argument x is true, False otherwise. The builtins True and False are the only two instances of the class bool. The class bool is a subclass of the class int, and cannot be subclassed.

anchor

anchor() -> type[Component]

The class anchoring this component's kind.

The anchor is the base-most class in the MRO that declares the kind (Connection for any connection subclass), so any component class resolves to the single per-kind authority.

Returns:

Type Description
type[Component]

The anchoring class.

bind

bind(name: str, *targets: Component) -> None

Bind components to one of this component's declared relations.

A many relation accumulates, skipping targets it already holds; a single-valued one replaces what it holds, so repointing it is a second bind and needs no unbind first.

A target whose kind or key the relation does not accept, this component itself, and more than one target at once on a single-valued relation are all refused with a ConfigError from _replace_binding, which every write path goes through.

Parameters:

Name Type Description Default
name str

The relation name as declared on the class.

required
*targets Component

The components to bind. Binding nothing is a no-op, so a caller may splat an empty list.

required

bound

bound(name: str) -> Component | list[Component] | None

What is explicitly bound to one of this component's declared relations.

Parameters:

Name Type Description Default
name str

The relation name as declared on the class.

required

Returns:

Type Description
Component | list[Component] | None

The bound components as a list for a many relation (empty when

Component | list[Component] | None

nothing is bound), otherwise the single bound component or None.

definition

definition() -> ComponentDefinition

Produce a structured definition of this component class.

The one place a class is described. A kind whose definition carries more (a source's assets, an asset's schema) builds its own model from this one: SourceDefinition(**dict(super().definition()), assets=...). Every FetchField provider the class declares is checked against its relations first, so a misnamed provider fails at catalog-build time rather than in a form.

Returns:

Type Description
ComponentDefinition

A ComponentDefinition with metadata derived from the class.

discriminator_field

discriminator_field() -> str | None

Name of the config field marked discriminator=True, if any.

Returns:

Type Description
str | None

The marked field name, or None when the class declares none.

event_metadata

event_metadata(
    metadata: Mapping[str, Any],
    window: PartitionWindow | None = None,
    **extra: Any,
) -> dict[str, Any]

Build the metadata of an event about this component.

One shape for every event: the run metadata, the component's identity, the window the event is scoped to (None when unscoped), the parent's id when the component has one, and whatever the producer adds (a message, an error, a destination key, an attempt).

Parameters:

Name Type Description Default
metadata Mapping[str, Any]

Run-level metadata (run id, backfill id, anything the caller passed).

required
window PartitionWindow | None

The window the event is scoped to, or None.

None
**extra Any

Producer-specific keys, written last so they win.

required

Returns:

Type Description
dict[str, Any]

The event metadata.

from_init

from_init(
    init: dict[str, Any], context: SerializationContext
) -> Self

Build a component from a loaded init, holding its references back for the document to bind.

Parameters:

Name Type Description Default
init dict[str, Any]

Constructor keyword arguments, nested specs already built and every {"ref": id} still a mapping.

required
context SerializationContext

The document being read: it keeps the held references and the instance, so a later reference finds it.

required

Returns:

Type Description
Self

The component, its references not yet bound.

inherited

inherited(child: Component, name: str, own: Any) -> Any

What an owned component reads for one of its inherited settings.

The one seam a cascade goes through: an owned component never holds a copy of what it inherits, it asks its owner on every read. The base hands down this component's own effective value; an owner that names part of its children's location overrides it (see Source.inherited).

Parameters:

Name Type Description Default
child Component

The owned component reading the setting.

required
name str

The setting's field name.

required
own Any

The child's stored value, None while unset.

required

Returns:

Type Description
Any

own when set, else this component's own value under name when

Any

it declares such a field, else None.

instance_name

instance_name() -> str

Display name for this instance: its discriminator value.

The class label is the fallback when no discriminator is declared or set, since the type is already visible alongside the name everywhere the name is shown, so it isn't repeated in it.

A derived default, not an identity: the persistence layer uses it to seed a blank component name, and users may override it freely. It never feeds physical naming.

Returns:

Type Description
str

E.g. "act_123", or "Facebook Ads" without a discriminator.

model_post_init

model_post_init(context: Any) -> None

Default id to a generated UUID if not provided.

Parameters:

Name Type Description Default
context Any

The pydantic validation context, unused here.

required

public_fields

public_fields() -> frozenset[str]

Names of the config fields marked x-public.

A public field may be shown wherever the component is, even when the rest of a sensitive kind's config may not.

Returns:

Type Description
frozenset[str]

The marked field names.

resolve

resolve(name: str) -> Any

What one of this component's declared relations actually resolves to.

Unlike bound, this reads through the owner (see _targets) and then falls back to what the relation can fill itself with, so a relation with a fallback (a config whose every field is defaulted, say) works without a binding.

Parameters:

Name Type Description Default
name str

The relation name as declared on the class.

required

Returns:

Type Description
Any

What the relation holds or reads through its owner, otherwise the

Any

relation's fallback for a single-valued relation and an empty list

Any

for a many one.

resolve_key

resolve_key(
    key: str, catalog: Catalog | None = None
) -> type[Self]

Resolve a catalog key to a component class of this (sub)class.

The key is looked up in catalog (or, when none is given, the settings-configured catalog, built lazily), and the class it names is imported.

Called on a subclass, the resolved class must be of that subclass (Source.resolve_key("facebook_ads")); anything else raises TypeError.

Parameters:

Name Type Description Default
key str

The catalog key naming the component class.

required
catalog Catalog | None

The catalog to look the key up in. Defaults to None, which builds the settings-configured catalog.

None

Returns:

Type Description
type[Self]

The resolved class.

Raises:

Type Description
CatalogKeyError

If the key is not in the catalog.

to_spec

to_spec(
    *, context: SerializationContext | None = None
) -> Spec

Serialize this instance to a reconstructible spec, relations and owned components included.

What a relation holds sits in init under the relation's name, a list for a many relation and a single value otherwise, each target written as SerializationContext.emit decides; a relation with nothing bound is left out. The components this one owns (see owned) travel inside this spec, under the field that holds them, as a map of key to init carrying the child's id: an owned component never has a spec of its own.

Parameters:

Name Type Description Default
context SerializationContext | None

The state shared with the other roots of one document. None starts a context for this component alone.

None

Returns:

Type Description
Spec

A Spec capturing this instance's state, identity and bindings.

unbind

unbind(name: str, *targets: Component) -> None

Detach components from one of this component's declared relations.

Detaching nothing the relation holds is a no-op; otherwise what remains goes through the same write path as bind, so the same checks apply (a non-optional relation cannot be emptied, ConfigError).

Parameters:

Name Type Description Default
name str

The relation name as declared on the class.

required
*targets Component

The components to detach; ones the relation does not hold are ignored.

required

validate_relations

validate_relations(
    nodes: Mapping[str, Component] | None = None,
) -> None

Check that every relation this component holds is sound.

Three checks per relation: it is bound, unless it is optional or has a fallback; a single-valued relation holds at most one target; every bound target is one the relation accepts. When nodes is given, a bound non-optional asset-kind target must also be one of nodes.

The check runs at the boundaries where a graph is whole: the DAG runs it with its nodes once resolution is done, and reconstruction runs it without nodes on every root of a document once its references are bound. Without nodes an unbound asset-kind relation only the graph can fill is left alone: one reaching outside the owner's own source (local), or any one on an owner that has no source to fill it from. Nothing before the graph can fill such a relation, so nothing before the graph can call it unfilled either.

Parameters:

Name Type Description Default
nodes Mapping[str, Component] | None

Every node materializing in the same run, keyed by id. When None, the DAG-membership check is skipped and a relation only the graph can fill is left alone.

None

Raises:

Type Description
ConfigError

If any relation is unbound and non-optional, holds several targets while single-valued, holds a target it does not accept, or (when nodes is given) points a non-optional relation at an asset absent from nodes.

ComponentDefinition

Bases: pydantic.main.BaseModel

Read-only view of a Component class's metadata.

Returned by Component.definition(). Not a separate architectural entity, just a structured projection of the class for API consumers. Every kind is self-describing: config_schema is the JSON Schema of its user-configurable fields, relations the links it declares toward other components, maturity how far it is from being relied on, group where the app shows it: its own key, unless the class declares another, as a star schema does its platform's key. A group is a display fact, any name; when it is a catalog key, that definition labels it.

model_config

model_config = {}

dict() -> new empty dictionary dict(mapping) -> new dictionary initialized from a mapping object's (key, value) pairs dict(iterable) -> new dictionary initialized as if via: d = {} for k, v in iterable: d[k] = v dict(**kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list. For example: dict(one=1, two=2)

ComponentIdentity

Bases: builtins.tuple

What a component is, for relation matching: its owning source key and its own key.

source_key is None for a component with no owning source (a connection, a config, a standalone asset); assets owned by a source carry that source's key.

key

key = _tuplegetter(1, 'Alias for field number 1')

Alias for field number 1

source_key

source_key = _tuplegetter(0, 'Alias for field number 0')

Alias for field number 0

of

of(component: Component) -> ComponentIdentity

Build the identity of an already-constructed component.

Parameters:

Name Type Description Default
component Component

The component to identify.

required

Returns:

Type Description
ComponentIdentity

The component's identity, its parent's key as source_key when

ComponentIdentity

it has a parent, None otherwise.

resolve

resolve(
    declared_key: str, *, own_source_key: str | None
) -> ComponentIdentity

The identity a declared relation key expects.

A bare key is scoped to the declaring component's own source, a qualified key (source.key) names the source explicitly.

Parameters:

Name Type Description Default
declared_key str

The relation key as written, bare ("orders") or qualified ("shop.orders", "*.campaigns").

required
own_source_key str | None

Key of the source declaring the relation, used to scope a bare key. None when the declaring component has no owning source.

required

Returns:

Type Description
ComponentIdentity

The expected identity; source_key is own_source_key for a

ComponentIdentity

bare key, or the part before the dot for a qualified key.

satisfies

satisfies(
    declared_key: str, *, own_source_key: str | None
) -> bool

Whether this identity is an acceptable match for a declared key.

A bare key expects a component of the declaring source, a qualified key a component of the named source, and *.key a component of that key owned by some source, whichever it is; an identity with no source never satisfies a wildcard.

Parameters:

Name Type Description Default
declared_key str

The relation key as written on the declaring component.

required
own_source_key str | None

Key of the source declaring the relation, used to scope a bare key. None when the declaring component has no owning source.

required

Returns:

Type Description
bool

True when the key matches and the source constraint holds.

Relation

Relation(
    kind_or_class: str | list[str] | type = "",
    key: str | list[str] = "",
    /,
    **data: Any,
)

Bases: pydantic.main.BaseModel

One declared link from an owner component to the components that may fill it.

kind and key name the acceptable targets: kind names one or several component kinds, key (when non-empty) narrows to specific keys within those kinds, matched through ComponentIdentity.satisfies. many marks a relation that binds several components at once; optional marks one that may stay unbound or be left empty, and says nothing else: on_delete alone decides what deleting a bound target does, block refusing the deletion while this relation holds the target and detach letting it through and dropping the binding. default and has_fallback together describe a relation that can be resolved without an explicit binding.

A relation is also its own descriptor: Component._collect installs the stamped copy under the relation's name, so the class attribute reads as the declaration and the instance attribute as what is bound to it, and assignment rebinds it.

The declaration form the framework writes pairs the relation with the annotation of what fills it (destinations: list[Destination] = Relation("destination", many=True)). The TYPE_CHECKING __new__ is what makes that form check: the type checker reads a Pydantic field of the annotated type, so the constructor kwarg and the instance attribute are both typed, while the runtime sees a Relation (Pydantic ignores it, and Component._collect drops the annotation before fields are collected).

Two attributes are not declared but derived. name is stamped from the attribute the relation is declared under, and target is the class the Relation(cls) shorthand was written with, the class a fallback is built from; neither is a constructor argument. default (intended type Callable[[], Component] | None) and target (intended type type[Component] | None) are typed Any for the type checker, since Component is only imported under TYPE_CHECKING; both stay out of dumps.

Build a relation from either a component class or explicit kind/key values.

Parameters:

Name Type Description Default
kind_or_class str | list[str] | type

A component class (its kind and key class attributes seed the relation and it becomes the target), or the relation's kind value as a string or list of strings.

''
key str | list[str]

The relation's key value, ignored when kind_or_class is a class.

''
**data Any

Remaining field values (many, optional, default, on_delete), forwarded to Pydantic, which rejects any other name rather than dropping it.

required

Raises:

Type Description
TypeError

If name is passed; it is stamped from the attribute the relation is declared under.

has_fallback

has_fallback

Whether this relation can be resolved without an explicit binding.

Three cases, all of them single-valued but for the first: a default factory is set; the target is a settings class, whose required fields come from the environment, so a resource the user never bound is still constructible where it is read; or every field of the target (besides id) is optional, so the class constructs bare.

Returns:

Type Description

True when fallback would produce a value.

keys

keys

Normalise key to a list.

Returns:

Type Description

key as a list, empty when unset (any key of the relation's

kinds is accepted), whether declared as a single string or a list.

kinds

kinds

Normalise kind to a list.

Returns:

Type Description

kind as a list, whether declared as a single string or already a list.

local

local

Whether every key this relation declares names a component of the owner's own source.

A bare key ("orders") is scoped to the declaring component's own source, so that source is the authority on what fills it; a qualified or wildcard key ("shop.orders", "*.orders") reaches outside it, and only the graph of a run can say what fills it.

Returns:

Type Description

True when the relation declares at least one key and none of them

is qualified; False for a relation declaring no key at all, which

accepts any key of its kinds wherever it comes from.

model_config

model_config = {
    "arbitrary_types_allowed": True,
    "extra": "forbid",
}

dict() -> new empty dictionary dict(mapping) -> new dictionary initialized from a mapping object's (key, value) pairs dict(iterable) -> new dictionary initialized as if via: d = {} for k, v in iterable: d[k] = v dict(**kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list. For example: dict(one=1, two=2)

target

target

The component class this relation was declared from, if it was declared from one.

Returns:

Type Description

The class given to Relation(cls), else None.

accepts

accepts(
    kind: str,
    identity: ComponentIdentity,
    *,
    owner: ComponentIdentity,
) -> bool

Whether a candidate component may fill this relation.

A bare key is scoped to the owner's source only when the candidate is an asset: an asset is the one kind whose key is source-local. Every other candidate kind is keyed globally by its catalog key, so a bare key there names that class wherever it comes from. Scoping by the relation's declared kinds instead of the candidate's own would wrongly scope a source or destination candidate to the owner's source the moment the relation also accepts assets (a job's targets, say).

Parameters:

Name Type Description Default
kind str

The candidate component's kind.

required
identity ComponentIdentity

The candidate component's identity.

required
owner ComponentIdentity

The identity of the component declaring this relation, used to scope bare keys.

required

Returns:

Type Description
bool

True when kind is one of kinds() and, when keys() is

bool

non-empty, identity satisfies at least one of them.

fallback

fallback() -> Any | None

Produce the value this relation resolves to when left unbound.

A settings target is constructed here and not at build time, which is what keeps a connection whose credentials live in the environment usable: the validation error of a credential that is neither bound nor in the environment propagates from the read that needed it.

Returns:

Type Description
Any | None

A fresh default() when set, a fresh target() when the

Any | None

target constructs on its own, or None when the relation has

Any | None

no fallback.

from_annotation

from_annotation(
    hint: Any, namespace: dict[str, Any]
) -> Relation | None

Build the relation an annotation declares, when it declares one.

Recognised shapes are a component class (connection: PostgresConnection), a forward reference to one, and the optional forms X | None and Optional[X]. Anything else is not a relation and stays an ordinary field: a scalar, a container, and Component itself, which names no kind and so cannot say what would fill the relation.

Parameters:

Name Type Description Default
hint Any

The annotation as written, already evaluated or still the forward-reference string from __future__ import annotations leaves behind.

required
namespace dict[str, Any]

The namespace a forward reference resolves against, normally the declaring class's module globals.

required

Returns:

Type Description
Relation | None

A relation targeting the annotated class, optional when the

Relation | None

annotation admits None, or None when the annotation

Relation | None

declares no relation.

Maturity

Bases: builtins.str, enum.Enum

How far a component is from being relied on in production.

Informational: it is shown beside the component wherever it is offered and never changes what the component does. ALPHA is early work whose behaviour, configuration and output may still change and that has not been proven against a live service; BETA works and is being proven, with changes still possible; STABLE, the default, is relied on; DEPRECATED still works but is being phased out, so new setups should avoid it.

A composite, such as an asset inside its source, shows the least mature of its parts, DEPRECATED counting as the least: a deprecated source deprecates its assets, and a deprecated asset stays deprecated whatever its source.

ALPHA

ALPHA = <Maturity.ALPHA: 'alpha'>

How far a component is from being relied on in production.

Informational: it is shown beside the component wherever it is offered and never changes what the component does. ALPHA is early work whose behaviour, configuration and output may still change and that has not been proven against a live service; BETA works and is being proven, with changes still possible; STABLE, the default, is relied on; DEPRECATED still works but is being phased out, so new setups should avoid it.

A composite, such as an asset inside its source, shows the least mature of its parts, DEPRECATED counting as the least: a deprecated source deprecates its assets, and a deprecated asset stays deprecated whatever its source.

BETA

BETA = <Maturity.BETA: 'beta'>

How far a component is from being relied on in production.

Informational: it is shown beside the component wherever it is offered and never changes what the component does. ALPHA is early work whose behaviour, configuration and output may still change and that has not been proven against a live service; BETA works and is being proven, with changes still possible; STABLE, the default, is relied on; DEPRECATED still works but is being phased out, so new setups should avoid it.

A composite, such as an asset inside its source, shows the least mature of its parts, DEPRECATED counting as the least: a deprecated source deprecates its assets, and a deprecated asset stays deprecated whatever its source.

DEPRECATED

DEPRECATED = <Maturity.DEPRECATED: 'deprecated'>

How far a component is from being relied on in production.

Informational: it is shown beside the component wherever it is offered and never changes what the component does. ALPHA is early work whose behaviour, configuration and output may still change and that has not been proven against a live service; BETA works and is being proven, with changes still possible; STABLE, the default, is relied on; DEPRECATED still works but is being phased out, so new setups should avoid it.

A composite, such as an asset inside its source, shows the least mature of its parts, DEPRECATED counting as the least: a deprecated source deprecates its assets, and a deprecated asset stays deprecated whatever its source.

STABLE

STABLE = <Maturity.STABLE: 'stable'>

How far a component is from being relied on in production.

Informational: it is shown beside the component wherever it is offered and never changes what the component does. ALPHA is early work whose behaviour, configuration and output may still change and that has not been proven against a live service; BETA works and is being proven, with changes still possible; STABLE, the default, is relied on; DEPRECATED still works but is being phased out, so new setups should avoid it.

A composite, such as an asset inside its source, shows the least mature of its parts, DEPRECATED counting as the least: a deprecated source deprecates its assets, and a deprecated asset stays deprecated whatever its source.

KINDS

KINDS = <interloper.registry.base.Registry object at 0x7febf21ea1b0>

Name → object registry, optionally fed by an entry-point group.

Entries register explicitly via register, or are loaded lazily from group on first lookup. Registration is first-wins and idempotent. adopt transforms a loaded entry into its (name, object) pair — the default keeps the entry name and object as declared.

One installed package never takes the others down: an entry point that fails to load or adopt is skipped with a warning naming it. Without adopt, an entry's name is its key, so a lookup by name loads only the entry points declared under that name; a listing loads them all.

Registry

Registry(
    group: str | None = None,
    adopt: Callable[[str, Any], tuple[str, T]]
    | None = None,
)

Bases: typing.Generic

Name → object registry, optionally fed by an entry-point group.

Entries register explicitly via register, or are loaded lazily from group on first lookup. Registration is first-wins and idempotent. adopt transforms a loaded entry into its (name, object) pair — the default keeps the entry name and object as declared.

One installed package never takes the others down: an entry point that fails to load or adopt is skipped with a warning naming it. Without adopt, an entry's name is its key, so a lookup by name loads only the entry points declared under that name; a listing loads them all.

Initialize the registry.

Parameters:

Name Type Description Default
group str | None

Entry-point group to load lazily; None for a purely code-registered registry.

None
adopt Callable[[str, Any], tuple[str, T]] | None

Optional transform from a loaded entry to its (name, object) pair.

None

get

get(name: str) -> T | None

Look up an entry by name.

Parameters:

Name Type Description Default
name str

The registry key.

required

Returns:

Type Description
T | None

The registered object, or None if unknown.

items

items() -> tuple[tuple[str, T], ...]

All registered entries.

Returns:

Type Description
tuple[tuple[str, T], ...]

(name, object) pairs, sorted by name.

keys

keys() -> tuple[str, ...]

All registered names.

Returns:

Type Description
tuple[str, ...]

The names, sorted.

register

register(name: str, entry: T) -> None

Register entry under name (first-wins, idempotent).

Parameters:

Name Type Description Default
name str

The registry key.

required
entry T

The object to register; ignored when the name is taken.

required

values

values() -> tuple[T, ...]

All registered objects.

Returns:

Type Description
tuple[T, ...]

The objects, ordered by name.