[yum-commits] 18 commits - cli.py docs/yum.8 output.py test/simpleupdatetests.py yumcommands.py yum/comps.py yum/igroups.py yum/__init__.py yum/rpmsack.py yum/transactioninfo.py yum/yumRepo.py

James Antill james at osuosl.org
Fri Aug 10 20:02:40 UTC 2012


 cli.py                    |  330 +++++++++++++++++++++++++++++++------------
 docs/yum.8                |    8 -
 output.py                 |   61 +++++++
 test/simpleupdatetests.py |   21 ++
 yum/__init__.py           |  352 ++++++++++++++++++++++++++++++++++++++++++----
 yum/comps.py              |  236 ++++++++++++++++++++++++++++++
 yum/igroups.py            |  134 ++++++++++++++++-
 yum/rpmsack.py            |    2 
 yum/transactioninfo.py    |   16 ++
 yum/yumRepo.py            |   22 ++
 yumcommands.py            |  134 +++++++++++++++--
 11 files changed, 1176 insertions(+), 140 deletions(-)

New commits:
commit 647660273c77a8f2c905d2c44cd520d2b4f8f42a
Merge: 985c156 ac1802f
Author: James Antill <james at and.org>
Date:   Fri Aug 10 16:02:33 2012 -0400

    Merge branch 'master' of ssh://yum.baseurl.org/srv/projects/yum/git/yum
    
    * 'master' of ssh://yum.baseurl.org/srv/projects/yum/git/yum: (4 commits)
      _readMirrorList: No whitespaces in mirror URLs. BZ 845765.
      ...

commit 985c1566ddb800a12eeccb3d43841960584983ab
Author: James Antill <james at and.org>
Date:   Fri Aug 10 15:57:01 2012 -0400

    Add group mark* commands, so we can test environments with groups as objects.

diff --git a/yumcommands.py b/yumcommands.py
index e83f426..52c3127 100644
--- a/yumcommands.py
+++ b/yumcommands.py
@@ -858,12 +858,18 @@ class GroupsCommand(YumCommand):
             ocmds_arg = ('mark-install', 'mark-remove',
                          'mark-packages', 'mark-packages-force',
                          'unmark-packages',
-                         'mark-packages-sync', 'mark-packages-sync-force')
+                         'mark-packages-sync', 'mark-packages-sync-force',
+                         'mark-groups', 'mark-groups-force',
+                         'unmark-groups',
+                         'mark-groups-sync', 'mark-groups-sync-force')
 
             ocmds_all = ('mark-install', 'mark-remove', 'mark-convert',
                          'mark-packages', 'mark-packages-force',
                          'unmark-packages',
-                         'mark-packages-sync', 'mark-packages-sync-force')
+                         'mark-packages-sync', 'mark-packages-sync-force',
+                         'mark-groups', 'mark-groups-force',
+                         'unmark-groups',
+                         'mark-groups-sync', 'mark-groups-sync-force')
 
         if cmd in ('install', 'remove', 'info') or cmd in ocmds_arg:
             checkGroupArg(base, cmd, extcmds)
@@ -925,18 +931,23 @@ class GroupsCommand(YumCommand):
                 return base.removeGroups(extcmds)
 
             if cmd == 'mark-install':
-                for strng in extcmds:
-                    for group in base.comps.return_groups(strng):
-                        base.igroups.add_group(group.groupid, group.packages)
+                gRG = base._groupReturnGroups(extcmds,ignore_case=False)
+                igrps, grps, ievgrps, evgrps = gRG
+                for evgrp in evgrps:
+                    base.igroups.add_environment(evgrp.environmentid,
+                                                 evgrp.allgroups)
+                for grp in grps:
+                    base.igroups.add_group(grp.groupid, grp.packages)
                 base.igroups.save()
                 return 0, ['Marked install: ' + ','.join(extcmds)]
 
             if cmd in ('mark-packages', 'mark-packages-force'):
                 if len(extcmds) < 2:
                     return 1, ['No group or package given']
-                igrps, grps = base._groupReturnGroups([extcmds[0]],
-                                                      ignore_case=False)
-                if igrps is not None and len(igrps) != 1:
+                gRG = base._groupReturnGroups([extcmds[0]],
+                                              ignore_case=False)
+                igrps, grps, ievgrps, evgrps = gRG
+                if igrps is None or len(igrps) != 1:
                     return 1, ['No group matched']
                 grp = igrps[0]
                 force = cmd == 'mark-packages-force'
@@ -956,7 +967,8 @@ class GroupsCommand(YumCommand):
                 return 0, ['UnMarked packages: ' + ','.join(extcmds)]
 
             if cmd in ('mark-packages-sync', 'mark-packages-sync-force'):
-                igrps, grps = base._groupReturnGroups(extcmds,ignore_case=False)
+                gRG = base._groupReturnGroups(extcmds,ignore_case=False)
+                igrps, grps, ievgrps, evgrps = gRG
                 if not igrps:
                     return 1, ['No group matched']
                 force = cmd == 'mark-packages-sync-force'
@@ -970,6 +982,60 @@ class GroupsCommand(YumCommand):
                 else:
                     return 0, ['Marked packages-sync: ' + ','.join(extcmds)]
 
+            if cmd in ('mark-groups', 'mark-groups-force'):
+                if len(extcmds) < 2:
+                    return 1, ['No environment or group given']
+                gRG = base._groupReturnGroups([extcmds[0]],
+                                              ignore_case=False)
+                igrps, grps, ievgrps, evgrps = gRG
+                if ievgrps is None or len(ievgrps) != 1:
+                    return 1, ['No environment matched']
+                evgrp = ievgrps[0]
+                force = cmd == 'mark-groups-force'
+                gRG = base._groupReturnGroups(extcmds[1:], ignore_case=False)
+                for grp in gRG[1]:
+                    # Packages full or empty?
+                    self.igroups.add_group(grp.groupid,
+                                           grp.packages, ievgrp)
+                if force:
+                    for grp in gRG[0]:
+                        grp.environment = evgrp.evgid
+                        base.igroups.changed = True
+                base.igroups.save()
+                return 0, ['Marked groups: ' + ','.join(extcmds[1:])]
+
+            if cmd == 'unmark-groups':
+                gRG = base._groupReturnGroups([extcmds[0]],
+                                              ignore_case=False)
+                igrps, grps, ievgrps, evgrps = gRG
+                if igrps is None:
+                    return 1, ['No groups matched']
+                for grp in igrps:
+                    grp.environment = None
+                    base.igroups.changed = True
+                base.igroups.save()
+                return 0, ['UnMarked groups: ' + ','.join(extcmds)]
+
+            if cmd in ('mark-groups-sync', 'mark-groups-sync-force'):
+                gRG = base._groupReturnGroups(extcmds,ignore_case=False)
+                igrps, grps, ievgrps, evgrps = gRG
+                if not ievgrps:
+                    return 1, ['No environment matched']
+                force = cmd == 'mark-groups-sync-force'
+                for evgrp in ievgrps:
+                    grp_names = ",".join(sorted(evgrp.grp_names))
+                    for grp in base.igroups.return_groups(grp_names):
+                        if not force and grp.environment is not None:
+                            continue
+                        grp.environment = evgrp.evgid
+                        base.igroups.changed = True
+                base.igroups.save()
+                if force:
+                    return 0, ['Marked groups-sync-force: '+','.join(extcmds)]
+                else:
+                    return 0, ['Marked groups-sync: ' + ','.join(extcmds)]
+
+            # FIXME: This doesn't do environment groups atm.
             if cmd == 'mark-convert':
                 # Convert old style info. into groups as objects.
 
@@ -1019,9 +1085,12 @@ class GroupsCommand(YumCommand):
                 return 0, ['Converted old style groups to objects.']
 
             if cmd == 'mark-remove':
-                for strng in extcmds:
-                    for group in base.comps.return_groups(strng):
-                        base.igroups.del_group(group.groupid)
+                gRG = base._groupReturnGroups(extcmds,ignore_case=False)
+                igrps, grps, ievgrps, evgrps = gRG
+                for evgrp in ievgrps:
+                    base.igroups.del_environment(evgrp.evgid)
+                for grp in igrps:
+                    base.igroups.del_group(grp.gid)
                 base.igroups.save()
                 return 0, ['Marked remove: ' + ','.join(extcmds)]
 
commit fece586425bda276bbae3f888abe81676e3f5b2e
Author: James Antill <james at and.org>
Date:   Fri Aug 10 15:56:09 2012 -0400

    Version 1.0 of environment groups, plus some fixes for groups as objects.

