1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 import types
17 import sys
18 from constants import *
19 try:
20 from xml.etree import cElementTree
21 except ImportError:
22 import cElementTree
23 iterparse = cElementTree.iterparse
24 from Errors import CompsException
25
26
27 import fnmatch
28 import re
29 from yum.i18n import to_unicode
30 from misc import get_my_lang_code
31
32 lang_attr = '{http://www.w3.org/XML/1998/namespace}lang'
39
42
44 """ Group/Category helper object. """
45
46
48 """ Return the "name" of the object for the C locale. """
49 return self.name
50
51 @property
55
56 @property
60
62 if other is None:
63 return 1
64
65 if self.display_order > other.display_order:
66 return 1
67 if self.display_order < other.display_order:
68 return -1
69
70 return cmp(self.ui_name, other.ui_name)
71
73 import gettext
74 languages = [lang]
75
76 if 'C' not in languages:
77 languages.append('C')
78
79
80 nelangs = []
81 for lang in languages:
82 for nelang in gettext._expand_lang(lang):
83 if nelang not in nelangs:
84 nelangs.append(nelang)
85 return nelangs
86
94
96 for langcode in self._expand_languages(lang):
97 if langcode in self.translated_description:
98 return to_unicode(self.translated_description[langcode])
99 return to_unicode(self.description)
100
101
102 -class Group(CompsObj):
103 """ Group object parsed from group data in each repo. and merged. """
104
106 self.user_visible = True
107 self.default = False
108 self.selected = False
109 self.name = ""
110 self.description = ""
111 self.translated_name = {}
112 self.translated_description = {}
113 self.mandatory_packages = {}
114 self.optional_packages = {}
115 self.default_packages = {}
116 self.conditional_packages = {}
117 self.langonly = None
118 self.groupid = None
119 self.display_order = 1024
120 self.installed = False
121 self.toremove = False
122
123 if elem:
124 self.parse(elem)
125
127
128 lst = self.mandatory_packages.keys() + \
129 self.optional_packages.keys() + \
130 self.default_packages.keys() + \
131 self.conditional_packages.keys()
132
133 return lst
134
135 packages = property(_packageiter)
136
138 for child in elem:
139
140 if child.tag == 'id':
141 myid = child.text
142 if self.groupid is not None:
143 raise CompsException
144 self.groupid = myid
145
146 elif child.tag == 'name':
147 text = child.text
148 if text:
149 text = text.encode('utf8')
150
151 lang = child.attrib.get(lang_attr)
152 if lang:
153 self.translated_name[lang] = text
154 else:
155 self.name = text
156
157
158 elif child.tag == 'description':
159 text = child.text
160 if text:
161 text = text.encode('utf8')
162
163 lang = child.attrib.get(lang_attr)
164 if lang:
165 self.translated_description[lang] = text
166 else:
167 if text:
168 self.description = text
169
170 elif child.tag == 'uservisible':
171 self.user_visible = parse_boolean(child.text)
172
173 elif child.tag == 'display_order':
174 self.display_order = parse_number(child.text)
175
176 elif child.tag == 'default':
177 self.default = parse_boolean(child.text)
178
179 elif child.tag in ['langonly', 'lang_only']:
180 text = child.text
181 if self.langonly is not None:
182 raise CompsException
183 self.langonly = text
184
185 elif child.tag == 'packagelist':
186 self.parse_package_list(child)
187
189 for child in packagelist_elem:
190 if child.tag == 'packagereq':
191 genre = child.attrib.get('type')
192 if not genre:
193 genre = u'mandatory'
194
195 if genre not in ('mandatory', 'default', 'optional', 'conditional'):
196
197 continue
198
199 package = child.text
200 if genre == 'mandatory':
201 self.mandatory_packages[package] = 1
202 elif genre == 'default':
203 self.default_packages[package] = 1
204 elif genre == 'optional':
205 self.optional_packages[package] = 1
206 elif genre == 'conditional':
207 self.conditional_packages[package] = child.attrib.get('requires')
208
209
210
211 - def add(self, obj):
212 """Add another group object to this object"""
213
214
215
216
217 for pkg in obj.mandatory_packages:
218 self.mandatory_packages[pkg] = 1
219 for pkg in obj.default_packages:
220 self.default_packages[pkg] = 1
221 for pkg in obj.optional_packages:
222 self.optional_packages[pkg] = 1
223 for pkg in obj.conditional_packages:
224 self.conditional_packages[pkg] = obj.conditional_packages[pkg]
225
226
227
228
229
230 if self.name == '' and obj.name != '':
231 self.name = obj.name
232
233 if self.description == '' and obj.description != '':
234 self.description = obj.description
235
236
237 for lang in obj.translated_name:
238 if not self.translated_name.has_key(lang):
239 self.translated_name[lang] = obj.translated_name[lang]
240
241 for lang in obj.translated_description:
242 if not self.translated_description.has_key(lang):
243 self.translated_description[lang] = obj.translated_description[lang]
244
246 """write out an xml stanza for the group object"""
247 msg ="""
248 <group>
249 <id>%s</id>
250 <default>%s</default>
251 <uservisible>%s</uservisible>
252 <display_order>%s</display_order>\n""" % (self.groupid, str(self.default).lower(),
253 str(self.user_visible).lower(), self.display_order)
254
255 if self.langonly:
256 msg += """ <langonly>%s</langonly>""" % self.langonly
257
258 msg +=""" <name>%s</name>\n""" % self.name
259 for (lang, val) in sorted(self.translated_name.items()):
260 msg += """ <name xml:lang="%s">%s</name>\n""" % (lang, val)
261
262 msg += """ <description>%s</description>\n""" % self.description
263 for (lang, val) in sorted(self.translated_description.items()):
264 msg += """ <description xml:lang="%s">%s</description>\n""" % (lang, val)
265
266 msg += """ <packagelist>\n"""
267 for pkg in sorted(self.mandatory_packages):
268 msg += """ <packagereq type="mandatory">%s</packagereq>\n""" % pkg
269 for pkg in sorted(self.default_packages):
270 msg += """ <packagereq type="default">%s</packagereq>\n""" % pkg
271 for pkg in sorted(self.optional_packages):
272 msg += """ <packagereq type="optional">%s</packagereq>\n""" % pkg
273 for (pkg, req) in sorted(self.conditional_packages.items()):
274 msg += """ <packagereq type="conditional" requires="%s">%s</packagereq>\n""" % (req, pkg)
275 msg += """ </packagelist>\n"""
276 msg += """ </group>"""
277
278 return msg
279
282 """ Category object parsed from group data in each repo. and merged. """
283
285 self.name = ""
286 self.categoryid = None
287 self.description = ""
288 self.translated_name = {}
289 self.translated_description = {}
290 self.display_order = 1024
291 self._groups = {}
292
293 if elem:
294 self.parse(elem)
295
297 return self._groups.keys()
298
299 groups = property(_groupiter)
300
302 for child in elem:
303 if child.tag == 'id':
304 myid = child.text
305 if self.categoryid is not None:
306 raise CompsException
307 self.categoryid = myid
308
309 elif child.tag == 'name':
310 text = child.text
311 if text:
312 text = text.encode('utf8')
313
314 lang = child.attrib.get(lang_attr)
315 if lang:
316 self.translated_name[lang] = text
317 else:
318 self.name = text
319
320 elif child.tag == 'description':
321 text = child.text
322 if text:
323 text = text.encode('utf8')
324
325 lang = child.attrib.get(lang_attr)
326 if lang:
327 self.translated_description[lang] = text
328 else:
329 self.description = text
330
331 elif child.tag == 'grouplist':
332 self.parse_group_list(child)
333
334 elif child.tag == 'display_order':
335 self.display_order = parse_number(child.text)
336
338 for child in grouplist_elem:
339 if child.tag == 'groupid':
340 groupid = child.text
341 self._groups[groupid] = 1
342
343 - def add(self, obj):
344 """Add another category object to this object"""
345
346 for grp in obj.groups:
347 self._groups[grp] = 1
348
349
350 for lang in obj.translated_name:
351 if not self.translated_name.has_key(lang):
352 self.translated_name[lang] = obj.translated_name[lang]
353
354 for lang in obj.translated_description:
355 if not self.translated_description.has_key(lang):
356 self.translated_description[lang] = obj.translated_description[lang]
357
359 """write out an xml stanza for the category object"""
360 msg ="""
361 <category>
362 <id>%s</id>
363 <display_order>%s</display_order>\n""" % (self.categoryid, self.display_order)
364
365 msg +=""" <name>%s</name>\n""" % self.name
366 for (lang, val) in self.translated_name.items():
367 msg += """ <name xml:lang="%s">%s</name>\n""" % (lang, val)
368
369 msg += """ <description>%s</description>\n""" % self.description
370 for (lang, val) in self.translated_description.items():
371 msg += """ <description xml:lang="%s">%s</description>\n""" % (lang, val)
372
373 msg += """ <grouplist>\n"""
374 for grp in self.groups:
375 msg += """ <groupid>%s</groupid>\n""" % grp
376 msg += """ </grouplist>\n"""
377 msg += """ </category>\n"""
378
379 return msg
380
383 - def __init__(self, overwrite_groups=False):
389
390
391
393 grps = self._groups.values()
394 grps.sort(key=lambda x: (x.display_order, x.name))
395 return grps
396
398 cats = self._categories.values()
399 cats.sort(key=lambda x: (x.display_order, x.name))
400 return cats
401
402 groups = property(get_groups)
403 categories = property(get_categories)
404
412
414 """Return the first group which matches"""
415 grps = self.return_groups(grpid)
416 if grps:
417 return grps[0]
418
419 return None
420
422 """return all groups which match either by glob or exact match"""
423 returns = {}
424
425 for item in group_pattern.split(','):
426 item = item.strip()
427 if self._groups.has_key(item):
428 thisgroup = self._groups[item]
429 returns[thisgroup.groupid] = thisgroup
430 continue
431
432 if case_sensitive:
433 match = re.compile(fnmatch.translate(item)).match
434 else:
435 match = re.compile(fnmatch.translate(item), flags=re.I).match
436
437 done = False
438 for group in self.groups:
439 for name in group.name, group.groupid, group.ui_name:
440 if match(name):
441 done = True
442 returns[group.groupid] = group
443 break
444 if done:
445 continue
446
447
448 for group in self.groups:
449 for name in group.translated_name.values():
450 if match(name):
451 returns[group.groupid] = group
452 break
453
454 return returns.values()
455
456
457
459 """return all categories which match either by glob or exact match"""
460 returns = {}
461
462 for item in pattern.split(','):
463 item = item.strip()
464 if item in self._categories:
465 cat = self._categories[item]
466 returns[cat.categoryid] = cat
467 continue
468
469 if not ignore_case:
470 match = re.compile(fnmatch.translate(item)).match
471 else:
472 match = re.compile(fnmatch.translate(item), flags=re.I).match
473
474 done = False
475 for cat in self.categories:
476 for name in cat.name, cat.categoryid, cat.ui_name:
477 if match(name):
478 done = True
479 returns[cat.categoryid] = cat
480 break
481 if done:
482 continue
483
484 for cat in self.categories:
485 for name in cat.translated_name.values():
486 if match(name):
487 returns[cat.categoryid] = cat
488 break
489
490 return returns.values()
491
493 if self._groups.has_key(group.groupid):
494 thatgroup = self._groups[group.groupid]
495 thatgroup.add(group)
496 else:
497 self._groups[group.groupid] = group
498
500 if self._categories.has_key(category.categoryid):
501 thatcat = self._categories[category.categoryid]
502 thatcat.add(category)
503 else:
504 self._categories[category.categoryid] = category
505
506 - def add(self, srcfile = None):
507 if not srcfile:
508 raise CompsException
509
510 if type(srcfile) in types.StringTypes:
511
512 infile = open(srcfile, 'rt')
513 else:
514
515 infile = srcfile
516
517 self.compscount += 1
518 self.compiled = False
519
520 parser = iterparse(infile)
521 try:
522 for event, elem in parser:
523 if elem.tag == "group":
524 group = Group(elem)
525 self.add_group(group)
526 if elem.tag == "category":
527 category = Category(elem)
528 self.add_category(category)
529 except SyntaxError, e:
530 raise CompsException, "comps file is empty/damaged"
531
532 del parser
533
535 """ compile the groups into installed/available groups """
536
537
538 inst_pkg_names = {}
539 for (n,a,e,v,r) in pkgtuplist:
540 inst_pkg_names[n] = 1
541
542
543 for group in self.groups:
544
545
546
547 if len(group.mandatory_packages) > 0:
548 group.installed = True
549 for pkgname in group.mandatory_packages:
550 if pkgname not in inst_pkg_names:
551 group.installed = False
552 break
553
554
555
556 else:
557 check_pkgs = group.optional_packages.keys() + group.default_packages.keys() + group.conditional_packages.keys()
558 group.installed = False
559 for pkgname in check_pkgs:
560 if inst_pkg_names.has_key(pkgname):
561 group.installed = True
562 break
563
564 self.compiled = True
565
567 """returns the xml of the comps files in this class, merged"""
568
569 if not self._groups and not self._categories:
570 return ""
571
572 msg = """<?xml version="1.0" encoding="UTF-8"?>
573 <!DOCTYPE comps PUBLIC "-//Red Hat, Inc.//DTD Comps info//EN" "comps.dtd">
574 <comps>
575 """
576
577 for g in self.get_groups():
578 msg += g.xml()
579 for c in self.get_categories():
580 msg += c.xml()
581
582 msg += """\n</comps>\n"""
583
584 return msg
585
589
590 try:
591 print sys.argv[1]
592 p = Comps()
593 for srcfile in sys.argv[1:]:
594 p.add(srcfile)
595
596 for group in p.groups:
597 print group
598 for pkg in group.packages:
599 print ' ' + pkg
600
601 for category in p.categories:
602 print category.name
603 for group in category.groups:
604 print ' ' + group
605
606 except IOError:
607 print >> sys.stderr, "newcomps.py: No such file:\'%s\'" % sys.argv[1]
608 sys.exit(1)
609
610 if __name__ == '__main__':
611 main()
612