Registry
coola.registry ¶
Experimental package with registry implementations.
coola.registry.Registry ¶
Bases: Generic[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.Registry.clear ¶
clear() -> None
Remove all entries from the registry.
This method empties the registry, leaving it in the same state as a newly created empty registry. This operation cannot be undone.
Example
>>> from coola.registry import Registry
>>> registry = Registry[str, int]({"key1": 42, "key2": 100})
>>> len(registry)
2
>>> registry.clear()
>>> len(registry)
0
>>> registry.has("key1")
False
coola.registry.Registry.equal ¶
equal(other: object, equal_nan: bool = False) -> bool
Indicate if two objects are equal or not.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
other
|
object
|
The object to compare with. |
required |
equal_nan
|
bool
|
If |
False
|
Returns:
| Type | Description |
|---|---|
bool
|
|
Example
>>> from coola.registry import Registry
>>> registry1 = Registry[str, int]({"key1": 42, "key2": 100})
>>> registry2 = Registry[str, int]({"key1": 42, "key2": 100})
>>> registry3 = Registry[str, int]({"key1": 42})
>>> registry1.equal(registry2)
True
>>> registry1.equal(registry3)
False
coola.registry.Registry.get ¶
get(key: K, default: V | None = None) -> V | None
Retrieve the value associated with a key.
This method performs a lookup in the registry and returns the corresponding value.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
K
|
The key whose value should be retrieved. |
required |
default
|
V | None
|
Value to return if the key does not exist. |
None
|
Returns:
| Type | Description |
|---|---|
V | None
|
The value associated with the specified key. |
Example
>>> from coola.registry import Registry
>>> registry = Registry[str, int]({"key1": 42, "key2": 100})
>>> registry.get("key1")
42
>>> registry.get("missing")
None
coola.registry.Registry.has ¶
has(key: K) -> bool
Check whether a key is registered in the registry.
This method provides a safe way to test for key existence without risking a KeyError exception.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
K
|
The key to check for existence. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if the key exists in the registry, False otherwise. |
Example
>>> from coola.registry import Registry
>>> registry = Registry[str, int]({"key1": 42, "key2": 100})
>>> registry.has("key1")
True
>>> registry.has("missing")
False
coola.registry.Registry.register ¶
register(
key: K, value: V, exist_ok: bool = False
) -> None
Register a new key-value pair in the registry.
By default, this method raises an error if you try to register a key that already exists. This prevents accidental overwriting of values. Set exist_ok=True to allow overwriting.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
K
|
The key to register. Must be hashable. |
required |
value
|
V
|
The value to associate with the key. |
required |
exist_ok
|
bool
|
Controls behavior when the key already exists. If False (default), raises RuntimeError for duplicate keys. If True, silently overwrites the existing value. |
False
|
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If the key is already registered and exist_ok is False. The error message provides guidance on how to resolve the conflict. |
Example
Basic registration:
>>> from coola.registry import Registry
>>> registry = Registry[str, int]()
>>> registry.register("key1", 42)
>>> registry.get("key1")
42
Attempting to register a duplicate key:
>>> registry.register("key1", 100) # doctest: +SKIP
RuntimeError: A value is already registered for 'key1'...
Overwriting with exist_ok:
>>> registry.register("key1", 100, exist_ok=True)
>>> registry.get("key1")
100
coola.registry.Registry.register_many ¶
register_many(
mapping: Mapping[K, V], exist_ok: bool = False
) -> None
Register multiple key-value pairs in a single operation.
This is a convenience method for bulk registration. It iterates through the provided mapping and registers each key-value pair. All registrations follow the same exist_ok policy. The operation is atomic when exist_ok is False - if any key already exists, no changes are made.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
mapping
|
Mapping[K, V]
|
A dictionary or mapping containing the key-value pairs to register. The keys and values must match the registry's type parameters. |
required |
exist_ok
|
bool
|
Controls behavior when any key already exists. If False (default), raises error on the first duplicate key. If True, overwrites all existing values without error. |
False
|
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If exist_ok is False and any key in the mapping is already registered. The error occurs on the first duplicate encountered, and no partial registration occurs. |
Example
Registering multiple entries at once:
>>> from coola.registry import Registry
>>> registry = Registry[str, int]()
>>> registry.register_many({"key1": 42, "key2": 100, "key3": 7})
>>> registry.get("key1")
42
>>> registry.get("key2")
100
>>> len(registry)
3
Bulk update with exist_ok:
>>> registry.register_many({"key1": 1, "key4": 4}, exist_ok=True)
>>> registry.get("key1")
1
>>> registry.get("key4")
4
coola.registry.Registry.unregister ¶
unregister(key: K) -> V
Remove a key-value pair from the registry and return the value.
This method removes the specified key from the registry and returns the value that was associated with it. This allows you to retrieve the value one last time before it's removed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
K
|
The key to unregister and remove from the registry. |
required |
Returns:
| Type | Description |
|---|---|
V
|
The value that was associated with the key before removal. |
Raises:
| Type | Description |
|---|---|
KeyError
|
If the key is not registered. The error message includes the key that was not found. |
Example
>>> from coola.registry import Registry
>>> registry = Registry[str, int]({"key1": 42, "key2": 100})
>>> registry.has("key1")
True
>>> value = registry.unregister("key1")
>>> value
42
>>> registry.has("key1")
False
coola.registry.TypeRegistry ¶
Bases: 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 cache for type resolution to optimize performance when repeatedly resolving the same types.
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[type, T]
|
Internal dictionary storing the type-value pairs. |
_cache |
dict[type, T]
|
Cached version 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.clear ¶
clear() -> None
Remove all entries from the registry.
This method empties the registry, leaving it in the same state as a newly created empty registry. This operation cannot be undone.
Example
>>> from coola.registry import TypeRegistry
>>> registry = TypeRegistry[str]({int: "I am an integer", float: "I am a float"})
>>> len(registry)
2
>>> registry.clear()
>>> len(registry)
0
>>> registry.has(int)
False
>>> registry.has(float)
False
coola.registry.TypeRegistry.equal ¶
equal(other: Any, equal_nan: bool = False) -> bool
Indicate if two registries are equal.
Two registries are considered equal if they contain the same type-value mappings. The comparison is order-independent since dictionaries are unordered collections.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
other
|
Any
|
The object to compare with. |
required |
equal_nan
|
bool
|
If |
False
|
Returns:
| Type | Description |
|---|---|
bool
|
|
Example
>>> from coola.registry import TypeRegistry
>>> registry1 = TypeRegistry[str]({int: "I am an integer", float: "I am a float"})
>>> registry2 = TypeRegistry[str]({int: "I am an integer", float: "I am a float"})
>>> registry3 = TypeRegistry[str]({int: "I am an integer"})
>>> registry1.equal(registry2)
True
>>> registry1.equal(registry3)
False
coola.registry.TypeRegistry.get ¶
get(dtype: type, default: T | None = None) -> T
Retrieve the value associated with a type.
This method performs a direct lookup in the registry and returns the
corresponding value. If the type doesn't exist, a KeyError is raised
with a descriptive message. For inheritance-based lookup, use the
resolve() method instead.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dtype
|
type
|
The type whose value should be retrieved. |
required |
default
|
T | None
|
Value to return if the key does not exist. |
None
|
Returns:
| Type | Description |
|---|---|
T
|
The value associated with the specified type. |
Example
>>> from coola.registry import TypeRegistry
>>> registry = TypeRegistry[int]()
>>> registry.register(str, 42)
>>> registry.get(str)
42
>>> registry.get(int) # doctest: +SKIP
KeyError: "Key <class 'int'> is not registered"
coola.registry.TypeRegistry.has ¶
has(dtype: type) -> bool
Check whether a type is registered in the registry.
This method provides a safe way to test for type existence without risking a KeyError exception. It only checks for direct type matches; it does not check parent types via MRO.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dtype
|
type
|
The type to check for existence. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if the type exists in the registry, False otherwise. |
Example
>>> from coola.registry import TypeRegistry
>>> registry = TypeRegistry[int]()
>>> registry.register(str, 42)
>>> registry.has(str)
True
>>> registry.has(int)
False
coola.registry.TypeRegistry.register ¶
register(
dtype: type, value: T, exist_ok: bool = False
) -> None
Register a new type-value pair in the registry.
By default, this method raises an error if you try to register a type that already exists. This prevents accidental overwriting of values. Set exist_ok=True to allow overwriting.
Registering a new type clears the internal resolution cache to ensure
that subsequent resolve() calls use the updated registry state.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dtype
|
type
|
The type to register. Must be a Python type object. |
required |
value
|
T
|
The value to associate with the type. |
required |
exist_ok
|
bool
|
Controls behavior when the type already exists. If False (default), raises RuntimeError for duplicate types. If True, silently overwrites the existing value. |
False
|
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If the type is already registered and exist_ok is False. The error message provides guidance on how to resolve the conflict. |
Example
Basic registration:
>>> from coola.registry import TypeRegistry
>>> registry = TypeRegistry[int]()
>>> registry.register(str, 42)
>>> registry.get(str)
42
Attempting to register a duplicate type:
>>> registry.register(str, 100) # doctest: +SKIP
RuntimeError: A value is already registered for '<class 'str'>'...
Overwriting with exist_ok:
>>> registry.register(str, 100, exist_ok=True)
>>> registry.get(str)
100
coola.registry.TypeRegistry.register_many ¶
register_many(
mapping: Mapping[type, T], exist_ok: bool = False
) -> None
Register multiple type-value pairs in a single operation.
This is a convenience method for bulk registration. It iterates through the provided mapping and registers each type-value pair. All registrations follow the same exist_ok policy. The operation is atomic when exist_ok is False - if any type already exists, no changes are made.
Registering new types clears the internal resolution cache to ensure
that subsequent resolve() calls use the updated registry state.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
mapping
|
Mapping[type, T]
|
A dictionary or mapping containing the type-value pairs to register. The keys must be Python type objects and values must match the registry's type parameter. |
required |
exist_ok
|
bool
|
Controls behavior when any type already exists. If False (default), raises error on the first duplicate type. If True, overwrites all existing values without error. |
False
|
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If exist_ok is False and any type in the mapping is already registered. The error occurs before any registration is performed, ensuring no partial updates. |
Example
Registering multiple entries at once:
>>> from coola.registry import TypeRegistry
>>> registry = TypeRegistry[int]()
>>> registry.register_many({str: 42, float: 100, list: 7})
>>> registry.get(str)
42
>>> registry.get(float)
100
>>> len(registry)
3
Bulk update with exist_ok:
>>> registry.register_many({str: 1, dict: 4}, exist_ok=True)
>>> registry.get(str)
1
>>> registry.get(dict)
4
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.
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'
coola.registry.TypeRegistry.unregister ¶
unregister(dtype: type) -> T
Remove a type-value pair from the registry and return the value.
This method removes the specified type from the registry and returns the value that was associated with it. This allows you to retrieve the value one last time before it's removed.
Unregistering a type clears the internal resolution cache to ensure
that subsequent resolve() calls reflect the updated registry state.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dtype
|
type
|
The type to unregister and remove from the registry. |
required |
Returns:
| Type | Description |
|---|---|
T
|
The value that was associated with the type before removal. |
Raises:
| Type | Description |
|---|---|
KeyError
|
If the type is not registered. The error message includes the type that was not found. |
Example
>>> from coola.registry import TypeRegistry
>>> registry = TypeRegistry[str]({int: "I am an integer"})
>>> registry.has(int)
True
>>> value = registry.unregister(int)
>>> value
'I am an integer'
>>> registry.has(int)
False