diff --git a/cli.py b/cli.py
index 91c2553..8f8fdf9 100755
--- a/cli.py
+++ b/cli.py
@@ -1596,56 +1596,125 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
             1 = we've errored, exit with error string
             2 = we've got work yet to do, onto the next stage        
         """
-        uservisible=1
+        return self._returnGroupLists(userlist)
+
+    def _returnGroupLists(self, userlist, summary=False):
+        # What data are we showing...
+        wts_map = {'hidden' : 'hidden',
+                   'language' : 'lang',
+                   'languages' : 'lang',
+                   'lang' : 'lang',
+                   'langs' : 'lang',
+                   'environment' : 'env',
+                   'environments' : 'env',
+                   'env' : 'env',
+                   'envs' : 'env',
+                   'package' : 'pkg',
+                   'packages' : 'pkg',
+                   'pkg' : 'pkg',
+                   'pkgs' : 'pkg',
+                   'available' : 'avail',
+                   'avail' : 'avail',
+                   'installed' : 'inst',
+                   'inst' : 'inst',
+                   'id' : 'id',
+                   'ids' : 'id',
+                   }
+        verb = self.verbose_logger.isEnabledFor(yum.logginglevels.DEBUG_3)
+        wts = {'hidden' : False,
+               'lang'   : False,
+               'env'    : True,
+               'pkg'    : True,
+               'inst'   : True,
+               'avail'  : True,
+               'id'     : verb}
             
-        if len(userlist) > 0:
-            if userlist[0] == 'hidden':
-                uservisible=0
-                userlist.pop(0)
+        while userlist:
+            arg = userlist[0]
+            val = True
+            if arg.startswith('no'):
+                arg = arg[2:]
+                val = False
+            if arg not in wts_map:
+                break
+            wts[wts_map[arg]] = val
+            userlist.pop(0)
         if not userlist:
             userlist = None # Match everything...
 
-        installed, available = self.doGroupLists(uservisible=uservisible,
-                                                 patterns=userlist)
+        uv  = not wts['hidden']
+        dGL = self.doGroupLists(patterns=userlist,
+                                uservisible=uv, return_evgrps=True)
+
+        installed, available, ievgrps, evgrps = dGL
+
+        if not wts['env']:
+            ievgrps = []
+            evgrps  = []
+
+        if not wts['inst']:
+            installed = []
+            ievgrps   = []
+        if not wts['avail']:
+            available = []
+            evgrps    = []
         
-        if not installed and not available:
-            self.logger.error(_('Warning: No groups match: %s'),
-                              ", ".join(userlist))
-            return 0, []
+        done = []
+        def _out_grp(sect, groups):
+            if not groups:
+                return
+
+            done.append(sect)
+            if summary:
+                self.verbose_logger.log(yum.logginglevels.INFO_2,
+                                        "%s %u", sect, len(groups))
+                return
 
-        def _out_grp(sect, group):
-            if not done:
-                self.verbose_logger.log(yum.logginglevels.INFO_2, sect)
-            msg = '   %s' % group.ui_name
-            if self.verbose_logger.isEnabledFor(yum.logginglevels.DEBUG_3):
-                msg += ' (%s)' % group.groupid
-            if group.langonly:
-                msg += ' [%s]' % group.langonly
-            self.verbose_logger.info('%s', msg)
+            self.verbose_logger.log(yum.logginglevels.INFO_2, sect)
 
-        done = False
+            for group in groups:
+                msg = '   %s' % group.ui_name
+                if wts['id']:
+                    msg += ' (%s)' % group.compsid
+                if group.langonly:
+                    msg += ' [%s]' % group.langonly
+                self.verbose_logger.info('%s', msg)
+
+        _out_grp(_('Installed Environment Groups:'), ievgrps)
+        _out_grp(_('Available Environment Groups:'), evgrps)
+
+        groups = []
         for group in installed:
             if group.langonly: continue
-            _out_grp(_('Installed Groups:'), group)
-            done = True
+            if not wts['pkg']: continue
+            groups.append(group)
+        _out_grp(_('Installed Groups:'), groups)
 
-        done = False
+        groups = []
         for group in installed:
             if not group.langonly: continue
-            _out_grp(_('Installed Language Groups:'), group)
-            done = True
+            if not wts['lang']: continue
+            groups.append(group)
+        _out_grp(_('Installed Language Groups:'), groups)
 
-        done = False
+        groups = []
         for group in available:
             if group.langonly: continue
-            _out_grp(_('Available Groups:'), group)
-            done = True
+            if not wts['pkg']: continue
+            groups.append(group)
+        _out_grp(_('Available Groups:'), groups)
 
-        done = False
+        groups = []
         for group in available:
             if not group.langonly: continue
-            _out_grp(_('Available Language Groups:'), group)
-            done = True
+            if not wts['lang']: continue
+            groups.append(group)
+        _out_grp(_('Available Language Groups:'), groups)
+
+        if not done:
+            self.logger.error(_('Warning: No Environments/Groups match: %s'),
+                              ", ".join(userlist))
+            return 0, []
 
         return 0, [_('Done')]
 
@@ -1664,47 +1733,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
             1 = we've errored, exit with error string
             2 = we've got work yet to do, onto the next stage
         """
-        uservisible=1
-            
-        if len(userlist) > 0:
-            if userlist[0] == 'hidden':
-                uservisible=0
-                userlist.pop(0)
-        if not userlist:
-            userlist = None # Match everything...
-
-        installed, available = self.doGroupLists(uservisible=uservisible,
-                                                 patterns=userlist)
-        
-        def _out_grp(sect, num):
-            if not num:
-                return
-            self.verbose_logger.log(yum.logginglevels.INFO_2, '%s %u', sect,num)
-        done = 0
-        for group in installed:
-            if group.langonly: continue
-            done += 1
-        _out_grp(_('Installed Groups:'), done)
-
-        done = 0
-        for group in installed:
-            if not group.langonly: continue
-            done += 1
-        _out_grp(_('Installed Language Groups:'), done)
-
-        done = False
-        for group in available:
-            if group.langonly: continue
-            done += 1
-        _out_grp(_('Available Groups:'), done)
-
-        done = False
-        for group in available:
-            if not group.langonly: continue
-            done += 1
-        _out_grp(_('Available Language Groups:'), done)
-
-        return 0, [_('Done')]
+        return self._returnGroupLists(userlist, summary=True)
     
     def returnGroupInfo(self, userlist):
         """Print complete information about the groups that match the
@@ -1722,12 +1751,27 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
         """
         for strng in userlist:
             group_matched = False
-            for group in self.comps.return_groups(strng):
-                self.displayPkgsInGroups(group)
-                group_matched = True
+
+            pkg_grp = True
+            grp_grp = True
+            if strng.startswith('@^'):
+                strng = strng[2:]
+                pkg_grp = False
+            elif strng.startswith('@'):
+                strng = strng[1:]
+                grp_grp = False
+
+            if grp_grp:
+                for evgroup in self.comps.return_environments(strng):
+                    self.displayGrpsInEnvironments(evgroup)
+                    group_matched = True
+            if pkg_grp:
+                for group in self.comps.return_groups(strng):
+                    self.displayPkgsInGroups(group)
+                    group_matched = True
 
             if not group_matched:
-                self.logger.error(_('Warning: Group %s does not exist.'), strng)
+                self.logger.error(_('Warning: Group/Environment %s does not exist.'), strng)
         
         return 0, []
         
@@ -1747,10 +1791,37 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
         pkgs_used = []
         
         for group_string in grouplist:
+
+            grp_grp = True
+            pkg_grp = True
+            if group_string.startswith('@^'):
+                pkg_grp = False
+                group_string = group_string[2:]
+            elif group_string.startswith('@'):
+                grp_grp = False
+                group_string = group_string[1:]
+
             group_matched = False
-            for group in self.comps.return_groups(group_string):
+            groups = []
+            if grp_grp:
+                groups = self.comps.return_environments(group_string)
+            for group in groups:
                 group_matched = True
 
+                try:
+                    txmbrs = self.selectEnvironment(group.environmentid,
+                                                    upgrade=upgrade)
+                except yum.Errors.GroupsError:
+                    self.logger.critical(_('Warning: Environment %s does not exist.'), group_string)
+                    continue
+                else:
+                    pkgs_used.extend(txmbrs)
+
+            groups = []
+            if pkg_grp:
+                groups = self.comps.return_groups(group_string)
+            for group in groups:
+                group_matched = True
             
                 try:
                     txmbrs = self.selectGroup(group.groupid, upgrade=upgrade)
@@ -1784,13 +1855,47 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
         """
         pkgs_used = []
         for group_string in grouplist:
-            try:
-                txmbrs = self.groupRemove(group_string)
-            except yum.Errors.GroupsError:
-                self.logger.critical(_('No group named %s exists'), group_string)
-                continue
-            else:
-                pkgs_used.extend(txmbrs)
+
+            grp_grp = True
+            pkg_grp = True
+            if group_string.startswith('@^'):
+                pkg_grp = False
+                group_string = group_string[2:]
+            elif group_string.startswith('@'):
+                grp_grp = False
+                group_string = group_string[1:]
+
+            groups = []
+            if grp_grp:
+                if self.conf.group_command == 'objects':
+                    groups = self.igroups.return_environments(group_string)
+                else:
+                    groups = self.comps.return_environments(group_string)
+                if not groups:
+                    self.logger.critical(_('No Environment named %s exists'), group_string)
+            for group in groups:
+                try:
+                    txmbrs = self.environmentRemove(group.environmentid)
+                except yum.Errors.GroupsError:
+                    continue
+                else:
+                    pkgs_used.extend(txmbrs)
+
+            groups = []
+            if pkg_grp:
+                if self.conf.group_command == 'objects':
+                    groups = self.igroups.return_groups(group_string)
+                else:
+                    groups = self.comps.return_groups(group_string)
+                if not groups:
+                    self.logger.critical(_('No group named %s exists'), group_string)
+            for group in groups:
+                try:
+                    txmbrs = self.groupRemove(group.groupid)
+                except yum.Errors.GroupsError:
+                    continue
+                else:
+                    pkgs_used.extend(txmbrs)
                 
         if not pkgs_used:
             return 0, [_('No packages to remove from groups')]
diff --git a/output.py b/output.py
index b3f2c75..d29eba8 100755
--- a/output.py
+++ b/output.py
@@ -1092,8 +1092,8 @@ class YumOutput:
         print _('\nGroup: %s') % group.ui_name
 
         verb = self.verbose_logger.isEnabledFor(logginglevels.DEBUG_3)
-        if verb:
-            print _(' Group-Id: %s') % to_unicode(group.groupid)
+        if True:
+            print _(' Group-Id: %s') % to_unicode(group.compsid)
 
         igroup_data = self._groupInstalledData(group)
         igrp_only   = set()
@@ -1144,6 +1144,63 @@ class YumOutput:
                                        columns=columns,
                                        igroup_data=igroup_data)
 
+    def displayGrpsInEnvironments(self, evgroup):
+        """Output information about the groups in a given evgroup
+
+        :param group: an Environment object to output information about
+        """
+        print _('\nEnvironment Group: %s') % evgroup.ui_name
+        print _(' Environment-Id: %s') % to_unicode(evgroup.compsid)
+
+        igroup_data = self._groupInstalledEnvData(evgroup)
+        igrp_only   = set()
+        for grp_name in igroup_data:
+            if igroup_data[grp_name] == 'installed':
+                igrp_only.add(grp_name)
+        igrp_only.difference_update(evgroup.allgroups)
+        all_grps = evgroup.allgroups + list(igrp_only)
+
+        if evgroup.ui_description:
+            print _(' Description: %s') % to_unicode(evgroup.ui_description)
+
+        def _get_igrp_data(item, indent):
+            if not igroup_data:
+                return indent
+
+            assert item in igroup_data
+            if item not in igroup_data or igroup_data[item] == 'available':
+                indent += '+' # Group up/in will install i
+            elif igroup_data[item] == 'installed':
+                indent += '=' # Installed via. group
+            elif igroup_data[item] == 'blacklisted-installed':
+                if False: # Not sure it's worth listing these...
+                    return None # On the other hand, there's mark-packages
+                indent += ' ' # Installed, not via. group
+            else:
+                assert igroup_data[item] == 'blacklisted-available'
+                if False: # Not sure it's worth listing these...
+                    return None
+                indent += '-' # Not installed, and won't be
+            return indent
+
+        sections = ((_(' Mandatory Groups:'), evgroup.groups),
+                    (_(' Optional Groups:'),  evgroup.options))
+
+        for (section_name, grp_names) in sections:
+            if len(grp_names) > 0:
+                print section_name
+                for grp_name in sorted(grp_names):
+                    pindent = _get_igrp_data(grp_name, "   ")
+                    if pindent is None:
+                        continue
+
+                    print "%s%s" % (pindent, grp_name)
+
+        if igrp_only:
+            print _(' Installed Groups:')
+            for grp_name in sorted(igrp_only):
+                print "%s%s", "  ", grp_name
+
     def depListOutput(self, results):
         """Format and output a list of findDeps results
 
