Example #1
0
    def check_list_needs(self, pkg):
        """
        Check the needs from list against pkg

        @param pkg the plugin to check
        @return [] if everything is OK or a list of not used needs (name, op, version)
        """

        # Clone the list and drops out pkg
        lst = self.pkg_lst[:]
        lst.remove(pkg)

        conflicts_lst = []

        for provide in pkg.provides:
            p_name, p_op, p_ver = Version.extract_version(provide)

            for n_pkg in lst:
                for need in n_pkg.needs:
                    n_name, n_op, n_ver = Version.extract_version(need)

                    if n_name != p_name:
                        continue

                    # It's our need and we have a dep! :D
                    if n_op(p_ver, n_ver) is True:
                        conflicts_lst.append((n_pkg, n_op, n_ver))

        return conflicts_lst
Example #2
0
    def load_plugin(self, pkg, force=False):
        """
        Load a plugin

        @param pkg the plugin to load
        @param force if True no checks for deps will performed
        @return None or raise a PluginException
        """

        if pkg in self.pkg_lst:
            raise PluginException(pkg, _("Plugin already loaded."))

        if not force:

            # 1) Check for conflicts
            ret = self.check_conflicts(pkg)

            if ret:
                reasons = []
                for phase, (p_name, p_op, p_ver), (c_name, c_op, c_ver) in ret:
                    reasons.append( \
                        "-%d- Plugin '%s' provides %s which conflicts with " \
                        "%s conflict entry for plugin '%s'" % \
                        ( \
                            phase, pkg, \
                            Version.stringify_version(p_name, p_op, p_ver), \
                            Version.stringify_version(p_name, c_op, c_ver), \
                            c_name \
                        ) \
                    )

                raise PluginException(pkg, "\n".join(reasons))


            # 2) Check for needs
            ret = self.check_needs(pkg)

            if ret:
                reasons = []
                for (n_name, n_op, n_ver) in ret:
                    reasons.append( \
                        "-3- Plugin '%s' needs %s, which actually is not " \
                        "provided by any plugin." % \
                        ( \
                            pkg, \
                            Version.stringify_version(n_name, n_op, n_ver) \
                        ) \
                    )

                raise PluginException(pkg, "\n".join(reasons))

        self.__load_hook(pkg)

        # Export to cache
        self.add_plugin_to_cache(pkg)
        self.dump()
Example #3
0
    def unload_plugin(self, pkg, force=False):
        """
        Unload a plugin
        @param pkg the plugin to unload
        @param force if False check the depends

        @return None or raise a PluginException
        """

        if pkg not in self.pkg_lst:
            raise Exception("Plugin not loaded.")

        if not force:

            # No conflict check here.
            # 1) We should check need's list against pkg

            ret = self.check_list_needs(pkg)

            if ret:
                reasons = []

                for i in ret:
                    reasons.append(
                        "Plugin %s requires %s" % \
                        (i[0], Version.stringify_version(pkg, i[1], i[2]))
                    )

                raise PluginException(pkg, "\n".join(reasons))

        self.__unload_hook(pkg)

        self.remove_plugin_from_cache(pkg)
        self.dump()
Example #4
0
    def add_plugin_to_cache(self, pkg):
        """
        Exports the needs/provides/conflicts to the global dicts
        """

        for attr in ('conflicts', 'provides', 'needs'):
            for dep in getattr(pkg, attr, []):
                try:
                    name, op, ver = Version.extract_version(dep)
                    d = getattr(self, "who_%s" % attr)
                    d[name] = (op, ver, pkg)
                except Exception, err:
                    log.warning(err)
                    log.warning("Ignoring %s entry" % dep)
Example #5
0
    def check_conflicts(self, pkg):
        """
        Check the conflicts between pkg and global list of provides or
        loaded plugins.

        The return list contains entry like:
            (phase-number:int,
                (p_name:str, p_op:Operator, p_ver:Version) -> Provider
                (c_name:str, c_op:Operator, c_ver:Version) -> Conflicter
            )

        @param pkg the plugin to check
        @return [] if everything is OK or a list of plugins
                conflicting with pkg
        """

        lst = []

        #
        # 1) We should check the cache conflicts agains pkg

        for provide in pkg.provides:
            try:
                name, op, ver = Version.extract_version(provide)

                if not name in self.who_conflicts:
                    continue

                for c_op, c_ver, c_pkg in self.who_conflicts[name]:

                    # We could have
                    # c_op, c_ver >=, 2.0.0
                    # ver = 3.0.0

                    # We could have a None if a conflict entry didn't provide
                    # any version or operator

                    if c_op.op_str == '.' or c_op(ver, c_ver) is True:
                        lst.append(
                            (1,
                             (name, op, ver),	# Provider
                             (c_pkg, c_op, c_ver)	# Conflicter
                             )
                        )
            except Exception, err:
                log.error(err)
                log.error("1) Ignoring conflict entry ...")

                continue
