Skip to content

Registry

The Registry can be used to register a class or a function.

Register an object

This section explains the basics on how to register an object (class or function). There are two approaches to register an object to the registry:

  • by using the decorator register
  • by using the function register_object

Decorator register

You can use the decorator register to register an object to the registry. For example if you want to register the class ClassToRegister, you can write:

# file: my_package/my_module.py
from objectory import Registry

registry = Registry()


@registry.register()
class ClassToRegister:
    pass


print(registry.registered_names())

Output:

{'my_package.my_module.ClassToRegister'}

The class ClassToRegister is registered with the name my_package.my_module.ClassToRegister. You can see the registered names by using the function registered_names. The function registered_names returns the set of registered names.

You can also use the decorator register to register a function:

# file: my_package/my_module.py
from objectory import Registry

registry = Registry()


@registry.register()
def function_to_register(*args, **kwargs):
    pass


print(registry.registered_names())

Output:

{'my_package.my_module.function_to_register'}

The function function_to_register is registered with the name my_package.my_module.function_to_register.

Function register_object

The decorator approach works well when you can change the source code, but it will not work if you cannot modify the source code. You cannot use the decorator register to register an object in a third party library. To register a class or function from a third party library, you can use the function register_object. For example if you want to register the class ClassToRegister of the library third_party, you can write the following lines:

# file: my_package/my_module.py
from objectory import Registry
from third_party import ClassToRegister

registry = Registry()

registry.register_object(ClassToRegister)

Similarly to the decorator register, you can use the function register_object to register a function:

# file: my_package/my_module.py
from objectory import Registry
from third_party import function_to_register

registry = Registry()

registry.register_object(function_to_register)

⚠ It is not possible to register a lambda function. Please use a regular python function instead. The registry will raise the exception IncorrectObjectFactoryError if you try to register a lambda function.

Advanced object registration

This section presents some advanced features to register an object.

Sub-registry

Previously we showed how to register an object to the registry, but all the objects will be registered to the same registry which can be messy in some cases. Instead, you can decide to use a hierarchical structure and to use sub-registries. The registry will have a tree structure. registry is the main registry, and you can add some sub-registries. Each sub-registry works like the main registry. In fact, the main registry and the sub-registries are implemented with the same class (Registry). It is not a hard coded rule, but it is recommended to use one sub-registry for each "family" of objects to group the similar objects.

If you want to register the class in the sub-registry other, you can write:

# file: my_package/my_module.py
from objectory import Registry

registry = Registry()


@registry.other.register()
class ClassToRegister:
    pass


print(registry.registered_names())
print(registry.other.registered_names())

Output:

{'other'}
{'my_package.my_module.ClassToRegister'}

You can check the registered objects in registry and registry.other. The class ClassToRegister is registered into registry.other and not registry. Note that the sub-registry other is registered to the main registry with the name other. It means you cannot use the name other to register an object. Each sub-registry is considered as a registered object in the parent registry. The sub-registry other will be created if it does not exist, so you do not have to manually register the new sub-registry. It is possible to have a nested structure where you add a sub-registry to a sub-registry:

from objectory import Registry

registry = Registry()


@registry.animal.cat.register()
class MyCat:
    pass


@registry.animal.bear.register()
class MyBear:
    pass

Register all the child classes

In some cases, you may want to register a class and all its child classes. Instead of doing manually, you can use the function register_child_classes. This function will automatically register the given class and all its child classes. It will find all the child classes and will add them to the registry. It finds the child classes recursively, so it will also find the child classes of the child classes.

Let's imagine you have the following classes and you want to register them.

# file: my_package/my_module.py
from objectory import Registry


class Foo:
    pass


class Bar(Foo):
    pass


class Baz(Foo):
    pass


class Bing(Bar):
    pass


registry = Registry()

# If you want to register Foo and all its child classes.
registry.register_child_classes(Foo)
print(registry.registered_names())

# If you want to register Bar and all its child classes.
registry.bar.register_child_classes(Bar)
print(registry.bar.registered_names())

Output:

{'my_package.my_module.Foo', 'my_package.my_module.Baz',
 'my_package.my_module.Bing', 'my_package.my_module.Bar'}
{'my_package.my_module.Bing', 'my_package.my_module.Bar'}

By default, the function register_child_classes ignores all the abstract classes because you cannot instantiate them. If you want to also register the abstract classes, you can use the argument ignore_abstract_class=False. The following example shows how to register all the torch.nn.Module including the abstract classes.

import torch
from objectory import Registry

registry = Registry()
registry.register_child_classes(torch.nn.Module, ignore_abstract_class=False)

Customized name

By default, the registry uses the full name of the object to identify the object in the registry. It is possible to customize the name used to register an object. For example if you want to register the class ClassToRegister with the name my_class, you can write the following code:

# file: my_package/my_module.py
from objectory import Registry

registry = Registry()


@registry.register("my_class")
class ClassToRegister:
    pass


registry.register_object(ClassToRegister, "my_class")
# or
registry.register_object(ClassToRegister, name="my_class")

