comps-wrapper: Port to libcomps

Instead of replacing yum.comps with an something from DNF, we can go
directly to libcomps. DNF does not have the equivalent functionality
(particularly it's impossible to load comps from file directly).

We would have depended on libcomps anyway transitively, so this is not a
big deal.

Fixes: #587
Signed-off-by: Lubomír Sedlář <lsedlar@redhat.com>
This commit is contained in:
Lubomír Sedlář 2017-04-12 14:50:51 +02:00
parent 39c3f42f77
commit 3861be3e08
2 changed files with 78 additions and 104 deletions

View File

@ -17,6 +17,7 @@ BuildRequires: python-jsonschema
BuildRequires: python-enum34 BuildRequires: python-enum34
BuildRequires: python2-dnf BuildRequires: python2-dnf
BuildRequires: python2-multilib BuildRequires: python2-multilib
BuildRequires: python2-libcomps
Requires: createrepo >= 0.4.11 Requires: createrepo >= 0.4.11
Requires: yum => 3.4.3-28 Requires: yum => 3.4.3-28
@ -44,6 +45,7 @@ Requires: libguestfs-tools-c
Requires: python-enum34 Requires: python-enum34
Requires: python2-dnf Requires: python2-dnf
Requires: python2-multilib Requires: python2-multilib
Requires: python2-libcomps
BuildArch: noarch BuildArch: noarch

View File

@ -14,10 +14,12 @@
# along with this program; if not, see <https://gnu.org/licenses/>. # along with this program; if not, see <https://gnu.org/licenses/>.
import sys import collections
from operator import attrgetter
import fnmatch import fnmatch
import libcomps
import sys
import xml.dom.minidom import xml.dom.minidom
import yum.comps
if sys.version_info[:2] < (2, 7): if sys.version_info[:2] < (2, 7):
@ -36,17 +38,25 @@ if sys.version_info[:2] < (2, 7):
xml.dom.minidom.Element = Element xml.dom.minidom.Element = Element
TYPE_MAPPING = collections.OrderedDict([
(libcomps.PACKAGE_TYPE_MANDATORY, 'mandatory'),
(libcomps.PACKAGE_TYPE_DEFAULT, 'default'),
(libcomps.PACKAGE_TYPE_OPTIONAL, 'optional'),
(libcomps.PACKAGE_TYPE_CONDITIONAL, 'conditional'),
])
class CompsWrapper(object): class CompsWrapper(object):
"""Class for reading and retreiving information from comps XML files""" """Class for reading and retreiving information from comps XML files"""
def __init__(self, comps_file): def __init__(self, comps_file):
self.comps = yum.comps.Comps() self.comps = libcomps.Comps()
self.comps.add(comps_file) self.comps.fromxml_f(comps_file)
self.comps_file = comps_file self.comps_file = comps_file
def get_comps_groups(self): def get_comps_groups(self):
"""Return a list of group IDs.""" """Return a list of group IDs."""
return [group.groupid for group in self.comps.get_groups()] return [group.id for group in self.comps.groups]
def write_comps(self, comps_obj=None, target_file=None): def write_comps(self, comps_obj=None, target_file=None):
if not comps_obj: if not comps_obj:
@ -63,31 +73,20 @@ class CompsWrapper(object):
doc = impl.createDocument(None, "comps", doctype) doc = impl.createDocument(None, "comps", doctype)
msg_elem = doc.documentElement msg_elem = doc.documentElement
groups = {} for group in sorted(self.comps.groups, key=attrgetter('id')):
for group_obj in self.comps.get_groups():
groupid = group_obj.groupid
groups[groupid] = {"group_obj": group_obj}
group_names = groups.keys()
group_names.sort()
for group_key in group_names:
group = groups[group_key]["group_obj"]
group_node = doc.createElement("group") group_node = doc.createElement("group")
msg_elem.appendChild(group_node) msg_elem.appendChild(group_node)
id_node = doc.createElement("id") id_node = doc.createElement("id")
id_node.appendChild(doc.createTextNode(group.groupid)) id_node.appendChild(doc.createTextNode(group.id))
group_node.appendChild(id_node) group_node.appendChild(id_node)
name_node = doc.createElement("name") name_node = doc.createElement("name")
name_node.appendChild(doc.createTextNode(group.name)) name_node.appendChild(doc.createTextNode(group.name))
group_node.appendChild(name_node) group_node.appendChild(name_node)
langs = group.translated_name.keys() for lang in sorted(group.name_by_lang):
langs.sort() text = group.name_by_lang[lang]
for lang in langs:
text = group.translated_name[lang].decode("UTF-8")
node = doc.createElement("name") node = doc.createElement("name")
node.setAttribute("xml:lang", lang) node.setAttribute("xml:lang", lang)
node.appendChild(doc.createTextNode(text)) node.appendChild(doc.createTextNode(text))
@ -95,13 +94,11 @@ class CompsWrapper(object):
node = doc.createElement("description") node = doc.createElement("description")
group_node.appendChild(node) group_node.appendChild(node)
if group.description and group.description != "": if group.desc and group.desc != "":
node.appendChild(doc.createTextNode(group.description)) node.appendChild(doc.createTextNode(group.desc))
langs = group.translated_description.keys()
langs.sort()
for lang in langs: for lang in sorted(group.desc_by_lang):
text = group.translated_description[lang].decode("UTF-8") text = group.desc_by_lang[lang]
node = doc.createElement("description") node = doc.createElement("description")
node.setAttribute("xml:lang", lang) node.setAttribute("xml:lang", lang)
node.appendChild(doc.createTextNode(text)) node.appendChild(doc.createTextNode(text))
@ -112,64 +109,60 @@ class CompsWrapper(object):
group_node.appendChild(node) group_node.appendChild(node)
node = doc.createElement("uservisible") node = doc.createElement("uservisible")
node.appendChild(doc.createTextNode("true" if group.user_visible else "false")) node.appendChild(doc.createTextNode("true" if group.uservisible else "false"))
group_node.appendChild(node) group_node.appendChild(node)
if group.langonly: if group.lang_only:
node = doc.createElement("langonly") node = doc.createElement("langonly")
node.appendChild(doc.createTextNode(group.langonly)) node.appendChild(doc.createTextNode(group.lang_only))
group_node.appendChild(node) group_node.appendChild(node)
packagelist = doc.createElement("packagelist") packagelist = doc.createElement("packagelist")
for package_type in ("mandatory", "default", "optional", "conditional"): packages_by_type = collections.defaultdict(list)
packages = getattr(group, package_type + "_packages").keys() for pkg in group.packages:
packages.sort() packages_by_type[TYPE_MAPPING[pkg.type]].append(pkg)
for package in packages:
for type_name in TYPE_MAPPING.values():
for package in sorted(packages_by_type[type_name], key=attrgetter('name')):
node = doc.createElement("packagereq") node = doc.createElement("packagereq")
node.appendChild(doc.createTextNode(package)) node.appendChild(doc.createTextNode(package.name))
node.setAttribute("type", package_type) node.setAttribute("type", type_name)
packagelist.appendChild(node) packagelist.appendChild(node)
if package_type == "conditional": if type_name == "conditional":
node.setAttribute("requires", group.conditional_packages[package]) node.setAttribute("requires", pkg.requires)
group_node.appendChild(packagelist) group_node.appendChild(packagelist)
categories = self.comps.get_categories() for category in self.comps.categories:
for category in categories: groups = set(x.name for x in category.group_ids) & set(self.get_comps_groups())
groups = set(category.groups) & set([i.groupid for i in self.comps.get_groups()])
if not groups: if not groups:
continue continue
cat_node = doc.createElement("category") cat_node = doc.createElement("category")
msg_elem.appendChild(cat_node) msg_elem.appendChild(cat_node)
id_node = doc.createElement("id") id_node = doc.createElement("id")
id_node.appendChild(doc.createTextNode(category.categoryid)) id_node.appendChild(doc.createTextNode(category.id))
cat_node.appendChild(id_node) cat_node.appendChild(id_node)
name_node = doc.createElement("name") name_node = doc.createElement("name")
name_node.appendChild(doc.createTextNode(category.name)) name_node.appendChild(doc.createTextNode(category.name))
cat_node.appendChild(name_node) cat_node.appendChild(name_node)
langs = category.translated_name.keys() for lang in sorted(category.name_by_lang):
langs.sort() text = category.name_by_lang[lang]
for lang in langs:
text = category.translated_name[lang].decode("UTF-8")
node = doc.createElement("name") node = doc.createElement("name")
node.setAttribute("xml:lang", lang) node.setAttribute("xml:lang", lang)
node.appendChild(doc.createTextNode(text)) node.appendChild(doc.createTextNode(text))
cat_node.appendChild(node) cat_node.appendChild(node)
if category.description and category.description != "": if category.desc and category.desc != "":
node = doc.createElement("description") node = doc.createElement("description")
node.appendChild(doc.createTextNode(category.description)) node.appendChild(doc.createTextNode(category.desc))
cat_node.appendChild(node) cat_node.appendChild(node)
langs = category.translated_description.keys()
langs.sort()
for lang in langs: for lang in sorted(category.desc_by_lang):
text = category.translated_description[lang].decode("UTF-8") text = category.desc_by_lang[lang]
node = doc.createElement("description") node = doc.createElement("description")
node.setAttribute("xml:lang", lang) node.setAttribute("xml:lang", lang)
node.appendChild(doc.createTextNode(text)) node.appendChild(doc.createTextNode(text))
@ -190,44 +183,37 @@ class CompsWrapper(object):
cat_node.appendChild(grouplist_node) cat_node.appendChild(grouplist_node)
# XXX environments = sorted(self.comps.environments, key=attrgetter('id'))
environments = self.comps.get_environments()
if environments: if environments:
for environment in environments: for environment in environments:
groups = set(environment.groups) & set([i.groupid for i in self.comps.get_groups()]) groups = set(x.name for x in environment.group_ids) & set(self.get_comps_groups())
if not groups: if not groups:
continue continue
env_node = doc.createElement("environment") env_node = doc.createElement("environment")
msg_elem.appendChild(env_node) msg_elem.appendChild(env_node)
id_node = doc.createElement("id") id_node = doc.createElement("id")
id_node.appendChild(doc.createTextNode(environment.environmentid)) id_node.appendChild(doc.createTextNode(environment.id))
env_node.appendChild(id_node) env_node.appendChild(id_node)
name_node = doc.createElement("name") name_node = doc.createElement("name")
name_node.appendChild(doc.createTextNode(environment.name)) name_node.appendChild(doc.createTextNode(environment.name))
env_node.appendChild(name_node) env_node.appendChild(name_node)
langs = environment.translated_name.keys() for lang in sorted(environment.name_by_lang):
langs.sort() text = environment.name_by_lang[lang]
for lang in langs:
text = environment.translated_name[lang].decode("UTF-8")
node = doc.createElement("name") node = doc.createElement("name")
node.setAttribute("xml:lang", lang) node.setAttribute("xml:lang", lang)
node.appendChild(doc.createTextNode(text)) node.appendChild(doc.createTextNode(text))
env_node.appendChild(node) env_node.appendChild(node)
if environment.description: if environment.desc:
node = doc.createElement("description") node = doc.createElement("description")
node.appendChild(doc.createTextNode(environment.description)) node.appendChild(doc.createTextNode(environment.desc))
env_node.appendChild(node) env_node.appendChild(node)
langs = environment.translated_description.keys() for lang in sorted(environment.desc_by_lang):
langs.sort() text = environment.desc_by_lang[lang]
for lang in langs:
text = environment.translated_description[lang].decode("UTF-8")
node = doc.createElement("description") node = doc.createElement("description")
node.setAttribute("xml:lang", lang) node.setAttribute("xml:lang", lang)
node.appendChild(doc.createTextNode(text)) node.appendChild(doc.createTextNode(text))
@ -246,26 +232,23 @@ class CompsWrapper(object):
grouplist_node.appendChild(node) grouplist_node.appendChild(node)
env_node.appendChild(grouplist_node) env_node.appendChild(grouplist_node)
optionids = sorted(environment.options) if environment.option_ids:
if optionids:
optionlist_node = doc.createElement("optionlist") optionlist_node = doc.createElement("optionlist")
for optionid in optionids: for optionid in sorted(x.name for x in environment.option_ids):
node = doc.createElement("groupid") node = doc.createElement("groupid")
node.appendChild(doc.createTextNode(optionid)) node.appendChild(doc.createTextNode(optionid))
optionlist_node.appendChild(node) optionlist_node.appendChild(node)
env_node.appendChild(optionlist_node) env_node.appendChild(optionlist_node)
# XXX if self.comps.langpacks:
langpacks = self.comps.get_langpacks()
if langpacks:
lang_node = doc.createElement("langpacks") lang_node = doc.createElement("langpacks")
msg_elem.appendChild(lang_node) msg_elem.appendChild(lang_node)
for langpack in sorted(langpacks, key=lambda x: x['name']): for name in sorted(self.comps.langpacks):
match_node = doc.createElement("match") match_node = doc.createElement("match")
match_node.setAttribute("name", langpack["name"]) match_node.setAttribute("name", name)
match_node.setAttribute("install", langpack["install"]) match_node.setAttribute("install", self.comps.langpacks[name])
lang_node.appendChild(match_node) lang_node.appendChild(match_node)
return doc return doc
@ -273,7 +256,7 @@ class CompsWrapper(object):
if group_dict["default"] is not None: if group_dict["default"] is not None:
group_obj.default = group_dict["default"] group_obj.default = group_dict["default"]
if group_dict["uservisible"] is not None: if group_dict["uservisible"] is not None:
group_obj.user_visible = group_dict["uservisible"] group_obj.uservisible = group_dict["uservisible"]
def _tweak_env(self, env_obj, env_dict): def _tweak_env(self, env_obj, env_dict):
if env_dict["display_order"] is not None: if env_dict["display_order"] is not None:
@ -282,7 +265,7 @@ class CompsWrapper(object):
# write actual display order back to env_dict # write actual display order back to env_dict
env_dict["display_order"] = env_obj.display_order env_dict["display_order"] = env_obj.display_order
# write group list back to env_dict # write group list back to env_dict
env_dict["groups"] = env_obj.groups[:] env_dict["groups"] = [g.name for g in env_obj.group_ids]
def filter_groups(self, group_dicts): def filter_groups(self, group_dicts):
"""Filter groups according to group definitions in group_dicts. """Filter groups according to group definitions in group_dicts.
@ -295,33 +278,27 @@ class CompsWrapper(object):
""" """
to_remove = [] to_remove = []
for group_obj in self.comps.groups: for group_obj in self.comps.groups:
found = False
for group_dict in group_dicts: for group_dict in group_dicts:
if group_dict["glob"]: if group_dict["glob"]:
if fnmatch.fnmatch(group_obj.groupid, group_dict["name"]): if fnmatch.fnmatch(group_obj.id, group_dict["name"]):
found = True
self._tweak_group(group_obj, group_dict) self._tweak_group(group_obj, group_dict)
break break
else: else:
if group_obj.groupid == group_dict["name"]: if group_obj.id == group_dict["name"]:
self._tweak_group(group_obj, group_dict) self._tweak_group(group_obj, group_dict)
found = True
break break
else:
to_remove.append(group_obj)
if not found: for group in to_remove:
to_remove.append(group_obj.groupid) self.comps.groups.remove(group)
if to_remove:
for key, value in self.comps._groups.items():
if key in to_remove:
del self.comps._groups[key]
# Sanity check to report warnings on unused group_dicts # Sanity check to report warnings on unused group_dicts
unmatched = set() unmatched = set()
for group_dict in group_dicts: for group_dict in group_dicts:
matcher = fnmatch.fnmatch if group_dict["glob"] else lambda x, y: x == y matcher = fnmatch.fnmatch if group_dict["glob"] else lambda x, y: x == y
for group_obj in self.comps.groups: for group_obj in self.comps.groups:
if matcher(group_obj.groupid, group_dict["name"]): if matcher(group_obj.id, group_dict["name"]):
break break
else: else:
unmatched.add(group_dict["name"]) unmatched.add(group_dict["name"])
@ -336,17 +313,12 @@ class CompsWrapper(object):
""" """
to_remove = [] to_remove = []
for env_obj in self.comps.environments: for env_obj in self.comps.environments:
found = False
for env_dict in env_dicts: for env_dict in env_dicts:
if env_obj.environmentid == env_dict["name"]: if env_obj.id == env_dict["name"]:
self._tweak_env(env_obj, env_dict) self._tweak_env(env_obj, env_dict)
found = True
break break
else:
to_remove.append(env_obj)
if not found: for env in to_remove:
to_remove.append(env_obj.environmentid) self.comps.environments.remove(env)
if to_remove:
for key, value in self.comps._environments.items():
if key in to_remove:
del self.comps._environments[key]