Skip to content

Instantly share code, notes, and snippets.

@RutledgePaulV
Last active August 29, 2015 14:07
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 RutledgePaulV/6fb72a4b700e5b3ff3fd to your computer and use it in GitHub Desktop.
Save RutledgePaulV/6fb72a4b700e5b3ff3fd to your computer and use it in GitHub Desktop.
Builds on a meta-class plugin architecture in a way that automatically registers plugins across django apps and allows for easy use in the form of a decorator.
from django.utils.module_loading import autodiscover_modules
# creating a decorator that effectively sets the 'Plug' class
# as the meta class and passes along the key and module
def Plugin(key, module):
class Plug(type):
# here because we need to allow kwargs but not pass them to the type constructor
def __new__(cls, name, bases, namespace, **kwargs):
return super().__new__(cls, name, bases, namespace)
# overriding the init method of the meta class
def __init__(cls, what, bases, namespace, **kwargs):
super().__init__(what, bases, namespace)
# check if the registry exists yet. if not, we must be defining the base class.
if not hasattr(cls, 'registry'):
# we want to create a dict on the base class for the plugins to live in.
cls._registry = {}
# during the first definition the registry must not have been populated yet.
cls._registry_populated = False
# we want to store the key by which to hash the plugins as they are registered
cls._registry_field_key = kwargs['key']
# gets the name of the module to look for in each of the apps
cls._modules_to_register = kwargs['module']
# defining a property function that will register all of our plugins in the
# various project apps the first time the registry field is accessed.
def get_registry(accessor):
if not cls._registry_populated:
autodiscover_modules(cls._modules_to_register)
cls._registry_populated = True
return cls._registry
# setting the registry as an accessible property
cls.registry = property(get_registry)
else:
# This must be a plugin implementation, which should be registered.
# Simply appending it to the list is all that's needed to keep
# track of it later.
cls._registry[getattr(cls, cls._registry_field_key)] = cls
return lambda cls: Plug(cls.__name__, cls.__bases__, dict(cls.__dict__), key=key, module=module)
# provide the definition somewhere that gets loaded, usually in
# the module that requires access to the instances. The first argument
# is the key that it should look for on the classes that inherit from
# the base in order to hash them in the registry. The second argument
# is the name of the module to look for in each of your django apps.
@Plugin('identifier', 'plugs')
class MyPluginBase(object):
identifier = ''
def perform_operation(self):
raise NotImplementedError
# inside some django app: PROJECT_DIR/APP_1/plugs.py
class Plugin1(MyPluginBase):
identifier = 'MY_FIRST_PLUGIN'
def perform_operation(self):
return 2 * 2
# inside some django app: PROJECT_DIR/APP_2/plugs.py
class Plugin2(MyPluginBase):
identifier = 'MY_SECOND_PLUGIN'
def perform_operation(self):
return 3 * 3
# somewhere where you need access to all the plugins
def dispatch_to_plugin(command):
handler = MyPluginBase.registry[command]
instance = handler()
return instance.perform_operation()
print(dispatch_to_plugin('MY_FIRST_PLUGIN')) # prints 4
print(dispatch_to_plugin('MY_SECOND_PLUGIN')) # prints 9
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment