Skip to content

Registry

coola.registry

General-purpose registry primitives used across the package.

coola.registry.BaseTypeDispatchRegistry

Bases: MultilineDisplayMixin, Generic[V]

Base class for registries that dispatch a single handler based on data type.

This class maintains a mapping from Python types to handler instances (e.g. equality testers, hashers, transformers) and uses the Method Resolution Order (MRO) for type lookup, via an internal TypeRegistry. When resolving a handler for some data, the most specific registered handler for the data's type is used, falling back to parent types (or a default handler, if one is registered for object) if needed.

Subclasses typically expose their own, more specifically named has_<x>/find_<x> wrappers around has/find plus a domain-specific entry point (e.g. hash, transform), but the registration and lookup machinery itself lives entirely here.

Parameters:

Name Type Description Default
initial_state dict[type, V] | None

Optional initial mapping of types to handlers. If provided, the state is copied to prevent external mutations.

None

Attributes:

Name Type Description
_state TypeRegistry[V]

Internal TypeRegistry of registered types to handlers.

coola.registry.BaseTypeDispatchRegistry.find

find(data_type: type) -> V

Find the appropriate handler for a given type.

Uses the Method Resolution Order (MRO) to find the most specific registered handler. For example, if a handler is registered for Sequence but not for list, lists will use the Sequence handler.

Parameters:

Name Type Description Default
data_type type

The Python type to find a handler for.

required

Returns:

Type Description
V

The most specific registered handler for this type,

V

resolved via MRO, or the default handler if no match is

V

found.

Raises:

Type Description
KeyError

If no handler is registered for data_type (or any of its parent types).

Note

Results are cached internally (see TypeRegistry.resolve) so repeated lookups for the same type are fast. The cache is invalidated automatically by register/register_many.

coola.registry.BaseTypeDispatchRegistry.has

has(data_type: type) -> bool

Check if a handler is explicitly registered for the given type.

Note that this only checks for direct registration. Even if this returns False, find may still return a handler via MRO lookup or the default handler.

Parameters:

Name Type Description Default
data_type type

The type to check.

required

Returns:

Type Description
bool

True if a handler is explicitly registered for this type, False otherwise.

coola.registry.BaseTypeDispatchRegistry.register

register(
    data_type: type, value: V, exist_ok: bool = False
) -> None

Register a handler for a given data type.

The internal type-lookup cache is automatically cleared after registration to ensure consistency.

Parameters:

Name Type Description Default
data_type type

The Python type to register (e.g., list, dict, custom classes).

required
value V

The handler instance that handles this type.

required
exist_ok bool

If False (default), raises an error if the type is already registered. If True, overwrites the existing registration silently.

False

Raises:

Type Description
RuntimeError

If the type is already registered and exist_ok is False.

coola.registry.BaseTypeDispatchRegistry.register_many

register_many(
    mapping: Mapping[type, V], exist_ok: bool = False
) -> None

Register multiple handlers at once.

This is a convenience method for bulk registration that internally calls register for each type-handler pair.

Parameters:

Name Type Description Default
mapping Mapping[type, V]

Dictionary mapping Python types to handler instances.

required
exist_ok bool

If False (default), raises an error if any type is already registered. If True, overwrites existing registrations silently.

False

Raises:

Type Description
RuntimeError

If any type is already registered and exist_ok is False.

coola.registry.Registry

Bases: BaseRegistry[K, V]

A thread-safe generic key-value registry for storing and managing typed mappings.

The Registry class provides a type-safe container for registering and retrieving values by key. It supports all standard dictionary operations through operator overloading and provides additional methods for safe registration and querying. All operations are protected by a lock to ensure thread safety in concurrent environments.

Parameters:

Name Type Description Default
initial_state dict[K, V] | None

An optional dictionary to initialize the registry with. If provided, a copy is made to prevent external modifications. Defaults to None, which creates an empty registry.

None

Attributes:

Name Type Description
_state dict[K, V]

Internal dictionary storing the key-value pairs.

_lock RLock

Threading lock for synchronizing access to the registry.

Example

Basic usage with registration and retrieval:

>>> from coola.registry import Registry
>>> registry = Registry[str, int]()
>>> registry.register("key1", 42)
>>> registry.get("key1")
42
>>> registry
Registry(
  (key1): 42
)

Using dictionary-style operations:

>>> from coola.registry import Registry
>>> registry = Registry[str, int]()
>>> registry["key2"] = 100
>>> "key2" in registry
True
>>> del registry["key2"]

Initializing with existing data:

>>> from coola.registry import Registry
>>> registry = Registry[str, int](initial_state={"a": 1, "b": 2})
>>> len(registry)
2

coola.registry.TypeRegistry

Bases: BaseRegistry[type, T], Generic[T]

A thread-safe type-based registry for storing and retrieving values.