diff --git a/yum/__init__.py b/yum/__init__.py
index 6ec5b15..c713cff 100644
--- a/yum/__init__.py
+++ b/yum/__init__.py
@@ -3228,25 +3228,81 @@ much more problems).
 
         return ret
 
+    def _groupInstalledEnvData(self, evgroup):
+        """ Return a dict of
+             grp_name =>
+             (installed, available,
+             backlisted-installed, blacklisted-available). """
+        ret = {}
+        if not evgroup or self.conf.group_command != 'objects':
+            return ret
+
+        grp_names = {}
+        if evgroup.environmentid in self.igroups.groups:
+            grp_names = self.igroups.environments[evgroup.environmentid]
+            grp_names = grp_names.grp_names
+
+        for grp_name in set(evgroup.allgroups + list(grp_names)):
+            igrp = self.igroups.groups.get(grp_name)
+            if grp_name not in grp_names and not igrp:
+                ret[grp_name] = 'available'
+                continue
+
+            if not igrp:
+                ret[grp_name] = 'blacklisted-available'
+                continue
+
+            if igrp.environment == evgroup.environmentid:
+                ret[grp_name] = 'installed'
+                break
+            else:
+                ret[grp_name] = 'blacklisted-installed'
+
+        return ret
+
     def _groupReturnGroups(self, patterns=None, ignore_case=True):
         igrps = None
+        ievgrps = None
         if patterns is None:
             grps = self.comps.groups
             if self.conf.group_command == 'objects':
                 igrps = self.igroups.groups.values()
-            return igrps, grps
+            evgrps = self.comps.environments
+            if False and self.conf.group_command == 'objects':
+                # FIXME: Environment groups.
+                ievgrps = self.igroups.environments.values()
+            return igrps, grps, ievgrps, evgrps
+
+        gpats = []
+        epats = []
+        for pat in patterns:
+            if pat.startswith('@^'):
+                epats.append(pat[2:])
+            elif pat.startswith('@'):
+                gpats.append(pat[1:])
+            else:
+                epats.append(pat)
+                gpats.append(pat)
+
+        epats = ",".join(epats)
+        gpats = ",".join(gpats)
 
-        pats = ",".join(patterns)
         cs   = not ignore_case
-        grps = self.comps.return_groups(pats, case_sensitive=cs)
+        grps = self.comps.return_groups(gpats, case_sensitive=cs)
         #  Because we want name matches too, and we don't store group names
         # we need to add the groupid's we've found:
         if self.conf.group_command == 'objects':
-            pats += "," + ",".join([grp.groupid for grp in grps])
-            igrps = self.igroups.return_groups(pats, case_sensitive=cs)
-        return igrps, grps
+            gpats = gpats + "," + ",".join([grp.groupid for grp in grps])
+            igrps = self.igroups.return_groups(gpats, case_sensitive=cs)
 
-    def doGroupLists(self, uservisible=0, patterns=None, ignore_case=True):
+        evgrps = self.comps.return_environments(epats, case_sensitive=cs)
+        if self.conf.group_command == 'objects':
+            epats = epats+ "," + ",".join([grp.environmentid for grp in evgrps])
+            ievgrps = self.igroups.return_environments(epats, case_sensitive=cs)
+        return igrps, grps, ievgrps, evgrps
+
+    def doGroupLists(self, uservisible=0, patterns=None, ignore_case=True,
+                     return_evgrps=False):
         """Return two lists of groups: installed groups and available
         groups.
 
@@ -3257,14 +3313,19 @@ much more problems).
            lists.  If not given, all groups will be included
         :param ignore_case: whether to ignore case when determining
            whether group names match the strings in *patterns*
+        :param return_evgrps: whether to return environment groups as well as
+           package groups
         """
         installed = []
         available = []
+        einstalled = []
+        eavailable = []
 
         if self.comps.compscount == 0:
             raise Errors.GroupsError, _('No group data available for configured repositories')
         
-        igrps, grps = self._groupReturnGroups(patterns, ignore_case)
+        igrps, grps, ievgrps, evgrps = self._groupReturnGroups(patterns,
+                                                               ignore_case)
 
         if igrps is not None:
             digrps = {}
@@ -3272,6 +3333,12 @@ much more problems).
                 digrps[igrp.gid] = igrp
             igrps = digrps
 
+        if ievgrps is not None:
+            digrps = {}
+            for ievgrp in ievgrps:
+                digrps[ievgrp.evgid] = ievgrp
+            ievgrps = digrps
+
         for grp in grps:
             if igrps is None:
                 grp_installed = grp.installed
@@ -3292,20 +3359,49 @@ much more problems).
                         available.append(grp)
                 else:
                     available.append(grp)
+
+        for evgrp in evgrps:
+            if ievgrps is None:
+                evgrp_installed = evgrp.installed
+            else:
+                evgrp_installed = evgrp.environmentid in ievgrps
+                if evgrp_installed:
+                    del ievgrps[evgrp.environmentid]
+
+            if evgrp_installed:
+                einstalled.append(evgrp)
+            else:
+                eavailable.append(evgrp)
             
         if igrps is None:
-            return sorted(installed), sorted(available)
+            igrps = {}
+        if ievgrps is None:
+            ievgrps = {}
 
         for igrp in igrps.values():
             #  These are installed groups that aren't in comps anymore. so we
             # create fake comps groups for them.
             grp = comps.Group()
+            grp.groupid = igrp.gid
             grp.installed = True
             grp.name = grp.groupid
             for pkg_name in igrp.pkg_names:
                 grp.mandatory_packages[pkg_name] = 1
             installed.append(grp)
 
+        for ievgrp in ievgrps.values():
+            #  These are installed evgroups that aren't in comps anymore. so we
+            # create fake comps evgroups for them.
+            evgrp = comps.Environment()
+            grp.environmentid = ievgrp.evgid
+            evgrp.installed = True
+            evgrp.name = evgrp.environmentid
+            evgrp._groups = list(ievgrp.groups)
+            einstalled.append(evgrp)
+
+        if return_evgrps:
+            return (sorted(installed), sorted(available),
+                    sorted(einstalled), sorted(eavailable))
         return sorted(installed), sorted(available)
     
     def groupRemove(self, grpid):
@@ -3317,8 +3413,11 @@ much more problems).
            transaction set by this function
         """
         txmbrs_used = []
-        
-        thesegroups = self.comps.return_groups(grpid)
+
+        if self.conf.group_command == 'objects':
+            thesegroups = self.igroups.return_groups(grpid)
+        else:
+            thesegroups = self.comps.return_groups(grpid)
         if not thesegroups:
             raise Errors.GroupsError, _("No Group named %s exists") % to_unicode(grpid)
 
@@ -3326,17 +3425,24 @@ much more problems).
             igroup_data = self._groupInstalledData(thisgroup)
 
             thisgroup.toremove = True
-            pkgs = thisgroup.packages
-            for pkg in thisgroup.packages:
+
+            if self.conf.group_command == 'objects':
+                pkgs = thisgroup.pkg_names
+                gid  = thisgroup.gid
+            else:
+                pkgs = thisgroup.packages
+                gid  = thisgroup.groupid
+
+            for pkg in pkgs:
                 if pkg in igroup_data and igroup_data[pkg] != 'installed':
                     continue
 
                 txmbrs = self.remove(name=pkg, silence_warnings=True)
                 txmbrs_used.extend(txmbrs)
                 for txmbr in txmbrs:
-                    txmbr.groups.append(thisgroup.groupid)
+                    txmbr.groups.append(gid)
             if igroup_data:
-                self.igroups.del_group(thisgroup.groupid)
+                self.igroups.del_group(gid)
             
         return txmbrs_used
 
@@ -3368,8 +3474,48 @@ much more problems).
                             self.tsInfo.remove(txmbr.po.pkgtup)
         
         
+    def environmentRemove(self, evgrpid):
+        """Mark all the packages in the given group to be removed.
+
+        :param evgrpid: the name of the environment containing the groups to
+           mark for removal
+        :return: a list of transaction members added to the
+           transaction set by this function
+        """
+        txmbrs_used = []
+
+        if self.conf.group_command == 'objects':
+            thesegroups = self.igroups.return_environments(evgrpid)
+        else:
+            thesegroups = self.comps.return_environments(evgrpid)
+        if not thesegroups:
+            raise Errors.GroupsError, _("No Environment named %s exists") % to_unicode(evgrpid)
+
+        for thisgroup in thesegroups:
+            igroup_data = self._groupInstalledEnvData(thisgroup)
+
+            if self.conf.group_command == 'objects':
+                grps  = thisgroup.grp_names
+                evgid = thisgroup.evgid
+            else:
+                grps  = thisgroup.allgroups
+                evgid = thisgroup.environmentid
+
+            for grp in grps:
+                if grp in igroup_data and igroup_data[grp] != 'installed':
+                    continue
+
+                txmbrs = self.groupRemove(grp)
+                txmbrs_used.extend(txmbrs)
+                for txmbr in txmbrs:
+                    txmbr.environments.append(evgid)
+            if igroup_data:
+                self.igroups.del_environment(evgid)
+
+        return txmbrs_used
+
     def selectGroup(self, grpid, group_package_types=[],
-                    enable_group_conditionals=None, upgrade=False):
+                    enable_group_conditionals=None, upgrade=False, ievgrp=None):
         """Mark all the packages in the given group to be installed.
 
         :param grpid: the name of the group containing the packages to
@@ -3395,6 +3541,9 @@ much more problems).
         if group_package_types:
             package_types = group_package_types
 
+        if self.conf.group_command == 'compat':
+            upgrade = False
+
         for thisgroup in thesegroups:
             if thisgroup.selected:
                 continue
@@ -3415,7 +3564,8 @@ much more problems).
                 if thisgroup.groupid in self.igroups.groups:
                     igrp = self.igroups.groups[thisgroup.groupid]
                 else:
-                    self.igroups.add_group(thisgroup.groupid,thisgroup.packages)
+                    self.igroups.add_group(thisgroup.groupid,
+                                           thisgroup.packages, ievgrp)
             pkgs.extend(list(igroup_data.keys()))
 
             old_txmbrs = len(txmbrs_used)
