[yum-commits] Branch 'yum-3_2_X' - 10 commits - yum/__init__.py yum/packageSack.py yum/repos.py yum/sqlitesack.py yumcommands.py

James Antill james at osuosl.org
Fri Jul 17 19:30:43 UTC 2009


 yum/__init__.py    |   97 ++++++++++------
 yum/packageSack.py |   27 +++-
 yum/repos.py       |   21 +++
 yum/sqlitesack.py  |  307 +++++++++++++++++++++++++++++++++++++----------------
 yumcommands.py     |  133 ++++++++++++++++++++++
 5 files changed, 449 insertions(+), 136 deletions(-)

New commits:
commit f21d1dd80dbb2ad350655c4fd3fe68c03028a71b
Author: James Antill <james at and.org>
Date:   Fri Jul 17 15:25:27 2009 -0400

     Use new pkgExcluder code to speed up cost excludes.
       Add listEnabled() cache to repos.
       Add _pkgtup2pkgs to sqlitesack.
    
     yum list blah ... now only needs to consider "blah" for cost excludes.
     yum list updates ... does everything (worst case), and still 25-30% faster.

diff --git a/yum/__init__.py b/yum/__init__.py
index af41e3f..e683860 100644
--- a/yum/__init__.py
+++ b/yum/__init__.py
@@ -68,6 +68,8 @@ from yum.i18n import to_unicode
 
 import string
 
+from weakref import proxy as weakref
+
 from urlgrabber.grabber import default_grabber
 
 __version__ = '3.2.23'
@@ -100,6 +102,28 @@ class _YumPreBaseConf:
         self.arch = None
         self.releasever = None
 
+class _YumCostExclude:
+    """ This excludes packages that are in repos. of lower cost than the passed
+        repo. """
+
+    def __init__(self, repo, repos):
+        self.repo   = weakref(repo)
+        self._repos = weakref(repos)
+
+    def __contains__(self, pkgtup):
+        # (n, a, e, v, r) = pkgtup
+        for repo in self._repos.listEnabled():
+            if repo.cost >= self.repo.cost:
+                break
+            #  searchNevra is a bit slower, although more generic for repos. 
+            # that don't use sqlitesack as the backend ... although they are
+            # probably screwed anyway.
+            #
+            # if repo.sack.searchNevra(n, e, v, r, a):
+            if pkgtup in repo.sack._pkgtup2pkgs:
+                return True
+        return False
+
 class YumBase(depsolve.Depsolve):
     """This is a primary structure and base class. It houses the objects and
        methods needed to perform most things in yum. It is almost an abstract
@@ -1094,45 +1118,30 @@ class YumBase(depsolve.Depsolve):
         self.rpmdb.dropCachedData()
 
     def costExcludePackages(self):
-        """exclude packages if they have an identical package in another repo
-        and their repo.cost value is the greater one"""
+        """ Create an excluder for repos. with higher cost. Eg.
+            repo-A:cost=1 repo-B:cost=2 ... here we setup an excluder on repo-B
+            that looks for pkgs in repo-B."""
         
-        # check to see if the cost per repo is anything other than equal
         # if all the repo.costs are equal then don't bother running things
         costs = {}
         for r in self.repos.listEnabled():
-            costs[r.cost] = 1
+            costs.setdefault(r.cost, []).append(r)
 
-        if len(costs) <= 1: # if all of our costs are the same then return
+        if len(costs) <= 1:
             return
-            
-        def _sort_by_cost(a, b):
-            if a.repo.cost < b.repo.cost:
-                return -1
-            if a.repo.cost == b.repo.cost:
-                return 0
-            if a.repo.cost > b.repo.cost:
-                return 1
-                
-        pkgdict = {}
-        for po in self.pkgSack:
-            if not pkgdict.has_key(po.pkgtup):
-                pkgdict[po.pkgtup] = []
-            pkgdict[po.pkgtup].append(po)
-        
-        for pkgs in pkgdict.values():
-            if len(pkgs) == 1:
-                continue
-                
-            pkgs.sort(_sort_by_cost)
-            lowcost = pkgs[0].repo.cost
-            #print '%s : %s : %s' % (pkgs[0], pkgs[0].repo, pkgs[0].repo.cost)
-            for pkg in pkgs[1:]:
-                if pkg.repo.cost > lowcost:
-                    msg = _('excluding for cost: %s from %s') % (pkg, pkg.repo.id)
-                    self.verbose_logger.log(logginglevels.DEBUG_3, msg)
-                    pkg.repo.sack.delPackage(pkg)
-            
+
+        done = False
+        exid = "yum.costexcludes"
+        orepos = []
+        for cost in sorted(costs):
+            if done: # Skip the first one, as they have lowest cost so are good.
+                for repo in costs[cost]:
+                    print "JDBG:", repo, cost, len(orepos)
+                    yce = _YumCostExclude(repo, self.repos)
+                    repo.sack.addPackageExcluder(repo.id, exid,
+                                                 'exclude.pkgtup.in', yce)
+            orepos.extend(costs[cost])
+            done = True
 
     def excludePackages(self, repo=None):
         """removes packages from packageSacks based on global exclude lists,
diff --git a/yum/repos.py b/yum/repos.py
index 5d9a343..ac3e197 100644
--- a/yum/repos.py
+++ b/yum/repos.py
@@ -59,6 +59,10 @@ class RepoStorage:
         self.gpg_import_func = _wrap_ayum_getKeyForRepo(ayum)
         self.confirm_func = None
 
+        # This allow listEnabled() to be O(1) most of the time.
+        self._cache_enabled_repos = []
+        self.quick_enable_disable = {}
+
     def doSetup(self, thisrepo = None):
         
         self.ayum.plugins.run('prereposetup')
@@ -92,7 +96,10 @@ class RepoStorage:
         if self.repos.has_key(repoobj.id):
             raise Errors.DuplicateRepoError, 'Repository %s is listed more than once in the configuration' % (repoobj.id)
         self.repos[repoobj.id] = repoobj
-        
+        if hasattr(repoobj, 'quick_enable_disable'):
+            repoobj.quick_enable_disable = self.quick_enable_disable
+        else:
+            self._cache_enabled_repos = None
 
     def delete(self, repoid):
         if self.repos.has_key(repoid):
@@ -163,12 +170,21 @@ class RepoStorage:
         
     def listEnabled(self):
         """return list of enabled repo objects"""
+
+        if (self._cache_enabled_repos is not None and
+            not self.quick_enable_disable):
+            return self._cache_enabled_repos
+
         returnlist = []
         for repo in self.repos.values():
             if repo.isEnabled():
                 returnlist.append(repo)
 
         returnlist.sort()
