base.registry

Simple Dictionary of Objects

With a Kickin’ Useful AutoRegister Metaclass

Many applications need some sort of dictionary that maps string names to objects. This is ours.


Simplest

The simplest possible use is really not much more than a dictionary:

import base

class MyClass:
  pass

base.registry.Register(MyClass, 'some_name')

assert(base.registry.Get('some_name') == MyClass)

You can register instances if you’d prefer:

base.registry.Register(MyClass(), 'some_name')

assert(isinstance(base.registry.Get('some_name'), MyClass))

Default Object Names

If you don’t provide a name for the registered object, we’ll use base.utils.ObjectName() instead. This is a two-part name with a single dot:

  • module_name.class_name

If all your code is in one file, your module_name is “__main__”, which is not interesting. But if your code is split across separate modules, the module’s name is probably what you expect.

Imagine a file mymodule.py that contains:

import base

class MyClass:
  pass

base.registry.Register(MyClass)

While the file mymain.py contains:

import base
import mymodule

print(base.registry.Get('mymodule.MyClass'))

Running python3 mymain.py would print:

<class 'mymodule.MyClass'>

The name is always two-part. If objects are defined in sub-modules, only the top-level module’s name is used. Said another way, they Python class mymodule.mysubmodule.mysubsubmodule.MyClass would be known to us only as: “mymodule.MyClass


Metaclass AutoRegister

The true joy of our registry is our AutoRegister metaclass.

class MyBaseClass(metaclass=base.registry.AutoRegister):
  pass

class MySubClass(MyBaseClass):
  pass

Here, both MyBaseClass and MySubClass are both registered automatically.

AutoRegister Options

The AutoRegister metaclass takes a few optional arguments:

  • skipinteger – default=0 – number of levels NOT to register
  • instancesboolean – default=False – register an instance of each class rather than the class itself
  • regnamestring – default=None – name of a property on your class from which to get the registry name

skip

The skip argument causes some number of higher-level classes to not be registered, while deeper sub-classes still get registered.

For example:

class MyBaseClass(metaclass=base.registry.AutoRegister, skip=1):
  pass

class MySubClass(MyBaseClass):
  pass

Here, MyBaseClass is not registered, while MySubClass does get registered.

instances

The instances, if set True, then an instance of each of your classes gets registered instead of the class itself.

regname

You can specify a property on your class that gets used to find a registry name.

For example:

class MyClass(metaclass=base.registry.AutoRegister, regname='slug'):
  @base.utils.anyproperty
  def slug(self):
    return base.utils.Slugify(base.utils.ClassName(self))

Here, the registered name will be “myclass” (the slugified class name) rather than “__main__.MyClass” (the full object name).

Note: this example uses our @base.utils.anyproperty decorator, which is like a @property that can be called on the class itself as well as instances of that class.


Back to “OctoBase”