summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ipalib/plugable.py84
-rw-r--r--ipalib/tests/test_plugable.py47
2 files changed, 131 insertions, 0 deletions
diff --git a/ipalib/plugable.py b/ipalib/plugable.py
index 0de31d82..054b12db 100644
--- a/ipalib/plugable.py
+++ b/ipalib/plugable.py
@@ -21,10 +21,94 @@
Utility classes for registering plugins, base classe for writing plugins.
"""
+
import inspect
import errors
+def to_cli(name):
+ assert isinstance(name, basestring)
+ return name.replace('__', '.').replace('_', '-')
+
+def from_cli(cli_name):
+ assert isinstance(cli_name, basestring)
+ return cli_name.replace('-', '_').replace('.', '__')
+
+
+class Plugin(object):
+ """
+ Base class for all plugins.
+ """
+
+ def __get_name(self):
+ """
+ Returns the class name of this instance.
+ """
+ return self.__class__.__name__
+ name = property(__get_name)
+
+ def __repr__(self):
+ """
+ Returns a valid Python expression that could create this plugin
+ instance given the appropriate environment.
+ """
+ return '%s.%s()' % (
+ self.__class__.__module__,
+ self.__class__.__name__
+ )
+
+
+class Proxy(object):
+ """
+ Used to only export certain attributes into the dynamic API.
+
+ Subclasses must list names of attributes to be proxied in the __slots__
+ class attribute.
+ """
+
+ __slots__ = (
+ '__obj',
+ 'name',
+ 'cli_name',
+ )
+
+ def __init__(self, obj, proxy_name=None):
+ """
+ Proxy attributes on `obj`.
+ """
+ if proxy_name is None:
+ proxy_name = obj.name
+ assert isinstance(proxy_name, str)
+ object.__setattr__(self, '_Proxy__obj', obj)
+ object.__setattr__(self, 'name', proxy_name)
+ object.__setattr__(self, 'cli_name', to_cli(proxy_name))
+ for name in self.__slots__:
+ object.__setattr__(self, name, getattr(obj, name))
+
+ def __setattr__(self, name, value):
+ """
+ Proxy instances are read-only. This raises an AttributeError
+ anytime an attempt is made to set an attribute.
+ """
+ raise AttributeError('cannot set %s.%s' %
+ (self.__class__.__name__, name)
+ )
+
+ def __delattr__(self, name):
+ """
+ Proxy instances are read-only. This raises an AttributeError
+ anytime an attempt is made to delete an attribute.
+ """
+ raise AttributeError('cannot del %s.%s' %
+ (self.__class__.__name__, name)
+ )
+
+ def __repr__(self):
+ return '%s(%r)' % (self.__class__.__name__, self.__obj)
+
+ def __str__(self):
+ return self.cli_name
+
class Registrar(object):
def __init__(self, *allowed):
diff --git a/ipalib/tests/test_plugable.py b/ipalib/tests/test_plugable.py
index f0bdeb4a..0421e72b 100644
--- a/ipalib/tests/test_plugable.py
+++ b/ipalib/tests/test_plugable.py
@@ -24,6 +24,53 @@ Unit tests for `ipalib.plugable` module.
from ipalib import plugable, errors
+def test_to_cli():
+ f = plugable.to_cli
+ assert f('initialize') == 'initialize'
+ assert f('find_everything') == 'find-everything'
+ assert f('user__add') == 'user.add'
+ assert f('meta_service__do_something') == 'meta-service.do-something'
+
+
+def test_from_cli():
+ f = plugable.from_cli
+ assert f('initialize') == 'initialize'
+ assert f('find-everything') == 'find_everything'
+ assert f('user.add') == 'user__add'
+ assert f('meta-service.do-something') == 'meta_service__do_something'
+
+
+def test_Plugin():
+ p = plugable.Plugin()
+ assert p.name == 'Plugin'
+ assert repr(p) == '%s.Plugin()' % plugable.__name__
+
+ class some_plugin(plugable.Plugin):
+ pass
+ p = some_plugin()
+ assert p.name == 'some_plugin'
+ assert repr(p) == '%s.some_plugin()' % __name__
+
+
+def test_Proxy():
+ class CommandProxy(plugable.Proxy):
+ __slots__ = (
+ 'get_label',
+ '__call__',
+ )
+
+ class Command(plugable.Plugin):
+ def get_label(self):
+ return 'Add User'
+ def __call__(self, *argv, **kw):
+ return (argv, kw)
+
+ i = Command()
+ p = CommandProxy(i, 'hello')
+ assert '__dict__' not in dir(p)
+ #assert repr(p) == 'CommandProxy(%s.Command())' % __name__
+
+
def test_Registrar():
class Base1(object):
pass