The TypeRegistry class provides a thread-safe container for mapping Python types to values. It supports standard dictionary operations through operator overloading and provides methods for safe registration and querying.

The registry uses the Method Resolution Order (MRO) for type lookup through the resolve() method. When resolving a type, it automatically selects the most specific registered type, walking up the inheritance hierarchy if needed. This makes it ideal for type-based dispatching systems.

The registry includes an internal LRU (least-recently-used) cache for type resolution, bounded to _MAX_CACHE_SIZE (1024) entries, to optimize performance when repeatedly resolving the same types without growing unbounded.

Note

resolve() walks dtype.__mro__, which reflects real (static) inheritance only. Virtual subclasses registered via abc.ABCMeta.register() do not appear in __mro__, so registering a value for an ABC does not make resolve() match that ABC's virtual subclasses even though isinstance would report them as instances of the ABC. See resolve() for details.

Parameters:

Name Type Description Default
initial_state dict[type, T] | None

An optional dictionary to initialize the registry with. If provided, a copy is made to prevent external modifications. Defaults to None, which creates an empty registry.

None

Attributes:

Name Type Description
_state dict[K, V]

Internal dictionary storing the type-value pairs.

_cache LRUCache[type, T]

Bounded LRU cache of type resolution lookups for performance.

_lock RLock

Threading lock for synchronizing access to both state and cache.

Example

Basic usage with registration and retrieval:

>>> from coola.registry import TypeRegistry
>>> registry = TypeRegistry[str]()
>>> registry.register(int, "I am an integer")
>>> registry.get(int)
'I am an integer'
>>> registry
TypeRegistry(
  (<class 'int'>): I am an integer
)

Using dictionary-style operations:

>>> from coola.registry import TypeRegistry
>>> registry = TypeRegistry[str]()
>>> registry[str] = "I am a string"
>>> str in registry
True
>>> registry[str]
'I am a string'
>>> del registry[str]
>>> str in registry
False

Initializing with existing data:

>>> from coola.registry import TypeRegistry
>>> registry = TypeRegistry[int](initial_state={str: 100, float: 200})
>>> len(registry)
2
>>> registry.get(str)
100

Using resolve() with inheritance (MRO lookup):

>>> from coola.registry import TypeRegistry
>>> registry = TypeRegistry[str]()
>>> registry.register(object, "I am an object")
>>> registry.register(int, "I am an integer")
>>> # Direct match
>>> registry.resolve(int)
'I am an integer'
>>> # Falls back to parent type via MRO
>>> registry.resolve(bool)  # bool inherits from int
'I am an integer'
>>> # Falls back to object
>>> registry.resolve(str)
'I am an object'

Bulk registration:

>>> from coola.registry import TypeRegistry
>>> registry = TypeRegistry[str]()
>>> registry.register_many({int: "integer", float: "float", str: "string"})
>>> len(registry)
3
>>> registry.get(float)
'float'

coola.registry.TypeRegistry.resolve

resolve(dtype: type) -> T

Resolve a type to its associated value using MRO lookup.

This method finds the most appropriate value for a given type by walking the Method Resolution Order (MRO). It first checks for a direct match, then searches through parent types in MRO order to find the most specific registered type.

Results are cached internally to optimize performance for repeated lookups of the same type.

Note

Resolution walks dtype.__mro__, i.e. real (static) inheritance only. Virtual subclasses registered with abc.ABCMeta.register() do not appear in __mro__, so registering a value for an ABC does not make this method resolve any of that ABC's virtual subclasses, even though isinstance(instance_of_virtual_subclass, the_abc) is True. To make a virtual subclass resolve to a value, either register the subclass itself or a real (non-virtual) ancestor of it.

Parameters:

Name Type Description Default
dtype type

The type to resolve.

required

Returns:

Type Description
T

The value associated with the type or its nearest registered

T

parent type in the MRO.

Raises:

Type Description
KeyError

If no matching type is found in the registry, including parent types in the MRO.

Example

Basic resolution with inheritance:

>>> from coola.registry import TypeRegistry
>>> registry = TypeRegistry[str]()
>>> registry.register(object, "base")
>>> registry.register(int, "integer")
>>> # Direct match
>>> registry.resolve(int)
'integer'
>>> # bool inherits from int, so resolves to int's value
>>> registry.resolve(bool)
'integer'
>>> # str inherits from object, so resolves to object's value
>>> registry.resolve(str)
'base'

Resolution with custom classes:

>>> from coola.registry import TypeRegistry
>>> class Animal:
...     pass
...
>>> class Dog(Animal):
...     pass
...
>>> class Poodle(Dog):
...     pass
...
>>> registry = TypeRegistry[str]()
>>> registry.register(Animal, "animal")
>>> registry.register(Dog, "dog")
>>> registry.resolve(Dog)
'dog'
>>> registry.resolve(Poodle)  # Resolves to parent Dog
'dog'