Blame dnf/comps.py

Packit 6f3914
# comps.py
Packit 6f3914
# Interface to libcomps.
Packit 6f3914
#
Packit 6f3914
# Copyright (C) 2013-2018 Red Hat, Inc.
Packit 6f3914
#
Packit 6f3914
# This copyrighted material is made available to anyone wishing to use,
Packit 6f3914
# modify, copy, or redistribute it subject to the terms and conditions of
Packit 6f3914
# the GNU General Public License v.2, or (at your option) any later version.
Packit 6f3914
# This program is distributed in the hope that it will be useful, but WITHOUT
Packit 6f3914
# ANY WARRANTY expressed or implied, including the implied warranties of
Packit 6f3914
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
Packit 6f3914
# Public License for more details.  You should have received a copy of the
Packit 6f3914
# GNU General Public License along with this program; if not, write to the
Packit 6f3914
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
Packit 6f3914
# 02110-1301, USA.  Any Red Hat trademarks that are incorporated in the
Packit 6f3914
# source code or documentation are not subject to the GNU General Public
Packit 6f3914
# License and may only be used or replicated with the express permission of
Packit 6f3914
# Red Hat, Inc.
Packit 6f3914
#
Packit 6f3914
Packit 6f3914
from __future__ import absolute_import
Packit 6f3914
from __future__ import print_function
Packit 6f3914
from __future__ import unicode_literals
Packit 6f3914
Packit 6f3914
import libdnf.transaction
Packit 6f3914
Packit 6f3914
from dnf.exceptions import CompsError
Packit 6f3914
from dnf.i18n import _, ucd
Packit 6f3914
from functools import reduce
Packit 6f3914
Packit 6f3914
import dnf.i18n
Packit 6f3914
import dnf.util
Packit 6f3914
import fnmatch
Packit 6f3914
import gettext
Packit 6f3914
import itertools
Packit 6f3914
import libcomps
Packit 6f3914
import locale
Packit 6f3914
import logging
Packit 6f3914
import operator
Packit 6f3914
import re
Packit 6f3914
import sys
Packit 6f3914
Packit 6f3914
logger = logging.getLogger("dnf")
Packit 6f3914
Packit 6f3914
# :api :binformat
Packit 6f3914
CONDITIONAL = libdnf.transaction.CompsPackageType_CONDITIONAL
Packit 6f3914
DEFAULT     = libdnf.transaction.CompsPackageType_DEFAULT
Packit 6f3914
MANDATORY   = libdnf.transaction.CompsPackageType_MANDATORY
Packit 6f3914
OPTIONAL    = libdnf.transaction.CompsPackageType_OPTIONAL
Packit 6f3914
Packit 6f3914
ALL_TYPES = CONDITIONAL | DEFAULT | MANDATORY | OPTIONAL
Packit 6f3914
Packit 6f3914
Packit 6f3914
def _internal_comps_length(comps):
Packit 6f3914
    collections = (comps.categories, comps.groups, comps.environments)
Packit 6f3914
    return reduce(operator.__add__, map(len, collections))
Packit 6f3914
Packit 6f3914
Packit 6f3914
def _first_if_iterable(seq):
Packit 6f3914
    if seq is None:
Packit 6f3914
        return None
Packit 6f3914
    return dnf.util.first(seq)
Packit 6f3914
Packit 6f3914
Packit 6f3914
def _by_pattern(pattern, case_sensitive, sqn):
Packit 6f3914
    """Return items from sqn matching either exactly or glob-wise."""
Packit 6f3914
Packit 6f3914
    pattern = dnf.i18n.ucd(pattern)
Packit 6f3914
    exact = {g for g in sqn if g.name == pattern or g.id == pattern}
Packit 6f3914
    if exact:
Packit 6f3914
        return exact
Packit 6f3914
Packit 6f3914
    if case_sensitive:
Packit 6f3914
        match = re.compile(fnmatch.translate(pattern)).match
Packit 6f3914
    else:
Packit 6f3914
        match = re.compile(fnmatch.translate(pattern), flags=re.I).match
Packit 6f3914
Packit 6f3914
    return {g for g in sqn if match(g.name) or match(g.id) or match(g.ui_name)}
Packit 6f3914
Packit 6f3914
Packit 6f3914
def _fn_display_order(group):
Packit 6f3914
    return sys.maxsize if group.display_order is None else group.display_order
Packit 6f3914
Packit 6f3914
Packit 6f3914
def install_or_skip(install_fnc, grp_or_env_id, types, exclude=None,
Packit 6f3914
                    strict=True, exclude_groups=None):
Packit 6f3914
    """Either mark in persistor as installed given `grp_or_env` (group
Packit 6f3914
       or environment) or skip it (if it's already installed).
Packit 6f3914
       `install_fnc` has to be Solver._group_install
Packit 6f3914
       or Solver._environment_install.
Packit 6f3914
       """
