[yum-commits] Branch 'yum-3_2_X' - 22 commits - cli.py docs/yum.8 docs/yum.conf.5 output.py test/rpmdb-cache.py test/testbase.py yumcommands.py yum/config.py yum/depsolve.py yum/history.py yum/__init__.py yum/misc.py yum/rpmsack.py yum/rpmtrans.py yum/transactioninfo.py

James Antill james at osuosl.org
Tue Dec 22 20:35:37 UTC 2009


 cli.py                 |   15 +-
 docs/yum.8             |   34 ++++-
 docs/yum.conf.5        |    5 
 output.py              |   32 +++-
 test/rpmdb-cache.py    |   87 +++++++++++++
 test/testbase.py       |   45 ++++++
 yum/__init__.py        |   31 ++++
 yum/config.py          |    2 
 yum/depsolve.py        |  104 ++++++++++++---
 yum/history.py         |   25 +--
 yum/misc.py            |    2 
 yum/rpmsack.py         |  320 ++++++++++++++++++++++++++++++++++++++++++++++++-
 yum/rpmtrans.py        |   25 ++-
 yum/transactioninfo.py |   39 +++++
 yumcommands.py         |   17 +-
 15 files changed, 707 insertions(+), 76 deletions(-)

New commits:
commit b872cb03bad17e5fa24077393e581a766804f5b4
Author: James Antill <james at and.org>
Date:   Tue Dec 22 15:31:18 2009 -0500

    2nd fix for reading rpm logging from files, "" == raise IOError on pipe()

diff --git a/yum/rpmtrans.py b/yum/rpmtrans.py
index 6fde5a8..f33f048 100644
--- a/yum/rpmtrans.py
+++ b/yum/rpmtrans.py
@@ -233,6 +233,8 @@ class RPMTransaction:
     def _scriptOutput(self):
         try:
             out = self._readpipe.read()
+            if not out:
+                return None
             return out
         except IOError:
             pass
commit 2ad118f41ff200e3e3a29b7278af5b7fe10e2708
Author: James Antill <james at and.org>
Date:   Tue Dec 22 15:16:38 2009 -0500

    Use different fds for Tempfile rpm logging, so we get different off_t's

diff --git a/yum/rpmtrans.py b/yum/rpmtrans.py
index 73f9af1..6fde5a8 100644
--- a/yum/rpmtrans.py
+++ b/yum/rpmtrans.py
@@ -206,10 +206,9 @@ class RPMTransaction:
 
     def _setupOutputLogging(self, rpmverbosity="info"):
         # UGLY... set up the transaction to record output from scriptlets
-        io_r = tempfile.TemporaryFile()
-        w = os.dup(io_r.fileno())
+        io_r = tempfile.NamedTemporaryFile()
         self._readpipe = io_r