One of the advantage of using customizable names is that you can define shorter name: my_class is shorter than my_package.my_module.ClassToRegister. But using customizable names is not a free lunch, and it has a cost: it will be your responsibility to manage the names to avoid the conflicts. If two objects are registered with the same name, only the last object will be registered. For this reason, it is recommended to use the full name (default) because the full name is unique.

Please think carefully about the pros and cons of this feature before to use it. In most of the cases you probably do not need it. Keep in mind that with power comes responsibility.

Factory

The reserved keywords are:

  • _target_: the class/function to instantiate
  • _init_: the constructor to use to instantiate a class

Remove objects

In the previous sections, we explain how to add objects to the registry. In this section, we will explain how to remove them.

Remove all the objects

If you want to remove all the objects in a registry, you can use the function clear:

from objectory import Registry

registry = Registry()


class ClassToRegister:
    pass


registry.register_object(ClassToRegister)
print(registry.registered_names())

registry.clear()
print(registry.registered_names())

Output:

set()

This function remove all the registered objects including the sub-registries. For example, if the registry has a sub-registry other, this line of code will also clear the sub-registry other. You can use this function on a sub-registry to clear only this sub-registry. For example, you can use the following command to remove all the classes and functions registered in the sub-registry other:

from objectory import Registry

registry = Registry()

registry.other.clear()
print(registry.other.registered_names())

Output:

set()

Note that by default, the function clear removes the sub-registries from the current registry but it does not clear the sub-registries.

# file: my_package/my_module.py
from objectory import Registry

registry = Registry()


class ClassToRegister:
    pass


registry.other.register_object(ClassToRegister)
registry_other = registry.other

registry.clear()
print(registry.registered_names())
print(registry_other.registered_names())

Output:

set()
{'my_package.my_module.ClassToRegister'}

To also clear all the sub-registries, you can set nested=True and the sub-registry other is also cleared.

# file: my_package/my_module.py
from objectory import Registry

registry = Registry()


class ClassToRegister:
    pass


registry.other.register_object(ClassToRegister)
registry_other = registry.other

registry.clear(nested=True)
print(registry.registered_names())
print(registry_other.registered_names())

Output:

set()
set()

Remove a specific object

The function clear is useful to remove all the objects in the registry, but it does not work if you want to only remove a registered object and keep all the other registered objects.

# file: my_package/my_module.py
from objectory import Registry

registry = Registry()


@registry.register()
class ClassToRegister1:
    pass


@registry.register()
class ClassToRegister2:
    pass


print(registry.registered_names())
registry.unregister("my_package.my_module.ClassToRegister1")
print(registry.registered_names())

Output:

{'my_package.my_module.ClassToRegister1', 'my_package.my_module.ClassToRegister2'}
{'my_package.my_module.ClassToRegister2'}

If the object name is unique in the registry, you can only specify the object name:

from objectory import Registry

registry = Registry()

registry.unregister("ClassToRegister")
# is equivalent to
registry.unregister("my_package.my_module.ClassToRegister")

Please read the name resolution documentation to learn more about this feature.

Filters

Child class filter

Usually you want to control the objects that you want to add to the registry. For example, it is usually a good idea to register similar objects in the same registry. A usual scenario is to define a base class, and then register only the child classes of the base class. For example if you want to register torch.nn.Module, you probably want to check that the object is a torch.nn.Module before to register it. Let's imagine you want to register only torch.nn.Module in the sub-registry other.

import torch
from objectory import Registry

registry = Registry()

registry.set_class_filter(torch.nn.Module)
registry.register_object(torch.nn.Linear)
registry.register_object(torch.nn.Conv2d)
...

You can only register classes that inherits torch.nn.Module in the sub-registry other. If you try to register a class which is not a child class, the registry will raise the exception IncorrectObjectFactoryError:

import torch
from objectory import Registry

registry = Registry()


class Foo:
    pass


registry.set_class_filter(torch.nn.Module)
registry.register_object(Foo)

The previous example will raise an exception because Foo is not a child class of torch.nn.Module. To unset the class filter, you can set it to None:

from objectory import Registry

registry = Registry()
registry.set_class_filter(None)

⚠ Note that the class filter is only used when you try to register a new object. It will not filter the existing objects into the registry. You can look at the following example:

# file: my_package/my_module.py
import torch

from objectory import Registry


class Foo:
    pass


registry = Registry()
registry.register_object(Foo)
print(registry.registered_names())

registry.set_class_filter(torch.nn.Module)
registry.register_object(torch.nn.Linear)
print(registry.registered_names())

Output:

{'my_package.my_module.Foo'}
{'my_package.my_module.Foo', 'torch.nn.Linear'}

You can see that the class my_package.my_module.Foo is still registered after the filter is set to torch.nn.Module. It is because this class was added before to set the class filter. If you want to filter all the objects before to add them to the registry, please set the class filter before to register the first object. It will guarantee that all the registered objects are inherited from the filtered class.

Clear all filters

You can remove all the filters of a registry by using the function clear_filters:

from objectory import Registry

registry = Registry()
registry.clear_filters()

By default, the function clear_filters removes the filters on the current registry but not the filters of the sub-registries. To also remove the filters of all the sub-registries, you can set nested=True:

from objectory import Registry

registry = Registry()
registry.clear_filters(nested=True)