Registries

class todd.registries.BuildPreHookMixin[source]

Bases: object

abstractmethod classmethod build_pre_hook(config, registry, item)[source]
Parameters:
Return type:

Config

class todd.registries.Item[source]

Bases: Protocol

__init__(*args, **kwargs)
class todd.registries.PartialRegistry[source]

Bases: object

data = {}
class todd.registries.PartialRegistryMeta[source]

Bases: RegistryMeta

class todd.registries.Registry[source]

Bases: object

Base registry.

To create custom registry, inherit from the Registry class:

>>> class CatRegistry(Registry): pass
data = {}
class todd.registries.RegistryMeta[source]

Bases: UserDict[str, Item], NonInstantiableMeta

Meta class for registries.

Underneath, registries are simply dictionaries:

>>> class Cat(metaclass=RegistryMeta): pass
>>> class BritishShorthair: pass
>>> Cat['british shorthair'] = BritishShorthair
>>> Cat['british shorthair']
<class '...BritishShorthair'>

In this example, Cat is a registry and the “british shorthair” is a category in the registry. BritishShorthair is an object or class that is associated to the “british shorthair” category.

For convenience, users can also access registries via higher level APIs, such as ‘register_’ and ‘build’. These provide easier interfaces to register and retrieve instances:

>>> class Persian: pass
>>> Cat.register_('persian')(Persian)
<class '...Persian'>
>>> Cat.build(Config(type='persian'))
<...Persian object at ...>

Registries can be subclassed as well to create specializations or child registries:

>>> class HairlessCat(Cat): pass
>>> Cat.child('HairlessCat')
<HairlessCat >

In the example above, HairlessCat can be seen as a subcategory or specialization of Cat. This allows to organize instances into a hierarchically structured registry.

__init__(*args, **kwargs)[source]

Initialize.

Return type:

None

build(config, **kwargs)[source]

Call the registered object to construct a new instance.

Parameters:
  • config (Config) – build parameters.

  • kwargs – default configuration.

Returns:

The built instance.

Return type:

Any

The type entry of config specifies the name of the registered object to be built. The other entries of config will be passed to the object’s call method.

>>> class Cat(metaclass=RegistryMeta): pass
>>> @Cat.register_()
... def tabby(name: str) -> str:
...     return f'Tabby {name}'
>>> Cat.build(Config(type='tabby', name='Garfield'))
'Tabby Garfield'

Keyword arguments are the default configuration:

>>> Cat.build(
...     Config(type='tabby'),
...     name='Garfield',
... )
'Tabby Garfield'

Override _build() for customization:

>>> class DomesticCat(Cat):
...     @classmethod
...     def _build(cls, item: Item, config: Config):
...         return item, config
>>> @DomesticCat.register_()
... class Maine: pass
>>> DomesticCat.build(Config(type='Maine', name='maine'), age=1.2)
(<class '...Maine'>, {'age': 1.2, 'name': 'maine'})

If the object has a property named build_pre_hook, the config is converted before construction:

>>> @Cat.register_()
... class Persian:
...     def __init__(self, friend: str) -> None:
...         self.friend = friend
...     @classmethod
...     def build_pre_hook(
...         cls,
...         config: Config,
...         registry: RegistryMeta,
...         item: Item,
...     ) -> Config:
...         config.friend = config.friend.type
...         return config
>>> persian = Cat.build(
...     Config(type='Persian'),
...     friend=dict(type='Siamese'),
... )
>>> persian.friend
'Siamese'
build_or_return(config, predicate=None, **kwargs)[source]
Parameters:
Return type:

Any

child(key)[source]

Retrieve a direct or indirect derived child registry.

Given a dot-separated string of subclass names, this method searches for the specified child registry within its inheritance tree and returns the matching child class.

Parameters:

key (str) – A string of dot-separated subclass names.

Raises:

ValueError – If no subclass or more than one subclass with the specified name exists.

Returns:

The specified child registry.

Return type:

RegistryMeta

parse(key)[source]

Parse key.

Returns:

The child registry and the corresponding type.

Parameters:

key (str)

Return type:

tuple[RegistryMeta, Item]

register_(*args, force=False, build_pre_hook=None)[source]

Register classes or functions to the registry.

Parameters:
Returns:

Wrapper function.

Return type:

Callable[[T], T]

The decorator can be applied to both classes and functions:

>>> class Cat(metaclass=RegistryMeta): pass
>>> @Cat.register_()
... class Munchkin: pass
>>> @Cat.register_()
... def munchkin() -> str:
...     return 'munchkin'

If no arguments are given, the name of the object being registered is used as the key:

>>> Cat['Munchkin']
<class '...Munchkin'>
>>> Cat['munchkin']
<function munchkin at ...>

It is possible to register an object with multiple names:

>>> @Cat.register_('British Longhair', 'british longhair')
... class BritishLonghair: pass
>>> 'British Longhair' in Cat
True
>>> 'british longhair' in Cat
True

It also allows one to specify child registries as part of the key during registration:

>>> class HairlessCat(Cat): pass
>>> @Cat.register_('HairlessCat.CanadianHairless')
... def canadian_hairless() -> str:
...     return 'canadian hairless'
>>> HairlessCat
<HairlessCat CanadianHairless=<function canadian_hairless at ...>>

If ‘forced’ is True and an item of the same name exists, the new item will replace the old one in the registry:

>>> class AnotherMunchkin: pass
>>> Cat.register_('Munchkin')(AnotherMunchkin)
Traceback (most recent call last):
    ...
KeyError: 'Munchkin'
>>> Cat.register_('Munchkin', force=True)(AnotherMunchkin)
<class '...AnotherMunchkin'>
>>> Cat['Munchkin']
<class '...AnotherMunchkin'>

build_pre_hook can be bind to objects during registration:

>>> build_pre_hook = lambda c, r, i: c
>>> @Cat.register_(build_pre_hook=build_pre_hook)
... class Maine: pass
>>> Maine.build_pre_hook is build_pre_hook
True