-        self._writepipe = os.fdopen(w, 'w+b')
+        self._writepipe = open(io_r.name, 'w+b')
         self.base.ts.scriptFd = self._writepipe.fileno()
         rpmverbosity = {'critical' : 'crit',
                         'emergency' : 'emerg',
commit 84ac0b274135b7a226b78df6c5ecbe15dff329e7
Author: James Antill <james at and.org>
Date:   Tue Dec 22 15:04:28 2009 -0500

    Add rpmverbosity config. and cmd-line-option

diff --git a/cli.py b/cli.py
index 68506f2..f787968 100644
--- a/cli.py
+++ b/cli.py
@@ -1231,6 +1231,9 @@ class YumOptionParser(OptionParser):
                     self.base.usage()
                     sys.exit(1)
 
+            if opts.rpmverbosity is not None:
+                self.base.conf.rpmverbosity = opts.rpmverbosity
+
             # setup the progress bars/callbacks
             self.base.setupProgressCallbacks()
             # setup the callbacks to import gpg pubkeys and confirm them
@@ -1331,6 +1334,9 @@ class YumOptionParser(OptionParser):
         group.add_option("-e", "--errorlevel", dest="errorlevel", default=None,
                 help=_("error output level"), type='int',
                 metavar='[error level]')
+        group.add_option("", "--rpmverbosity", default=None,
+                help=_("debugging output level for rpm"),
+                metavar='[debug level name]')
         group.add_option("-q", "--quiet", dest="quiet", action="store_true",
                         help=_("quiet operation"))
         group.add_option("-v", "--verbose", dest="verbose", action="store_true",
diff --git a/docs/yum.8 b/docs/yum.8
index c65fcaa..487f5d5 100644
--- a/docs/yum.8
+++ b/docs/yum.8
@@ -307,6 +307,11 @@ Configuration Option: \fBdebuglevel\fP
 Sets the error level to [number] Practical range 0 \- 10. 0 means print only critical errors about which you must be told. 1 means print all errors, even ones that are not overly important. 1+ means print more errors (if any) \-e 0 is good for cron jobs.
 .br
 Configuration Option: \fBerrorlevel\fP
+.IP "\fB\-e, \-\-rpmverbosity=[name]\fP" 
+Sets the debug level to [name] for rpm scriplets. 'info' is the default, other
+options are: 'critical', 'emergency', 'error', 'warn' and 'debug'.
+.br
+Configuration Option: \fBrpmverbosity\fP
 .IP "\fB\-R, \-\-randomwait=[time in minutes]\fP" 
 Sets the maximum amount of time yum will wait before performing a command \- it randomizes over the time.
 .IP "\fB\-C, \-\-cacheonly\fP" 
diff --git a/docs/yum.conf.5 b/docs/yum.conf.5
index eab9335..d0e1490 100644
--- a/docs/yum.conf.5
+++ b/docs/yum.conf.5
@@ -57,6 +57,11 @@ Debug message output level. Practical range is 0\-10. Default is `2'.
 Error message output level. Practical range is 0\-10. Default is `2'.
 
 .IP
+\fBrpmverbosity\fR
+Debug scriptlet output level. 'info' is the default, other
+options are: 'critical', 'emergency', 'error', 'warn' and 'debug'.
+
+.IP
 \fBlogfile\fR
 Full directory and file name for where yum should write its log file.
 
diff --git a/yum/config.py b/yum/config.py
index f1e8b48..7e2d781 100644
--- a/yum/config.py
+++ b/yum/config.py
@@ -708,6 +708,8 @@ class YumConf(StartupConf):
     history_record = BoolOption(True)
     history_record_packages = ListOption(['yum', 'rpm'])
 
+    rpmverbosity = Option('info')
+
     _reposlist = []
 
     def dump(self):
diff --git a/yum/rpmtrans.py b/yum/rpmtrans.py
index cf0efeb..73f9af1 100644
--- a/yum/rpmtrans.py
+++ b/yum/rpmtrans.py
@@ -183,7 +183,7 @@ class RPMTransaction:
         self.logger = logging.getLogger('yum.filelogging.RPMInstallCallback')
         self.filelog = False
 
-        self._setupOutputLogging()
+        self._setupOutputLogging(base.conf.rpmverbosity)
         if not os.path.exists(self.base.conf.persistdir):
             os.makedirs(self.base.conf.persistdir) # make the dir, just in case
 
@@ -204,14 +204,22 @@ class RPMTransaction:
             return
         fcntl.fcntl(fd, fcntl.F_SETFD, current_flags | flag)
 
-    def _setupOutputLogging(self):
+    def _setupOutputLogging(self, rpmverbosity="info"):
         # UGLY... set up the transaction to record output from scriptlets
         io_r = tempfile.TemporaryFile()
         w = os.dup(io_r.fileno())
         self._readpipe = io_r
         self._writepipe = os.fdopen(w, 'w+b')
         self.base.ts.scriptFd = self._writepipe.fileno()
-        rpm.setVerbosity(rpm.RPMLOG_INFO)
+        rpmverbosity = {'critical' : 'crit',
+                        'emergency' : 'emerg',
+                        'error' : 'err',
+                        'information' : 'info',
+                        'warn' : 'warning'}.get(rpmverbosity, rpmverbosity)
+        rpmverbosity = 'RPMLOG_' + rpmverbosity.upper()
+        if not hasattr(rpm, rpmverbosity):
+            rpmverbosity = 'RPMLOG_INFO'
+        rpm.setVerbosity(getattr(rpm, rpmverbosity))
         rpm.setLogFile(self._writepipe)
 
     def _shutdownOutputLogging(self):
commit 0be305779f20c1869ef20797f00198754f5d03a8
Author: James Antill <james at and.org>
Date:   Tue Dec 22 14:20:31 2009 -0500

     Change "history info" to display the last transaction, even aborted
    ones.
    
     Let undo/redo refer to the last transaction, without the user having
    to look it up manually.

diff --git a/docs/yum.8 b/docs/yum.8
index ddb44a8..c65fcaa 100644
--- a/docs/yum.8
+++ b/docs/yum.8
@@ -246,10 +246,13 @@ transactions (assuming the history_record config. option is set). You can use
 info/list/summary to view what happened, undo/redo to act on that information
 and new to start a new history file.
 
-The info/list/summary commands take either a transactions id or a package (with
+The info/list/summary commands take either a transaction id or a package (with
 wildcards, as in \fBSpecifying package names\fP), all three can also be passed
 no arguments. list can be passed the keyword "all" to list all the transactions.
-undo/redo just take a transaction id.
+
+The undo/redo commands take either a transaction id or the keyword last and
+an offset from the last transaction (Eg. if you've done 250 transactions,
+"last" refers to transaction 250, and "last-4" refers to transaction 246).
 
 In "history list" output the Altered column also gives some extra information
 if there was something not good with the transaction.
diff --git a/output.py b/output.py
index 89c8f91..7f92701 100755
--- a/output.py
+++ b/output.py
@@ -1297,12 +1297,30 @@ to exit.
             return None
 
         tids = []
-        try:
-            int(extcmds[1])
-            tids.append(extcmds[1])
-        except ValueError:
-            self.logger.critical(_('Bad transaction ID given'))
-            return None
+        last = None
+        for extcmd in extcmds[1:]:
+            try:
+                if extcmd == 'last' or extcmd.startswith('last-'):
+                    if last is None:
+                        cto = False
+                        last = self.history.last(complete_transactions_only=cto)
+                        if last is None:
+                            int("z")
+                    tid = last.tid
+                    if extcmd.startswith('last-'):
+                        off = int(extcmd[len('last-'):])
+                        if off <= 0:
+                            int("z")
+                        tid -= off
+                    tids.append(str(tid))
+                    continue
+
+                if int(extcmd) <= 0:
+                    int("z")
+                tids.append(extcmd)
+            except ValueError:
+                self.logger.critical(_('Bad transaction ID given'))
+                return None
 
         old = self.history.old(tids)
         if not old:
@@ -1330,7 +1348,7 @@ to exit.
             tids.update(self.history.search(pats))
 
         if not tids and len(extcmds) < 2:
-            old = self.history.last()
+            old = self.history.last(complete_transactions_only=False)
             if old is not None:
                 tids.add(old.tid)
 
commit da4ea927024018386915c2bf0c537c970e0ee731
Author: James Antill <james at and.org>
Date:   Tue Dec 22 14:03:03 2009 -0500

    Reuse .old() in .last(), and get complete_transactions_only for free

diff --git a/yum/history.py b/yum/history.py
index 5a1c5e0..5ef6960 100644
--- a/yum/history.py
+++ b/yum/history.py
@@ -539,23 +539,14 @@ class YumHistory:
 
         return ret
 
-    def last(self):
-        """ This is the last full transaction. So any imcomplete transactions
-            do not count. """
-        cur = self._get_cursor()
-        sql =  """SELECT tid,
-                         trans_beg.timestamp AS beg_ts,
-                         trans_beg.rpmdb_version AS beg_rv,
-                         trans_end.timestamp AS end_ts,
-                         trans_end.rpmdb_version AS end_rv,
-                         loginuid, return_code
-                  FROM trans_beg JOIN trans_end USING(tid)
-                  ORDER BY beg_ts DESC, tid ASC
-                  LIMIT 1"""
-        executeSQL(cur, sql)
-        for row in cur:
-            return YumHistoryTransaction(self, row)
-        return None
+    def last(self, complete_transactions_only=True):
+        """ This is the last full transaction. So any incomplete transactions
+            do not count, by default. """
+        ret = self.old([], 1, complete_transactions_only)
+        if not ret:
+            return None
+        assert len(ret) == 1
+        return ret[0]
 
     def _yieldSQLDataList(self, patterns, fields, ignore_case):
         """Yields all the package data for the given params. """
commit 06d3900910758a2e5a52e3e4963e8b947ea9923b
Author: James Antill <james at and.org>
Date:   Sun Dec 20 02:19:46 2009 -0500

    Don't throw an error when the rpmdb cache directory doesn't exist

diff --git a/yum/__init__.py b/yum/__init__.py
index 284f366..74c10f2 100644
--- a/yum/__init__.py
+++ b/yum/__init__.py
@@ -1746,7 +1746,10 @@ class YumBase(depsolve.Depsolve):
 
     def cleanRpmDB(self):
         cachedir = self.conf.cachedir + "/installed/"
-        filelist = misc.getFileList(cachedir, '', [])
+        if not os.path.exists(cachedir):
+            filelist = []
+        else:
+            filelist = misc.getFileList(cachedir, '', [])
         return self._cleanFilelist('rpmdb', filelist)
 
     def _cleanFiles(self, exts, pathattr, filetype):
commit 87ae1fd66ccb7964c7965b684680c5a9fb96d02f
Author: James Antill <james at and.org>
Date:   Thu Dec 10 18:28:28 2009 -0500

    getMembers returns txmbrs not POs ... *sigh*

diff --git a/yum/depsolve.py b/yum/depsolve.py
index d46452c..a09bd4b 100644
--- a/yum/depsolve.py
+++ b/yum/depsolve.py
@@ -1011,7 +1011,7 @@ class Depsolve(object):
             for pkgtup in reverselookup[filename]:
                 po = self.tsInfo.getMembersWithState(pkgtup, TS_INSTALL_STATES)
                 if po:
-                    po = po[0] # Should only have one
+                    po = po[0].po # Should only have one
                 else:
                     po = self.getInstalledPackageObject(pkgtup)
                 ret.append( (po, (filename, 0, '')) )
commit c62dab6f864dc07bee229e649f7f653a07dbb863
Author: James Antill <james at and.org>
Date:   Thu Dec 10 09:46:00 2009 -0500

    Fix when we have file require(s) from just to be installed pkgs. The mash bug

diff --git a/yum/depsolve.py b/yum/depsolve.py
index b5953c2..d46452c 100644
--- a/yum/depsolve.py
+++ b/yum/depsolve.py
@@ -1009,7 +1009,11 @@ class Depsolve(object):
                 continue 
 
             for pkgtup in reverselookup[filename]:
-                po = self.getInstalledPackageObject(pkgtup)
+                po = self.tsInfo.getMembersWithState(pkgtup, TS_INSTALL_STATES)
+                if po:
+                    po = po[0] # Should only have one
+                else:
+                    po = self.getInstalledPackageObject(pkgtup)
                 ret.append( (po, (filename, 0, '')) )
 
         self.rpmdb.transactionCacheFileRequires(self.installedFileRequires, 
commit d752c558b909f820d0a40298b70dfbc783bb3161
Author: James Antill <james at and.org>
Date:   Mon Dec 7 19:22:46 2009 -0500

    Rename rpmdb-cache to installed, as that's taken

diff --git a/yum/__init__.py b/yum/__init__.py
index 6756126..284f366 100644
--- a/yum/__init__.py
+++ b/yum/__init__.py
@@ -1745,7 +1745,7 @@ class YumBase(depsolve.Depsolve):
         return self._cleanFiles(exts, 'cachedir', 'metadata')
 
     def cleanRpmDB(self):
-        cachedir = self.conf.cachedir + "/rpmdb-cache/"
+        cachedir = self.conf.cachedir + "/installed/"
         filelist = misc.getFileList(cachedir, '', [])
         return self._cleanFilelist('rpmdb', filelist)
 
diff --git a/yum/rpmsack.py b/yum/rpmsack.py
index 710bc95..23ebd3e 100644
--- a/yum/rpmsack.py
+++ b/yum/rpmsack.py
@@ -173,8 +173,8 @@ class RPMDBPackageSack(PackageSackBase):
 
     def setCacheDir(self, cachedir):
         """ Sets the internal cachedir value for the rpmdb, to be the
-            "rpmdb-cache" directory from this parent. """
-        self._cachedir = self.root + '/' + cachedir + "/rpmdb-cache/"
+            "installed" directory from this parent. """
+        self._cachedir = self.root + '/' + cachedir + "/installed/"
 
     def readOnlyTS(self):
         if not self.ts:
commit 79bf0047b0e7b2b3dd769809dfaf27566b1639fa
Author: James Antill <james at and.org>
Date:   Mon Dec 7 19:20:40 2009 -0500

    Use persistdir instead of /var/lib/yum and use root for rpmdb loc.

diff --git a/yum/__init__.py b/yum/__init__.py
index d011513..6756126 100644
--- a/yum/__init__.py
+++ b/yum/__init__.py
@@ -470,6 +470,7 @@ class YumBase(depsolve.Depsolve):
                                     _('Reading Local RPMDB'))
             self._rpmdb = rpmsack.RPMDBPackageSack(root=self.conf.installroot,
                                                    releasever=self.yumvar['releasever'],
+                                                   persistdir=self.conf.persistdir,
                                                    cachedir=self.conf.cachedir)
             self.verbose_logger.debug('rpmdb time: %0.3f' % (time.time() - rpmdb_st))
         return self._rpmdb
@@ -714,7 +715,9 @@ class YumBase(depsolve.Depsolve):
         """auto create the history object that to access/append the transaction
            history information. """
         if self._history is None:
-            self._history = yum.history.YumHistory(root=self.conf.installroot)
+            pdb_path = self.conf.persistdir + "/history"
+            self._history = yum.history.YumHistory(root=self.conf.installroot,
+                                                   db_path=pdb_path)
         return self._history
     
     # properties so they auto-create themselves with defaults
diff --git a/yum/rpmsack.py b/yum/rpmsack.py
index c834f51..710bc95 100644
--- a/yum/rpmsack.py
+++ b/yum/rpmsack.py
@@ -103,7 +103,8 @@ class RPMDBPackageSack(PackageSackBase):
     # Do we want to cache rpmdb data in a file, for later use?
     __cache_rpmdb__ = True
 
-    def __init__(self, root='/', releasever=None, cachedir=None):
+    def __init__(self, root='/', releasever=None, cachedir=None,
+                 persistdir='/var/lib/yum'):
         self.root = root
         self._idx2pkg = {}
         self._name2pkg = {}
@@ -116,6 +117,7 @@ class RPMDBPackageSack(PackageSackBase):
         if cachedir is None:
             cachedir = misc.getCacheDir()
         self.setCacheDir(cachedir)
+        self._persistdir = root +  '/' + persistdir
         self._have_cached_rpmdbv_data = None
         self._cached_conflicts_data = None
         # Store the result of what happens, if a transaction completes.
@@ -133,7 +135,7 @@ class RPMDBPackageSack(PackageSackBase):
             'obsoletes' : { },
             }
         
-        addldb_path = os.path.normpath(root + '/' + '/var/lib/yum/yumdb')
+        addldb_path = os.path.normpath(self._persistdir + '/yumdb')
         self.yumdb = RPMDBAdditionalData(db_path=addldb_path)
 
     def _get_pkglist(self):
@@ -172,7 +174,7 @@ class RPMDBPackageSack(PackageSackBase):
     def setCacheDir(self, cachedir):
         """ Sets the internal cachedir value for the rpmdb, to be the
             "rpmdb-cache" directory from this parent. """
-        self._cachedir = cachedir + "/rpmdb-cache/"
+        self._cachedir = self.root + '/' + cachedir + "/rpmdb-cache/"
 
     def readOnlyTS(self):
         if not self.ts:
@@ -653,7 +655,7 @@ class RPMDBPackageSack(PackageSackBase):
         # http://lists.rpm.org/pipermail/rpm-maint/2007-November/001719.html
         # ...if anything gets implemented, we should change.
         rpmdbvfname = self._cachedir + "/version"
-        rpmdbfname  = "/var/lib/rpm/Packages"
+        rpmdbfname  = self.root + "/var/lib/rpm/Packages"
 
         if os.path.exists(rpmdbvfname) and os.path.exists(rpmdbfname):
             # See if rpmdb has "changed" ...
commit 7acf3f69e582a05337c25ff3dc2b4a121e371862
Author: James Antill <james at and.org>
Date:   Fri Dec 4 08:27:01 2009 -0500

     Fixes for clean functions, BZ 544173
    
     When nothing to be done, set removed.
     When doing "clean all" fix the var. name.
     When doing anything but clean rpmdb, fix the return.
     Allow rpmdb as a valid arg.
     Fix getFileList() with ext == ''

diff --git a/cli.py b/cli.py
index daf1164..68506f2 100644
--- a/cli.py
+++ b/cli.py
@@ -896,7 +896,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
             rpmcode, rpmresults = self.cleanRpmDB()
             self.plugins.run('clean')
             
-            code = hdrcode + pkgcode + xmlcode + dbcode + rpmdb
+            code = hdrcode + pkgcode + xmlcode + dbcode + rpmcode
             results = (hdrresults + pkgresults + xmlresults + dbresults +
                        rpmresults)
             for msg in results:
diff --git a/yum/__init__.py b/yum/__init__.py
index d901ae6..d011513 100644
--- a/yum/__init__.py
+++ b/yum/__init__.py
@@ -1748,15 +1748,15 @@ class YumBase(depsolve.Depsolve):
 
     def _cleanFiles(self, exts, pathattr, filetype):
         filelist = []
-        removed = 0
         for ext in exts:
             for repo in self.repos.listEnabled():
                 path = getattr(repo, pathattr)
                 if os.path.exists(path) and os.path.isdir(path):
                     filelist = misc.getFileList(path, ext, filelist)
-        self._cleanFilelist(filetype, filelist)
+        return self._cleanFilelist(filetype, filelist)
 
     def _cleanFilelist(self, filetype, filelist):
+        removed = 0
         for item in filelist:
             try:
                 misc.unlink_f(item)
diff --git a/yum/misc.py b/yum/misc.py
index 78c7950..3917a4f 100644
--- a/yum/misc.py
+++ b/yum/misc.py
@@ -340,7 +340,7 @@ def getFileList(path, ext, filelist):
         if os.path.isdir(path + '/' + d):
             filelist = getFileList(path + '/' + d, ext, filelist)
         else:
-            if d[-extlen:].lower() == '%s' % (ext):
+            if not ext or d[-extlen:].lower() == '%s' % (ext):
                 newpath = os.path.normpath(path + '/' + d)
                 filelist.append(newpath)
                     
diff --git a/yumcommands.py b/yumcommands.py
index e9ecb4c..aa7be12 100644
--- a/yumcommands.py
+++ b/yumcommands.py
@@ -85,7 +85,7 @@ def checkGroupArg(base, basecmd, extcmds):
 
 def checkCleanArg(base, basecmd, extcmds):
     VALID_ARGS = ('headers', 'packages', 'metadata', 'dbcache', 'plugins',
-                  'expire-cache', 'all')
+                  'expire-cache', 'rpmdb', 'all')
 
     if len(extcmds) == 0:
         base.logger.critical(_('Error: clean requires an option: %s') % (
commit e7ed030df8a016b3ff0ff333c55b3300638b75c8
Author: James Antill <james at and.org>
Date:   Mon Nov 9 00:10:11 2009 -0500

    Fix filelist always being required, by being lax on recording all providers

diff --git a/test/rpmdb-cache.py b/test/rpmdb-cache.py
index 3fc2185..7768a93 100755
--- a/test/rpmdb-cache.py
+++ b/test/rpmdb-cache.py
@@ -3,13 +3,16 @@
 import sys
 import yum
 
+__provides_of_requires_exact__ = False
+
 yb1 = yum.YumBase()
 yb1.conf.cache = True
 yb2 = yum.YumBase()
 yb2.conf.cache = True
 
-if len(sys.argv) > 1 and sys.argv[1].lower() == 'setcachedir':
-    yb2.setCacheDir()
+if len(sys.argv) > 1 and sys.argv[1].lower() == 'full':
+    print "Doing full test"
+    __provides_of_requires_exact__ = True
 
 assert hasattr(yb1.rpmdb, '__cache_rpmdb__')
 yb1.rpmdb.__cache_rpmdb__ = False
@@ -58,13 +61,17 @@ for pkgtup in frd2:
             print >>sys.stderr, ("Error: FileReq[%s] cache extra" % (pkgtup,),
                                  name)
 
-# File Provides (of requires)
+# File Provides (of requires) -- not exact
 if len(fpd1) != len(fpd2):
     print >>sys.stderr, "Error: FileProv len mismatch:", len(fpd1), len(fpd2)
 for name in fpd1:
     if name not in fpd2:
         print >>sys.stderr, "Error: FileProv cache missing", name
         continue
+
+    if not __provides_of_requires_exact__:
+        continue # We might be missing some providers
+
     if len(fpd1[name]) != len(fpd2[name]):
         print >>sys.stderr, ("Error: FileProv[%s] len mismatch:" % (pkgtup,),
                              len(fpd1[name]), len(fpd2[name]))
diff --git a/yum/depsolve.py b/yum/depsolve.py
index be875d0..b5953c2 100644
--- a/yum/depsolve.py
+++ b/yum/depsolve.py
@@ -981,23 +981,33 @@ class Depsolve(object):
         # check the file requires
         iFP = self.installedFileProviders
         for filename in fileRequires:
-            nprov = self.tsInfo.getNewProvides(filename)
-            if nprov:
-                iFP.setdefault(filename, []).extend([po.pkgtup for po in nprov])
-
-            #  If we've got a result, and we've seen this before (and thus. have
-            # the rpmdb results) ... we are done.
-            if (filename in self.installedFileProviders and
-                filename not in nfileRequires):
-                continue 
+            # In theory we need this to be:
+            #
+            # nprov, filename in iFP (or new), oprov
+            #
+            # ...this keeps the cache exactly the same as the non-cached data.
+            # However that also means that we'll always need the filelists, so
+            # we do:
+            #
+            # filename in iFP (if found return), oprov (if found return),
+            # nprov
+            #
+            # ...this means we'll always get the same _result_ (as we only need
+            # to know if _something_ provides), but our cache will be off on
+            # what does/doesn't provide the file.
+            if filename in self.installedFileProviders:
+                continue
 
             oprov = self.tsInfo.getOldProvides(filename)
             if oprov:
                 iFP.setdefault(filename, []).extend([po.pkgtup for po in oprov])
-
-            if filename in self.installedFileProviders:
                 continue
 
+            nprov = self.tsInfo.getNewProvides(filename)
+            if nprov:
+                iFP.setdefault(filename, []).extend([po.pkgtup for po in nprov])
+                continue 
+
             for pkgtup in reverselookup[filename]:
                 po = self.getInstalledPackageObject(pkgtup)
                 ret.append( (po, (filename, 0, '')) )
diff --git a/yum/rpmsack.py b/yum/rpmsack.py
index ab3da7f..c834f51 100644
--- a/yum/rpmsack.py
+++ b/yum/rpmsack.py
@@ -553,6 +553,10 @@ class RPMDBPackageSack(PackageSackBase):
         return iFR, iFP
 
     def fileRequiresData(self):
+        """ Get a cached copy of the fileRequiresData for
+            depsolving/checkFileRequires, note the giant comment in that
+            function about how we don't keep this perfect for the providers of
+            the requires. """
         if self.__cache_rpmdb__:
             iFR, iFP = self._read_file_requires()
             if iFR is not None:
commit 9a91f0297ac74af97392134081b5d829ab4a5f36
Author: James Antill <james at and.org>
Date:   Sun Nov 8 01:42:54 2009 -0500

    Add "version nogroups*" to version command, to ignore version groups data

diff --git a/yumcommands.py b/yumcommands.py
index 14eaf9d..e9ecb4c 100644
--- a/yumcommands.py
+++ b/yumcommands.py
@@ -1116,20 +1116,29 @@ class VersionCommand(YumCommand):
 
         verbose = base.verbose_logger.isEnabledFor(logginglevels.DEBUG_3)
         groups = {}
-        gconf = yum.config.readVersionGroupsConfig()
+        if vcmd in ('nogroups', 'nogroups-installed', 'nogroups-available',
+                    'nogroups-all'):
+            gconf = []
+            if vcmd == 'nogroups':
+                vcmd = 'installed'
+            else:
+                vcmd = vcmd[len('nogroups-'):]
+        else:
+            gconf = yum.config.readVersionGroupsConfig()
+
         for group in gconf:
             groups[group] = set(gconf[group].pkglist)
             if gconf[group].run_with_packages:
                 groups[group].update(base.run_with_package_names)
 
-        if vcmd in ('grouplist'):
+        if vcmd == 'grouplist':
             print _(" Yum version groups:")
             for group in sorted(groups):
                 print "   ", group
 
             return 0, ['version grouplist']
 
-        if vcmd in ('groupinfo'):
+        if vcmd == 'groupinfo':
             for group in groups:
                 if group not in extcmds[1:]:
                     continue
commit 667ae8c34b70d777bc48d17070173aeed4366977
Author: James Antill <james at and.org>
Date:   Sat Nov 7 00:54:45 2009 -0500

     Add .cleanRpmDB(), so the user can manually clean. And other minor fixes.
    
     Adds the "clean rpmdb" command.
     Fixes the clean documentation.
    
     Adds a rpmdb-cache.py testcase, which should show if the cache does
    not == the rpmdb.
    
     Also minor fixes for writting the caches to disk, as non-root etc.
    
     Adds a .setCacheDir() to the rpmdb, for setting the cachedir.
    
     Also fix "YumBase.setCacheDir()" to use rpmdb.setCacheDir()
    
     Also fix the mock class FakeRpmDb, to have the new methods rpmdb does.

diff --git a/cli.py b/cli.py
index b890231..daf1164 100644
--- a/cli.py
+++ b/cli.py
@@ -893,10 +893,12 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
             hdrcode, hdrresults = self.cleanHeaders()
             xmlcode, xmlresults = self.cleanMetadata()
             dbcode, dbresults = self.cleanSqlite()
+            rpmcode, rpmresults = self.cleanRpmDB()
             self.plugins.run('clean')
             
-            code = hdrcode + pkgcode + xmlcode + dbcode
-            results = hdrresults + pkgresults + xmlresults + dbresults
+            code = hdrcode + pkgcode + xmlcode + dbcode + rpmdb
+            results = (hdrresults + pkgresults + xmlresults + dbresults +
+                       rpmresults)
             for msg in results:
                 self.logger.debug(msg)
             return code, []
@@ -916,6 +918,9 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
         if 'expire-cache' in userlist or 'metadata' in userlist:
             self.logger.debug(_('Cleaning up expire-cache metadata'))
             expccode, expcresults = self.cleanExpireCache()
+        if 'rpmdb' in userlist:
+            self.logger.debug(_('Cleaning up cached rpmdb data'))
+            expccode, expcresults = self.cleanRpmDB()
         if 'plugins' in userlist:
             self.logger.debug(_('Cleaning up plugins'))
             self.plugins.run('clean')
diff --git a/docs/yum.8 b/docs/yum.8
index 7c102f7..ddb44a8 100644
--- a/docs/yum.8
+++ b/docs/yum.8
@@ -35,7 +35,7 @@ gnome\-packagekit application\&.
 .br 
 .I \fR * provides  | whatprovides feature1 [feature2] [\&.\&.\&.]
 .br  
-.I \fR * clean [ packages | headers | metadata | dbcache | all ]
+.I \fR * clean [ packages | metadata | expire-cache | rpmdb | plugins | all ]
 .br
 .I \fR * makecache
 .br
@@ -439,7 +439,8 @@ Eliminate the local data saying when the metadata and mirrorlists were downloade
 Eliminate any cached packages from the system.  Note that packages are not automatically deleted after they are downloaded.
 
 .IP "\fByum clean headers\fP"
-Eliminate all of the header files which yum uses for dependency resolution.
+Eliminate all of the header files, which old versions of yum used for
+dependency resolution.
 
 .IP "\fByum clean metadata\fP"
 Eliminate all of the files which yum uses to determine the remote
@@ -448,11 +449,22 @@ metadata the next time it is run.
 
 .IP "\fByum clean dbcache\fP"
 Eliminate the sqlite cache used for faster access to metadata.
-Using this option will force yum to recreate the cache the next time
-it is run.
+Using this option will force yum to download the sqlite metadata the next time
+it is run, or recreate the sqlite metadata if using an older repo.
+
+.IP "\fByum clean dbcache\fP"
+Eliminate the sqlite cache used for faster access to metadata.
+Using this option will force yum to download the sqlite metadata the next time
+it is run, or recreate the sqlite metadata if using an older repo.
+
+.IP "\fByum clean rpmdb\fP"
+Eliminate any cached data from the local rpmdb.
+
+.IP "\fByum clean plugins\fP"
+Tell any enabled plugins to eliminate their cached data.
 
 .IP "\fByum clean all\fP"
-Runs \fByum clean packages\fP and \fByum clean headers\fP, \fByum clean metadata\fP and \fByum clean dbcache\fP as above.
+Does all of the above.
 
 .PP
 .SH "MISC"
diff --git a/test/rpmdb-cache.py b/test/rpmdb-cache.py
new file mode 100755
index 0000000..3fc2185
--- /dev/null
+++ b/test/rpmdb-cache.py
@@ -0,0 +1,80 @@
+#! /usr/bin/python -tt
+
+import sys
+import yum
+
+yb1 = yum.YumBase()
+yb1.conf.cache = True
+yb2 = yum.YumBase()
+yb2.conf.cache = True
+
+if len(sys.argv) > 1 and sys.argv[1].lower() == 'setcachedir':
+    yb2.setCacheDir()
+
+assert hasattr(yb1.rpmdb, '__cache_rpmdb__')
+yb1.rpmdb.__cache_rpmdb__ = False
+yb2.setCacheDir()
+
+# Version
+ver1 = yb1.rpmdb.simpleVersion(main_only=True)[0]
+ver2 = yb2.rpmdb.simpleVersion(main_only=True)[0]
+if ver1 != ver2:
+    print >>sys.stderr, "Error: Version mismatch:", ver1, ver2
+
+# Conflicts
+cpkgs1 = yb1.rpmdb.returnConflictPackages()
+cpkgs2 = yb2.rpmdb.returnConflictPackages()
+if len(cpkgs1) != len(cpkgs2):
+    print >>sys.stderr, "Error: Conflict len mismatch:", len(cpkgs1),len(cpkgs2)
+for pkg in cpkgs1:
+    if pkg not in cpkgs2:
+        print >>sys.stderr, "Error: Conflict cache missing", pkg
+for pkg in cpkgs2:
+    if pkg not in cpkgs1:
+        print >>sys.stderr, "Error: Conflict cache extra", pkg
+
+# File Requires
+frd1, blah, fpd1 = yb1.rpmdb.fileRequiresData()
+frd2, blah, fpd2 = yb2.rpmdb.fileRequiresData()
+if len(frd1) != len(frd2):
+    print >>sys.stderr, "Error: FileReq len mismatch:", len(frd1), len(frd2)
+for pkgtup in frd1:
+    if pkgtup not in frd2:
+        print >>sys.stderr, "Error: FileReq cache missing", pkgtup
+        continue
+    if len(set(frd1[pkgtup])) != len(set(frd2[pkgtup])):
+        print >>sys.stderr, ("Error: FileReq[%s] len mismatch:" % (pkgtup,),
+                             len(frd1[pkgtup]), len(frd2[pkgtup]))
+    for name in frd1[pkgtup]:
+        if name not in frd2[pkgtup]:
+            print >>sys.stderr, ("Error: FileReq[%s] cache missing" % (pkgtup,),
+                                 name)
+for pkgtup in frd2:
+    if pkgtup not in frd1:
+        print >>sys.stderr, "Error: FileReq cache extra", pkgtup
+        continue
+    for name in frd2[pkgtup]:
+        if name not in frd1[pkgtup]:
+            print >>sys.stderr, ("Error: FileReq[%s] cache extra" % (pkgtup,),
+                                 name)
+
+# File Provides (of requires)
+if len(fpd1) != len(fpd2):
+    print >>sys.stderr, "Error: FileProv len mismatch:", len(fpd1), len(fpd2)
+for name in fpd1:
+    if name not in fpd2:
+        print >>sys.stderr, "Error: FileProv cache missing", name
+        continue
+    if len(fpd1[name]) != len(fpd2[name]):
+        print >>sys.stderr, ("Error: FileProv[%s] len mismatch:" % (pkgtup,),
+                             len(fpd1[name]), len(fpd2[name]))
+    for pkgtup in fpd1[name]:
+        if pkgtup not in fpd2[name]:
+            print >>sys.stderr,"Error: FileProv[%s] cache missing" % name,pkgtup
+for name in fpd2:
+    if name not in fpd1:
+        print >>sys.stderr, "Error: FileProv cache extra", name
+        continue
+    for pkgtup in fpd2[name]:
+        if pkgtup not in fpd1[name]:
+            print >>sys.stderr,"Error: FileProv[%s] cache extra" % name,pkgtup
diff --git a/test/testbase.py b/test/testbase.py
index c004bdb..28de577 100644
--- a/test/testbase.py
+++ b/test/testbase.py
@@ -14,6 +14,7 @@ from yum import packages
 from yum import packageSack
 from yum.constants import TS_INSTALL_STATES, TS_REMOVE_STATES
 from cli import YumBaseCli
+from yum.rpmsack import RPMDBPackageSack as _rpmdbsack
 import inspect
 from rpmUtils import arch
 
@@ -233,6 +234,50 @@ class FakeRpmDb(packageSack.PackageSack):
     def __init__(self):
         packageSack.PackageSack.__init__(self)
 
+    # Need to mock out rpmdb caching... copy&paste. Gack.
+    def returnConflictPackages(self):
+        ret = []
+        for pkg in self.returnPackages():
+            if len(pkg.conflicts):
+                ret.append(pkg)
+        return ret
+    def fileRequiresData(self):
+        installedFileRequires = {}
+        installedUnresolvedFileRequires = set()
+        resolved = set()
+        for pkg in self.returnPackages():
+            for name, flag, evr in pkg.requires:
+                if not name.startswith('/'):
+                    continue
+                installedFileRequires.setdefault(pkg.pkgtup, []).append(name)
+                if name not in resolved:
+                    dep = self.getProvides(name, flag, evr)
+                    resolved.add(name)
+                    if not dep:
+                        installedUnresolvedFileRequires.add(name)
+
+        fileRequires = set()
+        for fnames in installedFileRequires.itervalues():
+            fileRequires.update(fnames)
+        installedFileProviders = {}
+        for fname in fileRequires:
+            pkgtups = [pkg.pkgtup for pkg in self.getProvides(fname)]
+            installedFileProviders[fname] = pkgtups
+
+        ret =  (installedFileRequires, installedUnresolvedFileRequires,
+                installedFileProviders)
+
+        return ret
+    def transactionCacheFileRequires(self, installedFileRequires,
+                                     installedUnresolvedFileRequires,
+                                     installedFileProvides,
+                                     problems):
+        return
+    def transactionCacheConflictPackages(self, pkgs):
+        return
+    def transactionResultVersion(self, rpmdbv):
+        return
+
     def getProvides(self, name, flags=None, version=(None, None, None)):
         """return dict { packages -> list of matching provides }"""
         self._checkIndexes(failure='build')
diff --git a/yum/__init__.py b/yum/__init__.py
index 4750a52..d901ae6 100644
--- a/yum/__init__.py
+++ b/yum/__init__.py
@@ -1741,6 +1741,11 @@ class YumBase(depsolve.Depsolve):
         exts = ['cachecookie', 'mirrorlist.txt']
         return self._cleanFiles(exts, 'cachedir', 'metadata')
 
+    def cleanRpmDB(self):
+        cachedir = self.conf.cachedir + "/rpmdb-cache/"
+        filelist = misc.getFileList(cachedir, '', [])
+        return self._cleanFilelist('rpmdb', filelist)
+
     def _cleanFiles(self, exts, pathattr, filetype):
         filelist = []
         removed = 0
@@ -1749,7 +1754,9 @@ class YumBase(depsolve.Depsolve):
                 path = getattr(repo, pathattr)
                 if os.path.exists(path) and os.path.isdir(path):
                     filelist = misc.getFileList(path, ext, filelist)
+        self._cleanFilelist(filetype, filelist)
 
+    def _cleanFilelist(self, filetype, filelist):
         for item in filelist:
             try:
                 misc.unlink_f(item)
@@ -4218,7 +4225,9 @@ class YumBase(depsolve.Depsolve):
         if cachedir is None:
             return False # Tried, but failed, to get a "user" cachedir
 
-        self.repos.setCacheDir(cachedir + varReplace(suffix, self.yumvar))
+        cachedir += varReplace(suffix, self.yumvar)
+        self.repos.setCacheDir(cachedir)
+        self.rpmdb.setCacheDir(cachedir)
 
         return True # We got a new cache dir
 
diff --git a/yum/rpmsack.py b/yum/rpmsack.py
index 1fd876b..ab3da7f 100644
--- a/yum/rpmsack.py
+++ b/yum/rpmsack.py
@@ -115,7 +115,7 @@ class RPMDBPackageSack(PackageSackBase):
         self._loaded_gpg_keys = False
         if cachedir is None:
             cachedir = misc.getCacheDir()
-        self._cachedir = cachedir + "/rpmdb-cache/"
+        self.setCacheDir(cachedir)
         self._have_cached_rpmdbv_data = None
         self._cached_conflicts_data = None
         # Store the result of what happens, if a transaction completes.
@@ -169,6 +169,11 @@ class RPMDBPackageSack(PackageSackBase):
         self._have_cached_rpmdbv_data = None
         self._cached_conflicts_data = None
 
+    def setCacheDir(self, cachedir):
+        """ Sets the internal cachedir value for the rpmdb, to be the
+            "rpmdb-cache" directory from this parent. """
+        self._cachedir = cachedir + "/rpmdb-cache/"
+
     def readOnlyTS(self):
         if not self.ts:
             self.ts =  initReadOnlyTransaction(root=self.root)
@@ -392,7 +397,7 @@ class RPMDBPackageSack(PackageSackBase):
         return self._cached_conflicts_data
 
     def _write_conflicts_new(self, pkgs, rpmdbv):
-        if not self.__cache_rpmdb__:
+        if not os.access(self._cachedir, os.W_OK):
             return
 
         conflicts_fname = self._cachedir + '/conflicts'
@@ -447,14 +452,16 @@ class RPMDBPackageSack(PackageSackBase):
         return self._cached_conflicts_data
 
     def transactionCacheConflictPackages(self, pkgs):
-        self._trans_cache_store['conflicts'] = pkgs
+        if self.__cache_rpmdb__:
+            self._trans_cache_store['conflicts'] = pkgs
 
     def returnConflictPackages(self):
         """ Return a list of packages that have conflicts. """
         pkgs = self._read_conflicts()
         if pkgs is None:
             pkgs = self._uncached_returnConflictPackages()
-            self._write_conflicts(pkgs)
+            if self.__cache_rpmdb__:
+                self._write_conflicts(pkgs)
 
         return pkgs
 
@@ -463,6 +470,10 @@ class RPMDBPackageSack(PackageSackBase):
             rpmdb version when we finish. The idea being we can update all
             our rpmdb caches for that rpmdb version. """
 
+        if not self.__cache_rpmdb__:
+            self._trans_cache_store = {}
+            return
+
         if 'conflicts' in self._trans_cache_store:
             pkgs = self._trans_cache_store['conflicts']
             self._write_conflicts_new(pkgs, rpmdbv)
@@ -569,8 +580,13 @@ class RPMDBPackageSack(PackageSackBase):
             pkgtups = [pkg.pkgtup for pkg in self.getProvides(fname)]
             installedFileProviders[fname] = pkgtups
 
-        return (installedFileRequires, installedUnresolvedFileRequires,
+        ret =  (installedFileRequires, installedUnresolvedFileRequires,
                 installedFileProviders)
+        if self.__cache_rpmdb__:
+            rpmdbv = self.simpleVersion(main_only=True)[0]
+            self._write_file_requires(rpmdbv, ret)
+
+        return ret
 
     def transactionCacheFileRequires(self, installedFileRequires,
                                      installedUnresolvedFileRequires,
@@ -589,6 +605,9 @@ class RPMDBPackageSack(PackageSackBase):
         self._trans_cache_store['file-requires'] = data
 
     def _write_file_requires(self, rpmdbversion, data):
+        if not os.access(self._cachedir, os.W_OK):
+            return
+
         (installedFileRequires,
          installedUnresolvedFileRequires,
          installedFileProvides) = data
commit ef017d8f5c13789c62f1ad7fe8db37618a039473
Author: James Antill <james at and.org>
Date:   Sat Nov 7 00:13:14 2009 -0500

     Add transactionCacheFileRequires() and fileRequiresData()
    
     This is the most scarey part of the rpmdb caching. I think it's good.
    But it's somewhat complicated, and if we screw it up (it isn't in sync.
    with the rpmdb) bad things will likely result.
     We are protected from rpm doing crazy things due to the way
    transactionResultVersion() works, so all that matters is that our
    logic is right for what is in the transaction.
     Both installedFileRequires and installedFileProviders should always
    be updated for the transaction, and thus. be identical to rm'ing the
    cache.
    
     And I've tested it, and it seems to work :).

diff --git a/yum/depsolve.py b/yum/depsolve.py
index c313f1b..be875d0 100644
--- a/yum/depsolve.py
+++ b/yum/depsolve.py
@@ -918,6 +918,7 @@ class Depsolve(object):
 
     def _checkFileRequires(self):
         fileRequires = set()
+        nfileRequires = set() # These need to be looked up in the rpmdb.
         reverselookup = {}
         ret = []
 
@@ -925,7 +926,7 @@ class Depsolve(object):
         if self.installedFileRequires is None:
             self.installedFileRequires, \
               self.installedUnresolvedFileRequires, \
-              self.installedFileProviders = self.rpmdb._get_file_requires()
+              self.installedFileProviders = self.rpmdb.fileRequiresData()
 
         # get file requirements from packages not deleted
         todel = []
@@ -956,6 +957,8 @@ class Depsolve(object):
                                 break
                         if already_broken:
                             continue
+                    if name not in fileRequires:
+                        nfileRequires.add(name)
                     fileRequires.add(name)
                     reverselookup.setdefault(name, []).append(txmbr.po.pkgtup)
 
@@ -976,27 +979,30 @@ class Depsolve(object):
             del self.installedFileProviders[fname]
 
         # check the file requires
+        iFP = self.installedFileProviders
         for filename in fileRequires:
             nprov = self.tsInfo.getNewProvides(filename)
             if nprov:
-                pkgtups = [po.pkgtup for po in nprov]
-                self.installedFileProviders[filename] = pkgtups
-                continue
+                iFP.setdefault(filename, []).extend([po.pkgtup for po in nprov])
 
-            if filename in self.installedFileProviders:
+            #  If we've got a result, and we've seen this before (and thus. have
+            # the rpmdb results) ... we are done.
+            if (filename in self.installedFileProviders and
+                filename not in nfileRequires):
                 continue 
 
             oprov = self.tsInfo.getOldProvides(filename)
             if oprov:
-                pkgtups = [po.pkgtup for po in oprov]
-                self.installedFileProviders[filename] = pkgtups
+                iFP.setdefault(filename, []).extend([po.pkgtup for po in oprov])
+
+            if filename in self.installedFileProviders:
                 continue
 
             for pkgtup in reverselookup[filename]:
                 po = self.getInstalledPackageObject(pkgtup)
                 ret.append( (po, (filename, 0, '')) )
 
-        self.rpmdb._write_file_requires(self.installedFileRequires, 
+        self.rpmdb.transactionCacheFileRequires(self.installedFileRequires, 
                                         self.installedUnresolvedFileRequires,
                                         self.installedFileProviders,
                                         ret)
diff --git a/yum/rpmsack.py b/yum/rpmsack.py
index a16e57a..1fd876b 100644
--- a/yum/rpmsack.py
+++ b/yum/rpmsack.py
@@ -117,7 +117,6 @@ class RPMDBPackageSack(PackageSackBase):
             cachedir = misc.getCacheDir()
         self._cachedir = cachedir + "/rpmdb-cache/"
         self._have_cached_rpmdbv_data = None
-        self._use_cached_file_requires = True
         self._cached_conflicts_data = None
         # Store the result of what happens, if a transaction completes.
         self._trans_cache_store = {}
@@ -468,6 +467,10 @@ class RPMDBPackageSack(PackageSackBase):
             pkgs = self._trans_cache_store['conflicts']
             self._write_conflicts_new(pkgs, rpmdbv)
 
+        if 'file-requires' in self._trans_cache_store:
+            data = self._trans_cache_store['file-requires']
+            self._write_file_requires(rpmdbv, data)
+
         self._trans_cache_store = {}
 
     def returnGPGPubkeyPackages(self):
@@ -538,15 +541,12 @@ class RPMDBPackageSack(PackageSackBase):
 
         return iFR, iFP
 
-    def _get_file_requires(self):
-        print "JDBG:", 'BEG: _all_file_requires', self._use_cached_file_requires
-        if self.__cache_rpmdb__ and self._use_cached_file_requires:
+    def fileRequiresData(self):
+        if self.__cache_rpmdb__:
             iFR, iFP = self._read_file_requires()
             if iFR is not None:
-                print "JDBG:", 'CACHE'
                 return iFR, set(), iFP
 
-        print "JDBG:", '** no CACHE'
         installedFileRequires = {}
         installedUnresolvedFileRequires = set()
         resolved = set()
@@ -561,26 +561,41 @@ class RPMDBPackageSack(PackageSackBase):
                     if not dep:
                         installedUnresolvedFileRequires.add(name)
 
-        if self.__cache_rpmdb__:
-            self._write_file_requires(installedFileRequires,
-                                      installedUnresolvedFileRequires,
-                                      {}, [])
-            self._rename_file_requires(self.simpleVersion(main_only=True)[0])
-        return installedFileRequires, installedUnresolvedFileRequires, {}
-
-    def _write_file_requires(self, installedFileRequires,
-                             installedUnresolvedFileRequires,
-                             installedFileProvides,
-                             problems):
+        fileRequires = set()
+        for fnames in installedFileRequires.itervalues():
+            fileRequires.update(fnames)
+        installedFileProviders = {}
+        for fname in fileRequires:
+            pkgtups = [pkg.pkgtup for pkg in self.getProvides(fname)]
+            installedFileProviders[fname] = pkgtups
+
+        return (installedFileRequires, installedUnresolvedFileRequires,
+                installedFileProviders)
+
+    def transactionCacheFileRequires(self, installedFileRequires,
+                                     installedUnresolvedFileRequires,
+                                     installedFileProvides,
+                                     problems):
         if not self.__cache_rpmdb__:
             return
 
-        if not self._use_cached_file_requires:
-            return
         if installedUnresolvedFileRequires or problems:
             return
-        # FIXME: ... real tmp. file
-        fo = open(self._cachedir + '/file-requires.un.tmp', 'w')
+
+        data = (installedFileRequires,
+                installedUnresolvedFileRequires,
+                installedFileProvides)
+
+        self._trans_cache_store['file-requires'] = data
+
+    def _write_file_requires(self, rpmdbversion, data):
+        (installedFileRequires,
+         installedUnresolvedFileRequires,
+         installedFileProvides) = data
+
+        fo = open(self._cachedir + '/file-requires.tmp', 'w')
+        fo.write("%s\n" % rpmdbversion)
+
         fo.write("%u\n" % len(installedFileRequires))
         for pkgtup in sorted(installedFileRequires):
             for var in pkgtup:
@@ -600,20 +615,8 @@ class RPMDBPackageSack(PackageSackBase):
                 for var in pkgtup:
                     fo.write("%s\n" % var)
         fo.close()
-
-    def _rename_file_requires(self, rpmdbversion):
-        if not os.path.exists(self._cachedir + '/file-requires.un.tmp'):
-            return
-
-        rfo = open(self._cachedir + '/file-requires.un.tmp')
-        wfo = open(self._cachedir + '/file-requires.tmp', 'w')
-        wfo.write("%s\n" % rpmdbversion)
-        wfo.write(rfo.read())
-        rfo.close()
-        wfo.close()
         os.rename(self._cachedir + '/file-requires.tmp',
                   self._cachedir + '/file-requires')
-        os.unlink(self._cachedir + '/file-requires.un.tmp')
 
     def _get_cached_simpleVersion_main(self):
         """ Return the cached string of the main rpmdbv. """
commit 01eaaaf046d79d33f764125a2e5b1ab09d6aaaa6
Author: James Antill <james at and.org>
Date:   Fri Nov 6 17:59:54 2009 -0500

    Add transactionResultVersion() and transactionCacheConflictPackages()

diff --git a/yum/__init__.py b/yum/__init__.py
index 56da0f4..4750a52 100644
--- a/yum/__init__.py
+++ b/yum/__init__.py
@@ -1107,6 +1107,13 @@ class YumBase(depsolve.Depsolve):
         if self.conf.history_record:
             self.history.beg(rpmdbv, using_pkgs, list(self.tsInfo))
 
+        #  Just before we update the transaction, update what we think the
+        # rpmdb will look like. This needs to be done before the run, so that if
+        # "something" happens and the rpmdb is different from what we think it
+        # will be we store what we thought, not what happened (so it'll be an
+        # invalid cache).
+        self.rpmdb.transactionResultVersion(self.tsInfo.futureRpmDBVersion())
+
         errors = self.ts.run(cb.callback, '')
         # ts.run() exit codes are, hmm, "creative": None means all ok, empty 
         # list means some errors happened in the transaction and non-empty 
@@ -1217,7 +1224,6 @@ class YumBase(depsolve.Depsolve):
             if resultobject is not None:
                 ret = resultobject.return_code
             self.history.end(self.rpmdb.simpleVersion(main_only=True)[0], ret)
-        self.rpmdb.returnConflictPackages() # Cache it for next time :o
         self.rpmdb.dropCachedData()
 
     def costExcludePackages(self):
diff --git a/yum/depsolve.py b/yum/depsolve.py
index 42bb196..c313f1b 100644
--- a/yum/depsolve.py
+++ b/yum/depsolve.py
@@ -1005,9 +1005,11 @@ class Depsolve(object):
 
     def _checkConflicts(self):
         ret = [ ]
+        cpkgs = []
         for po in self.rpmdb.returnConflictPackages():
             if self.tsInfo.getMembersWithState(po.pkgtup, output_states=TS_REMOVE_STATES):
                 continue
+            cpkgs.append(po)
             for conflict in po.returnPrco('conflicts'):
                 (r, f, v) = conflict
                 for conflicting_po in self.tsInfo.getNewProvides(r, f, v):
@@ -1017,16 +1019,20 @@ class Depsolve(object):
                                  conflicting_po) )
         for txmbr in self.tsInfo.getMembersWithState(output_states=TS_INSTALL_STATES):
             po = txmbr.po
+            done = False
             for conflict in txmbr.po.returnPrco('conflicts'):
+                if not done:
+                    cpkgs.append(txmbr.po)
+                    done = True
                 (r, f, v) = conflict
                 for conflicting_po in self.tsInfo.getProvides(r, f, v):
                     if conflicting_po.pkgtup[0] == po.pkgtup[0] and conflicting_po.pkgtup[2:] == po.pkgtup[2:]:
                         continue
                     ret.append( (po, self._prco_req_nfv2req(r, f, v),
                                  conflicting_po) )
+        self.rpmdb.transactionCacheConflictPackages(cpkgs)
         return ret
 
-
     def isPackageInstalled(self, pkgname):
         lst = self.tsInfo.matchNaevr(name = pkgname)
         for txmbr in lst:
diff --git a/yum/rpmsack.py b/yum/rpmsack.py
index 8a6d122..a16e57a 100644
--- a/yum/rpmsack.py
+++ b/yum/rpmsack.py
@@ -119,6 +119,8 @@ class RPMDBPackageSack(PackageSackBase):
         self._have_cached_rpmdbv_data = None
         self._use_cached_file_requires = True
         self._cached_conflicts_data = None
+        # Store the result of what happens, if a transaction completes.
+        self._trans_cache_store = {}
         self.ts = None
         self.releasever = releasever
         self.auto_close = False # this forces a self.ts.close() after
@@ -390,12 +392,11 @@ class RPMDBPackageSack(PackageSackBase):
             self._cached_conflicts_data = ret
         return self._cached_conflicts_data
 
-    def _write_conflicts(self, pkgs):
+    def _write_conflicts_new(self, pkgs, rpmdbv):
         if not self.__cache_rpmdb__:
             return
 
         conflicts_fname = self._cachedir + '/conflicts'
-        rpmdbv = self.simpleVersion(main_only=True)[0]
         fo = open(conflicts_fname + '.tmp', 'w')
         fo.write("%s\n" % rpmdbv)
         fo.write("%u\n" % len(pkgs))
@@ -405,6 +406,10 @@ class RPMDBPackageSack(PackageSackBase):
         fo.close()
         os.rename(conflicts_fname + '.tmp', conflicts_fname)
 
+    def _write_conflicts(self, pkgs):
+        rpmdbv = self.simpleVersion(main_only=True)[0]
+        self._write_conflicts_new(pkgs, rpmdbv)
+
     def _read_conflicts(self):
         if not self.__cache_rpmdb__:
             return None
@@ -442,6 +447,9 @@ class RPMDBPackageSack(PackageSackBase):
         self._cached_conflicts_data = ret
         return self._cached_conflicts_data
 
+    def transactionCacheConflictPackages(self, pkgs):
+        self._trans_cache_store['conflicts'] = pkgs
+
     def returnConflictPackages(self):
         """ Return a list of packages that have conflicts. """
         pkgs = self._read_conflicts()
@@ -451,6 +459,17 @@ class RPMDBPackageSack(PackageSackBase):
 
         return pkgs
 
+    def transactionResultVersion(self, rpmdbv):
+        """ We are going to do a transaction, and the parameter will be the
+            rpmdb version when we finish. The idea being we can update all
+            our rpmdb caches for that rpmdb version. """
+
+        if 'conflicts' in self._trans_cache_store:
+            pkgs = self._trans_cache_store['conflicts']
+            self._write_conflicts_new(pkgs, rpmdbv)
+
+        self._trans_cache_store = {}
+
     def returnGPGPubkeyPackages(self):
         """ Return packages of the gpg-pubkeys ... hacky. """
         ts = self.readOnlyTS()
commit b24ac152b0f753f587906ae85830fc8677cc4de7
Author: James Antill <james at and.org>
Date:   Fri Nov 6 11:31:01 2009 -0500

    Add tsInfo.futureRpmDBVersion, so we can do better quality rpmdb caching

diff --git a/yum/transactioninfo.py b/yum/transactioninfo.py
index c235e69..30a2625 100644
--- a/yum/transactioninfo.py
+++ b/yum/transactioninfo.py
@@ -26,7 +26,7 @@ to rpm.
 """
 
 from constants import *
-from packageSack import PackageSack
+from packageSack import PackageSack, PackageSackVersion
 from packages import YumInstalledPackage
 from sqlitesack import YumAvailablePackageSqlite
 import Errors
@@ -564,6 +564,42 @@ class TransactionData:
         result.update(self.getNewRequires(name, flag, version))
         return result
 
+    def futureRpmDBVersion(self):
+        """ Return a simple version for the future rpmdb. Works like
+            rpmdb.simpleVersion(main_only=True)[0], but for the state the rpmdb
+            will be in after the transaction. """
+        pkgs = self.rpmdb.returnPackages()
+        _reinstalled_pkgtups = {}
+        for txmbr in self.getMembersWithState(None, TS_INSTALL_STATES):
+            # reinstalls have to use their "new" checksum data, in case it's
+            # different.
+            if hasattr(txmbr, 'reinstall') and txmbr.reinstall:
+                _reinstalled_pkgtups[txmbr.po.pkgtup] = txmbr.po
+            pkgs.append(txmbr.po)
+
+        main = PackageSackVersion()
+        for pkg in sorted(pkgs):
+            if pkg.repoid != 'installed':
+                # Paste from PackageSackBase.simpleVersion()
+                csum = pkg.returnIdSum()
+                main.update(pkg, csum)
+                continue
+
+            # Installed pkg, see if it's about to die
+            if self.getMembersWithState(pkg.pkgtup, TS_REMOVE_STATES):
+                continue
+            # ...or die and be risen again (Zombie!)
+            if pkg.pkgtup in _reinstalled_pkgtups:
+                continue
+
+            # Paste from rpmdb.simpleVersion()
+            ydbi = pkg.yumdb_info
+            csum = None
+            if 'checksum_type' in ydbi and 'checksum_data' in ydbi:
+                csum = (ydbi.checksum_type, ydbi.checksum_data)
+            main.update(pkg, csum)
+        return main
+
 class ConditionalTransactionData(TransactionData):
     """A transaction data implementing conditional package addition"""
     def __init__(self):
@@ -621,6 +657,7 @@ class SortableTransactionData(TransactionData):
         self._sorted.reverse()
         return self._sorted
 
+
 class TransactionMember:
     """Class to describe a Transaction Member (a pkg to be installed/
        updated/erased)."""
commit 13b4c6e0a56061ff4682e0e53b55cddaec122771
Author: James Antill <james at and.org>
Date:   Wed Nov 4 15:54:36 2009 -0500

    Cache "conflict packages", store the pkgtups of the packages with conflicts

diff --git a/yum/__init__.py b/yum/__init__.py
index 1819ada..56da0f4 100644
--- a/yum/__init__.py
+++ b/yum/__init__.py
@@ -1217,6 +1217,7 @@ class YumBase(depsolve.Depsolve):
             if resultobject is not None:
                 ret = resultobject.return_code
             self.history.end(self.rpmdb.simpleVersion(main_only=True)[0], ret)
+        self.rpmdb.returnConflictPackages() # Cache it for next time :o
         self.rpmdb.dropCachedData()
 
     def costExcludePackages(self):
diff --git a/yum/depsolve.py b/yum/depsolve.py
index 4b35cec..42bb196 100644
--- a/yum/depsolve.py
+++ b/yum/depsolve.py
@@ -1003,10 +1003,9 @@ class Depsolve(object):
 
         return ret
 
-
     def _checkConflicts(self):
         ret = [ ]
-        for po in self.rpmdb.returnPackages():
+        for po in self.rpmdb.returnConflictPackages():
             if self.tsInfo.getMembersWithState(po.pkgtup, output_states=TS_REMOVE_STATES):
                 continue
             for conflict in po.returnPrco('conflicts'):
diff --git a/yum/rpmsack.py b/yum/rpmsack.py
index d8c1858..8a6d122 100644
--- a/yum/rpmsack.py
+++ b/yum/rpmsack.py
@@ -118,6 +118,7 @@ class RPMDBPackageSack(PackageSackBase):
         self._cachedir = cachedir + "/rpmdb-cache/"
         self._have_cached_rpmdbv_data = None
         self._use_cached_file_requires = True
+        self._cached_conflicts_data = None
         self.ts = None
         self.releasever = releasever
         self.auto_close = False # this forces a self.ts.close() after
@@ -165,6 +166,7 @@ class RPMDBPackageSack(PackageSackBase):
             'obsoletes' : { },
             }
         self._have_cached_rpmdbv_data = None
+        self._cached_conflicts_data = None
 
     def readOnlyTS(self):
         if not self.ts:
@@ -379,6 +381,76 @@ class RPMDBPackageSack(PackageSackBase):
             pkgobjlist = pkgobjlist[0] + pkgobjlist[1]
         return pkgobjlist
 
+    def _uncached_returnConflictPackages(self):
+        if self._cached_conflicts_data is None:
+            ret = []
+            for pkg in self.returnPackages():
+                if len(pkg.conflicts):
+                    ret.append(pkg)
+            self._cached_conflicts_data = ret
+        return self._cached_conflicts_data
+
+    def _write_conflicts(self, pkgs):
+        if not self.__cache_rpmdb__:
+            return
+
+        conflicts_fname = self._cachedir + '/conflicts'
+        rpmdbv = self.simpleVersion(main_only=True)[0]
+        fo = open(conflicts_fname + '.tmp', 'w')
+        fo.write("%s\n" % rpmdbv)
+        fo.write("%u\n" % len(pkgs))
+        for pkg in sorted(pkgs):
+            for var in pkg.pkgtup:
+                fo.write("%s\n" % var)
+        fo.close()
+        os.rename(conflicts_fname + '.tmp', conflicts_fname)
+
+    def _read_conflicts(self):
+        if not self.__cache_rpmdb__:
+            return None
+
+        def _read_str(fo):
+            return fo.readline()[:-1]
+
+        conflict_fname = self._cachedir + '/conflicts'
+        if not os.path.exists(conflict_fname):
+            return None
+
+        fo = open(conflict_fname)
+        frpmdbv = fo.readline()
+        rpmdbv = self.simpleVersion(main_only=True)[0]
+        if not frpmdbv or rpmdbv != frpmdbv[:-1]:
+            return None
+
+        ret = []
+        try:
+            # Read the conflicts...
+            pkgtups_num = int(_read_str(fo))
+            while pkgtups_num > 0:
+                pkgtups_num -= 1
+
+                # n, a, e, v, r
+                pkgtup = (_read_str(fo), _read_str(fo),
+                          _read_str(fo), _read_str(fo), _read_str(fo))
+                int(pkgtup[2]) # Check epoch is valid
+                ret.extend(self.searchPkgTuple(pkgtup))
+            if fo.readline() != '': # Should be EOF
+                return None
+        except ValueError:
+            return None
+
+        self._cached_conflicts_data = ret
+        return self._cached_conflicts_data
+
+    def returnConflictPackages(self):
+        """ Return a list of packages that have conflicts. """
+        pkgs = self._read_conflicts()
+        if pkgs is None:
+            pkgs = self._uncached_returnConflictPackages()
+            self._write_conflicts(pkgs)
+
+        return pkgs
+
     def returnGPGPubkeyPackages(self):
         """ Return packages of the gpg-pubkeys ... hacky. """
         ts = self.readOnlyTS()
commit aea5c2cc7ddb88252f5b5aae41940d8b2d1339d4
Author: James Antill <james at and.org>
Date:   Wed Nov 4 14:19:18 2009 -0500

    Cache checkFileRequires data

diff --git a/yum/depsolve.py b/yum/depsolve.py
index 7871e98..4b35cec 100644
--- a/yum/depsolve.py
+++ b/yum/depsolve.py
@@ -923,26 +923,21 @@ class Depsolve(object):
 
         # generate list of file requirement in rpmdb
         if self.installedFileRequires is None:
-            self.installedFileRequires = {}
-            self.installedUnresolvedFileRequires = set()
-            resolved = set()
-            for pkg in self.rpmdb.returnPackages():
-                for name, flag, evr in pkg.requires:
-                    if not name.startswith('/'):
-                        continue
-                    self.installedFileRequires.setdefault(pkg, []).append(name)
-                    if name not in resolved:
-                        dep = self.rpmdb.getProvides(name, flag, evr)
-                        resolved.add(name)
-                        if not dep:
-                            self.installedUnresolvedFileRequires.add(name)
+            self.installedFileRequires, \
+              self.installedUnresolvedFileRequires, \
+              self.installedFileProviders = self.rpmdb._get_file_requires()
 
         # get file requirements from packages not deleted
-        for po, files in self.installedFileRequires.iteritems():
-            if not self._tsInfo.getMembersWithState(po.pkgtup, output_states=TS_REMOVE_STATES):
+        todel = []
+        for pkgtup, files in self.installedFileRequires.iteritems():
+            if self._tsInfo.getMembersWithState(pkgtup, output_states=TS_REMOVE_STATES):
+                todel.append(pkgtup)
+            else:
                 fileRequires.update(files)
                 for filename in files:
-                    reverselookup.setdefault(filename, []).append(po)
+                    reverselookup.setdefault(filename, []).append(pkgtup)
+        for pkgtup in todel:
+            del self.installedFileRequires[pkgtup]
 
         fileRequires -= self.installedUnresolvedFileRequires
 
@@ -950,6 +945,8 @@ class Depsolve(object):
         for txmbr in self._tsInfo.getMembersWithState(output_states=TS_INSTALL_STATES):
             for name, flag, evr in txmbr.po.requires:
                 if name.startswith('/'):
+                    pt = txmbr.po.pkgtup
+                    self.installedFileRequires.setdefault(pt, []).append(name)
                     # check if file requires was already unresolved in update
                     if name in self.installedUnresolvedFileRequires:
                         already_broken = False
@@ -960,13 +957,49 @@ class Depsolve(object):
                         if already_broken:
                             continue
                     fileRequires.add(name)
-                    reverselookup.setdefault(name, []).append(txmbr.po)
+                    reverselookup.setdefault(name, []).append(txmbr.po.pkgtup)
+
+        todel = []
+        for fname in self.installedFileProviders:
+            niFP_fname = []
+            for pkgtup in self.installedFileProviders[fname]:
+                if self._tsInfo.getMembersWithState(pkgtup, output_states=TS_REMOVE_STATES):
+                    continue
+                niFP_fname.append(pkgtup)
+
+            if not niFP_fname:
+                todel.append(fname)
+                continue
+
+            self.installedFileProviders[fname] = niFP_fname
+        for fname in todel:
+            del self.installedFileProviders[fname]
 
         # check the file requires
         for filename in fileRequires:
-            if not self.tsInfo.getOldProvides(filename) and not self.tsInfo.getNewProvides(filename):
-                for po in reverselookup[filename]:
-                    ret.append( (po, (filename, 0, '')) )
+            nprov = self.tsInfo.getNewProvides(filename)
+            if nprov:
+                pkgtups = [po.pkgtup for po in nprov]
+                self.installedFileProviders[filename] = pkgtups
+                continue
+
+            if filename in self.installedFileProviders:
+                continue 
+
+            oprov = self.tsInfo.getOldProvides(filename)
+            if oprov:
+                pkgtups = [po.pkgtup for po in oprov]
+                self.installedFileProviders[filename] = pkgtups
+                continue
+
+            for pkgtup in reverselookup[filename]:
+                po = self.getInstalledPackageObject(pkgtup)
+                ret.append( (po, (filename, 0, '')) )
+
+        self.rpmdb._write_file_requires(self.installedFileRequires, 
+                                        self.installedUnresolvedFileRequires,
+                                        self.installedFileProviders,
+                                        ret)
 
         return ret
 
diff --git a/yum/rpmsack.py b/yum/rpmsack.py
index 0a7456c..d8c1858 100644
--- a/yum/rpmsack.py
+++ b/yum/rpmsack.py
@@ -117,6 +117,7 @@ class RPMDBPackageSack(PackageSackBase):
             cachedir = misc.getCacheDir()
         self._cachedir = cachedir + "/rpmdb-cache/"
         self._have_cached_rpmdbv_data = None
+        self._use_cached_file_requires = True
         self.ts = None
         self.releasever = releasever
         self.auto_close = False # this forces a self.ts.close() after
@@ -388,6 +389,141 @@ class RPMDBPackageSack(PackageSackBase):
             ret.append(self._makePackageObject(hdr, mi.instance()))
         return ret
 
+    def _read_file_requires(self):
+        def _read_str(fo):
+            return fo.readline()[:-1]
+
+        assert self.__cache_rpmdb__
+        if not os.path.exists(self._cachedir + '/file-requires'):
+            return None, None
+
+        rpmdbv = self.simpleVersion(main_only=True)[0]
+        fo = open(self._cachedir + '/file-requires')
+        frpmdbv = fo.readline()
+        if not frpmdbv or rpmdbv != frpmdbv[:-1]:
+            return None, None
+
+        iFR = {}
+        iFP = {}
+        try:
+            # Read the requires...
+            pkgtups_num = int(_read_str(fo))
+            while pkgtups_num > 0:
+                pkgtups_num -= 1
+
+                # n, a, e, v, r
+                pkgtup = (_read_str(fo), _read_str(fo),
+                          _read_str(fo), _read_str(fo), _read_str(fo))
+                int(pkgtup[2]) # Check epoch is valid
+
+                files_num = int(_read_str(fo))
+                while files_num > 0:
+                    files_num -= 1
+
+                    fname = _read_str(fo)
+
+                    iFR.setdefault(pkgtup, []).append(fname)
+
+            # Read the provides...
+            files_num = int(_read_str(fo))
+            while files_num > 0:
+                files_num -= 1
+                fname = _read_str(fo)
+                pkgtups_num = int(_read_str(fo))
+                while pkgtups_num > 0:
+                    pkgtups_num -= 1
+
+                    # n, a, e, v, r
+                    pkgtup = (_read_str(fo), _read_str(fo),
+                              _read_str(fo), _read_str(fo), _read_str(fo))
+                    int(pkgtup[2]) # Check epoch is valid
+
+                    iFP.setdefault(fname, []).append(pkgtup)
+
+            if fo.readline() != '': # Should be EOF
+                return None, None
+        except ValueError:
+            return None, None
+
+        return iFR, iFP
+
+    def _get_file_requires(self):
+        print "JDBG:", 'BEG: _all_file_requires', self._use_cached_file_requires
+        if self.__cache_rpmdb__ and self._use_cached_file_requires:
+            iFR, iFP = self._read_file_requires()
+            if iFR is not None:
+                print "JDBG:", 'CACHE'
+                return iFR, set(), iFP
+
+        print "JDBG:", '** no CACHE'
+        installedFileRequires = {}
+        installedUnresolvedFileRequires = set()
+        resolved = set()
+        for pkg in self.returnPackages():
+            for name, flag, evr in pkg.requires:
+                if not name.startswith('/'):
+                    continue
+                installedFileRequires.setdefault(pkg.pkgtup, []).append(name)
+                if name not in resolved:
+                    dep = self.getProvides(name, flag, evr)
+                    resolved.add(name)
+                    if not dep:
+                        installedUnresolvedFileRequires.add(name)
+
+        if self.__cache_rpmdb__:
+            self._write_file_requires(installedFileRequires,
+                                      installedUnresolvedFileRequires,
+                                      {}, [])
+            self._rename_file_requires(self.simpleVersion(main_only=True)[0])
+        return installedFileRequires, installedUnresolvedFileRequires, {}
+
+    def _write_file_requires(self, installedFileRequires,
+                             installedUnresolvedFileRequires,
+                             installedFileProvides,
+                             problems):
+        if not self.__cache_rpmdb__:
+            return
+
+        if not self._use_cached_file_requires:
+            return
+        if installedUnresolvedFileRequires or problems:
+            return
+        # FIXME: ... real tmp. file
+        fo = open(self._cachedir + '/file-requires.un.tmp', 'w')
+        fo.write("%u\n" % len(installedFileRequires))
+        for pkgtup in sorted(installedFileRequires):
+            for var in pkgtup:
+                fo.write("%s\n" % var)
+            filenames = set(installedFileRequires[pkgtup])
+            fo.write("%u\n" % len(filenames))
+            for fname in sorted(filenames):
+                fo.write("%s\n" % fname)
+
+        fo.write("%u\n" % len(installedFileProvides))
+        for fname in sorted(installedFileProvides):
+            fo.write("%s\n" % fname)
+
+            pkgtups = set(installedFileProvides[fname])
+            fo.write("%u\n" % len(pkgtups))
+            for pkgtup in sorted(pkgtups):
+                for var in pkgtup:
+                    fo.write("%s\n" % var)
+        fo.close()
+
+    def _rename_file_requires(self, rpmdbversion):
+        if not os.path.exists(self._cachedir + '/file-requires.un.tmp'):
+            return
+
+        rfo = open(self._cachedir + '/file-requires.un.tmp')
+        wfo = open(self._cachedir + '/file-requires.tmp', 'w')
+        wfo.write("%s\n" % rpmdbversion)
+        wfo.write(rfo.read())
+        rfo.close()
+        wfo.close()
+        os.rename(self._cachedir + '/file-requires.tmp',
+                  self._cachedir + '/file-requires')
+        os.unlink(self._cachedir + '/file-requires.un.tmp')
+
     def _get_cached_simpleVersion_main(self):
         """ Return the cached string of the main rpmdbv. """
         if self._have_cached_rpmdbv_data is not None:
commit 77ae40fabb80bfb1ea527621827cbec1a98b8a60
Author: James Antill <james at and.org>
Date:   Wed Nov 4 12:50:00 2009 -0500

    Add simpleVersion() results to the rpmdb-cache

diff --git a/yum/rpmsack.py b/yum/rpmsack.py
index 9678553..0a7456c 100644
--- a/yum/rpmsack.py
+++ b/yum/rpmsack.py
@@ -100,6 +100,9 @@ class RPMDBPackageSack(PackageSackBase):
                            rpm.RPMTAG_OBSOLETEFLAGS)
             }
 
+    # Do we want to cache rpmdb data in a file, for later use?
+    __cache_rpmdb__ = True
+
     def __init__(self, root='/', releasever=None, cachedir=None):
         self.root = root
         self._idx2pkg = {}
@@ -113,6 +116,7 @@ class RPMDBPackageSack(PackageSackBase):
         if cachedir is None:
             cachedir = misc.getCacheDir()
         self._cachedir = cachedir + "/rpmdb-cache/"
+        self._have_cached_rpmdbv_data = None
         self.ts = None
         self.releasever = releasever
         self.auto_close = False # this forces a self.ts.close() after
@@ -159,6 +163,7 @@ class RPMDBPackageSack(PackageSackBase):
             'conflicts' : { },
             'obsoletes' : { },
             }
+        self._have_cached_rpmdbv_data = None
 
     def readOnlyTS(self):
         if not self.ts:
@@ -383,6 +388,51 @@ class RPMDBPackageSack(PackageSackBase):
             ret.append(self._makePackageObject(hdr, mi.instance()))
         return ret
 
+    def _get_cached_simpleVersion_main(self):
+        """ Return the cached string of the main rpmdbv. """
+        if self._have_cached_rpmdbv_data is not None:
+            return self._have_cached_rpmdbv_data
+
+        if not self.__cache_rpmdb__:
+            return None
+
+        #  This test is "obvious" and the only thing to come out of:
+        # http://lists.rpm.org/pipermail/rpm-maint/2007-November/001719.html
+        # ...if anything gets implemented, we should change.
+        rpmdbvfname = self._cachedir + "/version"
+        rpmdbfname  = "/var/lib/rpm/Packages"
+
+        if os.path.exists(rpmdbvfname) and os.path.exists(rpmdbfname):
+            # See if rpmdb has "changed" ...
+            nmtime = os.path.getmtime(rpmdbvfname)
+            omtime = os.path.getmtime(rpmdbfname)
+            if omtime <= nmtime:
+                rpmdbv = open(rpmdbvfname).readline()[:-1]
+                self._have_cached_rpmdbv_data  = rpmdbv
+        return self._have_cached_rpmdbv_data
+
+    def _put_cached_simpleVersion_main(self, rpmdbv):
+        self._have_cached_rpmdbv_data  = str(rpmdbv)
+
+        if not self.__cache_rpmdb__:
+            return
+
+        rpmdbvfname = self._cachedir + "/version"
+        if not os.access(self._cachedir, os.W_OK):
+            if os.path.exists(self._cachedir):
+                return
+
+            try:
+                os.makedirs(self._cachedir)
+            except (IOError, OSError), e:
+                return
+
+        fo = open(rpmdbvfname + ".tmp", "w")
+        fo.write(self._have_cached_rpmdbv_data)
+        fo.write('\n')
+        fo.close()
+        os.rename(rpmdbvfname + ".tmp", rpmdbvfname)
+
     def simpleVersion(self, main_only=False, groups={}):
         """ Return a simple version for all installed packages. """
         def _up_revs(irepos, repoid, rev, pkg, csum):
@@ -393,6 +443,11 @@ class RPMDBPackageSack(PackageSackBase):
                 rpsv = irevs.setdefault(rev, PackageSackVersion())
                 rpsv.update(pkg, csum)
 
+        if main_only and not groups:
+            rpmdbv = self._get_cached_simpleVersion_main()
+            if rpmdbv is not None:
+                return [rpmdbv, {}]
+
         main = PackageSackVersion()
         irepos = {}
         main_grps = {}
@@ -426,6 +481,9 @@ class RPMDBPackageSack(PackageSackBase):
                 if pkg.name in groups[group]:
                     _up_revs(irepos_grps[group], repoid, rev, pkg, csum)
 
+        if self._have_cached_rpmdbv_data is None:
+            self._put_cached_simpleVersion_main(main)
+
         if groups:
             return [main, irepos, main_grps, irepos_grps]
         return [main, irepos]
commit 3fbae4e9236738f341d3146740738d56077cf06a
Author: James Antill <james at and.org>
Date:   Wed Nov 4 12:29:40 2009 -0500

    Add cachedir to rpmdb, so we can get calculated data quickly

diff --git a/yum/__init__.py b/yum/__init__.py
index 92ca2b2..1819ada 100644
--- a/yum/__init__.py
+++ b/yum/__init__.py
@@ -469,7 +469,8 @@ class YumBase(depsolve.Depsolve):
             self.verbose_logger.log(logginglevels.DEBUG_4,
                                     _('Reading Local RPMDB'))
             self._rpmdb = rpmsack.RPMDBPackageSack(root=self.conf.installroot,
-                                                   releasever=self.yumvar['releasever'])
+                                                   releasever=self.yumvar['releasever'],
+                                                   cachedir=self.conf.cachedir)
             self.verbose_logger.debug('rpmdb time: %0.3f' % (time.time() - rpmdb_st))
         return self._rpmdb
 
diff --git a/yum/rpmsack.py b/yum/rpmsack.py
index 9b0cd29..9678553 100644
--- a/yum/rpmsack.py
+++ b/yum/rpmsack.py
@@ -100,7 +100,7 @@ class RPMDBPackageSack(PackageSackBase):
                            rpm.RPMTAG_OBSOLETEFLAGS)
             }
 
-    def __init__(self, root='/', releasever=None):
+    def __init__(self, root='/', releasever=None, cachedir=None):
         self.root = root
         self._idx2pkg = {}
         self._name2pkg = {}
@@ -110,6 +110,9 @@ class RPMDBPackageSack(PackageSackBase):
         self._get_pro_cache = {}
         self._get_req_cache  = {}
         self._loaded_gpg_keys = False
+        if cachedir is None:
+            cachedir = misc.getCacheDir()
+        self._cachedir = cachedir + "/rpmdb-cache/"
         self.ts = None
         self.releasever = releasever
         self.auto_close = False # this forces a self.ts.close() after
commit 45df3ce6e9f83addfa7a8e0e0c72570a73a26e49
Author: James Antill <james at and.org>
Date:   Thu Mar 12 15:51:09 2009 -0400

    Use a tempfile instead of a pipe for scriplet errors, fixes BZ 465766

diff --git a/yum/rpmtrans.py b/yum/rpmtrans.py
index f20b1b8..cf0efeb 100644
--- a/yum/rpmtrans.py
+++ b/yum/rpmtrans.py
@@ -26,6 +26,7 @@ import sys
 from yum.constants import *
 from yum import _
 import misc
+import tempfile
 
 class NoOutputCallBack:
     def __init__(self):
@@ -205,11 +206,10 @@ class RPMTransaction:
 
     def _setupOutputLogging(self):
         # UGLY... set up the transaction to record output from scriptlets
-        (r, w) = os.pipe()
-        # need fd objects, and read should be non-blocking
-        self._readpipe = os.fdopen(r, 'r')
-        self._fdSetNonblock(self._readpipe.fileno())
-        self._writepipe = os.fdopen(w, 'w')
+        io_r = tempfile.TemporaryFile()
+        w = os.dup(io_r.fileno())
+        self._readpipe = io_r
+        self._writepipe = os.fdopen(w, 'w+b')
         self.base.ts.scriptFd = self._writepipe.fileno()
         rpm.setVerbosity(rpm.RPMLOG_INFO)
         rpm.setLogFile(self._writepipe)


More information about the Yum-commits mailing list