@@ -3442,7 +3592,8 @@ much more problems).
                     if (upgrade and
                         (self.conf.group_command == 'simple' or
                          (igroup_data and igroup_data[pkg] == 'installed'))):
-                        txmbrs = self.update(name = pkg)
+                        txmbrs = self.update(name = pkg,
+                                             pkg_warning_level='debug2')
                     elif igroup_data and igroup_data[pkg] == 'installed':
                         pass # Don't upgrade on install.
                     else:
@@ -3464,7 +3615,8 @@ much more problems).
 
             count_cond_test = 0
             # FIXME: What do we do about group conditionals when group==objects
-            if group_conditionals:
+            #        or group upgrade for group_command=simple?
+            if not upgrade and group_conditionals:
                 for condreq, cond in thisgroup.conditional_packages.iteritems():
                     if self.isPackageInstalled(cond):
                         try:
@@ -3501,8 +3653,9 @@ much more problems).
                         if cond not in self.tsInfo.conditionals:
                             self.tsInfo.conditionals[cond] = []
                         self.tsInfo.conditionals[cond].extend(pkgs)
-            if len(txmbrs_used) == old_txmbrs:
-                self.logger.critical(_('Warning: Group %s does not have any packages.'), thisgroup.groupid)
+
+            if not upgrade and len(txmbrs_used) == old_txmbrs:
+                self.logger.critical(_('Warning: Group %s does not have any packages to install.'), thisgroup.groupid)
                 if count_cond_test:
                     self.logger.critical(_('Group %s does have %u conditional packages, which may get installed.'), count_cond_test)
         return txmbrs_used
@@ -3523,7 +3676,8 @@ much more problems).
         thesegroups = self.comps.return_groups(grpid)
         if not thesegroups:
             raise Errors.GroupsError, _("No Group named %s exists") % to_unicode(grpid)
-        
+
+        # FIXME: Do something with groups as objects, and env. groups.
         for thisgroup in thesegroups:
             thisgroup.selected = False
             
@@ -3549,6 +3703,86 @@ much more problems).
                         for pkg in self.tsInfo.conditionals.get(txmbr.name, []):
                             self.tsInfo.remove(pkg.pkgtup)
         