Packit 6f3914
    try:
Packit 6f3914
        return install_fnc(grp_or_env_id, types, exclude, strict, exclude_groups)
Packit 6f3914
    except dnf.comps.CompsError as e:
Packit 6f3914
        logger.warning("%s, %s", ucd(e)[:-1], _("skipping."))
Packit 6f3914
Packit 6f3914
Packit 6f3914
class _Langs(object):
Packit 6f3914
Packit 6f3914
    """Get all usable abbreviations for the current language."""
Packit 6f3914
Packit 6f3914
    def __init__(self):
Packit 6f3914
        self.last_locale = None
Packit 6f3914
        self.cache = None
Packit 6f3914
Packit 6f3914
    @staticmethod
Packit 6f3914
    def _dotted_locale_str():
Packit 6f3914
        lcl = locale.getlocale(locale.LC_MESSAGES)
Packit 6f3914
        if lcl == (None, None):
Packit 6f3914
            return 'C'
Packit 6f3914
        return'.'.join(lcl)
Packit 6f3914
Packit 6f3914
    def get(self):
Packit 6f3914
        current_locale = self._dotted_locale_str()
Packit 6f3914
        if self.last_locale == current_locale:
Packit 6f3914
            return self.cache
Packit 6f3914
Packit 6f3914
        self.cache = []
Packit 6f3914
        locales = [current_locale]
Packit 6f3914
        if current_locale != 'C':
Packit 6f3914
            locales.append('C')
Packit 6f3914
        for l in locales:
Packit 6f3914
            for nlang in gettext._expand_lang(l):
Packit 6f3914
                if nlang not in self.cache:
Packit 6f3914
                    self.cache.append(nlang)
Packit 6f3914
Packit 6f3914
        self.last_locale = current_locale
Packit 6f3914
        return self.cache
Packit 6f3914
Packit 6f3914
Packit 6f3914
class CompsQuery(object):
Packit 6f3914
Packit 6f3914
    AVAILABLE = 1
Packit 6f3914
    INSTALLED = 2
Packit 6f3914
Packit 6f3914
    ENVIRONMENTS = 1
Packit 6f3914
    GROUPS = 2
Packit 6f3914
Packit 6f3914
    def __init__(self, comps, history, kinds, status):
Packit 6f3914
        self.comps = comps
Packit 6f3914
        self.history = history
Packit 6f3914
        self.kinds = kinds
Packit 6f3914
        self.status = status
Packit 6f3914
Packit 6f3914
    def _get_groups(self, available, installed):
Packit 6f3914
        result = set()
Packit 6f3914
        if self.status & self.AVAILABLE:
Packit 6f3914
            result.update({i.id for i in available})
Packit 6f3914
        if self.status & self.INSTALLED:
Packit 6f3914
            for i in installed:
Packit 6f3914
                group = i.getCompsGroupItem()
Packit 6f3914
                if not group:
Packit 6f3914
                    continue
Packit 6f3914
                result.add(group.getGroupId())
Packit 6f3914
        return result
Packit 6f3914
Packit 6f3914
    def _get_envs(self, available, installed):
Packit 6f3914
        result = set()
Packit 6f3914
        if self.status & self.AVAILABLE:
Packit 6f3914
            result.update({i.id for i in available})
Packit 6f3914
        if self.status & self.INSTALLED:
Packit 6f3914
            for i in installed:
Packit 6f3914
                env = i.getCompsEnvironmentItem()
Packit 6f3914
                if not env:
Packit 6f3914
                    continue
Packit 6f3914
                result.add(env.getEnvironmentId())
Packit 6f3914
        return result
Packit 6f3914
Packit 6f3914
    def get(self, *patterns):
Packit 6f3914
        res = dnf.util.Bunch()
Packit 6f3914
        res.environments = []
Packit 6f3914
        res.groups = []
Packit 6f3914
        for pat in patterns:
Packit 6f3914
            envs = grps = []
Packit 6f3914
            if self.kinds & self.ENVIRONMENTS:
Packit 6f3914
                available = self.comps.environments_by_pattern(pat)
Packit 6f3914
                installed = self.history.env.search_by_pattern(pat)
Packit 6f3914
                envs = self._get_envs(available, installed)
Packit 6f3914
                res.environments.extend(envs)
Packit 6f3914
            if self.kinds & self.GROUPS:
Packit 6f3914
                available = self.comps.groups_by_pattern(pat)
Packit 6f3914
                installed = self.history.group.search_by_pattern(pat)
Packit 6f3914
                grps = self._get_groups(available, installed)
Packit 6f3914
                res.groups.extend(grps)
Packit 6f3914
            if not envs and not grps:
Packit 6f3914
                if self.status == self.INSTALLED:
Packit 6f3914
                    msg = _("Module or Group '%s' is not installed.") % ucd(pat)