+
+        if self._cache_enabled_repos is not None:
+            self._cache_enabled_repos = returnlist
+            self.quick_enable_disable.clear()
         return returnlist
 
     def listGroupsEnabled(self):
@@ -266,6 +282,7 @@ class Repository:
 
     def __init__(self, repoid):
         self.id = repoid
+        self.quick_enable_disable = {}
         self.disable()
 
     def __cmp__(self, other):
@@ -303,9 +320,11 @@ class Repository:
 
     def enable(self):
         self.setAttribute('enabled', 1)
+        self.quick_enable_disable[self.id] = True
                     
     def disable(self):
         self.setAttribute('enabled', 0)
+        self.quick_enable_disable[self.id] = False
 
     def getExcludePkgList(self):
         excludeList = self.getAttribute('exclude')
diff --git a/yum/sqlitesack.py b/yum/sqlitesack.py
index 3469867..7f61190 100644
--- a/yum/sqlitesack.py
+++ b/yum/sqlitesack.py
@@ -421,6 +421,7 @@ class YumSqlitePackageSack(yumRepo.YumPackageSack):
             }
         self._key2pkg = {}
         self._pkgname2pkgkeys = {}
+        self._pkgtup2pkgs = {}
         self._pkgnames_loaded = set()
         self._arch_allowed = None
         self._pkgExcluder = []
@@ -527,7 +528,11 @@ class YumSqlitePackageSack(yumRepo.YumPackageSack):
         self._excludes.add((repo, pkgKey))
         # Don't keep references around, just wastes memory.
         if repo in self._key2pkg:
-            self._key2pkg[repo].pop(pkgKey, 0)
+            po = self._key2pkg[repo].pop(pkgKey, None)
+            if po is not None: # Will also be in the pkgtup2pkgs cache...
+                pos = self._pkgtup2pkgs[po.pkgtup]
+                pos = filter(lambda x: id(x) == id(po), pos)
+                self._pkgtup2pkgs[po.pkgtup] = pos
 
     # Remove a package
     # Because we don't want to remove a package from the database we just
@@ -702,7 +707,9 @@ class YumSqlitePackageSack(yumRepo.YumPackageSack):
                 raise Errors.RepoError, msg
             if exclude and self._pkgExcludedRKD(repo, pkgKey, data):
                 return None
-            self._key2pkg[repo][pkgKey] = self.pc(repo, data)
+            po = self.pc(repo, data)
+            self._key2pkg[repo][pkgKey] = po
+            self._pkgtup2pkgs.setdefault(po.pkgtup, []).append(po)
             pkgkeys = self._pkgname2pkgkeys[repo].setdefault(data['name'], [])
             pkgkeys.append(pkgKey)
         return self._key2pkg[repo][pkgKey]
@@ -717,6 +724,7 @@ class YumSqlitePackageSack(yumRepo.YumPackageSack):
         if data['pkgKey'] not in self._key2pkg.get(repo, {}):
             po = self.pc(repo, data)
             self._key2pkg[repo][pkgKey] = po
+            self._pkgtup2pkgs.setdefault(po.pkgtup, []).append(po)
             pkgkeys = self._pkgname2pkgkeys[repo].setdefault(data['name'], [])
             pkgkeys.append(pkgKey)
         return self._key2pkg[repo][data['pkgKey']]
commit f43f75bcb2943efcd6a38e15f258405328595ab2
Author: James Antill <james at and.org>
Date:   Thu Jul 16 15:38:47 2009 -0400

    Use cleaned pkgobjlist for length as well as returnPackages

diff --git a/yum/sqlitesack.py b/yum/sqlitesack.py
index 5d74a41..3469867 100644
--- a/yum/sqlitesack.py
+++ b/yum/sqlitesack.py
@@ -441,6 +441,18 @@ class YumSqlitePackageSack(yumRepo.YumPackageSack):
         sql = "SELECT count(pkgId) FROM packages"
         return self._sql_MD('primary', repo, sql).fetchone()[0]
         
+    def _clean_pkgobjlist(self):
+        """ If the pkgobjlist is dirty (possible pkgs on it which are excluded)
+            then clean it, and return the clean list. """
+        assert hasattr(self, 'pkgobjlist')
+
+        if self._pkgobjlist_dirty:
+            pol = filter(lambda x: not self._pkgExcluded(x), self.pkgobjlist)
+            self.pkgobjlist = pol
+            self._pkgobjlist_dirty = False
+
+        return self.pkgobjlist
+
     def __len__(self):
         # First check if everything is excluded
         all_excluded = True
@@ -451,12 +463,12 @@ class YumSqlitePackageSack(yumRepo.YumPackageSack):
         if all_excluded:
             return 0
             
+        if hasattr(self, 'pkgobjlist'):
+            return len(self._clean_pkgobjlist())
+
         exclude_num = 0
         for repo in self.excludes:
             exclude_num += len(self.excludes[repo])
-        if hasattr(self, 'pkgobjlist') and not self._pkgobjlist_dirty:
-            return len(self.pkgobjlist) - exclude_num
-        
         pkg_num = 0
         sql = "SELECT count(pkgId) FROM packages"
         for repo in self.primarydb:
@@ -1507,11 +1519,7 @@ class YumSqlitePackageSack(yumRepo.YumPackageSack):
 
         internal_pkgoblist = hasattr(self, 'pkgobjlist')
         if internal_pkgoblist:
-            if self._pkgobjlist_dirty:
-                pol = filter(lambda x: not self._pkgExcluded(x),self.pkgobjlist)
-                self.pkgobjlist = pol
-                self._pkgobjlist_dirty = False
-            pkgobjlist = self.pkgobjlist
+            pkgobjlist = self._clean_pkgobjlist()
         else:
             pkgobjlist = self._buildPkgObjList(repoid, patterns, ignore_case)
 
commit 36f74ef814d2c2c14edabbbd6deb78b45470cdae
Author: James Antill <james at and.org>
Date:   Thu Jul 16 15:34:39 2009 -0400

     Add excluderid to make some things easier/nicer with repos. going on/off
    post init. ... add some explanation in the doc comments for
    addPackageExcluder().

diff --git a/yum/__init__.py b/yum/__init__.py
index c0402a1..af41e3f 100644
--- a/yum/__init__.py
+++ b/yum/__init__.py
@@ -1150,14 +1150,19 @@ class YumBase(depsolve.Depsolve):
                 return
             excludelist = self.conf.exclude
             repoid = None
+            exid_beg = 'yum.excludepkgs'
         else:
             if repo.id in self.conf.disable_excludes:
                 return
             excludelist = repo.getExcludePkgList()
             repoid = repo.id
+            exid_beg = 'yum.excludepkgs.' + repoid
 