Example #6
0
    def check_needs(self, pkg):
        """
        Check the needs between pkg and global list of provides.

        The return list contains entry like:
            (name:str, op:Operator, ver:Version) -> need not found

        @param pkg the plugin to check
        @return [] if everything is OK or a list of not resolved needs
        """

        # We have passed the conflict stage
        # so check for needs

        lst = []

        for need in pkg.needs:
            try:
                found = False
                name, op, ver = Version.extract_version(need)

                if not name in self.who_provides:
                    lst.append((name, op, ver))
                    continue

                for n_op, n_ver, n_pkg in self.who_provides[name]:
                    # for example we could have
                    # name, op, ver = dummy, >=, 2.0.0
                    # n_ver = 2.0.3

                    # TODO: check me
                    if op(n_ver, ver) is True or op(n_ver, ver) is None:
                        found = True
                        break

                # If we are here not applicable deps were found so add
                if not found:
                    lst.append((name, op, ver))

            except Exception, err:
                log.error(err)
                log.error("Ignoring need entry ...")

                continue
    def get_need(self, reader, needstr, classname=None, need_module=False):
        """
        Usefull function to return an instance of needstr:classname
        """
        lst = []

        # We create a list of needs for the same package
        for need in reader.needs:
            name, op, ver = Version.extract_version(need)

            if name == needstr:
                lst.append((op, ver, name))

        from umit.plugin.Engine import PluginEngine
        ret = PluginEngine().tree.get_provide(needstr, lst, need_module)

        log.debug(">>> Core.get_need() -> %s (module: %s)" %
                  (ret, need_module))

        if not ret:
            return None

        if not need_module:
            if not classname:
                log.debug(
                    ">>> Core.get_need(): No classname specified. Returning first instance"
                )
                return ret[0]

            for instance in ret:
                if instance.__class__.__name__ == classname:
                    return instance
        else:
            # We need a module

            if len(ret) > 1:
                log.debug(">>> Core.get_need(): Returning the first module")

            return ret[0]

        return None
Example #8
0
    def get_need(self, reader, needstr, classname=None, need_module=False):
        """
        Usefull function to return an instance of needstr:classname
        """
        lst = []

        # We create a list of needs for the same package
        for need in reader.needs:
            name, op, ver = Version.extract_version(need)

            if name == needstr:
                lst.append((op, ver, name))

        from umit.plugin.Engine import PluginEngine
        ret = PluginEngine().tree.get_provide(needstr, lst, need_module)

        log.debug(">>> Core.get_need() -> %s (module: %s)" % (ret, need_module))

        if not ret:
            return None

        if not need_module:
            if not classname:
                log.debug(">>> Core.get_need(): No classname specified. Returning first instance")
                return ret[0]

            for instance in ret:
                if instance.__class__.__name__ == classname:
                    return instance
        else:
            # We need a module

            if len(ret) > 1:
                log.debug(">>> Core.get_need(): Returning the first module")

            return ret[0]

        return None
Example #9
0
    def remove_plugin_from_cache(self, pkg):
        for attr in ('conflicts', 'provides', 'needs'):
            for dep in getattr(pkg, attr, []):
                try:
                    name, op, ver = Version.extract_version(dep)
                    d = getattr(self, "who_%s" % attr)

                    # It's more probably that the entry is in the last

                    for i in xrange(len(d[name]) - 1, -1, -1):
                        _op, _ver, _pkg = d[name][i]

                        if pkg == _pkg and \
                           ver == _ver and \
                           op == _op:
                            del d[name][i]

                    # Remove unused keys.
                    if not d[name]:
                        del d[name]

                except Exception, err:
                    log.warning(err)
                    log.warning("Ignoring %s entry" % dep)
