We're back after a server migration that caused effbot.org to fall over a bit harder than expected. Expect some glitches.

Using Metaclasses to Create Self-Registering Plugins

Fredrik Lundh | July 2008 | Based on a comp.lang.python post

Background: This was posted to a thread about how to best register plugin classes. The original approach was to scan all plugin modules for Plugin subclasses; this note describes an alternative solution that uses a self-registering base class. I have no opinion on what’s best here; this is just another way to do it.

A metaclass is a something that’s responsible for creating a class, much like an ordinary class is responsible for creating an object. When Python executes the following statement,

class Spam:
    attrib = 1
    def func(self):
        pass
    # <-- end of class statement

it will create a new scope for the class content, execute the class body, and then, when it reaches the end, call the “metaclass” to create the actual class object. The metaclass is given the requested name (“Spam” in this case), any base classes, and a dictionary containing everything from the class scope (“attrib” and “func”, in this case). The thing that’s returned is assigned to the “Spam” variable.

The default metaclass (the “type” built-in, actually) just creates an ordinary class object, but if you replace that with your own metaclass, you can completely override that behaviour, or just extend it (e.g. by registering the subclasses in a common registry). Like, say, this:

registry = [] # list of subclasses

class Plugin(object):
    class __metaclass__(type):
        def __init__(cls, name, bases, dict):
            type.__init__(name, bases, dict)
            registry.append((name, cls))

# in your plugin modules
class SpamPlugin(Plugin):
    pass

class BaconPlugin(Plugin):
    pass

# in your plugin loader
# import all plugin modules

# loop over registered plugins
for name, cls in registry:
    if cls is not Plugin:
    print name, cls

Here, the presence of an inner __metaclass__ class (which is a subclass of “type”) causes Python’s class machinery to use that class instead of “type” when creating class objects for Plugin or any subclass thereof. The extra code in the __init__ method just adds all plugin classes to a list.

For more on this, see e.g.

http://www.python.org/download/releases/2.2/descrintro/#metaclasses