Last active
August 29, 2015 14:07
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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