Packit 6f3914
                elif self.status == self.AVAILABLE:
Packit 6f3914
                    msg = _("Module or Group '%s' is not available.") % ucd(pat)
Packit 6f3914
                else:
Packit 6f3914
                    msg = _("Module or Group '%s' does not exist.") % ucd(pat)
Packit 6f3914
                raise CompsError(msg)
Packit 6f3914
        return res
Packit 6f3914
Packit 6f3914
Packit 6f3914
class Forwarder(object):
Packit 6f3914
    def __init__(self, iobj, langs):
Packit 6f3914
        self._i = iobj
Packit 6f3914
        self._langs = langs
Packit 6f3914
Packit 6f3914
    def __getattr__(self, name):
Packit 6f3914
        return getattr(self._i, name)
Packit 6f3914
Packit 6f3914
    def _ui_text(self, default, dct):
Packit 6f3914
        for l in self._langs.get():
Packit 6f3914
            t = dct.get(l)
Packit 6f3914
            if t is not None:
Packit 6f3914
                return t
Packit 6f3914
        return default
Packit 6f3914
Packit 6f3914
    @property
Packit 6f3914
    def ui_description(self):
Packit 6f3914
        return self._ui_text(self.desc, self.desc_by_lang)
Packit 6f3914
Packit 6f3914
    @property
Packit 6f3914
    def ui_name(self):
Packit 6f3914
        return self._ui_text(self.name, self.name_by_lang)
Packit 6f3914
Packit 6f3914
class Category(Forwarder):
Packit 6f3914
    # :api
Packit 6f3914
    def __init__(self, iobj, langs, group_factory):
Packit 6f3914
        super(Category, self).__init__(iobj, langs)
Packit 6f3914
        self._group_factory = group_factory
Packit 6f3914
Packit 6f3914
    def _build_group(self, grp_id):
Packit 6f3914
        grp = self._group_factory(grp_id.name)
Packit 6f3914
        if grp is None:
Packit 6f3914
            msg = "no group '%s' from category '%s'"
Packit 6f3914
            raise ValueError(msg % (grp_id.name, self.id))
Packit 6f3914
        return grp
Packit 6f3914
Packit 6f3914
    def groups_iter(self):
Packit 6f3914
        for grp_id in self.group_ids:
Packit 6f3914
            yield self._build_group(grp_id)
Packit 6f3914
Packit 6f3914
    @property
Packit 6f3914
    def groups(self):
Packit 6f3914
        return list(self.groups_iter())
Packit 6f3914
Packit 6f3914
class Environment(Forwarder):
Packit 6f3914
    # :api
Packit 6f3914
Packit 6f3914
    def __init__(self, iobj, langs, group_factory):
Packit 6f3914
        super(Environment, self).__init__(iobj, langs)
Packit 6f3914
        self._group_factory = group_factory
Packit 6f3914
Packit 6f3914
    def _build_group(self, grp_id):
Packit 6f3914
        grp = self._group_factory(grp_id.name)
Packit 6f3914
        if grp is None:
Packit 6f3914
            msg = "no group '%s' from environment '%s'"
Packit 6f3914
            raise ValueError(msg % (grp_id.name, self.id))
Packit 6f3914
        return grp
Packit 6f3914
Packit 6f3914
    def _build_groups(self, ids):
Packit 6f3914
        groups = []
Packit 6f3914
        for gi in ids:
Packit 6f3914
            try:
Packit 6f3914
                groups.append(self._build_group(gi))
Packit 6f3914
            except ValueError as e:
Packit 6f3914
                logger.error(e)
Packit 6f3914
Packit 6f3914
        return groups
Packit 6f3914
Packit 6f3914
    def groups_iter(self):
Packit 6f3914
        for grp_id in itertools.chain(self.group_ids, self.option_ids):
Packit 6f3914
            try:
Packit 6f3914
                yield self._build_group(grp_id)
Packit 6f3914
            except ValueError as e:
Packit 6f3914
                logger.error(e)
Packit 6f3914
Packit 6f3914
    @property
Packit 6f3914
    def mandatory_groups(self):
Packit 6f3914
        return self._build_groups(self.group_ids)
Packit 6f3914
Packit 6f3914
    @property
Packit 6f3914
    def optional_groups(self):
Packit 6f3914
        return self._build_groups(self.option_ids)
Packit 6f3914
Packit 6f3914
class Group(Forwarder):
Packit 6f3914
    # :api
Packit 6f3914
    def __init__(self, iobj, langs, pkg_factory):
Packit 6f3914
        super(Group, self).__init__(iobj, langs)
Packit 6f3914
        self._pkg_factory = pkg_factory
Packit 6f3914
        self.selected = iobj.default
Packit 6f3914
Packit 6f3914
    def _packages_of_type(self, type_):