+    def selectEnvironment(self, evgrpid, group_package_types=[],
+                          enable_group_conditionals=None, upgrade=False):
+        """Mark all the groups in the given environment group to be installed.
+
+        :param evgrpid: the name of the env. group containing the groups to
+           mark for installation
+        :param group_package_types: a list of the types of groups to
+           work with.  This overrides self.conf.group_package_types
+        :param enable_group_conditionals: overrides
+           self.conf.enable_group_conditionals
+        :return: a list of transaction members added to the
+           transaction set by this function
+        """
+        evgrps = self.comps.return_environments(evgrpid)
+        if not evgrps:
+            raise Errors.GroupsError, _("No Environment named %s exists") % to_unicode(evgrpid)
+
+        ret = []
+        for evgrp in evgrps:
+
+            ievgrp = None
+            if self.conf.group_command == 'compat':
+                grps = ",".join(sorted(evgrp.groups))
+            elif self.conf.group_command == 'simple':
+                if not upgrade:
+                    grps = ",".join(sorted(evgrp.groups))
+                else: # Only upgrade the installed groups...
+                    grps = []
+                    for grpid in evgrp.groups:
+                        grp = self.comps.return_group(grpid)
+                        if grp is None:
+                            continue
+                        if not grp.installed:
+                            continue
+                        grps.append(grpid)
+                    grps = ",".join(sorted(grps))
+            elif self.conf.group_command == 'objects':
+                igroup_data = self._groupInstalledEnvData(evgrp)
+ 
+                grps = []
+                for grpid in evgrp.groups:
+                    if (grpid not in igroup_data or
+                        igroup_data[grpid].startswith('blacklisted')):
+                        msg = _('Skipping group %s from environment %s'),
+                        self.verbose_logger.log(logginglevels.DEBUG_2,
+                                                msg, grpid, evgrp.environmentid)
+                        continue
+                    grps.apped(grp)
+                if evgrp.environmentid in self.igroups.environments:
+                    ievgrp = self.igroups.environments[evgrp.environmentid]
+                else:
+                    self.igroups.add_environment(evgrp.environmentid,
+                                                 evgrp.allgroups)
+                grps = ",".join(sorted(grps))
+
+            txs = self.selectGroup(grps,
+                                   group_package_types,
+                                   enable_group_conditionals, upgrade,
+                                   ievgrp=ievgrp)
+            ret.extend(txs)
+        return ret
+
+    def deselectEnvironment(self, evgrpid, force=False):
+        """Unmark the groups in the given environment group from being
+        installed.
+
+        :param evgrpid: the name of the environment group containing the
+           groups to unmark from installation
+        :param force: if True, force remove all the packages in the
+           given groups from the transaction
+        """
+        evgrps = self.comps.return_environments(evgrpid)
+        if not thesegroups:
+            raise Errors.GroupsError, _("No Environment named %s exists") % to_unicode(evgrpid)
+
+        for evgrp in evgrps:
+            grps = ",".join(sorted(evgrp.groups))
+            self.deselectGroup(grps, force)
+            # FIXME: env. needs to be marked not-to-be-installed, etc.
+
     def getPackageObject(self, pkgtup, allow_missing=False):
         """Return a package object that corresponds to the given
         package tuple.
@@ -3953,6 +4187,20 @@ much more problems).
         assert pattern[0] == '@'
         group_string = pattern[1:]
         tx_return = []
+
+        if group_string and group_string[0] == '^':
+            group_string = group_string[1:]
+            # Actually dealing with "environment groups".
+            for env_grp in self.comps.return_environments(group_string):
+                try:
+                    txmbrs = self.selectEnvironment(env_grp.environmentid,
+                                                    upgrade=upgrade)
+                    tx_return.extend(txmbrs)
+                except yum.Errors.GroupsError:
+                    self.logger.critical(_('Warning: Environment Group %s does not exist.'), group_string)
+                    continue
+            return tx_return
+
         for group in self.comps.return_groups(group_string):
             try:
                 txmbrs = self.selectGroup(group.groupid, upgrade=upgrade)
@@ -3971,6 +4219,18 @@ much more problems).
         assert pattern[0] == '@'
         group_string = pattern[1:]
         tx_return = []
+
+        if group_string and group_string[0] == '^':
+            group_string = group_string[1:]
+            # Actually dealing with "environment groups".
+            try:
+                txmbrs = self.environmentRemove(group_string)
+            except yum.Errors.GroupsError:
+                self.logger.critical(_('Warning: Environment Group %s does not exist.'), group_string)
+            else:
+                tx_return.extend(txmbrs)
+            return tx_return
+
         try:
             txmbrs = self.groupRemove(group_string)
         except yum.Errors.GroupsError:
@@ -3986,6 +4246,8 @@ much more problems).
         assert pattern[0] == '@'
         grpid = pattern[1:]
 
+        # FIXME: **** environment groups and groups as objects... ****
+
         thesegroups = self.comps.return_groups(grpid)
         if not thesegroups:
             raise Errors.GroupsError, _("No Group named %s exists") % to_unicode(grpid)
@@ -3999,6 +4261,10 @@ much more problems).
         assert pattern[0] == '-'
         pat = pattern[1:].strip()
 
+        if pat and pat.startswith('@^'):
+            pat = pat[2:]
+            return self.deselectEnvironment(pat)
+
         if pat and pat[0] == '@':
             pat = pat[1:]
             return self.deselectGroup(pat)
@@ -4389,6 +4655,15 @@ much more problems).
         # if no po do kwargs
         # uninstalled pkgs called for update get returned with errors in a list, maybe?
 
+        pkg_warn = kwargs.get('pkg_warning_level', 'flibble')
+        def _dbg2(*args, **kwargs):
+            self.verbose_logger.log(logginglevels.DEBUG_2, *args, **kwargs)
+        level2func = {'debug2' : _dbg2,
+                      'warning' : self.verbose_logger.warning}
+        if pkg_warn not in level2func:
+            pkg_warn = 'warning'
+        pkg_warn = level2func[pkg_warn]
+
         tx_return = []
         if not po and not kwargs: # update everything (the easy case)
             self.verbose_logger.log(logginglevels.DEBUG_2, _('Updating Everything'))
@@ -4505,7 +4780,7 @@ much more problems).
                     availpkgs = self._compare_providers(availpkgs, requiringPo)
                     availpkgs = map(lambda x: x[0], availpkgs)
                 elif not availpkgs:
-                    self.logger.warning(_("No package matched to upgrade: %s"), self._ui_nevra_dict(nevra_dict))
+                    pkg_warn(_("No package matched to upgrade: %s"), self._ui_nevra_dict(nevra_dict))
        
         # for any thing specified
         # get the list of available pkgs matching it (or take the po)
commit bf7301ebab00451d5bb01558a0a47427c26db053
Author: James Antill <james at and.org>
Date:   Fri Aug 10 15:55:03 2012 -0400

    Add igroup APIs so we can read/write environment groups.

diff --git a/yum/igroups.py b/yum/igroups.py
index 625ee66..6a04f3a 100644
--- a/yum/igroups.py
+++ b/yum/igroups.py
@@ -13,7 +13,7 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 #
-# Copyright 2010 Red Hat
+# Copyright 2010, 2012 Red Hat
 #
 # James Antill <james at fedoraproject.org>
 
@@ -25,6 +25,7 @@ class InstalledGroup(object):
     def __init__(self, gid):
         self.gid       = gid
         self.pkg_names = set()
+        self.environment = None
 
     def __cmp__(self, other):
         if other is None:
@@ -40,12 +41,36 @@ class InstalledGroup(object):
         return sorted(pkg_names.difference(self.pkg_names))
 
 
+class InstalledEnvironment(object):
+    def __init__(self, evgid):
+        self.evgid     = evgid
+        self.grp_names = set()
+
+    def __cmp__(self, other):
+        if other is None:
+            return 1
+        return cmp(self.evgid, other.evgid)
+
+    def _additions(self, grp_names):
+        grp_names = set(grp_names)
+        return sorted(grp_names.difference(self.grp_names))
+
+    def _removals(self, grp_names):
+        grp_names = set(grp_names)
+        return sorted(grp_names.difference(self.grp_names))
+
+
 class InstalledGroups(object):
     def __init__(self, db_path):
-        self.filename = db_path + "/installed"
         self.groups   = {}
         self.changed  = False
+        self.environments = {}
+
+        self._read_pkg_grps(db_path)
+        self._read_grp_grps(db_path)
 
+    def _read_pkg_grps(self, db_path):
+        self.filename = db_path + "/installed"
         if not os.access(self.filename, os.R_OK):
             return
 
@@ -69,6 +94,38 @@ class InstalledGroups(object):
                 num -= 1
                 grp.pkg_names.add(_read_str(fo))
 
+    def _read_grp_grps(self, db_path):
+        self.grp_filename = db_path + "/environment"
+        if not os.access(self.grp_filename, os.R_OK):
+            return
+
+        def _read_str(fo):
+            return fo.readline()[:-1]
+
+        fo = open(self.grp_filename)
+        ver = int(_read_str(fo))
+        if ver != 1:
+            return
+
+        groups_num = int(_read_str(fo))
+        while groups_num > 0:
+            groups_num -= 1
+
+            evgrp = InstalledEnvironment(_read_str(fo))
+            self.environments[evgrp.evgid] = evgrp
+
+            num = int(_read_str(fo))
+            while num > 0:
+                num -= 1
+                grpname = _read_str(fo)
+                memb = _read_str(fo)
+                evgrp.grp_names.add(grpname)
+                assert memb in ('true', 'false')
+                if memb == 'true':
+                    assert grpname in self.groups
+                    if grpname in self.groups:
+                        self.groups[grpname].environment = evgrp.evgid
+
     def close(self):
         pass
 
@@ -87,6 +144,12 @@ class InstalledGroups(object):
         if not os.access(db_path, os.W_OK):
             return False
 
+        self._write_pkg_grps()
+        self._write_grp_grps()
+
+        self.changed = False
+
+    def _write_pkg_grps(self):
         fo = open(self.filename + '.tmp', 'w')
 
         fo.write("1\n") # version
@@ -98,9 +161,26 @@ class InstalledGroups(object):
                 fo.write("%s\n" % pkgname)
         fo.close()
         os.rename(self.filename + '.tmp', self.filename)
-        self.changed = False
 
-    def add_group(self, groupid, pkg_names):
+    def _write_grp_grps(self):
+        fo = open(self.grp_filename + '.tmp', 'w')
+
+        fo.write("1\n") # version
+        fo.write("%u\n" % len(self.environments))
+        for evgrp in sorted(self.environments.values()):
+            fo.write("%s\n" % evgrp.evgid)
+            fo.write("%u\n" % len(evgrp.grp_names))
+            for grpname in sorted(evgrp.grp_names):
+                fo.write("%s\n" % grpname)
+                if self.groups[grpname].environment == evgrp.evgid:
+                    fo.write("%s\n" % "true")
+                else:
+                    fo.write("%s\n" % "false")
+
+        fo.close()
+        os.rename(self.grp_filename + '.tmp', self.grp_filename)
+
+    def add_group(self, groupid, pkg_names, ievgrp=None):
         self.changed = True
 
         if groupid not in self.groups:
@@ -110,6 +190,11 @@ class InstalledGroups(object):
         for pkg_name in pkg_names:
             grp.pkg_names.add(pkg_name)
 
+        if ievgrp is not None:
+            grp.environment = ievgrp.evgid
+            ievgrp.grp_names.add(groupid)
+        return grp
+
     def del_group(self, groupid):
         self.changed = True
 
@@ -139,3 +224,44 @@ class InstalledGroups(object):
                     break
 
         return returns.values()
+
+    def add_environment(self, evgroupid, grp_names):
+        self.changed = True
+
+        if evgroupid not in self.environments:
+            self.environments[evgroupid] = InstalledEnvironment(evgroupid)
+        grp = self.environments[evgroupid]
+
+        for grp_name in grp_names:
+            grp.grp_names.add(grp_name)
+        return grp
+
+    def del_environment(self, evgroupid):
+        self.changed = True
+
+        if evgroupid in self.environments:
+            del self.environments[evgroupid]
+
+    def return_environments(self, evgroup_pattern, case_sensitive=False):
+        returns = {}
+
+        for item in evgroup_pattern.split(','):
+            item = item.strip()
+            if item in self.environments:
+                thisgroup = self.environments[item]
+                returns[thisgroup.evgid] = thisgroup
+                continue
+
+            if case_sensitive:
+                match = re.compile(fnmatch.translate(item)).match
+            else:
+                match = re.compile(fnmatch.translate(item), flags=re.I).match
+
+            done = False
+            for group in self.environments.values():
+                if match(group.evgid):
+                    done = True
+                    returns[group.evgid] = group
+                    break
+
+        return returns.values()
commit 7e7c1a33470019683c0d847d5a0c7e66f66c3621
Author: James Antill <james at and.org>
Date:   Fri Aug 10 15:54:20 2012 -0400

    Add environment holders to transaction data, as we do for groups.

diff --git a/yum/transactioninfo.py b/yum/transactioninfo.py
index b584338..e7377bf 100644
--- a/yum/transactioninfo.py
+++ b/yum/transactioninfo.py
@@ -96,6 +96,8 @@ class TransactionData:
         # lists of txmbrs in their states - just placeholders
         self.instgroups = []
         self.removedgroups = []
+        self.instenvironments = []
+        self.removedenvironments = []
         self.removed = []
         self.installed = []
         self.updated = []
@@ -352,6 +354,8 @@ class TransactionData:
            
         self.instgroups = []
         self.removedgroups = []
+        self.instenvironments = []
+        self.removedenvironments = []
         self.removed = []
         self.installed = []
         self.updated = []
@@ -383,6 +387,10 @@ class TransactionData:
                     for g in txmbr.groups:
                         if g not in self.instgroups:
                             self.instgroups.append(g)
+                if txmbr.environments:
+                    for evg in txmbr.environments:
+                        if evg not in self.instenvironments:
+                            self.instenvironments.append(evg)
                 if txmbr.isDep:
                     self.depinstalled.append(txmbr)
                 else:
@@ -395,6 +403,9 @@ class TransactionData:
                 for g in txmbr.groups:
                     if g not in self.instgroups:
                         self.removedgroups.append(g)
+                for evg in txmbr.environments:
+                    if evg not in self.instenvironments:
+                        self.removedenvironments.append(evg)
                 if txmbr.isDep:
                     self.depremoved.append(txmbr)
                 else:
@@ -420,6 +431,8 @@ class TransactionData:
         self.depremoved.sort()
         self.instgroups.sort()
         self.removedgroups.sort()
+        self.instenvironments.sort()
+        self.removedenvironments.sort()
         self.reinstalled.sort()
         self.downgraded.sort()
         self.failed.sort()
@@ -778,6 +791,7 @@ class TransactionMember:
         self.downgraded_by = []
         self.reinstall = False
         self.groups = [] # groups it's in
+        self.environments = [] # Env. groups it's in
         self._poattr = ['pkgtup', 'repoid', 'name', 'arch', 'epoch', 'version',
                         'release']
 
@@ -851,5 +865,7 @@ class TransactionMember:
                 
         if self.groups:
             msg += "  groups: %s\n" % ' '.join(self.groups)
+        if self.environments:
+            msg += "  environments: %s\n" % ' '.join(self.environments)
 
         return msg
commit b912a265328382b9e9ffefc543b9fdaed3e9e19b
Author: James Antill <james at and.org>
Date:   Fri Aug 10 15:53:37 2012 -0400

    Add compsid, and compile environments.

diff --git a/yum/comps.py b/yum/comps.py
index e9e0326..cf671b6 100755
--- a/yum/comps.py
+++ b/yum/comps.py
@@ -43,6 +43,16 @@ class CompsObj(object):
         return self.name
 
     @property
+    def compsid(self):
+        """ Return the "id": categoryid, groupid, environmentid. """
+
+        for idT in ('categoryid', 'groupid', 'environmentid'):
+            if hasattr(self, idT):
+                return getattr(self, idT)
+
+        return None
+
+    @property
     def ui_name(self):
         """ Return the "name" of the object for the current locale. """
         return self.nameByLang(get_my_lang_code())
@@ -108,7 +118,7 @@ class Group(CompsObj):
         self.optional_packages = {}
         self.default_packages = {}
         self.conditional_packages = {}
-        self.langonly = None ## what the hell is this?
+        self.langonly = None
         self.groupid = None
         self.display_order = 1024
         self.installed = False
@@ -282,12 +292,21 @@ class Environment(CompsObj):
         self.translated_name = {}
         self.translated_description = {}
         self.display_order = 1024
+        self.langonly = None
+        self.installed = False
         self._groups = {}
         self._options = {}
 
         if elem:
             self.parse(elem)
 
+    def _allgroupiter(self):
+        lst = self._groups.keys() + \
+              self._options.keys()
+        return lst
+
+    allgroups = property(_allgroupiter)
+
     def _groupiter(self):
         return self._groups.keys()
 
@@ -747,6 +766,24 @@ class Comps(object):
                     if pkgname in inst_pkg_names:
                         group.installed = True
                         break
+
+        # Now do basically the same thing for evgroups.
+        inst_grp_names = {}
+        for group in self.groups:
+            inst_grp_names[group.groupid] = group.installed
+        for evgroup in self.environments:
+            if evgroup.groups:
+                evgroup.installed = True
+                for grpname in evgroup.groups:
+                    if not inst_grp_names.get(grpname):
+                        evgroup.installed = False
+                        break
+            else:
+                evgroup.installed = False
+                for grpname in evgroup.optional:
+                    if grpname in inst_grp_names:
+                        evgroup.installed = True
+                        break
         
         self.compiled = True
     
commit 5cae71edb167bd30fab5bf336ece58eb4a9712a3
Author: Bill Nottingham <notting at redhat.com>
Date:   Mon Aug 6 13:49:19 2012 -0400

    Rename 'install class' to 'environment'.

diff --git a/yum/comps.py b/yum/comps.py
index 880d373..e9e0326 100755
--- a/yum/comps.py
+++ b/yum/comps.py
@@ -272,12 +272,12 @@ class Group(CompsObj):
         return msg      
 
 
-class InstallClass(CompsObj):
-    """ Installation class object parsed from group data in each repo, and merged """
+class Environment(CompsObj):
+    """ Environment object parsed from group data in each repo, and merged """
 
     def __init__(self, elem=None):
         self.name = ""
-        self.classid = None
+        self.environmentid = None
         self.description = ""
         self.translated_name = {}
         self.translated_description = {}
@@ -302,9 +302,9 @@ class InstallClass(CompsObj):
         for child in elem:
             if child.tag == 'id':
                 myid = child.text
-                if self.classid is not None:
+                if self.environmentid is not None:
                     raise CompsException
-                self.classid = myid
+                self.environmentid = myid
 
             elif child.tag == 'name':
                 text = child.text
@@ -368,11 +368,11 @@ class InstallClass(CompsObj):
                 self.translated_description[lang] = obj.translated_description[lang]
 
     def xml(self):
-        """write out an xml stanza for the installclass object"""
+        """write out an xml stanza for the environment object"""
         msg ="""
