1
# This program is free software; you can redistribute it and/or modify
2
# it under the terms of the GNU General Public License as published by
3
# the Free Software Foundation; either version 2 of the License, or
4
# (at your option) any later version.
6
# This program is distributed in the hope that it will be useful,
7
# but WITHOUT ANY WARRANTY; without even the implied warranty of
8
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9
# GNU Library General Public License for more details.
11
# You should have received a copy of the GNU General Public License
12
# along with this program; if not, write to the Free Software
13
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
14
# Copyright 2005 Duke University
4
from cElementTree import iterparse
14
if s.lower() in ('yes', 'true'):
18
def getattrib(elem, key, default=None):
19
'''Retrieve an attribute named key from elem. Return default if not found.
21
This is required because ElementTree includes namespace information in the
22
name of each attribute.
24
for k, v in elem.attrib.iteritems():
25
k = k.split('}', 1)[-1]
30
class CompsException(exceptions.Exception):
34
def __init__(self, comps, elem=None):
36
self.user_visible = TRUE
38
self.removable = FALSE
17
from constants import *
19
from xml.etree import cElementTree
22
iterparse = cElementTree.iterparse
23
from Errors import CompsException
24
#FIXME - compsexception isn't caught ANYWHERE so it's pointless to raise it
25
# switch all compsexceptions to grouperrors after api break
27
lang_attr = '{http://www.w3.org/XML/1998/namespace}lang'
29
def parse_boolean(strng):
30
if BOOLEAN_STATES.has_key(strng.lower()):
31
return BOOLEAN_STATES[strng.lower()]
35
def parse_number(strng):
39
def __init__(self, elem=None):
40
self.user_visible = True
40
44
self.description = ""
41
45
self.translated_name = {}
42
46
self.translated_description = {}
47
self.mandatory_packages = {}
48
self.optional_packages = {}
49
self.default_packages = {}
50
self.conditional_packages = {}
51
self.langonly = None ## what the hell is this?
53
self.display_order = 1024
54
self.installed = False
50
# FIXME: this is a horrible, horrible hack. but we can't stick it
51
# in with the package without making the tuple larger and that's
52
# certain to break something and I don't have time to track them
53
# all down. so we have to keep separate track of any "requirements"
54
# the package has to have to be installed. this is *only* used right
55
# now for some i18n packages. if it gets used for anything else, it
56
# is guaranteed to break within anaconda. jlk - 12 aug 2002
57
self.pkgConditionals = {}
64
def _packageiter(self):
65
# Gah, FIXME: real iterator/class
66
lst = self.mandatory_packages.keys() + \
67
self.optional_packages.keys() + \
68
self.default_packages.keys() + \
69
self.conditional_packages.keys()
73
packages = property(_packageiter)
75
def nameByLang(self, lang):
76
if self.translated_name.has_key[lang]:
77
return self.translated_name[lang]
82
def descriptionByLang(self, lang):
83
if self.translated_description.has_key[lang]:
84
return self.translated_description[lang]
86
return self.description
62
88
def parse(self, elem):
65
if child.tag == 'name':
93
if self.groupid is not None:
97
elif child.tag == 'name':
68
100
text = text.encode('utf8')
69
lang = getattrib(child, 'lang')
102
lang = child.attrib.get(lang_attr)
71
104
self.translated_name[lang] = text
75
elif child.tag == 'id':
77
if self.id is not None:
81
109
elif child.tag == 'description':
84
112
text = text.encode('utf8')
85
lang = getattrib(child, 'lang')
114
lang = child.attrib.get(lang_attr)
87
116
self.translated_description[lang] = text
89
self.description = text
119
self.description = text
91
121
elif child.tag == 'uservisible':
92
122
self.user_visible = parse_boolean(child.text)
124
elif child.tag == 'display_order':
125
self.display_order = parse_number(child.text)
94
127
elif child.tag == 'default':
95
128
self.default = parse_boolean(child.text)
97
elif child.tag == 'requires':
98
# FIXME: this isn't in use anymore
100
if text in self.requires:
102
self.requires.append(text)
104
elif child.tag == 'langonly':
130
elif child.tag == 'langonly': ## FIXME - what the hell is langonly?
105
131
text = child.text
106
132
if self.langonly is not None:
107
133
raise CompsException
108
134
self.langonly = text
110
136
elif child.tag == 'packagelist':
111
137
self.parse_package_list(child)
113
elif child.tag == 'grouplist':
114
self.parse_group_list(child)
116
139
def parse_package_list(self, packagelist_elem):
118
140
for child in packagelist_elem:
119
141
if child.tag == 'packagereq':
120
type = child.attrib.get('type')
142
genre = child.attrib.get('type')
124
if type not in ('mandatory', 'default', 'optional'):
146
if genre not in ('mandatory', 'default', 'optional', 'conditional'):
147
# just ignore bad package lines
127
150
package = child.text
128
self.packages[package] = (type, package)
130
# see note above about the hack this is.
131
reqs = child.attrib.get('requires')
133
self.pkgConditionals[package] = reqs
151
if genre == 'mandatory':
152
self.mandatory_packages[package] = 1
153
elif genre == 'default':
154
self.default_packages[package] = 1
155
elif genre == 'optional':
156
self.optional_packages[package] = 1
157
elif genre == 'conditional':
158
self.conditional_packages[package] = child.attrib.get('requires')
163
"""Add another group object to this object"""
165
# we only need package lists and any translation that we don't already
168
for pkg in obj.mandatory_packages:
169
self.mandatory_packages[pkg] = 1
170
for pkg in obj.default_packages:
171
self.default_packages[pkg] = 1
172
for pkg in obj.optional_packages:
173
self.optional_packages[pkg] = 1
174
for pkg in obj.conditional_packages:
175
self.conditional_packages[pkg] = obj.conditional_packages[pkg]
177
# Handle cases where a comps.xml without name & decription tags
178
# has been setup first, so the name & decription for this object is blank.
181
if self.name == '' and obj.name != '':
184
if self.description == '' and obj.description != '':
185
self.description = obj.description
187
# name and description translations
188
for lang in obj.translated_name:
189
if not self.translated_name.has_key(lang):
190
self.translated_name[lang] = obj.translated_name[lang]
192
for lang in obj.translated_description:
193
if not self.translated_description.has_key(lang):
194
self.translated_description[lang] = obj.translated_description[lang]
197
"""write out an xml stanza for the group object"""
201
<default>%s</default>
202
<uservisible>%s</uservisible>
203
<display_order>%s</display_order>\n""" % (self.groupid, str(self.default),
204
str(self.user_visible), self.display_order)
207
msg =+ """ <lang_only>%s</lang_only>""" % self.langonly
209
msg +=""" <name>%s</name>\n""" % self.name
210
for (lang, val) in self.translated_name.items():
211
msg += """ <name xml:lang="%s">%s</name>\n""" % (lang, val)
213
msg += """ <description>%s</description>\n""" % self.description
214
for (lang, val) in self.translated_description.items():
215
msg += """ <description xml:lang="%s">%s</description>\n""" % (lang, val)
217
msg += """ <packagelist>\n"""
218
for pkg in self.mandatory_packages.keys():
219
msg += """ <packagereq type="mandatory">%s</packagereq>\n""" % pkg
220
for pkg in self.default_packages.keys():
221
msg += """ <packagereq type="default">%s</packagereq>\n""" % pkg
222
for pkg in self.optional_packages.keys():
223
msg += """ <packagereq type="optional">%s</packagereq>\n""" % pkg
224
for (pkg, req) in self.conditional_packages.items():
225
msg += """ <packagereq type="conditional" requires="%s">%s</packagereq>\n""" % (pkg, req)
226
msg += """ </packagelist>\n"""
227
msg += """ </group>"""
234
class Category(object):
235
def __init__(self, elem=None):
237
self.categoryid = None
238
self.description = ""
239
self.translated_name = {}
240
self.translated_description = {}
241
self.display_order = 1024
250
def _groupiter(self):
251
return self._groups.keys()
253
groups = property(_groupiter)
255
def parse(self, elem):
257
if child.tag == 'id':
259
if self.categoryid is not None:
261
self.categoryid = myid
263
elif child.tag == 'name':
266
text = text.encode('utf8')
268
lang = child.attrib.get(lang_attr)
270
self.translated_name[lang] = text
274
elif child.tag == 'description':
277
text = text.encode('utf8')
279
lang = child.attrib.get(lang_attr)
281
self.translated_description[lang] = text
283
self.description = text
285
elif child.tag == 'grouplist':
286
self.parse_group_list(child)
288
elif child.tag == 'display_order':
289
self.display_order = parse_number(child.text)
135
291
def parse_group_list(self, grouplist_elem):
137
292
for child in grouplist_elem:
138
if child.tag == 'groupreq':
139
type = getattrib(child, 'type')
143
if type not in ('mandatory', 'default', 'optional'):
147
self.groups[group] = (type, group)
149
elif child.tag == 'metapkg':
150
type = getattrib(child, 'type')
153
if type not in ('default', 'optional'):
156
self.metapkgs[group] = (type, group)
158
def sanity_check(self):
163
for (type, package) in self.packages.values():
165
self.comps.packages[package]
168
# raise CompsException
172
def __init__(self, comps, elem=None):
176
self.supported = FALSE
177
self.excludearch = None
178
self.dependencies = []
183
def sanity_check(self):
184
if self.name == None:
187
def parse_dependency_list(self, packagedeps_elem):
188
for child in packagedeps_elem:
189
if child.tag == 'dependency':
190
self.dependencies.append(child.text)
192
def parse(self, group_elem):
193
for child in group_elem:
194
if child.tag == 'name':
195
self.name = child.text
197
elif child.tag == 'version':
198
self.version = child.text
200
elif child.tag == 'excludearch':
201
self.excludearch = child.text
203
elif child.tag == 'packagelist':
204
self.parse_package_list(child)
206
elif child.tag == 'supported':
207
self.supported = parse_boolean(child.text)
209
elif child.tag == 'dependencylist':
210
self.parse_dependency_list(child)
212
class GroupHierarchy(dict):
213
def __init__(self, comps, elem=None):
216
self.translations = {}
220
def parse(self, elem):
222
if child.tag == "category":
223
self.parse_category(child)
225
print "unhandled node in <comps.grouphierarchy>: " + child.tag
227
def parse_category(self, category_elem):
232
for child in category_elem:
233
if child.tag == "name":
236
text = text.encode('utf8')
237
lang = getattrib(child, 'lang')
239
translations[lang] = text
243
elif child.tag == "subcategories":
244
subs.extend(self.parse_subcategories(child))
247
print "unhandled node in <comps.grouphierarchy.category>: " + \
251
raise CompsException, "no name specified"
253
if not self.has_key(name):
254
self.order.append(name)
257
self[name] = self[name].extend(subs)
258
self.translations[name] = translations
260
def parse_subcategories(self, category_elem):
262
for child in category_elem:
263
if child.tag == "subcategory":
269
print "unhandled node in <comps.grouphierarchy.parse_category>:" + child.tag
293
if child.tag == 'groupid':
295
self._groups[groupid] = 1
298
"""Add another category object to this object"""
300
for grp in obj.groups:
301
self._groups[grp] = 1
303
# name and description translations
304
for lang in obj.translated_name:
305
if not self.translated_name.has_key(lang):
306
self.translated_name[lang] = obj.translated_name[lang]
308
for lang in obj.translated_description:
309
if not self.translated_description.has_key(lang):
310
self.translated_description[lang] = obj.translated_description[lang]
313
"""write out an xml stanza for the group object"""
317
<display_order>%s</display_order>\n""" % (self.categoryid, self.display_order)
319
msg +=""" <name>%s</name>\n""" % self.name
320
for (lang, val) in self.translated_name.items():
321
msg += """ <name xml:lang="%s">%s</name>\n""" % (lang, val)
323
msg += """ <description>%s</description>\n""" % self.description
324
for (lang, val) in self.translated_description.items():
325
msg += """ <description xml:lang="%s">%s</description>\n""" % (lang, val)
327
msg += """ <grouplist>\n"""
328
for grp in self.groups:
329
msg += """ <groupid>%s</groupid>\n""" % grp
330
msg += """ </grouplist>\n"""
331
msg += """ </category>\n"""
275
335
class Comps(object):
276
def __init__(self, srcfile=None):
284
def load(self, srcfile):
336
def __init__(self, overwrite_groups=False):
338
self._categories = {}
340
self.overwrite_groups = overwrite_groups
341
self.compiled = False # have groups been compiled into avail/installed
345
def __sort_order(self, item1, item2):
346
if item1.display_order > item2.display_order:
348
elif item1.display_order == item2.display_order:
353
def get_groups(self):
354
grps = self._groups.values()
355
grps.sort(self.__sort_order)
358
def get_categories(self):
359
cats = self._categories.values()
360
cats.sort(self.__sort_order)
364
groups = property(get_groups)
365
categories = property(get_categories)
369
def has_group(self, grpid):
370
exists = self.return_group(grpid)
377
def return_group(self, grpid):
378
if self._groups.has_key(grpid):
379
return self._groups[grpid]
381
# do matches against group names and ids, too
382
for group in self.groups:
383
names = [ group.name, group.groupid ]
384
names.extend(group.translated_name.values())
392
def add(self, srcfile = None):
285
396
if type(srcfile) == type('str'):
286
397
# srcfile is a filename string
287
398
infile = open(srcfile, 'rt')
289
400
# srcfile is a file object
404
self.compiled = False
292
406
parser = iterparse(infile)
294
for event, elem in parser:
295
if elem.tag == "group":
296
group = Group(self, elem)
297
self.groups[group.name] = group
299
elif elem.tag == "package":
300
package = Package(self, elem)
301
self.packages[package.name] = package
303
elif elem.tag == "grouphierarchy":
304
self.hierarchy = GroupHierarchy(self, elem)
307
for group in self.hierarchy.order:
309
print self.hierarchy[group]
311
for group in self.groups.values():
313
for package in self.packages.values():
315
print package.dependencies
316
for group in self.groups.values():
318
for package in self.packages.values():
319
package.sanity_check()
322
usage = "usage: pkggroup.py compsfile.xml"
408
for event, elem in parser:
409
if elem.tag == "group":
411
if self._groups.has_key(group.groupid):
412
thatgroup = self._groups[group.groupid]
415
self._groups[group.groupid] = group
417
if elem.tag == "category":
418
category = Category(elem)
419
if self._categories.has_key(category.categoryid):
420
thatcat = self._categories[category.categoryid]
421
thatcat.add(category)
423
self._categories[category.categoryid] = category
424
except SyntaxError, e:
425
raise CompsException, "comps file is empty/damaged"
430
def compile(self, pkgtuplist):
431
""" compile the groups into installed/available groups """
433
# convert the tuple list to a simple dict of pkgnames
435
for (n,a,e,v,r) in pkgtuplist:
436
inst_pkg_names[n] = 1
439
for group in self.groups:
440
# if there are mandatory packages in the group, then make sure
441
# they're all installed. if any are missing, then the group
443
if len(group.mandatory_packages) > 0:
444
group.installed = True
445
for pkgname in group.mandatory_packages:
446
if not inst_pkg_names.has_key(pkgname):
447
group.installed = False
449
# if it doesn't have any of those then see if it has ANY of the
450
# optional/default packages installed.
451
# If so - then the group is installed
453
check_pkgs = group.optional_packages.keys() + group.default_packages.keys() + group.conditional_packages.keys()
454
group.installed = False
455
for pkgname in check_pkgs:
456
if inst_pkg_names.has_key(pkgname):
457
group.installed = True
463
"""returns the xml of the comps files in this class, merged"""
465
if not self._groups and not self._categories:
468
msg = """<?xml version="1.0" encoding="UTF-8"?>
469
<!DOCTYPE comps PUBLIC "-//Red Hat, Inc.//DTD Comps info//EN" "comps.dtd">
473
for g in self.get_groups():
475
for c in self.get_categories():
478
msg += """</comps>\n"""
326
signal.signal(signal.SIGINT, signal.SIG_DFL)
327
opts, args = getopt.getopt(sys.argv[1:], '',
330
for opt, arg in opts:
335
print >> sys.stderr, usage
489
for srcfile in sys.argv[1:]:
492
for group in p.groups:
494
for pkg in group.packages:
497
for category in p.categories:
499
for group in category.groups:
341
print >> sys.stderr, "pkggroup.py: No such file:\'%s\'" % args[0]
503
print >> sys.stderr, "newcomps.py: No such file:\'%s\'" % sys.argv[1]
343
506
if __name__ == '__main__':