From 8b148a195996b9bdc46f1f12b9ba9fb0b6a99714 Mon Sep 17 00:00:00 2001 From: Leandro Lucarella Date: Thu, 11 Oct 2007 18:06:57 -0300 Subject: [PATCH] Add support to "operation tagging" to ListSubHandler and DictSubHandler. "Operation tagging" means that if the contained objects has an _add, _update or _delete attribute, it set them to true when the item is added, updated or deleted respectively (in case that it's deleted, it's not removed from the container, but it's not listed either, i.e. the items are "logically" deleted, not really removed from the container). Now ListSubHandler and DictSubHandler ihnerit from a new class ContainerSubHandler which handles 95% of the code. Because of this, now the "magic" attributes starts with _cont_ instead of _list_ or _dict_. Existing handler were updated, but new ones should take this into account. --- TODO | 6 + pymin/services/dhcp/__init__.py | 4 +- pymin/services/firewall/__init__.py | 4 +- pymin/services/proxy/__init__.py | 8 +- pymin/services/util.py | 216 +++++++++++++++------------- 5 files changed, 133 insertions(+), 105 deletions(-) diff --git a/TODO b/TODO index 40b489a..2aead12 100644 --- a/TODO +++ b/TODO @@ -8,6 +8,12 @@ Ideas / TODO: * Logging. * Paths. +* SubHandlers: + * ComposeDictSubHandler con soporte de dirty/del/add (para ip y DNS). + * Agregar SimpleDictSubHandler? (que no use una clase, que use un dict + de strings directamente, para Proxy Users por ej.). Ídem List. + * Agregar SetSubHandler? (para Proxy Hosts) + * Agregar logging. * Agregar validación con formencode. diff --git a/pymin/services/dhcp/__init__.py b/pymin/services/dhcp/__init__.py index dd2fa9d..6e6f198 100644 --- a/pymin/services/dhcp/__init__.py +++ b/pymin/services/dhcp/__init__.py @@ -54,8 +54,8 @@ class HostHandler(DictSubHandler): handler_help = u"Manage DHCP hosts" - _dict_subhandler_attr = 'hosts' - _dict_subhandler_class = Host + _cont_subhandler_attr = 'hosts' + _cont_subhandler_class = Host class DhcpHandler(Restorable, ConfigWriter, InitdHandler, TransactionalHandler, ParametersHandler): diff --git a/pymin/services/firewall/__init__.py b/pymin/services/firewall/__init__.py index b0784bf..c13ad01 100644 --- a/pymin/services/firewall/__init__.py +++ b/pymin/services/firewall/__init__.py @@ -81,8 +81,8 @@ class RuleHandler(ListSubHandler): handler_help = u"Manage firewall rules" - _list_subhandler_attr = 'rules' - _list_subhandler_class = Rule + _cont_subhandler_attr = 'rules' + _cont_subhandler_class = Rule class FirewallHandler(Restorable, ConfigWriter, ServiceHandler, TransactionalHandler): diff --git a/pymin/services/proxy/__init__.py b/pymin/services/proxy/__init__.py index 0577938..85bafd8 100644 --- a/pymin/services/proxy/__init__.py +++ b/pymin/services/proxy/__init__.py @@ -35,8 +35,8 @@ class HostHandler(DictSubHandler): handler_help = u"Manage proxy hosts" - _dict_subhandler_attr = 'hosts' - _dict_subhandler_class = Host + _cont_subhandler_attr = 'hosts' + _cont_subhandler_class = Host class User(Sequence): def __init__(self, name, password): @@ -52,8 +52,8 @@ class UserHandler(DictSubHandler): handler_help = u"Manage proxy users" - _dict_subhandler_attr = 'users' - _dict_subhandler_class = User + _cont_subhandler_attr = 'users' + _cont_subhandler_class = User class ProxyHandler(Restorable, ConfigWriter, InitdHandler, TransactionalHandler, ParametersHandler): diff --git a/pymin/services/util.py b/pymin/services/util.py index 95a07e9..8d3775e 100644 --- a/pymin/services/util.py +++ b/pymin/services/util.py @@ -590,162 +590,184 @@ class SubHandler(Handler): r"Initialize the object, see the class documentation for details." self.parent = parent -class ListSubHandler(SubHandler): - r"""ListSubHandler(parent) -> ListSubHandler instance. - - This is a helper class to inherit from to automatically handle subcommands - that operates over a list parent attribute. - - The list attribute to handle and the class of objects that it contains can - be defined by calling the constructor or in a more declarative way as - class attributes, like: - - class TestHandler(ListSubHandler): - _list_subhandler_attr = 'some_list' - _list_subhandler_class = SomeClass - - This way, the parent's some_list attribute (self.parent.some_list) will be - managed automatically, providing the commands: add, update, delete, get, - list and show. New items will be instances of SomeClass, which should - provide a cmp operator to see if the item is on the list and an update() - method, if it should be possible to modify it. +class ContainerSubHandler(SubHandler): + r"""ContainerSubHandler(parent) -> ContainerSubHandler instance. + + This is a helper class to implement ListSubHandler and DictSubHandler. You + should not use it directly. + + The container attribute to handle and the class of objects that it + contains can be defined by calling the constructor or in a more declarative + way as class attributes, like: + + class TestHandler(ContainerSubHandler): + _cont_subhandler_attr = 'some_cont' + _cont_subhandler_class = SomeClass + + This way, the parent's some_cont attribute (self.parent.some_cont) + will be managed automatically, providing the commands: add, update, + delete, get and show. New items will be instances of SomeClass, + which should provide a cmp operator to see if the item is on the + container and an update() method, if it should be possible to modify + it. If SomeClass has an _add, _update or _delete attribute, it set + them to true when the item is added, updated or deleted respectively + (in case that it's deleted, it's not removed from the container, + but it's not listed either). """ def __init__(self, parent, attr=None, cls=None): r"Initialize the object, see the class documentation for details." self.parent = parent if attr is not None: - self._list_subhandler_attr = attr + self._cont_subhandler_attr = attr if cls is not None: - self._list_subhandler_class = cls + self._cont_subhandler_class = cls + + def _cont(self): + return getattr(self.parent, self._cont_subhandler_attr) - def _list(self): - return getattr(self.parent, self._list_subhandler_attr) + def _vcont(self): + if isinstance(self._cont(), dict): + return dict([(k, i) for (k, i) in self._cont().items() + if not hasattr(i, '_delete') or not i._delete]) + return [i for i in self._cont() + if not hasattr(i, '_delete') or not i._delete] @handler(u'Add a new item') def add(self, *args, **kwargs): r"add(...) -> None :: Add an item to the list." - item = self._list_subhandler_class(*args, **kwargs) - if item in self._list(): + item = self._cont_subhandler_class(*args, **kwargs) + if hasattr(item, '_add'): + item._add = True + key = item + if isinstance(self._cont(), dict): + key = item.as_tuple()[0] + # do we have the same item? then raise an error + if key in self._vcont(): raise ItemAlreadyExistsError(item) - self._list().append(item) + # do we have the same item, but logically deleted? then update flags + if key in self._cont(): + index = key + if not isinstance(self._cont(), dict): + index = self._cont().index(item) + if hasattr(item, '_add'): + self._cont()[index]._add = False + if hasattr(item, '_delete'): + self._cont()[index]._delete = False + else: # it's *really* new + if isinstance(self._cont(), dict): + self._cont()[key] = item + else: + self._cont().append(item) @handler(u'Update an item') def update(self, index, *args, **kwargs): - r"update(index, ...) -> None :: Update an item of the list." + r"update(index, ...) -> None :: Update an item of the container." # TODO make it right with metaclasses, so the method is not created # unless the update() method really exists. # TODO check if the modified item is the same of an existing one - index = int(index) # TODO validation - if not hasattr(self._list_subhandler_class, 'update'): + if not isinstance(self._cont(), dict): + index = int(index) # TODO validation + if not hasattr(self._cont_subhandler_class, 'update'): raise CommandNotFoundError(('update',)) try: - self._list()[index].update(*args, **kwargs) + item = self._vcont()[index] + item.update(*args, **kwargs) + if hasattr(item, '_update'): + item._update = True except IndexError: raise ItemNotFoundError(index) @handler(u'Delete an item') def delete(self, index): - r"delete(index) -> None :: Delete an item of the list." - index = int(index) # TODO validation + r"delete(index) -> None :: Delete an item of the container." + if not isinstance(self._cont(), dict): + index = int(index) # TODO validation try: - return self._list().pop(index) + item = self._vcont()[index] + if hasattr(item, '_delete'): + item._delete = True + else: + del self._cont()[index] + return item except IndexError: raise ItemNotFoundError(index) @handler(u'Get information about an item') def get(self, index): - r"get(index) -> Host :: List all the information of an item." - index = int(index) # TODO validation + r"get(index) -> item :: List all the information of an item." + if not isinstance(self._cont(), dict): + index = int(index) # TODO validation try: - return self._list()[index] + return self._vcont()[index] except IndexError: raise ItemNotFoundError(index) - @handler(u'Get how many items are in the list') - def len(self): - r"len() -> int :: Get how many items are in the list." - return len(self._list()) - @handler(u'Get information about all items') def show(self): - r"show() -> list of Hosts :: List all the complete items information." - return self._list() + r"show() -> list of items :: List all the complete items information." + if isinstance(self._cont(), dict): + return self._cont().values() + return self._vcont() -class DictSubHandler(SubHandler): - r"""DictSubHandler(parent) -> DictSubHandler instance. +class ListSubHandler(ContainerSubHandler): + r"""ListSubHandler(parent) -> ListSubHandler instance. This is a helper class to inherit from to automatically handle subcommands - that operates over a dict parent attribute. + that operates over a list parent attribute. - The dict attribute to handle and the class of objects that it contains can + The list attribute to handle and the class of objects that it contains can be defined by calling the constructor or in a more declarative way as class attributes, like: - class TestHandler(DictSubHandler): - _dict_subhandler_attr = 'some_dict' - _dict_subhandler_class = SomeClass + class TestHandler(ListSubHandler): + _cont_subhandler_attr = 'some_list' + _cont_subhandler_class = SomeClass - This way, the parent's some_dict attribute (self.parent.some_dict) will be + This way, the parent's some_list attribute (self.parent.some_list) will be managed automatically, providing the commands: add, update, delete, get, list and show. New items will be instances of SomeClass, which should - provide a constructor with at least the key value and an update() method, - if it should be possible to modify it. + provide a cmp operator to see if the item is on the list and an update() + method, if it should be possible to modify it. If SomeClass has an _add, + _update or _delete attribute, it set them to true when the item is added, + updated or deleted respectively (in case that it's deleted, it's not + removed from the list, but it's not listed either). """ - def __init__(self, parent, attr=None, cls=None): - r"Initialize the object, see the class documentation for details." - self.parent = parent - if attr is not None: - self._dict_subhandler_attr = attr - if cls is not None: - self._dict_subhandler_class = cls + @handler(u'Get how many items are in the list') + def len(self): + r"len() -> int :: Get how many items are in the list." + return len(self._vcont()) - def _dict(self): - return getattr(self.parent, self._dict_subhandler_attr) +class DictSubHandler(ContainerSubHandler): + r"""DictSubHandler(parent) -> DictSubHandler instance. - @handler(u'Add a new item') - def add(self, key, *args, **kwargs): - r"add(key, ...) -> None :: Add an item to the dict." - item = self._dict_subhandler_class(key, *args, **kwargs) - if key in self._dict(): - raise ItemAlreadyExistsError(key) - self._dict()[key] = item + This is a helper class to inherit from to automatically handle subcommands + that operates over a dict parent attribute. - @handler(u'Update an item') - def update(self, key, *args, **kwargs): - r"update(key, ...) -> None :: Update an item of the dict." - # TODO make it right with metaclasses, so the method is not created - # unless the update() method really exists. - if not hasattr(self._dict_subhandler_class, 'update'): - raise CommandNotFoundError(('update',)) - if not key in self._dict(): - raise ItemNotFoundError(key) - self._dict()[key].update(*args, **kwargs) + The dict attribute to handle and the class of objects that it contains can + be defined by calling the constructor or in a more declarative way as + class attributes, like: - @handler(u'Delete an item') - def delete(self, key): - r"delete(key) -> None :: Delete an item of the dict." - if not key in self._dict(): - raise ItemNotFoundError(key) - del self._dict()[key] + class TestHandler(DictSubHandler): + _cont_subhandler_attr = 'some_dict' + _cont_subhandler_class = SomeClass - @handler(u'Get information about an item') - def get(self, key): - r"get(key) -> Host :: List all the information of an item." - if not key in self._dict(): - raise ItemNotFoundError(key) - return self._dict()[key] + This way, the parent's some_dict attribute (self.parent.some_dict) will be + managed automatically, providing the commands: add, update, delete, get, + list and show. New items will be instances of SomeClass, which should + provide a constructor with at least the key value, an as_tuple() method + and an update() method, if it should be possible to modify + it. If SomeClass has an _add, _update or _delete attribute, it set + them to true when the item is added, updated or deleted respectively + (in case that it's deleted, it's not removed from the dict, but it's + not listed either). + """ @handler(u'List all the items by key') def list(self): r"list() -> tuple :: List all the item keys." - return self._dict().keys() - - @handler(u'Get information about all items') - def show(self): - r"show() -> list of Hosts :: List all the complete items information." - return self._dict().values() + return self._cont().keys() if __name__ == '__main__': -- 2.43.0