-  <installclass>
+  <environment>
    <id>%s</id>
-   <display_order>%s</display_order>\n""" % (self.classid, self.display_order)
+   <display_order>%s</display_order>\n""" % (self.environmentid, self.display_order)
 
         msg +="""   <name>%s</name>\n""" % self.name
         for (lang, val) in self.translated_name.items():
@@ -390,7 +390,7 @@ class InstallClass(CompsObj):
         for grp in self.options:
             msg += """     <optionid>%s</optionid>\n""" % grp
         msg += """    </optionlist>\n"""
-        msg += """  </installclass>\n"""
+        msg += """  </environment>\n"""
 
         return msg
 
@@ -498,7 +498,7 @@ class Category(CompsObj):
 class Comps(object):
     def __init__(self, overwrite_groups=False):
         self._groups = {}
-        self._installclasses = {}
+        self._environments = {}
         self._categories = {}
         self.compscount = 0
         self.overwrite_groups = overwrite_groups
@@ -511,10 +511,10 @@ class Comps(object):
         grps.sort(key=lambda x: (x.display_order, x.name))
         return grps
         
-    def get_installclasses(self):
-        classes = self._installclasses.values()
-        classes.sort(key=lambda x: (x.display_order, x.name))
-        return classes
+    def get_environments(self):
+        environments = self._environments.values()
+        environments.sort(key=lambda x: (x.display_order, x.name))
+        return environments
 
     def get_categories(self):
         cats = self._categories.values()
@@ -522,7 +522,7 @@ class Comps(object):
         return cats
     
     groups = property(get_groups)
-    installclasses = property(get_installclasses)
+    environments = property(get_environments)
     categories = property(get_categories)
     
     def has_group(self, grpid):
@@ -576,31 +576,31 @@ class Comps(object):
 
         return returns.values()
 
-    def has_installclass(self, classid):
-        exists = self.return_installclasses(classid)
+    def has_environment(self, environmentid):
+        exists = self.return_environments(environmentid)
 
         if exists:
             return True
 
         return False
 
-    def return_installclass(self, classid):
+    def return_environment(self, environmentid):
         """Return the first group which matches"""
-        classes = self.return_installclasses(classid)
-        if classes:
-            return classes[0]
+        environments = self.return_environments(environmentid)
+        if environments:
+            return environments[0]
 
         return None
 
-    def return_installclasses(self, class_pattern, case_sensitive=False):
-        """return all installclasses which match either by glob or exact match"""
+    def return_environments(self, env_pattern, case_sensitive=False):
+        """return all environments which match either by glob or exact match"""
         returns = {}
 
-        for item in class_pattern.split(','):
+        for item in env_pattern.split(','):
             item = item.strip()
-            if item in self._installclasses:
-                thisclass = self._installclasses[item]
-                returns[thisclass.classid] = thisclass
+            if item in self._environments:
+                env = self._environments[item]
+                returns[env.environmentid] = env
                 continue
 
             if case_sensitive:
@@ -609,20 +609,20 @@ class Comps(object):
                 match = re.compile(fnmatch.translate(item), flags=re.I).match
 
             done = False
-            for aclass in self.installclasses:
-                for name in aclass.name, aclass.classid, aclass.ui_name:
+            for env in self.environments:
+                for name in env.name, env.environmentid, env.ui_name:
                     if match(name):
                         done = True
-                        returns[aclass.classid] = aclass
+                        returns[env.environmentid] = env
                         break
             if done:
                 continue
 
             # If we didn't match to anything in the current locale, try others
-            for aclass in self.installclasses:
-                for name in aclass.translated_name.values():
+            for env in self.environments:
+                for name in env.translated_name.values():
                     if match(name):
-                        returns[aclass.classid] = aclass
+                        returns[env.environmentid] = env
                         break
 
         return returns.values()
@@ -670,12 +670,12 @@ class Comps(object):
         else:
             self._groups[group.groupid] = group
 
-    def add_installclass(self, installclass):
-        if installclass.classid in self._installclasses:
-            thatclass = self._installclasses[installclass.classid]
-            thatclass.add(installclass)
+    def add_environment(self, environment):
+        if environment.environmentid in self._environments:
+            env = self._environments[environment.environmentid]
+            env.add(environment)
         else:
-            self._installclasses[installclass.classid] = installclass
+            self._environments[environment.environmentid] = environment
 
     def add_category(self, category):
         if category.categoryid in self._categories:
@@ -707,9 +707,9 @@ class Comps(object):
                 if elem.tag == "group":
                     group = Group(elem)
                     self.add_group(group)
-                if elem.tag == "installclass":
-                    installclass = InstallClass(elem)
-                    self.add_installclass(installclass)
+                if elem.tag == "environment":
+                    environment = Environment(elem)
+                    self.add_environment(environment)
                 if elem.tag == "category":
                     category = Category(elem)
                     self.add_category(category)
@@ -785,11 +785,11 @@ def main():
             for pkg in group.packages:
                 print '  ' + pkg
         
-        for installclass in p.installclasses:
-            print installclass.name
-            for group in installclass.groups:
+        for environment in p.environments:
+            print environment.name
+            for group in environment.groups:
                 print '  ' + group
-            for group in installclass.options:
+            for group in environment.options:
                 print '  *' + group
 
         for category in p.categories:
commit 10bc80de8bea6da42ca3fa16d25e00716ac5bef6
Author: James Antill <james at and.org>
Date:   Thu Aug 2 15:16:25 2012 -0400

    Add all the things, when using mdpolicy=group:all

diff --git a/yum/yumRepo.py b/yum/yumRepo.py
index dc8a33b..3d95736 100644
--- a/yum/yumRepo.py
+++ b/yum/yumRepo.py
@@ -332,7 +332,7 @@ class YumRepository(Repository, config.RepoConf):
         self.copy_local = 0
         # holder for stuff we've grabbed
         self.retrieved = { 'primary':0, 'filelists':0, 'other':0, 'group':0,
-                           'updateinfo':0, 'prestodelta' : 0}
+                           'updateinfo':0}
 
         # callbacks
         self.callback = None  # for the grabber
@@ -1436,6 +1436,14 @@ Insufficient space in download directory %s
             return True
 
         all_mdtypes = self.retrieved.keys()
+        # Add in any extra stuff we don't know about.
+        for mdtype in self.repoXML.fileTypes():
+            if mdtype in all_mdtypes:
+                continue
+            if mdtype in ('primary_db', 'filelists_db', 'other_db'):
+                continue
+            all_mdtypes.append(mdtype)
+
         if mdtypes is None:
             mdtypes = all_mdtypes
 
commit a405a95c8df0de830b94907c0909782d3bd0333d
Author: James Antill <james at and.org>
Date:   Thu Aug 2 15:15:56 2012 -0400

    Simple test for install foo.i686 means foo.x86_64 needs an upgrade, prob. dup.

diff --git a/test/simpleupdatetests.py b/test/simpleupdatetests.py
index 2c8bcb3..9e22a6b 100644
--- a/test/simpleupdatetests.py
+++ b/test/simpleupdatetests.py
@@ -1059,3 +1059,24 @@ class SimpleUpdateTests(OperationsTests):
         # Ideal:
         # self.assertResult((foo11, bar12, cbar11))
         self.assertResult((foo12, bar12, cbar11))
+
+    def testInstallOtherArch1(self):
+        #  Installing something with dep. on newer version should upgrade older
+        # version with other arch.
+        # Eg. BZ 791326
+        pax1 = FakePackage('inst', '1', '1', '0', 'x86_64')
+        pai1 = FakePackage('inst', '1', '1', '0', 'i686')
+        pax2 = FakePackage('inst', '2', '1', '0', 'x86_64')
+        pax2.addProvides('foo-x', 'EQ', ('0', '2', '1'))
+        pai2 = FakePackage('inst', '2', '1', '0', 'i686')
+        pai2.addProvides('foo-i', 'EQ', ('0', '2', '1'))
+
+        pa2 = FakePackage('bar', '1', '1', '0', 'i386')
+        pa2.addRequires('foo-i', 'EQ', ('0', '2', '1'))
+
+        res, msg = self.runOperation(['install', 'bar'],
+                                     [pax1],
+                                     [pax1, pai1, pax2, pai2, pa2])
+        self.assert_(res=='ok', msg)
+        self.assertResult((pax2, pai2, pa2))
+
commit 07fe65cd87298cb468ad92c0923acd5ef28068cb
Author: James Antill <james at and.org>
Date:   Tue Jul 31 16:08:22 2012 -0400

    Mostly fix the makecache and using updateinfo/etc. with -C. BZ 815568.

diff --git a/yumcommands.py b/yumcommands.py
index 585a10b..e83f426 100644
--- a/yumcommands.py
+++ b/yumcommands.py
@@ -1128,6 +1128,18 @@ class MakeCacheCommand(YumCommand):
             # 2. Download a .xml.gz and convert to .xml.gz.sqlite
             base.repos.populateSack(mdtype='all', cacheonly=1)
 
+            # Now decompress stuff, so that -C works, sigh.
+            fname_map = {'group_gz'   : 'groups.xml',
+                         'pkgtags'    : 'pkgtags.sqlite',
+                         'updateinfo' : 'updateinfo.xml',
+                         }
+            for repo in base.repos.listEnabled():
+                for MD in repo.repoXML.fileTypes():
+                    if MD not in fname_map:
+                        continue
+                    misc.repo_gen_decompress(repo.retrieveMD(MD),
+                                             fname_map[MD],
+                                             cached=repo.cache)
 
         except yum.Errors.YumBaseError, e:
             return 1, [exception2msg(e)]
commit dd59c6676575355dae4da8bd233f428aa582d0d0
Author: James Antill <james at and.org>
Date:   Tue Jul 31 16:35:28 2012 -0400

    Show giant message about mutilib errors, to help people understand. BZ 840543

diff --git a/yum/__init__.py b/yum/__init__.py
index 4650639..6ec5b15 100644
--- a/yum/__init__.py
+++ b/yum/__init__.py
@@ -1184,6 +1184,37 @@ class YumBase(depsolve.Depsolve):
                 if first.verEQ(other):
                     continue
                 msg = _('Protected multilib versions: %s != %s')
