diff --git a/ipalib/base.py b/ipalib/base.py index 09355f41b..b4d20450a 100644 --- a/ipalib/base.py +++ b/ipalib/base.py @@ -21,6 +21,7 @@ Base classes for plug-in architecture and generative API. """ +import re import inspect import exceptions @@ -141,6 +142,53 @@ class Named(object): name = property(__get_name) +class AbstractCommand(object): + def __call__(self): + print 'You called %s()' % self.name + +class Attribute(Named): + __locked = False + __obj = None + + def __init__(self): + m = re.match('^([a-z]+)__([a-z]+)$', self.__class__.__name__) + assert m + self.__obj_name = m.group(1) + self.__attr_name = m.group(2) + + def __get_obj(self): + return self.__obj + obj = property(__get_obj) + + def set_obj(self, obj=None): + if self.__locked: + raise exceptions.TwiceSetError(self.__class__.__name__, 'obj') + self.__locked = True + if obj is None: + return + assert isinstance(obj, Object) + assert obj.name == self.__obj_name + self.__obj = obj + + def __get_obj_name(self): + return self.__obj_name + obj_name = property(__get_obj_name) + + def __get_attr_name(self): + return self.__attr_name + attr_name = property(__get_attr_name) + + +class Method(AbstractCommand, Attribute): + def _get_name(self): + return '%s_%s' % (self.attr_name, self.obj_name) + + +class Property(Attribute): + def _get_name(self): + return self.attr_name + + class WithObj(Named): _obj = None __obj = None @@ -168,8 +216,7 @@ class Command(WithObj): def __call__(self): print 'You called %s()' % self.name -class Property(WithObj): - pass + class Object(Named): __commands = None @@ -187,6 +234,31 @@ class Object(Named): commands = property(__get_commands, __set_commands) + + +class AttributeCollector(object): + def __init__(self): + self.__d = {} + + def __getitem__(self, key): + assert isinstance(key, str) + if key not in self.__d: + self.__d[key] = {} + return self.__d[key] + + def __iter__(self): + for key in self.__d: + yield key + + def add(self, i): + assert isinstance(i, Attribute) + self[i.obj_name][i.attr_name] = i + + def namespaces(self): + for key in self: + yield (key, NameSpace(self[key])) + + class Collector(object): def __init__(self): self.__d = {} diff --git a/ipalib/tests/test_base.py b/ipalib/tests/test_base.py index d06847a26..9d2c1b051 100644 --- a/ipalib/tests/test_base.py +++ b/ipalib/tests/test_base.py @@ -191,6 +191,77 @@ def test_Named(): assert i.name == 'named_class' +def test_Attribute(): + class user__add(base.Attribute): + pass + i = user__add() + assert i.obj_name == 'user' + assert i.attr_name == 'add' + assert read_only(i, 'obj') is None + class user(base.Object): + pass + u = user() + i.set_obj(u) + assert read_only(i, 'obj') is u + raised = False + try: + i.set_obj(u) + except exceptions.TwiceSetError: + raised = True + assert raised + + +def test_Method(): + class user__mod(base.Method): + pass + i = user__mod() + assert isinstance(i, base.Attribute) + assert isinstance(i, base.AbstractCommand) + assert i.obj_name == 'user' + assert i.attr_name == 'mod' + assert i.name == 'mod_user' + + +def test_Property(): + class user__firstname(base.Property): + pass + i = user__firstname() + assert isinstance(i, base.Attribute) + assert i.obj_name == 'user' + assert i.attr_name == 'firstname' + assert i.name == 'firstname' + + +def test_AttributeCollector(): + class user__add(base.Attribute): + pass + class user__mod(base.Attribute): + pass + class group__add(base.Attribute): + pass + u_a = user__add() + u_m = user__mod() + g_a = group__add() + + ac = base.AttributeCollector() + ac.add(u_a) + ac.add(u_m) + ac.add(g_a) + + assert set(ac) == set(['user', 'group']) + + u = ac['user'] + assert set(u) == set(['add', 'mod']) + assert set(u.values()) == set([u_a, u_m]) + + g = ac['group'] + assert g.keys() == ['add'] + assert g.values() == [g_a] + + + + + def test_WithObj(): class some_object(base.Named): pass