Frequently Asked Questions

Is this magic?

No, it’s metaclasses.

How is method checking handled differently from abc?

Pluginlib checks methods when classes are declared, not when they are initialized.

How can I check if a class is a plugin?

It’s usually best to check inheritance against a specific parent.

issubclass(ChildPlugin, ParentPlugin)

For a more general case, use the Plugin class.

issubclass(ChildPlugin, pluginlib.Plugin)

Why does creating a parent by subclassing another parent raise a warning?

A warning is raised when subclassing a plugin parent if it does not meet the conditions of being a child plugin. Generally, it’s best to avoid this situation completely by having both plugin parent classes inherit from a common, non-plugin, class. If it’s unavoidable, set the _skipload_ class attribute to False to avoid evaluating the class as a child plugin.

Why does calling super() with no arguments in a parent class raise a TypeError?

This is a side-effect of metaclasses. Take the following example:

>>> import pluginlib
>>>
>>> class Plugin():
...     def __init__(self):
...         pass
...
>>> @pluginlib.Parent('collector')
... class Collector(Plugin):
...     def __init__(self):
...         super().__init__()
...
>>> class Child(Collector):
...     def __init__(self):
...         super().__init__()
...
>>> Child()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in __init__
  File "<stdin>", line 4, in __init__
TypeError: super(type, obj): obj must be an instance or subtype of type

We get this error when super() is called with no arguments in Collector because the type of Collector is PluginType, not Plugin.

>>> type(Collector)
<class 'pluginlib._parent.PluginType'>

super() will use this type for the first argument if one is not supplied. To work around this, we simply have to supply arguments to super(). As you can see, this is only required in parent classes.

>>> import pluginlib
>>>
>>> class Plugin():
...     def __init__(self):
...         pass
...
>>> @pluginlib.Parent('collector')
... class Collector(Plugin):
...     def __init__(self):
...         super(Collector, self).__init__()
...
>>> class Child(Collector):
...     def __init__(self):
...         super().__init__()
...
>>> Child()
<__main__.Child object at 0x7f5786e8d490>

Why am I getting TypeError: metaclass conflict?

This happens when a parent class inherits from a class derived from a metaclass. This is not supported. Here is an example that illustrates the behavior.

import pluginlib

class Meta(type):
    pass

class ClassFromMeta(metaclass=Meta):
    pass

@pluginlib.Parent('widget')
class Widget(ClassFromMeta):
    pass

This will raise the following error.

TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases

An alternative is to make an instance of the class an attribute of the parent class.

@pluginlib.Parent('widget')
class Widget():
    def __init__(self):
        self._widget = ClassFromMeta()

If desired, __getattr__() can be used to provide pass-through access.

@pluginlib.Parent('widget')
class Widget():
    def __init__(self):
        self._widget = ClassFromMeta()

def __getattr__(self, attr):
    return getattr(self._widget, attr)