Packit 6f3914
        return [pkg for pkg in self.packages if pkg.type == type_]
Packit 6f3914
Packit 6f3914
    @property
Packit 6f3914
    def conditional_packages(self):
Packit 6f3914
        return self._packages_of_type(libcomps.PACKAGE_TYPE_CONDITIONAL)
Packit 6f3914
Packit 6f3914
    @property
Packit 6f3914
    def default_packages(self):
Packit 6f3914
        return self._packages_of_type(libcomps.PACKAGE_TYPE_DEFAULT)
Packit 6f3914
Packit 6f3914
    def packages_iter(self):
Packit 6f3914
        # :api
Packit 6f3914
        return map(self._pkg_factory, self.packages)
Packit 6f3914
Packit 6f3914
    @property
Packit 6f3914
    def mandatory_packages(self):
Packit 6f3914
        return self._packages_of_type(libcomps.PACKAGE_TYPE_MANDATORY)
Packit 6f3914
Packit 6f3914
    @property
Packit 6f3914
    def optional_packages(self):
Packit 6f3914
        return self._packages_of_type(libcomps.PACKAGE_TYPE_OPTIONAL)
Packit 6f3914
Packit 6f3914
    @property
Packit 6f3914
    def visible(self):
Packit 6f3914
        return self._i.uservisible
Packit 6f3914
Packit 6f3914
class Package(Forwarder):
Packit 6f3914
    """Represents comps package data. :api"""
Packit 6f3914
Packit 6f3914
    _OPT_MAP = {
Packit 6f3914
        libcomps.PACKAGE_TYPE_CONDITIONAL : CONDITIONAL,
Packit 6f3914
        libcomps.PACKAGE_TYPE_DEFAULT     : DEFAULT,
Packit 6f3914
        libcomps.PACKAGE_TYPE_MANDATORY   : MANDATORY,
Packit 6f3914
        libcomps.PACKAGE_TYPE_OPTIONAL    : OPTIONAL,
Packit 6f3914
    }
Packit 6f3914
Packit 6f3914
    def __init__(self, ipkg):
Packit 6f3914
        self._i = ipkg
Packit 6f3914
Packit 6f3914
    @property
Packit 6f3914
    def name(self):
Packit 6f3914
        # :api
Packit 6f3914
        return self._i.name
Packit 6f3914
Packit 6f3914
    @property
Packit 6f3914
    def option_type(self):
Packit 6f3914
        # :api
Packit 6f3914
        return self._OPT_MAP[self.type]
Packit 6f3914
Packit 6f3914
class Comps(object):
Packit 6f3914
    # :api
Packit 6f3914
Packit 6f3914
    def __init__(self):
Packit 6f3914
        self._i = libcomps.Comps()
Packit 6f3914
        self._langs = _Langs()
Packit 6f3914
Packit 6f3914
    def __len__(self):
Packit 6f3914
        return _internal_comps_length(self._i)
Packit 6f3914
Packit 6f3914
    def _build_category(self, icategory):
Packit 6f3914
        return Category(icategory, self._langs, self._group_by_id)
Packit 6f3914
Packit 6f3914
    def _build_environment(self, ienvironment):
Packit 6f3914
        return Environment(ienvironment, self._langs, self._group_by_id)
Packit 6f3914
Packit 6f3914
    def _build_group(self, igroup):
Packit 6f3914
        return Group(igroup, self._langs, self._build_package)
Packit 6f3914
Packit 6f3914
    def _build_package(self, ipkg):
Packit 6f3914
        return Package(ipkg)
Packit 6f3914
Packit 6f3914
    def _add_from_xml_filename(self, fn):
Packit 6f3914
        comps = libcomps.Comps()
Packit 6f3914
        try:
Packit 6f3914
            comps.fromxml_f(fn)
Packit 6f3914
        except libcomps.ParserError:
Packit 6f3914
            errors = comps.get_last_errors()
Packit 6f3914
            raise CompsError(' '.join(errors))
Packit 6f3914
        self._i += comps
Packit 6f3914
Packit 6f3914
    @property
Packit 6f3914
    def categories(self):
Packit 6f3914
        # :api
Packit 6f3914
        return list(self.categories_iter())
Packit 6f3914
Packit 6f3914
    def category_by_pattern(self, pattern, case_sensitive=False):
Packit 6f3914
        # :api
Packit 6f3914
        assert dnf.util.is_string_type(pattern)
Packit 6f3914
        cats = self.categories_by_pattern(pattern, case_sensitive)
Packit 6f3914
        return _first_if_iterable(cats)
Packit 6f3914
Packit 6f3914
    def categories_by_pattern(self, pattern, case_sensitive=False):
Packit 6f3914
        # :api
Packit 6f3914
        assert dnf.util.is_string_type(pattern)
Packit 6f3914
        return _by_pattern(pattern, case_sensitive, self.categories)
