Commit 7be249d4 authored by Anael Beutot's avatar Anael Beutot
Browse files

Refactored TagDB into two classes.

New class RootDB has no parents and handles RPC calls to the cc-server in a
transparent fashion from its children.
parent 4f433c7b
Loading
Loading
Loading
Loading
+4 −11
Original line number Diff line number Diff line
@@ -12,7 +12,7 @@ from sjrpc.utils import ConnectionProxy, RpcHandler, threadless

from ccnode import __version__
from ccnode.config import NodeConfigParser
from ccnode.tags import Tag, get_tags, TagDB
from ccnode.tags import Tag, get_tags, RootTagDB
from ccnode.jobs import JobManager
from ccnode.exc import PluginError

@@ -210,7 +210,7 @@ class MainLoop(object):
        self.main_plugin = None

        # tag database
        self.tag_db = TagDB(self, tags=DEFAULT_TAGS)
        self.tag_db = RootTagDB(self, tags=DEFAULT_TAGS)

        # job manages
        self.job_manager = JobManager(self)
@@ -266,7 +266,7 @@ class MainLoop(object):
        self.registered_plugins.add(plugin)

        # register tags
        self.tag_db.update_from_db(plugin.tag_db)
        plugin.tag_db.set_parent(self.tag_db)

        # register handler
        self.rpc_handler.update(plugin.rpc_handler)
@@ -280,14 +280,7 @@ class MainLoop(object):
            raise PluginError('Plugin was not registered, cannot remove')

        # remove tags
        for db_name, tag_db in plugin.tag_db:
            for tag_name in tag_db:
                del self.tag_db[db_name][tag_name]

            if not self.tag_db[db_name]:  # if there's no more tag in the db
                del self.tag_db[db_name]

        self.tag_db.delete_from_db(plugin.tag_db)
        plugin.tag_db.set_parent(None)

        # remove handlers
        for handler_name in plugin.rpc_handler:
+1 −1
Original line number Diff line number Diff line
@@ -20,7 +20,7 @@ class Base(object):
        self.main = kwargs.pop('loop')

        # plugins may define tags (see :mod:`ccnode.tags`)
        self.tag_db = TagDB(self.main, parent_db=self.main.tag_db)
        self.tag_db = TagDB()

        # plugins may define handler functions that would be called by the
        # server
+145 −105
Original line number Diff line number Diff line
@@ -222,19 +222,17 @@ class TagDB(object):
    Handles common operations such as registering tag on the cc-server, updating
    its values, etc.

    TagDB can have a parent TagDB, in this case, the latter will handle tag
    TagDB can have a parent TagDB, in this case, the latter could handle tag
    registration on the cc-server.

    """
    def __init__(self, main, parent_db=None, tags=None, sub_tags=None):
    def __init__(self, parent_db=None, tags=None, sub_tags=None):
        """
        :param main: MainLoop instance
        :param TagDB parent_db: TagDB parent object
        :param iterable tags: initial tags
        :param dict sub_tags: initial subtags
        """
        self.main = weakref.proxy(main)
        self.parent = parent_db
        self._parent = parent_db

        if tags is None:
            tags = tuple()
@@ -254,9 +252,116 @@ class TagDB(object):
            for tag in tags:
                self.add_sub_tag(sub_id, tag)

    def set_parent(self, parent):
        """Set parent tag database."""
        # check if previous parent
        if self._parent is not None:
            # we must remove tags from old parent
            self._parent.remove_tags(self.db['__main__'])
            for sub_id in self.db:
                if sub_id == '__main__':
                    continue
                self._parent.remove_sub_object(sub_id)
        # set new parent
        self._parent = parent
        if self._parent is not None:
            # add tags in new parent
            self._parent.add_tags(self.db['__main__'].itervalues())

            for sub_id, db in self.db.iteritems():
                if sub_id == '__main__':
                    continue
                self._parent.add_sub_object(sub_id, db.itervalues())

    # tag handling part, used by plugins
    def add_tags(self, tags):
        """
        :param iterable tags: list of tags to add
        """
        for tag in tags:
            self.add_tag(tag)

    def add_sub_tags(self, sub_id, tags):
        for tag in tags:
            self.add_sub_tag(sub_id, tag)

    def remove_tags(self, tag_names):
        """
        :param iterable tag_names: list of tag names to remove
        """
        for name in tag_names:
            self.remove_tag(name)

    def add_tag(self, tag):
        # set special attributes on tag instance
        if self._parent is not None:
            self._parent.add_tag(tag)
        self.db['__main__'][tag.name] = tag

    def remove_tag(self, tag_name):
        self.db['__main__'].pop(tag_name)
        if self._parent is not None:
            self._parent.remove_tag(tag_name)

    def add_sub_tag(self, sub_id, tag):
        if self._parent is not None:
            self._parent.add_sub_tag(sub_id, tag)
        self.db[sub_id][tag.name] = tag

    def remove_sub_tag(self, sub_id, tag_name):
        self.db[sub_id].pop(tag_name)
        if self._parent is not None:
            self._parent.remove_sub_tag(sub_id, tag_name)

    def add_sub_object(self, sub_id, tags):
        if self._parent is not None:
            # tags will be added after
            self._parent.add_sub_object(sub_id, tuple())
        # add sub object tags
        for t in tags:
            self.add_sub_tag(sub_id, t)

    def remove_sub_object(self, sub_id):
        self.db.pop(sub_id)
        if self._parent is not None:
            self._parent.remove_sub_object(sub_id)

    # dict like
    def get(self, key, default=None):
        return self.db.get(key, default)

    def __getitem__(self, key):
        return self.db[key]

    def keys(self):
        return self.db.keys()

    def iteritems(self):
        return self.db.iteritems()

    def itervalues(self):
        return self.db.itervalues()
    # end dict like


class RootTagDB(TagDB):
    """Root tag database.

    It takes care of tag registration with cc-server. It has no parent.
    """
    def __init__(self, main, tags=None, sub_tags=None):
        """
        :param main: MainLoop instance
        :param tags: initial tags
        :param sub_tags: initial sub tags
        """
        self.main = main

        #: dict for async call storage, keep a part of log message
        self.async_calls = dict()

        TagDB.__init__(self, tags=tags, sub_tags=sub_tags)

    # RPC part
    def rpc_call(self, opaque, callback, remote_name, *args, **kwargs):
        """Local helper for all rpc calls.