Example #10
0
    def __update_rich_list(self):
        """
        This is the timeout callback called to update the gui
        in the find phase
        """

        working = False

        for upd_obj in self.p_window.update_eng.list:
            upd_obj.lock.acquire()

            try:
                # Check if we are working
                if upd_obj.status == LATEST_GETTING:
                    working = True

                # Update the row
                row = upd_obj.object
                row.message = upd_obj.label
            finally:
                upd_obj.lock.release()

        # No locking from here we have finished

        if not working:
            # Mark as finished
            self.p_window.update_eng.stop()
            self.find_updates_btn.set_sensitive(True)

            lst = filter( \
                lambda x: (x.status == LATEST_GETTED) and (x) or (None),\
                self.p_window.update_eng.list \
            )
            elst = filter( \
                lambda x: (x.status == LATEST_ERROR) and (x) or (None),\
                self.p_window.update_eng.list \
            )

            if not lst and not elst:
                self.p_window.toolbar.unset_status()

                self.p_window.animated_bar.label = \
                    _('<b>No updates found</b>')
                self.p_window.animated_bar.start_animation(True)

                self.richlist.clear()
                self.populate()

                self.menu_enabled = True
            else:
                # Now prepare the download page

                if not elst:
                    self.p_window.toolbar.show_message( \
                        _("<b>Updates found: %d</b>") % len(lst), \
                        stock=gtk.STOCK_APPLY \
                    )
                else:
                    self.p_window.toolbar.show_message( \
                        _('<b>Updates found: %d with %d errors</b>')  \
                        % (len(lst), len(elst)), stock=gtk.STOCK_APPLY \
                    )

                for obj in self.p_window.update_eng.list:
                    row = obj.object
                    active = (obj.status == LATEST_GETTED)

                    if active:
                        obj.last_update_idx = 0
                        row.show_include = True

                        log.debug("Connecting 'query-tooltip' for %s" % obj)

                        # The tooltip is showed and hidden continuously
                        row.versions_button.props.has_tooltip = True
                        row.versions_button.connect(
                            'query-tooltip',
                            self.__query_tooltip_versions_button, obj
                        )

                        row.versions_model.clear()

                        row.versions_model.append([
                            gtk.STOCK_CANCEL, _("Skip")
                        ])

                        for update in obj.updates:
                            cur_v = Version(row.reader.version)
                            new_v = Version(update.version)

                            if new_v > cur_v:
                                row.versions_model.append([
                                    gtk.STOCK_GO_UP, _("Update to %s") % \
                                    update.version
                                ])
                            elif new_v == cur_v:
                                row.versions_model.append([
                                    gtk.STOCK_REFRESH, _("Reinstall %s") % \
                                    update.version
                                ])
                            else:
                                row.versions_model.append([
                                    gtk.STOCK_GO_DOWN, _("Downgrade to %s") % \
                                    update.version
                                ])

                        row.versions_button.set_active(0)
                        row.message = obj.label
                    else:
                        row.saturate = True

                if lst:
                    self.install_updates_btn.show()

                self.find_updates_btn.hide()
                self.skip_install_btn.show()

        return working