Packit 6f3914
Packit 6f3914
    def categories_iter(self):
Packit 6f3914
        # :api
Packit 6f3914
        return (self._build_category(c) for c in self._i.categories)
Packit 6f3914
Packit 6f3914
    @property
Packit 6f3914
    def environments(self):
Packit 6f3914
        # :api
Packit 6f3914
        return sorted(self.environments_iter(), key=_fn_display_order)
Packit 6f3914
Packit 6f3914
    def _environment_by_id(self, id):
Packit 6f3914
        assert dnf.util.is_string_type(id)
Packit 6f3914
        return dnf.util.first(g for g in self.environments_iter() if g.id == id)
Packit 6f3914
Packit 6f3914
    def environment_by_pattern(self, pattern, case_sensitive=False):
Packit 6f3914
        # :api
Packit 6f3914
        assert dnf.util.is_string_type(pattern)
Packit 6f3914
        envs = self.environments_by_pattern(pattern, case_sensitive)
Packit 6f3914
        return _first_if_iterable(envs)
Packit 6f3914
Packit 6f3914
    def environments_by_pattern(self, pattern, case_sensitive=False):
Packit 6f3914
        # :api
Packit 6f3914
        assert dnf.util.is_string_type(pattern)
Packit 6f3914
        envs = list(self.environments_iter())
Packit 6f3914
        found_envs = _by_pattern(pattern, case_sensitive, envs)
Packit 6f3914
        return sorted(found_envs, key=_fn_display_order)
Packit 6f3914
Packit 6f3914
    def environments_iter(self):
Packit 6f3914
        # :api
Packit 6f3914
        return (self._build_environment(e) for e in self._i.environments)
Packit 6f3914
Packit 6f3914
    @property
Packit 6f3914
    def groups(self):
Packit 6f3914
        # :api
Packit 6f3914
        return sorted(self.groups_iter(), key=_fn_display_order)
Packit 6f3914
Packit 6f3914
    def _group_by_id(self, id_):
Packit 6f3914
        assert dnf.util.is_string_type(id_)
Packit 6f3914
        return dnf.util.first(g for g in self.groups_iter() if g.id == id_)
Packit 6f3914
Packit 6f3914
    def group_by_pattern(self, pattern, case_sensitive=False):
Packit 6f3914
        # :api
Packit 6f3914
        assert dnf.util.is_string_type(pattern)
Packit 6f3914
        grps = self.groups_by_pattern(pattern, case_sensitive)
Packit 6f3914
        return _first_if_iterable(grps)
Packit 6f3914
Packit 6f3914
    def groups_by_pattern(self, pattern, case_sensitive=False):
Packit 6f3914
        # :api
Packit 6f3914
        assert dnf.util.is_string_type(pattern)
Packit 6f3914
        grps = _by_pattern(pattern, case_sensitive, list(self.groups_iter()))
Packit 6f3914
        return sorted(grps, key=_fn_display_order)
Packit 6f3914
Packit 6f3914
    def groups_iter(self):
Packit 6f3914
        # :api
Packit 6f3914
        return (self._build_group(g) for g in self._i.groups)
Packit 6f3914
Packit 6f3914
class CompsTransPkg(object):
Packit 6f3914
    def __init__(self, pkg_or_name):
Packit 6f3914
        if dnf.util.is_string_type(pkg_or_name):
Packit 6f3914
            # from package name
Packit 6f3914
            self.basearchonly = False
Packit 6f3914
            self.name = pkg_or_name
Packit 6f3914
            self.optional = True
Packit 6f3914
            self.requires = None
Packit 6f3914
        elif isinstance(pkg_or_name, libdnf.transaction.CompsGroupPackage):
Packit 6f3914
            # from swdb package
Packit 6f3914
            # TODO:
Packit 6f3914
            self.basearchonly = False
Packit 6f3914
            # self.basearchonly = pkg_or_name.basearchonly
Packit 6f3914
            self.name = pkg_or_name.getName()
Packit 6f3914
            self.optional = pkg_or_name.getPackageType() & libcomps.PACKAGE_TYPE_OPTIONAL
Packit 6f3914
            # TODO:
Packit 6f3914
            self.requires = None
Packit 6f3914
            # self.requires = pkg_or_name.requires
Packit 6f3914
        else:
Packit 6f3914
            # from comps package
Packit 6f3914
            self.basearchonly = pkg_or_name.basearchonly
Packit 6f3914
            self.name = pkg_or_name.name
Packit 6f3914
            self.optional = pkg_or_name.type & libcomps.PACKAGE_TYPE_OPTIONAL
Packit 6f3914
            self.requires = pkg_or_name.requires
Packit 6f3914
Packit 6f3914
    def __eq__(self, other):