+        count = 0
         for match in excludelist:
-            self.pkgSack.addPackageExcluder(repoid, 'exclude.match', match)
+            count += 1
+            exid = "%s.%u" % (exid_beg, count)
+            self.pkgSack.addPackageExcluder(repoid, exid,'exclude.match', match)
 
     def includePackages(self, repo):
         """removes packages from packageSacks based on list of packages, to include.
@@ -1171,10 +1176,15 @@ class YumBase(depsolve.Depsolve):
         # includepkgs actually means "exclude everything that doesn't match".
         #  So we mark everything, then wash those we want to keep and then
         # exclude everything that is marked.
-        self.pkgSack.addPackageExcluder(repo.id, 'mark.washed')
+        exid = "yum.includepkgs.1"
+        self.pkgSack.addPackageExcluder(repo.id, exid, 'mark.washed')
+        count = 0
         for match in includelist:
-            self.pkgSack.addPackageExcluder(repo.id, 'wash.match', match)
-        self.pkgSack.addPackageExcluder(repo.id, 'exclude.marked')
+            count += 1
+            exid = "%s.%u" % ("yum.includepkgs.2", count)
+            self.pkgSack.addPackageExcluder(repo.id, exid, 'wash.match', match)
+        exid = "yum.includepkgs.3"
+        self.pkgSack.addPackageExcluder(repo.id, exid, 'exclude.marked')
         
     def doLock(self, lockfile = YUM_PID_FILE):
         """perform the yum locking, raise yum-based exceptions, not OSErrors"""
diff --git a/yum/packageSack.py b/yum/packageSack.py
index 633100d..b71356a 100644
--- a/yum/packageSack.py
+++ b/yum/packageSack.py
@@ -172,8 +172,14 @@ class PackageSackBase(object):
         """return list of all packages"""
         raise NotImplementedError()
 
-    def addPackageExcluder(self, repoid, excluder, *args):
-        """exclude packages, for a variety of reasons"""
+    def addPackageExcluder(self, repoid, excluderid, excluder, *args):
+        """ Add an "excluder" for all packages in the repo/sack. Can basically
+            do anything based on nevra, changes lots of exclude decisions from
+            "preload package; test; delPackage" into "load excluder".
+            Excluderid is used so the caller doesn't have to track
+            "have I loaded the excluder for this repo.", it's probably only
+            useful when repoid is None ... if it turns out utterly worthless
+            then it's still not a huge wart. """
         raise NotImplementedError()
 
     def simpleVersion(self):
@@ -444,12 +450,19 @@ class MetaSack(PackageSackBase):
         return self.sacks[repoid].returnPackages(patterns=patterns,
                                                  ignore_case=ignore_case)
 
-    def addPackageExcluder(self, repoid, excluder, *args):
-        """exclude packages, for a variety of reasons"""
+    def addPackageExcluder(self, repoid, excluderid, excluder, *args):
+        """ Add an "excluder" for all packages in the repo/sack. Can basically
+            do anything based on nevra, changes lots of exclude decisions from
+            "preload package; test; delPackage" into "load excluder".
+            Excluderid is used so the caller doesn't have to track
+            "have I loaded the excluder for this repo.", it's probably only
+            useful when repoid is None ... if it turns out utterly worthless
+            then it's still not a huge wart. """
         if not repoid:
-            return self._computeAggregateListResult("addPackageExcluder",
-                                                    None, excluder, *args)
-        return self.sacks[repoid].addPackageExcluder(None, excluder, *args)
+            calr = self._computeAggregateListResult
+            return calr("addPackageExcluder", None, excluderid, excluder, *args)
+        return self.sacks[repoid].addPackageExcluder(None,
+                                                     excluderid,excluder, *args)
 
     def returnNewestByNameArch(self, naTup=None,
                                patterns=None, ignore_case=False):
diff --git a/yum/sqlitesack.py b/yum/sqlitesack.py
index 984c470..5d74a41 100644
--- a/yum/sqlitesack.py
+++ b/yum/sqlitesack.py
@@ -424,6 +424,7 @@ class YumSqlitePackageSack(yumRepo.YumPackageSack):
         self._pkgnames_loaded = set()
         self._arch_allowed = None
         self._pkgExcluder = []
+        self._pkgExcludeIds = {}
         self._pkgobjlist_dirty = False
 
     @catchSqliteException
@@ -494,6 +495,9 @@ class YumSqlitePackageSack(yumRepo.YumPackageSack):
         self._excludes = set()
         self._exclude_whitelist = set()
         self._all_excludes = {}
+        self._pkgExcluder = []
+        self._pkgExcludeIds = {}
+        self._pkgobjlist_dirty = False
 
         yumRepo.YumPackageSack.close(self)
 