@@ -270,6 +375,7 @@ class TagDB(object):
        """
        # call only if connected and authenticated to the cc-server
        if self.main.rpc_authenticated:
            # logger.debug('RPC call %s %s', remote_name, args)
            self.async_calls[self.main.rpc_con.rpc.async_call_cb(
                callback, remote_name, *args, **kwargs)] = opaque

@@ -355,122 +461,56 @@ class TagDB(object):
        'Error while trying to update tag %s, %s("%s")')
    # end RPC part

    # tag handling part, used by plugins
    def add_tags(self, tags):
        """
        :param iterable tags: list of tags to add
        """
        for tag in tags:
            self.add_tag(tag)

    def add_sub_tags(self, sub_id, tags):
        for tag in tags:
            self.add_sub_tag(sub_id, tag)

    def remove_tags(self, tag_names):
        """
        :param iterable tag_names: list of tag names to remove
        """
        for name in tag_names:
            self.remove_tag(name)

    def add_tag(self, tag):
        # set special attributes on tag instance
        if self.parent is None:
        tag.db = self
        tag.sub_id = '__main__'
        tag.start(self.main.evloop)
        # register tag on the cc-server
        if tag.value is not None:
            self.rpc_register_tag(tag)
        else:
            self.parent.add_tag(tag)
        self.db['__main__'][tag.name] = tag

    def remove_tag(self, tag_name):
        tag = self.db['__main__'].pop(tag_name)
        if self.parent is None:
        tag.db = None
        tag.sub_id = None
        tag.stop()
        # unregister tag on the cc-server
        if tag.value is not None:
            self.rpc_unregister_tag(tag_name)
        else:
            self.parent.remove_tag(tag_name)

    def add_sub_tag(self, sub_id, tag):
        if self.parent is None:
        tag.db = self
        tag.sub_id = sub_id
        tag.start(self.main.evloop)
        # register tag to the cc-server
        if tag.value is not None:
            self.rpc_register_sub_tag(sub_id, tag)
        else:
            self.parent.add_sub_tag(sub_id, tag)
        self.db[sub_id][tag.name] = tag

    def remove_sub_tag(self, sub_id, tag_name):
        tag = self.db[sub_id].pop(tag_name)
        if self.parent is None:
        tag.db = None
        tag.sub_id = None
        tag.stop()
        # unregister tag to the cc-server
        if tag.value is not None:
            self.rpc_unregister_sub_tag(sub_id, tag_name)
        else:
            self.parent.remove_sub_tag(sub_id, tag_name)

    def add_sub_object(self, sub_id, tags):
        if self.parent is None:
        self.rpc_register_sub_object(sub_id)
        else:
            # tags will be added after
            self.parent.add_sub_object(sub_id, tuple())
        # FIXME not sure this is needed
        # add sub object tags
        for t in tags:
            self.add_sub_tag(sub_id, t)

    def remove_sub_object(self, sub_id):
        if self.parent is None:
            for name in self.db[sub_id].keys():
                self.remove_sub_tag(sub_id, name)
        for tag in self.db[sub_id].itervalues():
            tag.stop()
            tag.db = None
            tag.sub_id = None
            # we don't need to unregister each sub tag on the cc-server because
            # it will be done when we unregister the object
        del self.db[sub_id]
        self.rpc_unregister_sub_object(sub_id)
        else:
            sub_tags = self.db.pop(sub_id)
            self.parent.remove_sub_object(sub_id)

    # dict like
    def get(self, key, default=None):
        return self.db.get(key, default)

    def __getitem__(self, key):
        return self.db[key]

    def keys(self):
        return self.db.keys()

    def iteritems(self):
        return self.db.iteritems()

    def itervalues(self):
        return self.db.itervalues()

    def update_from_db(self, tag_db):
        """Update self tag db from other instance."""
        self.add_tags(tag_db['__main__'].itervalues())

        for sub_id, db in tag_db.iteritems():
            if sub_id == '__main__':
                continue
            self.add_sub_tags(sub_id, db.itervalues())

    def delete_from_db(self, tag_db):
        self.remove_tags(tag_db['__main__'])
        for sub_id in tag_db:
            if sub_id == '__main__':
                continue
            self.remove_suobject(sub_id)