Packit 6f3914
        return (self.name == other.name and
Packit 6f3914
                self.basearchonly == self.basearchonly and
Packit 6f3914
                self.optional == self.optional and
Packit 6f3914
                self.requires == self.requires)
Packit 6f3914
Packit 6f3914
    def __str__(self):
Packit 6f3914
        return self.name
Packit 6f3914
Packit 6f3914
    def __hash__(self):
Packit 6f3914
        return hash((self.name,
Packit 6f3914
                    self.basearchonly,
Packit 6f3914
                    self.optional,
Packit 6f3914
                    self.requires))
Packit 6f3914
Packit 6f3914
class TransactionBunch(object):
Packit 6f3914
    def __init__(self):
Packit 6f3914
        self._install = set()
Packit 6f3914
        self._install_opt = set()
Packit 6f3914
        self._remove = set()
Packit 6f3914
        self._upgrade = set()
Packit 6f3914
Packit 6f3914
    def __iadd__(self, other):
Packit 6f3914
        self._install.update(other._install)
Packit 6f3914
        self._install_opt.update(other._install_opt)
Packit 6f3914
        self._upgrade.update(other._upgrade)
Packit 6f3914
        self._remove = (self._remove | other._remove) - \
Packit 6f3914
            self._install - self._install_opt - self._upgrade
Packit 6f3914
        return self
Packit 6f3914
Packit 6f3914
    def __len__(self):
Packit 6f3914
        return len(self.install) + len(self.install_opt) + len(self.upgrade) + len(self.remove)
Packit 6f3914
Packit 6f3914
    @staticmethod
Packit 6f3914
    def _set_value(param, val):
Packit 6f3914
        for item in val:
Packit 6f3914
            if isinstance(item, CompsTransPkg):
Packit 6f3914
                param.add(item)
Packit 6f3914
            else:
Packit 6f3914
                param.add(CompsTransPkg(item))
Packit 6f3914
Packit 6f3914
    @property
Packit 6f3914
    def install(self):
Packit 6f3914
        """
Packit 6f3914
        Packages to be installed with strict=True - transaction will
Packit 6f3914
        fail if they cannot be installed due to dependency errors etc.
Packit 6f3914
        """
Packit 6f3914
        return self._install
Packit 6f3914
Packit 6f3914
    @install.setter
Packit 6f3914
    def install(self, value):
Packit 6f3914
        self._set_value(self._install, value)
Packit 6f3914
Packit 6f3914
    @property
Packit 6f3914
    def install_opt(self):
Packit 6f3914
        """
Packit 6f3914
        Packages to be installed with strict=False - they will be
Packit 6f3914
        skipped if they cannot be installed
Packit 6f3914
        """
Packit 6f3914
        return self._install_opt
Packit 6f3914
Packit 6f3914
    @install_opt.setter
Packit 6f3914
    def install_opt(self, value):
Packit 6f3914
        self._set_value(self._install_opt, value)
Packit 6f3914
Packit 6f3914
    @property
Packit 6f3914
    def remove(self):
Packit 6f3914
        return self._remove
Packit 6f3914
Packit 6f3914
    @remove.setter
Packit 6f3914
    def remove(self, value):
Packit 6f3914
        self._set_value(self._remove, value)
Packit 6f3914
Packit 6f3914
    @property
Packit 6f3914
    def upgrade(self):
Packit 6f3914
        return self._upgrade
Packit 6f3914
Packit 6f3914
    @upgrade.setter
Packit 6f3914
    def upgrade(self, value):
Packit 6f3914
        self._set_value(self._upgrade, value)
Packit 6f3914
Packit 6f3914
Packit 6f3914
class Solver(object):
Packit 6f3914
    def __init__(self, history, comps, reason_fn):
Packit 6f3914
        self.history = history
Packit 6f3914
        self.comps = comps
Packit 6f3914
        self._reason_fn = reason_fn
Packit 6f3914
Packit 6f3914
    @staticmethod
Packit 6f3914
    def _mandatory_group_set(env):
Packit 6f3914
        return {grp.id for grp in env.mandatory_groups}
Packit 6f3914
Packit 6f3914
    @staticmethod
Packit 6f3914
    def _full_package_set(grp):
Packit 6f3914
        return {pkg.getName() for pkg in grp.mandatory_packages +
Packit 6f3914
                grp.default_packages + grp.optional_packages +
Packit 6f3914
                grp.conditional_packages}
Packit 6f3914
Packit 6f3914
    @staticmethod
Packit 6f3914
    def _pkgs_of_type(group, pkg_types, exclude=[]):
Packit 6f3914
        def filter(pkgs):
Packit 6f3914
            return [pkg for pkg in pkgs
Packit 6f3914
                    if pkg.name not in exclude]
Packit 6f3914
Packit 6f3914
        pkgs = set()
Packit 6f3914
        if pkg_types & MANDATORY:
Packit 6f3914
            pkgs.update(filter(group.mandatory_packages))
Packit 6f3914
        if pkg_types & DEFAULT:
Packit 6f3914
            pkgs.update(filter(group.default_packages))
Packit 6f3914
        if pkg_types & OPTIONAL:
Packit 6f3914
            pkgs.update(filter(group.optional_packages))
Packit 6f3914
        if pkg_types & CONDITIONAL:
Packit 6f3914
            pkgs.update(filter(group.conditional_packages))
Packit 6f3914
        return pkgs
Packit 6f3914
Packit 6f3914
    def _removable_pkg(self, pkg_name):
Packit 6f3914
        assert dnf.util.is_string_type(pkg_name)
Packit 6f3914
        return self.history.group.is_removable_pkg(pkg_name)
Packit 6f3914
Packit 6f3914
    def _removable_grp(self, group_id):
Packit 6f3914
        assert dnf.util.is_string_type(group_id)
Packit 6f3914
        return self.history.env.is_removable_group(group_id)
Packit 6f3914
Packit 6f3914
    def _environment_install(self, env_id, pkg_types, exclude, strict=True, exclude_groups=None):
Packit 6f3914
        assert dnf.util.is_string_type(env_id)
Packit 6f3914
        comps_env = self.comps._environment_by_id(env_id)
Packit 6f3914
        swdb_env = self.history.env.new(env_id, comps_env.name, comps_env.ui_name, pkg_types)
Packit 6f3914
        self.history.env.install(swdb_env)
Packit 6f3914
Packit 6f3914
        trans = TransactionBunch()
Packit 6f3914
        for comps_group in comps_env.mandatory_groups:
Packit 6f3914
            if exclude_groups and comps_group.id in exclude_groups:
Packit 6f3914
                continue
Packit 6f3914
            trans += self._group_install(comps_group.id, pkg_types, exclude, strict)
Packit 6f3914
            swdb_env.addGroup(comps_group.id, True, MANDATORY)
Packit 6f3914
Packit 6f3914
        for comps_group in comps_env.optional_groups:
Packit 6f3914
            if exclude_groups and comps_group.id in exclude_groups:
Packit 6f3914
                continue
Packit 6f3914
            swdb_env.addGroup(comps_group.id, False, OPTIONAL)
Packit 6f3914
            # TODO: if a group is already installed, mark it as installed?
Packit 6f3914
        return trans
Packit 6f3914
Packit 6f3914
    def _environment_remove(self, env_id):
Packit 6f3914
        assert dnf.util.is_string_type(env_id) is True
Packit 6f3914
        swdb_env = self.history.env.get(env_id)
Packit 6f3914
        if not swdb_env:
Packit 6f3914
            raise CompsError(_("Environment '%s' is not installed.") % env_id)
Packit 6f3914
Packit 6f3914
        self.history.env.remove(swdb_env)
Packit 6f3914
Packit 6f3914
        trans = TransactionBunch()
Packit 6f3914
        group_ids = set([i.getGroupId() for i in swdb_env.getGroups()])
Packit 6f3914
        for group_id in group_ids:
Packit 6f3914
            if not self._removable_grp(group_id):
Packit 6f3914
                continue
Packit 6f3914
            trans += self._group_remove(group_id)
Packit 6f3914
        return trans
Packit 6f3914
Packit 6f3914
    def _environment_upgrade(self, env_id):
Packit 6f3914
        assert dnf.util.is_string_type(env_id)
Packit 6f3914
        comps_env = self.comps._environment_by_id(env_id)
Packit 6f3914
        swdb_env = self.history.env.get(comps_env.id)
Packit 6f3914
        if not swdb_env:
Packit 6f3914
            raise CompsError(_("Environment '%s' is not installed.") % env_id)
Packit 6f3914
        if not comps_env:
Packit 6f3914
            raise CompsError(_("Environment '%s' is not available.") % env_id)
Packit 6f3914
Packit 6f3914
        old_set = set([i.getGroupId() for i in swdb_env.getGroups() if i.getInstalled()])
Packit 6f3914
        pkg_types = swdb_env.getPackageTypes()
Packit 6f3914
Packit 6f3914
        # create a new record for current transaction
Packit 6f3914
        swdb_env = self.history.env.new(comps_env.id, comps_env.name, comps_env.ui_name, pkg_types)
Packit 6f3914
Packit 6f3914
        trans = TransactionBunch()
Packit 6f3914
        for comps_group in comps_env.mandatory_groups:
Packit 6f3914
            if comps_group.id in old_set:
Packit 6f3914
                # upgrade existing group
Packit 6f3914
                trans += self._group_upgrade(comps_group.id)
Packit 6f3914
            else:
Packit 6f3914
                # install new group
Packit 6f3914
                trans += self._group_install(comps_group.id, pkg_types)