@@ -630,7 +634,17 @@ class YumSqlitePackageSack(yumRepo.YumPackageSack):
             Takes a package object. '''
         return self._pkgExcludedRKT(po.repo, po.pkgKey, po.pkgtup)
 
-    def addPackageExcluder(self, repoid, excluder, *args):
+    def addPackageExcluder(self, repoid, excluderid, excluder, *args):
+        """ Add an "excluder" for all packages in the repo/sack. Can basically
+            do anything based on nevra, changes lots of exclude decisions from
+            "preload package; test; delPackage" into "load excluder".
+            Excluderid is used so the caller doesn't have to track
+            "have I loaded the excluder for this repo.", it's probably only
+            useful when repoid is None ... if it turns out utterly worthless
+            then it's still not a huge wart. """
+        if excluderid is not None and excluderid in self._pkgExcludeIds:
+            return
+
         match        = None
         regexp_match = None
         if False: pass
@@ -655,6 +669,8 @@ class YumSqlitePackageSack(yumRepo.YumPackageSack):
         # or it does nothing.
         # self._pkgobjlist_dirty = True
         self._pkgExcluder.append((repoid, excluder, match, regexp_match))
+        if excluderid is not None:
+            self._pkgExcludeIds[excluderid] = len(self._pkgExcluder)
 
     def _packageByKey(self, repo, pkgKey, exclude=True):
         """ Lookup a pkg by it's pkgKey, if we don't have it load it """
commit 12eb1aa0566abcc4eb8897823a664ed592d41247
Author: James Antill <james at and.org>
Date:   Wed Jul 15 23:12:10 2009 -0400

    Fix moved code for n, now data["n"], in excluder code

diff --git a/yum/sqlitesack.py b/yum/sqlitesack.py
index 613dd19..984c470 100644
--- a/yum/sqlitesack.py
+++ b/yum/sqlitesack.py
@@ -123,13 +123,13 @@ def _excluder_match(excluder, match, regexp_match, data, e,v,r,a):
 
     elif excluder == 'nevr.eq':
         if 'nevr' not in data:
-            data['nevr'] = '%s-%s:%s-%s' % (n, e, v, r)
+            data['nevr'] = '%s-%s:%s-%s' % (data['n'], e, v, r)
         if match == data['nevr']:
             return True
 
     elif excluder in ('nevra.eq', 'nevra.match'):
         if 'nevra' not in data:
-            data['nevra'] = '%s-%s:%s-%s.%s' % (n, e, v, r, a)
+            data['nevra'] = '%s-%s:%s-%s.%s' % (data['n'], e, v, r, a)
         if _parse_pkg_n(match, regexp_match, data['nevra']):
             return True
 
@@ -139,25 +139,25 @@ def _excluder_match(excluder, match, regexp_match, data, e,v,r,a):
 
     elif excluder == 'nevr.in':
         if 'nevr' not in data:
-            data['nevr'] = '%s-%s:%s-%s' % (n, e, v, r)
+            data['nevr'] = '%s-%s:%s-%s' % (data['n'], e, v, r)
         if data['nevr'] in match:
             return True
 
     elif excluder == 'nevra.in':
         if 'nevra' not in data:
-            data['nevra'] = '%s-%s:%s-%s.%s' % (n, e, v, r, a)
+            data['nevra'] = '%s-%s:%s-%s.%s' % (data['n'], e, v, r, a)
         if data['nevra'] in match:
             return True
 
     elif excluder == 'pkgtup.eq':
         if 'pkgtup' not in data:
-            data['pkgtup'] = (n, a, e, v, r)
+            data['pkgtup'] = (data['n'], a, e, v, r)
         if match == data['pkgtup']:
             return True
 
     elif excluder == 'pkgtup.in':
         if 'pkgtup' not in data:
-            data['pkgtup'] = (n, a, e, v, r)
+            data['pkgtup'] = (data['n'], a, e, v, r)
         if data['pkgtup'] in match:
             return True
 
commit 4338a70aea599986bf380a3a0883baafd8143480
Author: James Antill <james at and.org>
Date:   Wed Jul 15 16:23:17 2009 -0400

    Add nevr and pkgtup to pkgExcluder

diff --git a/yum/sqlitesack.py b/yum/sqlitesack.py
index 2e2da3c..613dd19 100644
--- a/yum/sqlitesack.py
+++ b/yum/sqlitesack.py
@@ -121,6 +121,12 @@ def _excluder_match(excluder, match, regexp_match, data, e,v,r,a):
         if _parse_pkg_n(match, regexp_match, a):
             return True
 
+    elif excluder == 'nevr.eq':
+        if 'nevr' not in data:
+            data['nevr'] = '%s-%s:%s-%s' % (n, e, v, r)
+        if match == data['nevr']:
+            return True
+
     elif excluder in ('nevra.eq', 'nevra.match'):
         if 'nevra' not in data:
             data['nevra'] = '%s-%s:%s-%s.%s' % (n, e, v, r, a)
@@ -131,12 +137,30 @@ def _excluder_match(excluder, match, regexp_match, data, e,v,r,a):
         if data['n'] in match:
             return True
 
+    elif excluder == 'nevr.in':
+        if 'nevr' not in data:
+            data['nevr'] = '%s-%s:%s-%s' % (n, e, v, r)
+        if data['nevr'] in match:
+            return True
+
     elif excluder == 'nevra.in':
         if 'nevra' not in data:
             data['nevra'] = '%s-%s:%s-%s.%s' % (n, e, v, r, a)
         if data['nevra'] in match:
             return True
 
+    elif excluder == 'pkgtup.eq':
+        if 'pkgtup' not in data:
+            data['pkgtup'] = (n, a, e, v, r)
+        if match == data['pkgtup']:
+            return True
+
+    elif excluder == 'pkgtup.in':
+        if 'pkgtup' not in data:
+            data['pkgtup'] = (n, a, e, v, r)
+        if data['pkgtup'] in match:
+            return True
+
     elif excluder == 'marked':
         if data['marked']:
             return True
commit 434679afead379a530d209af2bf3439d6c5ed0ac
Author: James Antill <james at and.org>
Date:   Wed Jul 15 16:19:09 2009 -0400

    Fix delPackage() after pkgobjlist has been set

diff --git a/yum/sqlitesack.py b/yum/sqlitesack.py
index 787dc85..2e2da3c 100644
--- a/yum/sqlitesack.py
+++ b/yum/sqlitesack.py
@@ -400,6 +400,7 @@ class YumSqlitePackageSack(yumRepo.YumPackageSack):
         self._pkgnames_loaded = set()
         self._arch_allowed = None
         self._pkgExcluder = []
+        self._pkgobjlist_dirty = False
 
     @catchSqliteException
     def _sql_MD(self, MD, repo, sql, *args):
@@ -428,7 +429,7 @@ class YumSqlitePackageSack(yumRepo.YumPackageSack):
         exclude_num = 0
         for repo in self.excludes:
             exclude_num += len(self.excludes[repo])
-        if hasattr(self, 'pkgobjlist'):
+        if hasattr(self, 'pkgobjlist') and not self._pkgobjlist_dirty:
             return len(self.pkgobjlist) - exclude_num
         
         pkg_num = 0
@@ -444,6 +445,7 @@ class YumSqlitePackageSack(yumRepo.YumPackageSack):
             del self._memoize_provides
         if hasattr(self, 'pkgobjlist'):
             del self.pkgobjlist
+        self._pkgobjlist_dirty = False
         self._key2pkg = {}
         self._pkgname2pkgkeys = {}
         self._pkgnames_loaded = set()
@@ -483,6 +485,9 @@ class YumSqlitePackageSack(yumRepo.YumPackageSack):
         ''' Exclude a package so that _pkgExcluded*() knows it's gone.
             Note that this doesn't update self.exclude. '''
         self._excludes.add((repo, pkgKey))
+        # Don't keep references around, just wastes memory.
+        if repo in self._key2pkg:
+            self._key2pkg[repo].pop(pkgKey, 0)
 
     # Remove a package
     # Because we don't want to remove a package from the database we just
@@ -494,6 +499,7 @@ class YumSqlitePackageSack(yumRepo.YumPackageSack):
         if (obj.repo, obj.pkgKey) in self._exclude_whitelist:
             self._exclude_whitelist.discard((obj.repo, obj.pkgKey))
         self._delPackageRK(obj.repo, obj.pkgKey)
+        self._pkgobjlist_dirty = True
 
     def _delAllPackages(self, repo):
         """ Exclude all packages from the repo. """
@@ -621,6 +627,9 @@ class YumSqlitePackageSack(yumRepo.YumPackageSack):
             assert len(args) == 0
         elif excluder.endswith('.washed'):
             assert len(args) == 0
