1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
|
from abc import ABCMeta
from inspect import isabstract as is_abstract
from .registry import BaseMutableRegistry
__all__ = [
'AutoRegister',
]
def AutoRegister(registry: BaseMutableRegistry,
base_type: type = ABCMeta) -> type:
"""
Creates a metaclass that automatically registers all non-abstract
subclasses in the specified registry.
IMPORTANT: Python defines abstract as "having at least one unimplemented
abstract method"; specifying :py:class:`ABCMeta` as the metaclass is not
enough!
Example::
commands = ClassRegistry(attr_name='command_name')
# Specify ``AutoRegister`` as the metaclass:
class BaseCommand(metaclass=AutoRegister(commands)):
@abstractmethod
def print(self):
raise NotImplementedError()
class PrintCommand(BaseCommand):
command_name = 'print'
def print(self):
...
print(list(commands.items())) # [('print', PrintCommand)]
:param registry:
The registry that new classes will be added to.
Note: the registry's ``attr_name`` attribute must be set!
:param base_type:
The base type of the metaclass returned by this function.
99.99% of the time, this should be :py:class:`ABCMeta`.
"""
if not registry.attr_name:
raise ValueError(
'Missing `attr_name` in {registry}.'.format(registry=registry),
)
class _metaclass(base_type):
def __init__(self, what, bases=None, attrs=None):
super(_metaclass, self).__init__(what, bases, attrs)
if not is_abstract(self):
registry.register(self)
return _metaclass
|