+                if not xrestring:
+                    #  People are confused about protected mutilib ... so give
+                    # them a nicer message.
+                    bigmsg = _("""\
+ Multilib version problems found. This often means that the root
+cause is something else and multilib version checking is just
+pointing it that there is a problem. Eg.:
+
+  1. You have an upgrade for %(name)s which is missing some
+     dependency that another package requires. Yum is trying to
+     solve this by installing an older version of %(name)s of the
+     different architecture. If you exclude the bad architecture
+     yum will tell you what the root cause is (which package
+     requires what).
+
+  2. You have multiple architectures of %(name)s installed, but
+     yum can only see an upgrade for one of those arcitectures.
+     If you don't want/need both architectures anymore then you
+     can remove the one with the missing update and everything
+     will work.
+
+  3. You have duplicate versions of %(name)s installed already.
+     You can use "yum check" to get yum show these errors.
+
+...you can also use --setopt=protected_multilib=false to remove
+this checking, however this is almost never the correct thing to
+do as something else is very likely to go wrong (often causing
+much more problems).
+
+""") % {'name' : pkgname}
+                    msg = bigmsg + msg
                 xrestring.append(msg % (first, other))
         if xrestring:
             rescode = 1
commit 56bb0d8070170525bb7eed6b7102b10e216d0634
Author: Bill Nottingham <notting at redhat.com>
Date:   Wed Jul 18 15:19:34 2012 -0400

    Add support for parsing 'install classes' to the comps file.
    
    Install classes are a construct for use in anaconda for installing the
    system. They consist of:
    - id
    - name
    - description
    - display order
    - a list of groups that make up that install class
    - a list of groups that are options for that install class
    
    Expected usage case is that anaconda will offer for you to pick an
    install class, and once you've done so, offer you the list of options
    to optionally install.

diff --git a/yum/comps.py b/yum/comps.py
index 65f6d5e..880d373 100755
--- a/yum/comps.py
+++ b/yum/comps.py
@@ -272,6 +272,128 @@ class Group(CompsObj):
         return msg      
 
 
+class InstallClass(CompsObj):
+    """ Installation class object parsed from group data in each repo, and merged """
+
+    def __init__(self, elem=None):
+        self.name = ""
+        self.classid = None
+        self.description = ""
+        self.translated_name = {}
+        self.translated_description = {}
+        self.display_order = 1024
+        self._groups = {}
+        self._options = {}
+
+        if elem:
+            self.parse(elem)
+
+    def _groupiter(self):
+        return self._groups.keys()
+
+    groups = property(_groupiter)
+
+    def _optioniter(self):
+        return self._options.keys()
+
+    options = property(_optioniter)
+
+    def parse(self, elem):
+        for child in elem:
+            if child.tag == 'id':
+                myid = child.text
+                if self.classid is not None:
+                    raise CompsException
+                self.classid = myid
+
+            elif child.tag == 'name':
+                text = child.text
+                if text:
+                    text = text.encode('utf8')
+
+                lang = child.attrib.get(lang_attr)
+                if lang:
+                    self.translated_name[lang] = text
+                else:
+                    self.name = text
+
+            elif child.tag == 'description':
+                text = child.text
+                if text:
+                    text = text.encode('utf8')
+
+                lang = child.attrib.get(lang_attr)
+                if lang:
+                    self.translated_description[lang] = text
+                else:
+                    self.description = text
+
+            elif child.tag == 'grouplist':
+                self.parse_group_list(child)
+
+            elif child.tag == 'optionlist':
+                self.parse_option_list(child)
+
+            elif child.tag == 'display_order':
+                self.display_order = parse_number(child.text)
+
+    def parse_group_list(self, grouplist_elem):
+        for child in grouplist_elem:
+            if child.tag == 'groupid':
+                groupid = child.text
+                self._groups[groupid] = 1
+
+    def parse_option_list(self, optionlist_elem):
+        for child in optionlist_elem:
+            if child.tag == 'groupid':
+                optionid = child.text
+                self._options[optionid] = 1
+
+    def add(self, obj):
+        """Add another category object to this object"""
+
+        for grp in obj.groups:
+            self._groups[grp] = 1
+
+        for grp in obj.options:
+            self._options[grp] = 1
+
+        # name and description translations
+        for lang in obj.translated_name:
+            if lang not in self.translated_name:
+                self.translated_name[lang] = obj.translated_name[lang]
+
+        for lang in obj.translated_description:
+            if lang not in self.translated_description:
+                self.translated_description[lang] = obj.translated_description[lang]
+
+    def xml(self):
+        """write out an xml stanza for the installclass object"""
+        msg ="""
+  <installclass>
+   <id>%s</id>
+   <display_order>%s</display_order>\n""" % (self.classid, self.display_order)
+
+        msg +="""   <name>%s</name>\n""" % self.name
+        for (lang, val) in self.translated_name.items():
+            msg += """   <name xml:lang="%s">%s</name>\n""" % (lang, val)
+
+        msg += """   <description>%s</description>\n""" % self.description
+        for (lang, val) in self.translated_description.items():
+            msg += """    <description xml:lang="%s">%s</description>\n""" % (lang, val)
+
+        msg += """    <grouplist>\n"""
+        for grp in self.groups:
+            msg += """     <groupid>%s</groupid>\n""" % grp
+        msg += """    </grouplist>\n"""
+        msg += """    <optionlist>\n"""
+        for grp in self.options:
+            msg += """     <optionid>%s</optionid>\n""" % grp
+        msg += """    </optionlist>\n"""
+        msg += """  </installclass>\n"""
+
+        return msg
+
 class Category(CompsObj):
     """ Category object parsed from group data in each repo. and merged. """
 
@@ -376,6 +498,7 @@ class Category(CompsObj):
 class Comps(object):
     def __init__(self, overwrite_groups=False):
         self._groups = {}
+        self._installclasses = {}
         self._categories = {}
         self.compscount = 0
         self.overwrite_groups = overwrite_groups
@@ -388,12 +511,18 @@ class Comps(object):
         grps.sort(key=lambda x: (x.display_order, x.name))
         return grps
         
+    def get_installclasses(self):
+        classes = self._installclasses.values()
+        classes.sort(key=lambda x: (x.display_order, x.name))
+        return classes
+
     def get_categories(self):
         cats = self._categories.values()
         cats.sort(key=lambda x: (x.display_order, x.name))
         return cats
     
     groups = property(get_groups)
+    installclasses = property(get_installclasses)
     categories = property(get_categories)
     
     def has_group(self, grpid):
@@ -447,6 +576,57 @@ class Comps(object):
 
         return returns.values()
 
+    def has_installclass(self, classid):
+        exists = self.return_installclasses(classid)
+
+        if exists:
+            return True
+
+        return False
+
+    def return_installclass(self, classid):
+        """Return the first group which matches"""
+        classes = self.return_installclasses(classid)
+        if classes:
+            return classes[0]
+
+        return None
+
+    def return_installclasses(self, class_pattern, case_sensitive=False):
+        """return all installclasses which match either by glob or exact match"""
+        returns = {}
+
+        for item in class_pattern.split(','):
+            item = item.strip()
+            if item in self._installclasses:
+                thisclass = self._installclasses[item]
+                returns[thisclass.classid] = thisclass
+                continue
+
+            if case_sensitive:
+                match = re.compile(fnmatch.translate(item)).match
+            else:
+                match = re.compile(fnmatch.translate(item), flags=re.I).match
+
+            done = False
+            for aclass in self.installclasses:
+                for name in aclass.name, aclass.classid, aclass.ui_name:
+                    if match(name):
+                        done = True
+                        returns[aclass.classid] = aclass
+                        break
+            if done:
+                continue
+
+            # If we didn't match to anything in the current locale, try others
+            for aclass in self.installclasses:
+                for name in aclass.translated_name.values():
+                    if match(name):
+                        returns[aclass.classid] = aclass
+                        break
+
+        return returns.values()
+
     #  This is close to returnPackages() etc. API ... need to std. these names
     # the above return_groups uses different, but equal, API.
     def return_categories(self, pattern, ignore_case=True):
@@ -490,6 +670,13 @@ class Comps(object):
         else:
             self._groups[group.groupid] = group
 
+    def add_installclass(self, installclass):
+        if installclass.classid in self._installclasses:
+            thatclass = self._installclasses[installclass.classid]
+            thatclass.add(installclass)
+        else:
+            self._installclasses[installclass.classid] = installclass
+
     def add_category(self, category):
         if category.categoryid in self._categories:
             thatcat = self._categories[category.categoryid]
@@ -520,6 +707,9 @@ class Comps(object):
                 if elem.tag == "group":
                     group = Group(elem)
                     self.add_group(group)
+                if elem.tag == "installclass":
+                    installclass = InstallClass(elem)
+                    self.add_installclass(installclass)
                 if elem.tag == "category":
                     category = Category(elem)
                     self.add_category(category)
@@ -595,6 +785,13 @@ def main():
             for pkg in group.packages:
                 print '  ' + pkg
         
+        for installclass in p.installclasses:
+            print installclass.name
+            for group in installclass.groups:
+                print '  ' + group
+            for group in installclass.options:
+                print '  *' + group
+
         for category in p.categories:
             print category.name
             for group in category.groups:
commit 885b5b696c988182f543ea407b980a836b2b7d2d
Author: James Antill <james at and.org>
Date:   Tue Jul 17 16:56:02 2012 -0400

     Add the opt. commands: remove-n, remove-na, remove-nevra. Like install-*.
    
     Also fix install-nevra, and add user messages.

diff --git a/cli.py b/cli.py
index 93f1d20..91c2553 100755
--- a/cli.py
+++ b/cli.py
@@ -831,12 +831,22 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
                 elif basecmd == 'install-n':
                     txmbrs = self.install(name=arg)
                 elif basecmd == 'install-na':
-                    n,a = arg.split('.')
+                    try:
+                        n,a = arg.rsplit('.', 1)
+                    except:
+                        self.verbose_logger.warning(_('Bad %s argument %s.'),
+                                                    basecmd, arg)
+                        continue
                     txmbrs = self.install(name=n, arch=a)
                 elif basecmd == 'install-nevra':
-                    nevr,a = arg.rsplit('.', 2)
-                    n,ev,r = nevr.rsplit('-', 3)
-                    e,v    = ev.split(':', 2)
+                    try:
+                        nevr,a = arg.rsplit('.', 1)
+                        n,ev,r = nevr.rsplit('-', 2)
+                        e,v    = ev.split(':', 1)
+                    except:
+                        self.verbose_logger.warning(_('Bad %s argument %s.'),
+                                                    basecmd, arg)
+                        continue
                     txmbrs = self.install(name=n,
                                           epoch=e, version=v, release=r, arch=a)
                 else:
@@ -1024,7 +1034,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
         else:
             return 0, [_('No Packages marked for Distribution Synchronization')]
 