Packit 6f3914
            swdb_env.addGroup(comps_group.id, True, MANDATORY)
Packit 6f3914
Packit 6f3914
        for comps_group in comps_env.optional_groups:
Packit 6f3914
            swdb_env.addGroup(comps_group.id, False, OPTIONAL)
Packit 6f3914
            # TODO: if a group is already installed, mark it as installed?
Packit 6f3914
        self.history.env.upgrade(swdb_env)
Packit 6f3914
        return trans
Packit 6f3914
Packit 6f3914
    def _group_install(self, group_id, pkg_types, exclude=None, strict=True, exclude_groups=None):
Packit 6f3914
        assert dnf.util.is_string_type(group_id)
Packit 6f3914
        comps_group = self.comps._group_by_id(group_id)
Packit 6f3914
        if not comps_group:
Packit 6f3914
            raise ValueError(_("Group_id '%s' does not exist.") % ucd(group_id))
Packit 6f3914
Packit 6f3914
        swdb_group = self.history.group.new(group_id, comps_group.name, comps_group.ui_name, pkg_types)
Packit 6f3914
        for i in comps_group.packages_iter():
Packit 6f3914
            swdb_group.addPackage(i.name, False, i.type)
Packit 6f3914
        self.history.group.install(swdb_group)
Packit 6f3914
Packit 6f3914
        trans = TransactionBunch()
Packit 6f3914
        # TODO: remove exclude
Packit 6f3914
        if strict:
Packit 6f3914
            trans.install.update(self._pkgs_of_type(comps_group, pkg_types, exclude=[]))
Packit 6f3914
        else:
Packit 6f3914
            trans.install_opt.update(self._pkgs_of_type(comps_group, pkg_types, exclude=[]))
Packit 6f3914
        return trans
Packit 6f3914
Packit 6f3914
    def _group_remove(self, group_id):
Packit 6f3914
        assert dnf.util.is_string_type(group_id)
Packit 6f3914
        swdb_group = self.history.group.get(group_id)
Packit 6f3914
        self.history.group.remove(swdb_group)
Packit 6f3914
Packit 6f3914
        trans = TransactionBunch()
Packit 6f3914
        trans.remove = {pkg for pkg in swdb_group.getPackages() if self._removable_pkg(pkg.getName())}
Packit 6f3914
        return trans
Packit 6f3914
Packit 6f3914
    def _group_upgrade(self, group_id):
Packit 6f3914
        assert dnf.util.is_string_type(group_id)
Packit 6f3914
        comps_group = self.comps._group_by_id(group_id)
Packit 6f3914
        swdb_group = self.history.group.get(group_id)
Packit 6f3914
        exclude = []
Packit 6f3914
Packit 6f3914
        if not swdb_group:
Packit 6f3914
            argument = comps_group.ui_name if comps_group else group_id
Packit 6f3914
            raise CompsError(_("Module or Group '%s' is not installed.") % argument)
Packit 6f3914
        if not comps_group:
Packit 6f3914
            raise CompsError(_("Module or Group '%s' is not available.") % group_id)
Packit 6f3914
        pkg_types = swdb_group.getPackageTypes()
Packit 6f3914
        old_set = set([i.getName() for i in swdb_group.getPackages()])
Packit 6f3914
        new_set = self._pkgs_of_type(comps_group, pkg_types, exclude)
Packit 6f3914
Packit 6f3914
        # create a new record for current transaction
Packit 6f3914
        swdb_group = self.history.group.new(group_id, comps_group.name, comps_group.ui_name, pkg_types)
Packit 6f3914
        for i in comps_group.packages_iter():
Packit 6f3914
            swdb_group.addPackage(i.name, False, i.type)
Packit 6f3914
        self.history.group.upgrade(swdb_group)
Packit 6f3914
Packit 6f3914
        trans = TransactionBunch()
Packit 6f3914
        trans.install = {pkg for pkg in new_set if pkg.name not in old_set}
Packit 6f3914
        trans.remove = {name for name in old_set
Packit 6f3914
                        if name not in [pkg.name for pkg in new_set]}
Packit 6f3914
        trans.upgrade = {pkg for pkg in new_set if pkg.name in old_set}
Packit 6f3914
        return trans
Packit 6f3914
Packit 6f3914
    def _exclude_packages_from_installed_groups(self, base):
Packit 6f3914
        for group in self.persistor.groups:
Packit 6f3914
            p_grp = self.persistor.group(group)
Packit 6f3914
            if p_grp.installed:
Packit 6f3914
                installed_pkg_names = \
Packit 6f3914
                    set(p_grp.full_list) - set(p_grp.pkg_exclude)
Packit 6f3914
                installed_pkgs = base.sack.query().installed().filterm(name=installed_pkg_names)
Packit 6f3914
                for pkg in installed_pkgs:
Packit 6f3914
                    base._goal.install(pkg)