diff options
author | Jason Gerard DeRose <jderose@redhat.com> | 2008-07-28 04:34:25 +0000 |
---|---|---|
committer | Jason Gerard DeRose <jderose@redhat.com> | 2008-07-28 04:34:25 +0000 |
commit | bc1675dc3853748064dbf1485bf58bce0e344add (patch) | |
tree | 1403fd2ae76c21b49f3ee16f91d72c79aac2d76e /ipalib/plugable.py | |
parent | 8b64314359950801f1b3220f655261bcee2ead85 (diff) | |
download | freeipa-bc1675dc3853748064dbf1485bf58bce0e344add.tar.gz freeipa-bc1675dc3853748064dbf1485bf58bce0e344add.tar.xz freeipa-bc1675dc3853748064dbf1485bf58bce0e344add.zip |
30: Added plugable module with more generic implementation of Registrar; added corresponding unit tests
Diffstat (limited to 'ipalib/plugable.py')
-rw-r--r-- | ipalib/plugable.py | 95 |
1 files changed, 95 insertions, 0 deletions
diff --git a/ipalib/plugable.py b/ipalib/plugable.py new file mode 100644 index 00000000..ba2241b9 --- /dev/null +++ b/ipalib/plugable.py @@ -0,0 +1,95 @@ +# Authors: +# Jason Gerard DeRose <jderose@redhat.com> +# +# Copyright (C) 2008 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; version 2 only +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +""" +Utility classes for registering plugins, base classe for writing plugins. +""" + +import inspect +import exceptions + + + +class Registrar(object): + def __init__(self, *allowed): + """ + `*allowed` is a list of the base classes plugins can be subclassed + from. + """ + self.__allowed = frozenset(allowed) + self.__d = {} + self.__registered = set() + assert len(self.__allowed) == len(allowed) + for base in self.__allowed: + assert inspect.isclass(base) + assert base.__name__ not in self.__d + self.__d[base.__name__] = {} + + def __findbase(self, cls): + """ + If `cls` is a subclass of a base in self.__allowed, returns that + base; otherwise raises SubclassError. + """ + assert inspect.isclass(cls) + for base in self.__allowed: + if issubclass(cls, base): + return base + raise exceptions.SubclassError(cls, self.__allowed) + + def __call__(self, cls, override=False): + """ + Register the plugin `cls`. + """ + if not inspect.isclass(cls): + raise TypeError('plugin must be a class: %r' % cls) + + # Find the base class or raise SubclassError: + base = self.__findbase(cls) + sub_d = self.__d[base.__name__] + + # Raise DuplicateError if this exact class was already registered: + if cls in self.__registered: + raise exceptions.DuplicateError(cls) + + # Check override: + if cls.__name__ in sub_d: + # Must use override=True to override: + if not override: + raise exceptions.OverrideError(base, cls) + else: + # There was nothing already registered to override: + if override: + raise exceptions.MissingOverrideError(base, cls) + + # The plugin is okay, add to __registered and sub_d: + self.__registered.add(cls) + sub_d[cls.__name__] = cls + + def __getitem__(self, name): + """ + Returns a copy of the namespace dict of the base class named `name`. + """ + return dict(self.__d[name]) + + def __iter__(self): + """ + Iterates through the names of the allowed base classes. + """ + for key in self.__d: + yield key |