Example #11
0
class PluginsTree(object):
    """
    Manages and tracks the loads/unloads of plugins objects
    """

    def __init__(self):
        """
        Create a new PluginsTree

        @param parent a PluginEngine instance
        """

        # A dict to trace plugin instances
        self.instances = {}
        self.modules = {}

        self.who_conflicts, \
            self.who_provides, \
            self.who_needs = DepDict(), DepDict(), DepDict()
        self.pkg_lst = list()

    def dump(self):
        log.info(">>> dump(): conflicts/provides/needs: %d / %d / %d" % \
              (
                  len(self.who_conflicts),
                  len(self.who_provides),
                  len(self.who_needs)
              )
        )

    def check_conflicts(self, pkg):
        """
        Check the conflicts between pkg and global list of provides or
        loaded plugins.

        The return list contains entry like:
            (phase-number:int,
                (p_name:str, p_op:Operator, p_ver:Version) -> Provider
                (c_name:str, c_op:Operator, c_ver:Version) -> Conflicter
            )

        @param pkg the plugin to check
        @return [] if everything is OK or a list of plugins
                conflicting with pkg
        """

        lst = []

        #
        # 1) We should check the cache conflicts agains pkg

        for provide in pkg.provides:
            try:
                name, op, ver = Version.extract_version(provide)

                if not name in self.who_conflicts:
                    continue

                for c_op, c_ver, c_pkg in self.who_conflicts[name]:

                    # We could have
                    # c_op, c_ver >=, 2.0.0
                    # ver = 3.0.0

                    # We could have a None if a conflict entry didn't provide
                    # any version or operator

                    if c_op.op_str == '.' or c_op(ver, c_ver) is True:
                        lst.append(
                            (1,
                             (name, op, ver),	# Provider
                             (c_pkg, c_op, c_ver)	# Conflicter
                             )
                        )
            except Exception, err:
                log.error(err)
                log.error("1) Ignoring conflict entry ...")

                continue

        #
        # 2) We should check the package conflicts against cache provides

        for conflict in pkg.conflicts:
            try:
                name, op, ver = Version.extract_version(conflict)

                if not name in self.who_provides:
                    continue

                # Only '=' operator presents:
                # providers['woot'] = [('=', '2.0', pkg1), ('=', '2.1', pkg2), ..]

                for p_op, p_ver, p_pkg in self.who_provides[name]:
                    # for example we could have
                    # name, op, ver = dummy, >=, 2.0.0
                    # p_ver = 2.0.3

                    # strict checking for avoid false results
                    # if None was returned
                    if op(p_ver, ver) is True:
                        # So we have a conflict. Just add to the list
                        lst.append(
                            (2,
                             (p_pkg, p_op, p_ver),   # Provider
                             (name, op, ver)	# Conflicter
                             )
                        )

            except Exception, err:
                log.error(err)
                log.error("2) Ignoring conflict entry ...")

                continue
    def __process_manifest(self, file, data, exc, obj):
        """
        Callback to parse latest.xml file containing meta information about
        the avaiable update.
        """

        if isinstance(exc, ErrorNetException):
            obj.lock.acquire()

            try:
                if obj.last_update_idx + 1 < len(obj.object.reader.update):
                    obj.last_update_idx += 1

                    obj.status = LATEST_GETTING
                    obj.label = _('Cycling to next update url. Waiting...')

                    self.update_lst.append(obj)
                elif isinstance(exc, ErrorNetException):
                    obj.status = LATEST_ERROR
                    obj.label = _(
                        'Cannot find newer version (%s)') % exc.reason

                self.__process_next()
                return
            finally:
                obj.lock.release()

        elif isinstance(exc, StopNetException):
            try:
                obj.parse_latest_file()
            except Exception, exc:

                if obj.last_update_idx + 1 < len(obj.object.reader.update):
                    obj.last_update_idx += 1

                    obj.status = LATEST_GETTING
                    obj.label = _('Cycling to next update url. Waiting...')

                    self.update_lst.append(obj)
                else:
                    obj.status = LATEST_ERROR
                    obj.label = _('Cannot find newer version (%s)') % str(exc)

                self.__process_next()
                return

            version = None
            type = -1  # -1 no action / 0 update / 1 downgrade

            # If we have only 1 update..
            if len(obj.updates) == 1:
                version = obj.updates[0].version

                new_v = Version(version)
                cur_v = Version(obj.object.reader.version)

                if new_v > cur_v:
                    type = 0
                elif cur_v < new_v:
                    type = 1

            obj.lock.acquire()

            try:
                if obj.updates:

                    # We check if the path is the plugins in config_dir

                    plug_dir = os.path.join(Path.config_dir, 'plugins')

                    if os.path.dirname(obj.object.reader.get_path()) != plug_dir and \
                       os.access(plug_dir, os.O_RDWR):

                        obj.status = LATEST_ERROR

                        if type == -1:
                            obj.label = _(
                                'Various versions available but require manual intervention.'
                            )
                        elif type == 0:
                            obj.label = _(
                                'Version %s available but need manual update.'
                            ) % version
                        elif type == 1:
                            obj.label = _(
                                'Version %s available but need manual downgrade.'
                            ) % version
                    else:
                        obj.status = LATEST_GETTED
                        if type == -1:
                            obj.label = _('Various versions available')
                        else:
                            obj.label = _('Version %s available.') % version

                else:
                    obj.status = LATEST_ERROR

                    if type < 0:
                        obj.label = _('Unable to parse latest.xml')
                    else:
                        obj.label = _('No applicable updates found')

                self.__process_next()
            finally:
                obj.lock.release()