-    def erasePkgs(self, userlist, pos=False):
+    def erasePkgs(self, userlist, pos=False, basecmd='remove'):
         """Take user commands and populate a transaction wrapper with
         packages to be erased.
 
@@ -1047,7 +1057,31 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
                     all_rms.extend(rms)
                 continue
 
-            rms = self.remove(pattern=arg)
+            if False: pass
+            elif basecmd in ('erase-n', 'remove-n'):
+                rms = self.remove(name=arg)
+            elif basecmd in ('erase-na', 'remove-na'):
+                try:
+                    n,a = arg.rsplit('.', 1)
+                except:
+                    self.verbose_logger.warning(_('Bad %s argument %s.'),
+                                                basecmd, arg)
+                    continue
+                rms = self.remove(name=n, arch=a)
+            elif basecmd in ('erase-nevra', 'remove-nevra'):
+                try:
+                    nevr,a = arg.rsplit('.', 1)
+                    n,ev,r = nevr.rsplit('-', 2)
+                    e,v    = ev.split(':', 1)
+                except:
+                    self.verbose_logger.warning(_('Bad %s argument %s.'),
+                                                basecmd, arg)
+                    continue
+                rms = self.remove(name=n, epoch=e, version=v, release=r, arch=a)
+            else:
+                assert basecmd in ('erase', 'remove'), basecmd
+                rms = self.remove(pattern=arg)
+
             if not rms:
                 self._checkMaybeYouMeant(arg, always_output=False, rpmdb_only=True)
             all_rms.extend(rms)
diff --git a/docs/yum.8 b/docs/yum.8
index f84507b..188cd3e 100644
--- a/docs/yum.8
+++ b/docs/yum.8
@@ -166,6 +166,11 @@ the "install" command\&.(See \fBSpecifying package names\fP for more information
 
 Note that "yum" is included in the protected_packages configuration, by default.
 So you can't accidentally remove yum itself.
+
+Because remove does a lot of work to make it as easy as possible to use, there
+are also a few specific remove commands "\fBremove-n\fP", "\fBremove-na\fP"
+and "\fBremove-nevra\fP". These only work on package names, and do not process
+wildcards etc.
 .IP 
 .IP "\fBlist\fP"
 Is used to list various information about available
diff --git a/yumcommands.py b/yumcommands.py
index a078ef1..585a10b 100644
--- a/yumcommands.py
+++ b/yumcommands.py
@@ -681,7 +681,9 @@ class EraseCommand(YumCommand):
 
         :return: a list containing the names of this command
         """
-        return ['erase', 'remove', 'autoremove']
+        return ['erase', 'remove', 'autoremove',
+                'erase-n', 'erase-na', 'erase-nevra',
+                'remove-n', 'remove-na', 'remove-nevra']
 
     def getUsage(self):
         """Return a usage string for this command.
@@ -745,7 +747,7 @@ class EraseCommand(YumCommand):
 
         self.doneCommand(base, _("Setting up Remove Process"))
         try:
-            ret = base.erasePkgs(extcmds, pos=pos)
+            ret = base.erasePkgs(extcmds, pos=pos, basecmd=basecmd)
         except yum.Errors.YumBaseError, e:
             ret = (1, [exception2msg(e)])
 
commit e34133e5ab72b35f5c9ff069c49655e8a2894ee2
Author: James Antill <james at and.org>
Date:   Tue Jul 17 16:35:39 2012 -0400

    Minor opt. for completely loaded setting on rpmdb.

diff --git a/yum/rpmsack.py b/yum/rpmsack.py
index ed8e3d1..a4da4ab 100644
--- a/yum/rpmsack.py
+++ b/yum/rpmsack.py
@@ -628,7 +628,7 @@ class RPMDBPackageSack(PackageSackBase):
             for hdr, idx in self._get_packages():
                 if self._match_repattern(rpats, hdr, ignore_case):
                     self._makePackageObject(hdr, idx)
-            self._completely_loaded = patterns is None
+            self._completely_loaded = rpats is None
 
         pkgobjlist = self._idx2pkg.values()
         # Remove gpg-pubkeys, as no sane callers expects/likes them...
commit 4ad8960e12a19cbd2f6e3bb039c7b65015f59e19
Author: James Antill <james at and.org>
Date:   Thu Jul 12 14:02:01 2012 -0400

    Add autoremove alias for clean_reqs_on_remove, for apt ppl (also no arg.).

diff --git a/cli.py b/cli.py
index 6ad3397..93f1d20 100755
--- a/cli.py
+++ b/cli.py
@@ -1024,7 +1024,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
         else:
             return 0, [_('No Packages marked for Distribution Synchronization')]
 
-    def erasePkgs(self, userlist):
+    def erasePkgs(self, userlist, pos=False):
         """Take user commands and populate a transaction wrapper with
         packages to be erased.
 
@@ -1041,6 +1041,12 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
 
         all_rms = []
         for arg in userlist:
+            if pos:
+                rms = self.remove(po=arg)
+                if rms:
+                    all_rms.extend(rms)
+                continue
+
             rms = self.remove(pattern=arg)
             if not rms:
                 self._checkMaybeYouMeant(arg, always_output=False, rpmdb_only=True)
diff --git a/yumcommands.py b/yumcommands.py
index bc30bd4..a078ef1 100644
--- a/yumcommands.py
+++ b/yumcommands.py
@@ -681,7 +681,7 @@ class EraseCommand(YumCommand):
 
         :return: a list containing the names of this command
         """
-        return ['erase', 'remove']
+        return ['erase', 'remove', 'autoremove']
 
     def getUsage(self):
         """Return a usage string for this command.
@@ -708,6 +708,8 @@ class EraseCommand(YumCommand):
         :param extcmds: the command line arguments passed to *basecmd*
         """
         checkRootUID(base)
+        if basecmd == 'autoremove':
+            return
         checkPackageArg(base, basecmd, extcmds)
 
     def doCommand(self, base, basecmd, extcmds):
@@ -724,11 +726,30 @@ class EraseCommand(YumCommand):
             1 = we've errored, exit with error string
             2 = we've got work yet to do, onto the next stage
         """
+
+        pos = False
+        if basecmd == 'autoremove':
+            #  We have to alter this, as it's used in resolving stage. Which
+            # sucks. Just be careful in "yum shell".
+            base.conf.clean_requirements_on_remove = True
+
+            if not extcmds:
+                pos = True
+                extcmds = []
+                for pkg in sorted(base.rpmdb.returnLeafNodes()):
+                    if 'reason' not in pkg.yumdb_info:
+                        continue
+                    if pkg.yumdb_info.reason != 'dep':
+                        continue
+                    extcmds.append(pkg)
+
         self.doneCommand(base, _("Setting up Remove Process"))
         try:
-            return base.erasePkgs(extcmds)
+            ret = base.erasePkgs(extcmds, pos=pos)
         except yum.Errors.YumBaseError, e:
-            return 1, [exception2msg(e)]
+            ret = (1, [exception2msg(e)])
+
+        return ret
 
     def needTs(self, base, basecmd, extcmds):
         """Return whether a transaction set must be set up before this
commit b8200ba87ca848c50b74d0e0901d6bfc6b52b9a3
Author: James Antill <james at and.org>
Date:   Thu Jul 12 14:08:25 2012 -0400

    Make tolerant turn on recheck_installed_reqs ... maybe similar tradeoffs.

diff --git a/cli.py b/cli.py
index fef154e..6ad3397 100755
--- a/cli.py
+++ b/cli.py
@@ -1922,6 +1922,9 @@ class YumOptionParser(OptionParser):
         try:
             # config file is parsed and moving us forward
             # set some things in it.
+
+            if opts.tolerant or self.base.conf.tolerant: # Make it slower capt.
+                self.base.conf.recheck_installed_requires = True
                 
             # Handle remaining options
             if opts.assumeyes:
diff --git a/docs/yum.8 b/docs/yum.8
index 604377b..f84507b 100644
--- a/docs/yum.8
+++ b/docs/yum.8
@@ -569,7 +569,8 @@ option will corrupt your cache (and you can use $releasever in your cachedir
 configuration to stop this).
 .PP 
 .IP "\fB\-t, \-\-tolerant\fP"
-This option currently does nothing.
+This option makes yum go slower, checking for things that shouldn't be possible
+making it more tolerant of external errors.
 .br
 .IP "\fB\-\-setopt=option=value\fP"
 Set any config option in yum config or repo files. For options in the global 
commit d1a1712dd854132b2933c65d5fe4451477b5ab52
Author: James Antill <james at and.org>
Date:   Mon Jul 9 17:12:45 2012 -0400

    Add a simple checkfunc if we don't have one, for pkg d/l. Helps BZ 681224

diff --git a/yum/yumRepo.py b/yum/yumRepo.py
index 4772a72..dc8a33b 100644
--- a/yum/yumRepo.py
+++ b/yum/yumRepo.py
@@ -947,6 +947,9 @@ Insufficient space in download directory %s
                 return local
             misc.unlink_f(local)
 
+        if checkfunc is None:
+            checkfunc = (package.verifyLocalPkg, (), {})
+
         ret = self._getFile(url=basepath,
                         relative=remote,
                         local=local,
@@ -956,6 +959,7 @@ Insufficient space in download directory %s
                         size=package.size,
                         **kwargs
                         )
+
         if not package.verifyLocalPkg(): # Don't return as "success" when bad.
             msg = "Downloaded package %s, from %s, but it was invalid."
             msg = msg % (package, pacakge.repo.id)
commit 4ba7b69a5de061a4b65eb68f37a3ec23fb85ca57
Author: James Antill <james at and.org>
Date:   Thu Jun 28 15:55:15 2012 -0400

    Add a final check for package downloads. BZ 681224

diff --git a/yum/yumRepo.py b/yum/yumRepo.py
index 128795b..4772a72 100644
--- a/yum/yumRepo.py
+++ b/yum/yumRepo.py
@@ -947,7 +947,7 @@ Insufficient space in download directory %s
                 return local
             misc.unlink_f(local)
 
-        return self._getFile(url=basepath,
+        ret = self._getFile(url=basepath,
                         relative=remote,
                         local=local,
                         checkfunc=checkfunc,
@@ -956,6 +956,12 @@ Insufficient space in download directory %s
                         size=package.size,
                         **kwargs
                         )
+        if not package.verifyLocalPkg(): # Don't return as "success" when bad.
+            msg = "Downloaded package %s, from %s, but it was invalid."
+            msg = msg % (package, pacakge.repo.id)
+            raise Errors.RepoError, msg
+
+        return ret
 
     def getHeader(self, package, checkfunc = None, reget = 'simple',
             cache = True):


More information about the Yum-commits mailing list