+        #  Really need to do this, need to cleanup pkgExcluder first though
+        # or it does nothing.
+        # self._pkgobjlist_dirty = True
         self._pkgExcluder.append((repoid, excluder, match, regexp_match))
 
     def _packageByKey(self, repo, pkgKey, exclude=True):
@@ -1458,6 +1467,10 @@ class YumSqlitePackageSack(yumRepo.YumPackageSack):
 
         internal_pkgoblist = hasattr(self, 'pkgobjlist')
         if internal_pkgoblist:
+            if self._pkgobjlist_dirty:
+                pol = filter(lambda x: not self._pkgExcluded(x),self.pkgobjlist)
+                self.pkgobjlist = pol
+                self._pkgobjlist_dirty = False
             pkgobjlist = self.pkgobjlist
         else:
             pkgobjlist = self._buildPkgObjList(repoid, patterns, ignore_case)
@@ -1468,16 +1481,15 @@ class YumSqlitePackageSack(yumRepo.YumPackageSack):
                                        unique='repo-pkgkey')
             pkgobjlist = pkgobjlist[0] + pkgobjlist[1]
 
-        if True: # NOTE: Can't unexclude things...
+        # Can't unexclude things, and new excludes are done above...
+        if repoid is None:
             if internal_pkgoblist:
                 pkgobjlist = pkgobjlist[:]
             return pkgobjlist
 
         returnList = []
         for po in pkgobjlist:
-            if repoid is not None and repoid != po.repoid:
-                continue
-            if self._pkgExcluded(po):
+            if repoid != po.repoid:
                 continue
             returnList.append(po)
 
commit 36b366b33de1b2fdf39af14f4f17445452658780
Author: James Antill <james at and.org>
Date:   Thu Apr 16 18:23:14 2009 -0400

     Cache pkg names, faster for long lived YumBases.
       Call searchNames() from searchNevra()
       Call searchNames() from returnPackages() (if we can).

diff --git a/yum/sqlitesack.py b/yum/sqlitesack.py
index 61e60a2..787dc85 100644
--- a/yum/sqlitesack.py
+++ b/yum/sqlitesack.py
@@ -396,6 +396,8 @@ class YumSqlitePackageSack(yumRepo.YumPackageSack):
             'requires' : { },
             }
         self._key2pkg = {}
+        self._pkgname2pkgkeys = {}
+        self._pkgnames_loaded = set()
         self._arch_allowed = None
         self._pkgExcluder = []
 
@@ -443,6 +445,8 @@ class YumSqlitePackageSack(yumRepo.YumPackageSack):
         if hasattr(self, 'pkgobjlist'):
             del self.pkgobjlist
         self._key2pkg = {}
