Skip to content

Instantly share code, notes, and snippets.

@exhuma
Created September 17, 2018 12:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save exhuma/0bdd1a9640d375173b4b1eb2c1aa5bf8 to your computer and use it in GitHub Desktop.
Save exhuma/0bdd1a9640d375173b4b1eb2c1aa5bf8 to your computer and use it in GitHub Desktop.
Python ABC to register subclasses with a name for easy retrieval.
class NamedRegistry(ABCMeta):
'''
A metaclass which automatically registers each subclass by name when it is
defined.
Each subclass must define the class-attribute ``NAME``
The classes can be retrieved using ``NamedRegistry.get_query``.
'''
GLOBAL_REGISTRY = {} # type: Dict[type, Dict[str, type]]
__DISCRIMINATOR = None
def __new__(mcs, name, bases, namespace, **kwargs):
cls = super().__new__(mcs, name, bases, namespace, **kwargs)
# Without a discriminator it would not be possible to have two separete
# subclass trees using the registry
if cls.__DISCRIMINATOR is None:
cls.__DISCRIMINATOR = cls
# We don't want the top-level class to show up in the registry. We can
# use the discriminator we used earlier for that.
if cls.mro()[0] == cls.__DISCRIMINATOR:
return cls
registry = NamedRegistry.GLOBAL_REGISTRY.setdefault(cls.__DISCRIMINATOR, {})
if cls.NAME in registry:
raise KeyError(
'The name %r for class %r has already been registered. '
'Please use another name!' % (cls.NAME, cls))
registry[cls.NAME] = cls
return cls
def get_query(cls, name: str):
'''
Retrieves a class by defined name.
'''
return NamedRegistry.GLOBAL_REGISTRY[cls][name]
def all(cls):
'''
Return all registered classes under *cls*
'''
return NamedRegistry.GLOBAL_REGISTRY[cls].values()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment