From c50d6f6dee9a95266c795cd1e7d0bf82863248d8 Mon Sep 17 00:00:00 2001 From: Hans Ulrich Niedermann Date: Thu, 26 Jun 2008 00:52:07 +0200 Subject: Add newplugins.py --- src/nbblib/newplugin-test.py | 19 ++++ src/nbblib/newplugins.py | 242 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 261 insertions(+) create mode 100644 src/nbblib/newplugin-test.py create mode 100644 src/nbblib/newplugins.py (limited to 'src') diff --git a/src/nbblib/newplugin-test.py b/src/nbblib/newplugin-test.py new file mode 100644 index 0000000..8082aec --- /dev/null +++ b/src/nbblib/newplugin-test.py @@ -0,0 +1,19 @@ +#!/usr/bin/python +"""Test newplugins module""" + +import logging +import sys + +logging.basicConfig(level = logging.DEBUG, + format = "%(levelname)s: %(message)s", + stream = sys.stderr) +if True: + logging.debug("xxx debug") + logging.info("xxx info") + logging.warning("xxx warn") + logging.error("xxx error") + + +import newplugins +newplugins.selftest() + diff --git a/src/nbblib/newplugins.py b/src/nbblib/newplugins.py new file mode 100644 index 0000000..06905fe --- /dev/null +++ b/src/nbblib/newplugins.py @@ -0,0 +1,242 @@ +"""Usage of newplugins module: + +np = __import__(newplugins) + +class NonDetectPluginType(object): + __metaclass__ = np.GenericPluginMeta + +class PluginA(NonDetectPluginType): + name = "PA" +class PluginB(NonDetectPluginType): + name = "PB" + +class MyPluginType(np.GenericDetectPlugin): + __metaclass__ = np.GenericPluginMeta + [no_match_exception = ...] + [ambigous_match_exception = ...] + [ + @classmethod + def validate(cls, obj, *args, **kwargs): + return True # or False, depending on params + ] + +class MyPluginA(MyPluginType): + name = "MA" + def __init__(self, context): + super(MyPluginA, self).__init__(self, context) + if not some_detection_successful: + raise np.PluginNoMatch() +class MyPluginB(MyPluginType): + name = "MB" + def __init__(self, context): + super(MyPluginB, self).__init__(self, context) + if not other_detection_successful: + raise np.PluginNoMatch() + +""" + + +import sys +import logging + + +class NoPluginsRegistered(Exception): + def __init__(self, cls): + super(NoPluginsRegistered, self).__init__() + self.cls = cls + def __str__(self): + return "No plugins of type %s registered" % (self.cls.__name__) + + +class DuplicatePluginName(Exception): + pass + + +class PluginNoMatch(Exception): + def __init__(self, *args, **kwargs): + super(PluginNoMatch, self).__init__() + self.args = args + self.kwargs = kwargs + def __str__(self): + return "%s(%s,%s)" % (self.__class__.__name__, + self.args, self.kwargs) + + +class AmbigousPluginDetection(Exception): + def __init__(self, matches, cls, context, *args, **kwargs): + self.matches = matches + self.cls = cls + self.context = context + self.args = args + self.kwargs = kwargs + def __str__(self): + return "%s(%s.%s, %s, %s, %s, %s)" % (self.__class__.__name__, + self.cls.__module__, + self.cls.__name__, + self.matches, + self.context, + self.args, + self.kwargs) + + +class PluginDict(dict): + """Helper for GenericPluginMeta class + + Behaves basically like a standard dict, but will raise an exception + when asked to update an existing value. + """ + + # This is the important difference between PluginDict and dict. + def __setitem__(self, key, value): + if self.has_key(key): + raise DuplicatePluginName() + else: + super(PluginDict, self).__setitem__(key, value) + + +######################################################################## +# Generic plugin system +######################################################################## +# Plugin architecture (metaclass tricks) by Marty Alchin from +# http://gulopine.gamemusic.org/2008/jan/10/simple-plugin-framework/ +# Slightly modified go store plugins as dict. +######################################################################## + + +class GenericPluginMeta(type): + def __init__(cls, name, bases, attrs): + logging.debug("%s %s %s %s", cls, name, bases, attrs) + if not hasattr(cls, 'plugins'): + # This branch only executes when processing the mount point itself. + # So, since this is a new plugin type, not an implementation, this + # class shouldn't be registered as a plugin. Instead, it sets up a + # list where plugins can be registered later. + cls.plugins = PluginDict() + elif cls.name is not None: + # 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. + logging.debug("%s %s", cls, cls.plugins) + cls.plugins[cls.name] = cls + else: + # This must be an abstract subclass of plugins. + pass + + +class GenericDetectPlugin(object): + no_match_exception = PluginNoMatch + ambigous_match_exception = AmbigousPluginDetection + + def __init__(self, context): + super(GenericDetectPlugin, self).__init__() + self.context = context + + @classmethod + def validate(cls, obj, *args, **kwargs): + logging.debug("GDval") + return True + + @classmethod + def detect(cls, context, *args, **kwargs): + """Detect stuff""" + logging.debug("DETECT %s", cls) + if len(cls.plugins) < 1: + raise NoPluginsRegistered(cls) + matches = PluginDict() + for key, klass in cls.plugins.iteritems(): + try: + t = klass(context, *args, **kwargs) + logging.debug("KLASS %s unvalidated, %s", klass, + klass.validate) + if klass.validate(t, *args, **kwargs): + logging.debug("KLASS %s validated", klass) + matches[key] = t + except PluginNoMatch, e: + pass + if len(matches) > 1: + raise cls.ambigous_match_exception(matches, + cls, context, + *args, **kwargs) + elif len(matches) < 1: + raise cls.no_match_exception(*args, **kwargs) + return matches[matches.keys()[0]] + + +def selftest(): + + class PluginNoMatchA(PluginNoMatch): + pass + class AmbigousPluginDetectionA(AmbigousPluginDetection): + pass + + + class TestDetectPluginA(GenericDetectPlugin): + __metaclass__ = GenericPluginMeta + no_match_exception = PluginNoMatchA + ambigous_match_exception = AmbigousPluginDetectionA + @classmethod + def validate(cls, obj, *args, **kwargs): + logging.debug("Aval") + return False + + + class TestDetectPluginB(GenericDetectPlugin): + __metaclass__ = GenericPluginMeta + + + class TestDetectPluginC(GenericDetectPlugin): + __metaclass__ = GenericPluginMeta + @classmethod + def validate(cls, obj, *args, **kwargs): + logging.debug("Cval") + return False + + + class TestDetectPluginA1(TestDetectPluginA): + name = "A1" + class TestDetectPluginA2(TestDetectPluginA): + name = "A2" + class TestDetectPluginA3(TestDetectPluginA): + name = "A3" + + class TestDetectPluginB1(TestDetectPluginB): + name = "B1" + class TestDetectPluginB2(TestDetectPluginB): + name = "B2" + class TestDetectPluginB3(TestDetectPluginB): + name = "B3" + + class TestDetectPluginC1(TestDetectPluginC): + name = "C1" + class TestDetectPluginC2(TestDetectPluginC): + name = "C2" + class TestDetectPluginC3(TestDetectPluginC): + name = "C3" + @classmethod + def validate(cls, obj, *args, **kwargs): + logging.debug("C3val") + return True + + ctx = None + + print "GenericPluginMeta", dir(GenericPluginMeta) + print "GenericDetectPlugin", dir(GenericDetectPlugin) + print "TestDetectPluginA", dir(TestDetectPluginA) + print "TestDetectPluginA", dir(TestDetectPluginA) + print "TestDetectPluginB", dir(TestDetectPluginB) + print "TestDetectPluginC", dir(TestDetectPluginC) + + try: + a = TestDetectPluginA.detect(ctx) + except: + logging.error("aaaa", exc_info=True) + + try: + b = TestDetectPluginB.detect(ctx) + except: + logging.error("bbbb", exc_info=True) + + try: + c = TestDetectPluginC.detect(ctx) + except: + logging.error("cccc", exc_info=True) -- cgit