+        self._pkgname2pkgkeys = {}
+        self._pkgnames_loaded = set()
         self._search_cache = {
             'provides' : { },
             'requires' : { },
@@ -498,6 +502,8 @@ class YumSqlitePackageSack(yumRepo.YumPackageSack):
             del self.excludes[repo]
         if repo in self._key2pkg:
             del self._key2pkg[repo]
+        if repo in self._pkgname2pkgkeys:
+            del self._pkgname2pkgkeys[repo]
 
     def _excluded(self, repo, pkgId):
         if repo in self._all_excludes:
@@ -625,6 +631,7 @@ class YumSqlitePackageSack(yumRepo.YumPackageSack):
 
         if not self._key2pkg.has_key(repo):
             self._key2pkg[repo] = {}
+            self._pkgname2pkgkeys[repo] = {}
         if not self._key2pkg[repo].has_key(pkgKey):
             sql = "SELECT pkgKey, pkgId, name, epoch, version, release, arch " \
                   "FROM packages WHERE pkgKey = ?"
@@ -635,17 +642,39 @@ class YumSqlitePackageSack(yumRepo.YumPackageSack):
             if exclude and self._pkgExcludedRKD(repo, pkgKey, data):
                 return None
             self._key2pkg[repo][pkgKey] = self.pc(repo, data)
+            pkgkeys = self._pkgname2pkgkeys[repo].setdefault(data['name'], [])
+            pkgkeys.append(pkgKey)
         return self._key2pkg[repo][pkgKey]
         
     def _packageByKeyData(self, repo, pkgKey, data, exclude=True):
         """ Like _packageByKey() but we already have the data for .pc() """
         if exclude and self._pkgExcludedRKD(repo, pkgKey, data):
             return None
+        if repo not in self._key2pkg:
+            self._key2pkg[repo] = {}
+            self._pkgname2pkgkeys[repo] = {}
         if data['pkgKey'] not in self._key2pkg.get(repo, {}):
             po = self.pc(repo, data)
-            self._key2pkg.setdefault(repo, {})[pkgKey] = po
+            self._key2pkg[repo][pkgKey] = po
+            pkgkeys = self._pkgname2pkgkeys[repo].setdefault(data['name'], [])
+            pkgkeys.append(pkgKey)
         return self._key2pkg[repo][data['pkgKey']]
 
+    def _packagesByName(self, pkgname):
+        """ Load all pkgnames from cache, with a given name. """
+        ret = []
+        for repo in self.primarydb:
+            pkgkeys = self._pkgname2pkgkeys.get(repo, {}).get(pkgname, [])
+            if not pkgkeys:
+                continue
+
+            for pkgkey in pkgkeys:
+                pkg = self._packageByKey(repo, pkgkey)
+                if pkg is None:
+                    continue
+                ret.append(pkg)
+        return ret
+
     def addDict(self, repo, datatype, dataobj, callback=None):
         if self.added.has_key(repo):
             if datatype in self.added[repo]:
@@ -1098,23 +1127,25 @@ class YumSqlitePackageSack(yumRepo.YumPackageSack):
         if self._skip_all():
             return []
         
+        loaded_all_names = hasattr(self, 'pkgobjlist')
         returnList = []
-        if hasattr(self, 'pkgobjlist'):
-            names = set(names)
-            for po in self.pkgobjlist:
-                if po.name not in names:
-                    continue
-                if self._pkgExcluded(po):
-                    continue
-                returnList.append(po)
+        user_names = set(names)
+        names = []
+        for pkgname in user_names:
+            if loaded_all_names or pkgname in self._pkgnames_loaded:
+                returnList.extend(self._packagesByName(pkgname))
+            else:
+                names.append(pkgname)
+
+        if not names:
             return returnList
 
         max_entries = constants.PATTERNS_INDEXED_MAX
         if len(names) > max_entries:
-            returnList = set() # Unique
+            # Unique is done at user_names time, above.
             for names in seq_max_split(names, max_entries):
-                returnList.update(self.searchNames(names))
-            return list(returnList)
+                returnList.extend(self.searchNames(names))
+            return returnList
 
         pat_sqls = []
         qsql = """select pkgId,pkgKey,name,epoch,version,release,arch
@@ -1125,10 +1156,13 @@ class YumSqlitePackageSack(yumRepo.YumPackageSack):
 
         for (repo, cache) in self.primarydb.items():
             cur = cache.cursor()
-            executeSQL(cur, qsql, list(names))
+            executeSQL(cur, qsql, names)
 
             self._sql_pkgKey2po(repo, cur, returnList, have_data=True)
 
+        # Mark all the processed pkgnames as fully loaded
+        self._pkgnames_loaded.update([name for name in names])
+
         return returnList
  
     @catchSqliteException
@@ -1364,11 +1398,15 @@ class YumSqlitePackageSack(yumRepo.YumPackageSack):
             patterns = self._sql_esc_glob(patterns)
         else:
             tmp = []
+            need_glob = False
             for pat in patterns:
                 if misc.re_glob(pat):
                     tmp.append((pat, 'glob'))
+                    need_glob = True
                 else:
                     tmp.append((pat, '='))
+            if not need_full and not need_glob and patterns:
+                return self.searchNames(patterns)
             patterns = tmp
 
         for (repo,cache) in self.primarydb.items():
@@ -1401,8 +1439,13 @@ class YumSqlitePackageSack(yumRepo.YumPackageSack):
                     if po is None:
                         continue
                     returnList.append(po)
-        if not patterns:
+        if not patterns and repoid is None:
             self.pkgobjlist = returnList
+            self._pkgnames_loaded = set() # Save memory
+        if not need_full and repoid is None:
+            # Mark all the processed pkgnames as fully loaded
+            self._pkgnames_loaded.update([po.name for po in returnList])
+
         return returnList
                 
     def returnPackages(self, repoid=None, patterns=None, ignore_case=False):
@@ -1448,6 +1491,18 @@ class YumSqlitePackageSack(yumRepo.YumPackageSack):
 
         returnList = []
         
+        if name: # Almost always true...
+            for pkg in self.searchNames(names=[name]):
+                match = True
+                for (col, var) in [('epoch', epoch), ('version', ver),
+                                   ('arch', arch), ('release', rel)]:
+                    if var and getattr(pkg, col) != var:
+                        match = False
+                        break
+                if match:
+                    returnList.append(pkg)
+            return returnList
+
         # make sure some dumbass didn't pass us NOTHING to search on
         empty = True
         for arg in (name, epoch, ver, rel, arch):
commit 5f83b3409061b7ff40a361b59291108c637cc288
Author: James Antill <james at and.org>
Date:   Tue Jul 14 18:11:09 2009 -0400

    Use mark/wash in includepkgs to allow further include/exclude(s)

diff --git a/yum/__init__.py b/yum/__init__.py
index 221d4d7..c0402a1 100644
--- a/yum/__init__.py
+++ b/yum/__init__.py
@@ -523,8 +523,8 @@ class YumBase(depsolve.Depsolve):
         if repos == 'enabled':
             repos = self.repos.listEnabled()
         for repo in repos:
-            self.excludePackages(repo)
             self.includePackages(repo)
+            self.excludePackages(repo)
         self.plugins.run('exclude')
         self._pkgSack.buildIndexes()
 
@@ -1168,9 +1168,13 @@ class YumBase(depsolve.Depsolve):
         if len(includelist) == 0:
             return
         
+        # includepkgs actually means "exclude everything that doesn't match".
+        #  So we mark everything, then wash those we want to keep and then
+        # exclude everything that is marked.
+        self.pkgSack.addPackageExcluder(repo.id, 'mark.washed')
         for match in includelist:
-            self.pkgSack.addPackageExcluder(repo.id, 'include.match', match)
-        self.pkgSack.addPackageExcluder(repo.id, 'exclude.*')
+            self.pkgSack.addPackageExcluder(repo.id, 'wash.match', match)
+        self.pkgSack.addPackageExcluder(repo.id, 'exclude.marked')
         
     def doLock(self, lockfile = YUM_PID_FILE):
         """perform the yum locking, raise yum-based exceptions, not OSErrors"""
commit 2ab9442f1d93f1dd8177118c26e5c5415945f4f8
Author: James Antill <james at and.org>
Date:   Tue Jul 14 17:57:41 2009 -0400

    Combine include/exclude tests in pkgExcluder, add mark/wash ops

diff --git a/yum/sqlitesack.py b/yum/sqlitesack.py
index 013156e..61e60a2 100644
--- a/yum/sqlitesack.py
+++ b/yum/sqlitesack.py
@@ -107,6 +107,54 @@ def _parse_pkg(match, regexp_match, data, e,v,r,a):
             return True
     return False
 
+def _excluder_match(excluder, match, regexp_match, data, e,v,r,a):
+    if False: pass
+    elif excluder in ('eq', 'match'):
+        if _parse_pkg(match, regexp_match, data, e,v,r,a):
+            return True
+
+    elif excluder in ('name.eq', 'name.match'):
+        if _parse_pkg_n(match, regexp_match, data['n']):
+            return True
+
+    elif excluder in ('arch.eq', 'arch.match'):
+        if _parse_pkg_n(match, regexp_match, a):
+            return True
+
+    elif excluder in ('nevra.eq', 'nevra.match'):
+        if 'nevra' not in data:
+            data['nevra'] = '%s-%s:%s-%s.%s' % (n, e, v, r, a)
+        if _parse_pkg_n(match, regexp_match, data['nevra']):
+            return True
+
+    elif excluder == 'name.in':
+        if data['n'] in match:
+            return True
+
+    elif excluder == 'nevra.in':
+        if 'nevra' not in data:
+            data['nevra'] = '%s-%s:%s-%s.%s' % (n, e, v, r, a)
+        if data['nevra'] in match:
+            return True
+
+    elif excluder == 'marked':
+        if data['marked']:
+            return True
+
+    elif excluder == 'washed':
+        if not data['marked']:
+            return True
+
+    elif excluder == '*':
+        return True
+
+    else:
+        assert False, 'Bad excluder: ' + excluder
+        return None
+
+    return False
+
+
 class YumAvailablePackageSqlite(YumAvailablePackage, PackageObject, RpmBase):
     def __init__(self, repo, db_obj):
         self.prco = { 'obsoletes': (),
@@ -484,7 +532,7 @@ class YumSqlitePackageSack(yumRepo.YumPackageSack):
             self._delPackageRK(repo, pkgKey)
             return True
 
-        data = {'n' : n.lower()}
+        data = {'n' : n.lower(), 'marked' : False}
         e = e.lower()
         v = v.lower()
         r = r.lower()
@@ -494,75 +542,33 @@ class YumSqlitePackageSack(yumRepo.YumPackageSack):
             if repoid is not None and repoid != repo.id:
                 continue
 
-            if False: pass
-            elif excluder in ('exclude.eq', 'exclude.match'):
-                if _parse_pkg(match, regexp_match, data, e,v,r,a):
-                    self._delPackageRK(repo, pkgKey)
-                    return True
-
-            elif excluder in ('exclude.name.eq', 'exclude.name.match'):
-                if _parse_pkg_n(match, regexp_match, data['n']):
-                    self._delPackageRK(repo, pkgKey)
-                    return True
-
-            elif excluder in ('exclude.arch.eq', 'exclude.arch.match'):
-                if _parse_pkg_n(match, regexp_match, a):
-                    self._delPackageRK(repo, pkgKey)
-                    return True
-
-            elif excluder in ('exclude.nevra.eq', 'exclude.nevra.match'):
-                if 'nevra' not in data:
-                    data['nevra'] = '%s-%s:%s-%s.%s' % (n, e, v, r, a)
-                if _parse_pkg_n(match, regexp_match, data['nevra']):
-                    self._delPackageRK(repo, pkgKey)
-                    return True
-
-            elif excluder == 'exclude.name.in':
-                if data['n'] in match:
-                    self._delPackageRK(repo, pkgKey)
-                    return True
+            exSPLIT = excluder.split('.', 1)
+            if len(exSPLIT) != 2:
+                assert False, 'Bad excluder: ' + excluder
+                continue
 
-            elif excluder == 'exclude.nevra.in':
-                if 'nevra' not in data:
-                    data['nevra'] = '%s-%s:%s-%s.%s' % (n, e, v, r, a)
-                if data['nevra'] in match:
+            exT, exM = exSPLIT
+            if False: pass
+            elif exT == 'exclude':
+                if _excluder_match(exM, match, regexp_match, data, e,v,r,a):
                     self._delPackageRK(repo, pkgKey)
                     return True
 
-            elif excluder in ('include.eq', 'include.match'):
-                if _parse_pkg(match, regexp_match, data, e,v,r,a):
+            elif exT == 'include':
+                if _excluder_match(exM, match, regexp_match, data, e,v,r,a):
                     break
 
-            elif excluder in ('include.name.eq', 'include.name.match'):
-                if _parse_pkg_n(match, regexp_match, data['n']):
-                    break
-
-            elif excluder in ('include.arch.eq', 'include.arch.match'):
-                if _parse_pkg_n(match, regexp_match, a):
-                    break
+            elif exT == 'mark':
+                if data['marked']:
+                    pass # Speed opt. don't do matches we don't need to do.
+                elif _excluder_match(exM, match, regexp_match, data, e,v,r,a):
+                    data['marked'] = True
 
-            elif excluder in ('include.nevra.eq', 'include.nevra.match'):
-                if 'nevra' not in data:
-                    data['nevra'] = '%s-%s:%s-%s.%s' % (n, e, v, r, a)
-                if _parse_pkg_n(match, regexp_match, data['nevra']):
-                    break
-
-            elif excluder == 'include.name.in':
-                if data['n'] in match:
-                    break
-
-            elif excluder == 'include.nevra.in':
-                if 'nevra' not in data:
-                    data['nevra'] = '%s-%s:%s-%s.%s' % (n, e, v, r, a)
-                if data['nevra'] in match:
-                    break
-
-            elif excluder == 'exclude.*':
-                self._delPackageRK(repo, pkgKey)
-                return True
-
-            elif excluder == 'include.*':
-                break
+            elif exT == 'wash':
+                if not data['marked']:
+                    pass # Speed opt. don't do matches we don't need to do.
+                elif _excluder_match(exM, match, regexp_match, data, e,v,r,a):
+                    data['marked'] = False
 
             else:
                 assert False, 'Bad excluder: ' + excluder
@@ -605,6 +611,10 @@ class YumSqlitePackageSack(yumRepo.YumPackageSack):
                 regexp_match = re.compile(fnmatch.translate(match)).match
         elif excluder.endswith('.*'):
             assert len(args) == 0
+        elif excluder.endswith('.marked'):
+            assert len(args) == 0
+        elif excluder.endswith('.washed'):
+            assert len(args) == 0
         self._pkgExcluder.append((repoid, excluder, match, regexp_match))
 
     def _packageByKey(self, repo, pkgKey, exclude=True):
commit b41e57b64f4b511412ad7c98fd7e230c3a4b0ab4
Author: James Antill <james at and.org>
Date:   Sun Jul 12 23:45:52 2009 -0400

    Add "help recent", so people can see what new commands are available

diff --git a/yumcommands.py b/yumcommands.py
index edaddfd..1227484 100644
--- a/yumcommands.py
+++ b/yumcommands.py
@@ -28,7 +28,7 @@ import operator
 import locale
 import fnmatch
 import time
-from yum.i18n import utf8_width, utf8_width_fill, to_unicode
+from yum.i18n import utf8_width, utf8_width_fill, utf8_text_fill, to_unicode
 
 def checkRootUID(base):
     """
@@ -120,7 +120,10 @@ def checkShellArg(base, basecmd, extcmds):
         raise cli.CliError
 
 class YumCommand:
-        
+
+    # Epoch seconds. Use: date -d '2006-09-26 13:24:58 +0000' +'%s' ... etc.
+    created = 0
+
     def __init__(self):
         self.done_command_once = False
 
@@ -160,6 +163,9 @@ class YumCommand:
         return True
         
 class InstallCommand(YumCommand):
+
+    created = 1159277098
+
     def getNames(self):
         return ['install']
 
@@ -182,6 +188,9 @@ class InstallCommand(YumCommand):
             return 1, [str(e)]
 
 class UpdateCommand(YumCommand):
+
+    created = 1159277098
+
     def getNames(self):
         return ['update']
 
@@ -234,6 +243,9 @@ def _list_cmd_calc_columns(base, ypl):
     return (-columns[0], -columns[1], -columns[2])
 
 class InfoCommand(YumCommand):
+
+    created = 1159277098
+
     def getNames(self):
         return ['info']
 
@@ -340,6 +352,9 @@ class InfoCommand(YumCommand):
         return True
 
 class ListCommand(InfoCommand):
+
+    created = 1159277098
+
     def getNames(self):
         return ['list']
 
@@ -348,7 +363,9 @@ class ListCommand(InfoCommand):
 
 
 class EraseCommand(YumCommand):
-        
+
+    created = 1159277098
+
     def getNames(self):
         return ['erase', 'remove']
 
@@ -376,6 +393,9 @@ class EraseCommand(YumCommand):
         return True
 
 class GroupCommand(YumCommand):
+
+    created = 1159277098
+
     def doCommand(self, base, basecmd, extcmds):
         self.doneCommand(base, _("Setting up Group Process"))
 
@@ -479,6 +499,8 @@ class GroupInfoCommand(GroupCommand):
 
 class MakeCacheCommand(YumCommand):
 
+    created = 1159277098
+
     def getNames(self):
         return ['makecache']
 
@@ -520,7 +542,9 @@ class MakeCacheCommand(YumCommand):
         return False
 
 class CleanCommand(YumCommand):
-    
+
+    created = 1159277098
+
     def getNames(self):
         return ['clean']
 
@@ -541,6 +565,9 @@ class CleanCommand(YumCommand):
         return False
 
 class ProvidesCommand(YumCommand):
+
+    created = 1159277098
+
     def getNames(self):
         return ['provides', 'whatprovides']
 
@@ -561,6 +588,9 @@ class ProvidesCommand(YumCommand):
             return 1, [str(e)]
 
 class CheckUpdateCommand(YumCommand):
+
+    created = 1159277098
+
     def getNames(self):
         return ['check-update']
 
@@ -610,6 +640,9 @@ class CheckUpdateCommand(YumCommand):
             return result, []
 
 class SearchCommand(YumCommand):
+
+    created = 1159277098
+
     def getNames(self):
         return ['search']
 
@@ -633,6 +666,9 @@ class SearchCommand(YumCommand):
         return False
 
 class UpgradeCommand(YumCommand):
+
+    created = 1159277098
+
     def getNames(self):
         return ['upgrade']
 
@@ -655,6 +691,9 @@ class UpgradeCommand(YumCommand):
             return 1, [str(e)]
 
 class LocalInstallCommand(YumCommand):
+
+    created = 1159277098
+
     def getNames(self):
         return ['localinstall', 'localupdate']
 
@@ -682,6 +721,9 @@ class LocalInstallCommand(YumCommand):
         return False
 
 class ResolveDepCommand(YumCommand):
+
+    created = 1159277098
+
     def getNames(self):
         return ['resolvedep']
 
@@ -699,6 +741,9 @@ class ResolveDepCommand(YumCommand):
             return 1, [str(e)]
 
 class ShellCommand(YumCommand):
+
+    created = 1159277098
+
     def getNames(self):
         return ['shell']
 
@@ -723,6 +768,9 @@ class ShellCommand(YumCommand):
 
 
 class DepListCommand(YumCommand):
+
+    created = 1159277098
+
     def getNames(self):
         return ['deplist']
 
@@ -744,6 +792,8 @@ class DepListCommand(YumCommand):
 
 
 class RepoListCommand(YumCommand):
+
+    created = 1178147972
     
     def getNames(self):
         return ('repolist',)
@@ -932,6 +982,24 @@ class RepoListCommand(YumCommand):
 
 class HelpCommand(YumCommand):
 
+    created = 1201218844
+
+    @staticmethod
+    def _key_created(cmd):
+        if hasattr(cmd, 'created'):
+            return cmd.created
+        return 0
+    @staticmethod
+    def _key_summary(cmd):
+        if hasattr(cmd, 'getSummary'):
+            return cmd.getSummary()
+        return ''
+    @staticmethod
+    def _key_usage(cmd):
+        if hasattr(cmd, 'getUsage'):
+            return cmd.getUsage()
+        return ''
+
     def getNames(self):
         return ['help']
 
@@ -945,6 +1013,10 @@ class HelpCommand(YumCommand):
         if len(extcmds) == 0:
             base.usage()
             raise cli.CliError
+        elif len(extcmds) in (1, 2) and extcmds[0] == 'recent':
+            pass
+        elif len(extcmds) == 2 and extcmds[0] in ('cmd', 'command'):
+            pass
         elif len(extcmds) > 1 or extcmds[0] not in base.yum_cli_commands:
             base.usage()
             raise cli.CliError
@@ -986,6 +1058,50 @@ class HelpCommand(YumCommand):
         return help_output
 
     def doCommand(self, base, basecmd, extcmds):
+        if extcmds[0] == 'recent':
+            last = "4"
+            if len(extcmds) > 1:
+                last = extcmds[1]
+            last = int(last)
+            if last < 1:
+                last = 1
+
+            cols = base.term.columns
+            print base.fmtSection("%d most recent command(s)" % (last,))
+            colname = _("Command")
+            maxname = utf8_width(colname)
+            cur = 0
+            for cmd in sorted(base.yum_cli_commands.values(), reverse=True,
+                              key=self._key_created):
+                if maxname < len(cmd.getNames()[0]):
+                    maxname = len(cmd.getNames()[0])
+                cur += 1
+                if cur >= last:
+                    break
+            print "%s %s %s" % (utf8_width_fill(colname, maxname),
+                                utf8_width_fill(_("Created"), 10, 10),
+                                _("Summary"))
+            cur = 0
+            for cmd in sorted(base.yum_cli_commands.values(), reverse=True,
+                              key=self._key_created):
+                name = cmd.getNames()[0]
+                created = self._key_created(cmd)
+                if not created:
+                    created = 'N/A' # Not i18n, because it should be fixed
+                else:
+                    created = time.strftime("%Y-%m-%d", time.localtime(created))
+                summary = self._key_summary(cmd)
+                text = "%s %-10s " % (utf8_width_fill(name, maxname), created)
+                print utf8_text_fill(summary, width=cols, initial_indent=text,
+                                     subsequent_indent=' ' * utf8_width(text))
+
+                cur += 1
+                if cur >= last:
+                    break
+            return 0, []
+
+        if extcmds[0] in ('cmd', 'command'):
+            extcmds = extcmds[1:]
         if base.yum_cli_commands.has_key(extcmds[0]):
             command = base.yum_cli_commands[extcmds[0]]
             base.verbose_logger.log(logginglevels.INFO_2,
@@ -996,6 +1112,9 @@ class HelpCommand(YumCommand):
         return False
 
 class ReInstallCommand(YumCommand):
+
+    created = 1202159716
+
     def getNames(self):
         return ['reinstall']
 
@@ -1022,6 +1141,9 @@ class ReInstallCommand(YumCommand):
         return False
         
 class DowngradeCommand(YumCommand):
+
+    created = 1237904399
+
     def getNames(self):
         return ['downgrade']
 
@@ -1048,6 +1170,9 @@ class DowngradeCommand(YumCommand):
         
 
 class VersionCommand(YumCommand):
+
+    created = 1242925190
+
     def getNames(self):
         return ['version']
 


More information about the Yum-commits mailing list