Blob Blame History Raw
diff --git a/Makefile b/Makefile
index 740b616..fc04853 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-SUBDIRS = rpmUtils yum etc docs po
+SUBDIRS = rpmUtils yum yum-cron etc docs po
 PYFILES = $(wildcard *.py)
 PYLINT_MODULES =  *.py yum rpmUtils
 PYLINT_IGNORE = oldUtils.py
@@ -38,6 +38,10 @@ install:
 
 	for d in $(SUBDIRS); do make PYTHON=$(PYTHON) DESTDIR=`cd $(DESTDIR); pwd` -C $$d install; [ $$? = 0 ] || exit 1; done
 
+apidocs:
+	make -C docs/sphinxdocs html
+	echo "Docs are in: docs/sphinxdocs/_build/html/*"
+
 .PHONY: docs test
 
 DOCS = yum rpmUtils callback.py yumcommands.py shell.py output.py cli.py utils.py\
diff --git a/callback.py b/callback.py
index 2f6154e..2e5a052 100644
--- a/callback.py
+++ b/callback.py
@@ -27,10 +27,7 @@ from yum.constants import *
 
 
 class RPMInstallCallback:
-
-    """
-    Yum command line callback class for callbacks from the RPM library.
-    """
+    """Yum command line callback class for callbacks from the RPM library."""
 
     def __init__(self, output=1):
         self.output = output
@@ -105,6 +102,21 @@ class RPMInstallCallback:
         return pkg
 
     def callback(self, what, bytes, total, h, user):
+        """Handle callbacks from the RPM library.
+
+        :param what: number identifying the type of callback
+        :param bytes: the number of bytes associated with the
+           callback; the exact meaning depends on the type of 
+           the callback.  For example, for a RPMCALLBACK_INST_PROGRESS
+           callback, bytes will represent the current amount of work done
+        :param total: the total amount of work associated with the
+           callback; the exact meaning depends on the type of the
+           callback. For example, *total* may represent the total
+           number of transactions in a transaction set
+        :param h: a package object or string identifying the package
+           involved in the callback
+        :param user: unused
+        """
         if what == rpm.RPMCALLBACK_TRANS_START:
             if bytes == 6:
                 self.total_actions = total
diff --git a/cli.py b/cli.py
old mode 100644
new mode 100755
index 6056d38..3a68616
--- a/cli.py
+++ b/cli.py
@@ -25,7 +25,7 @@ import sys
 import time
 import random
 import logging
-from optparse import OptionParser,OptionGroup
+from optparse import OptionParser,OptionGroup,SUPPRESS_HELP
 import rpm
 
 from weakref import proxy as weakref
@@ -43,7 +43,7 @@ from yum.rpmtrans import RPMTransaction
 import signal
 import yumcommands
 
-from yum.i18n import to_unicode, to_utf8
+from yum.i18n import to_unicode, to_utf8, exception2msg
 
 #  This is for yum-utils/yumdownloader in RHEL-5, where it isn't importing this
 # directly but did do "from cli import *", and we did have this in 3.2.22. I
@@ -51,23 +51,24 @@ from yum.i18n import to_unicode, to_utf8
 from yum.packages import parsePackages
 
 def sigquit(signum, frame):
-    """ SIGQUIT handler for the yum cli. """
+    """SIGQUIT handler for the yum cli.  This function will print an
+    error message and exit the program.
+    
+    :param signum: unused
+    :param frame: unused
+    """
     print >> sys.stderr, "Quit signal sent - exiting immediately"
     sys.exit(1)
 
 class CliError(yum.Errors.YumBaseError):
-
-    """
-    Command line interface related Exception.
-    """
+    """Command line interface related Exception."""
 
     def __init__(self, args=''):
         yum.Errors.YumBaseError.__init__(self)
         self.args = args
 
 class YumBaseCli(yum.YumBase, output.YumOutput):
-    """This is the base class for yum cli.
-       Inherits from yum.YumBase and output.YumOutput """
+    """This is the base class for yum cli."""
        
     def __init__(self):
         # handle sigquit early on
@@ -106,15 +107,24 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
         self.registerCommand(yumcommands.LoadTransactionCommand())
 
     def registerCommand(self, command):
+        """Register a :class:`yumcommands.YumCommand` so that it can be called by
+        any of the names returned by its
+        :func:`yumcommands.YumCommand.getNames` method.
+        
+        :param command: the :class:`yumcommands.YumCommand` to register
+        """
         for name in command.getNames():
             if name in self.yum_cli_commands:
                 raise yum.Errors.ConfigError(_('Command "%s" already defined') % name)
             self.yum_cli_commands[name] = command
             
     def doRepoSetup(self, thisrepo=None, dosack=1):
-        """grabs the repomd.xml for each enabled repository 
-           and sets up the basics of the repository"""
-        
+        """Grab the repomd.xml for each enabled and set up the basics
+        of the repository.
+
+        :param thisrepo: the repository to set up
+        :param dosack: whether to get the repo sack
+        """
         if self._repos and thisrepo is None:
             return self._repos
             
@@ -163,8 +173,18 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
         mainopts = yum.misc.GenericHolder()
         mainopts.items = []
 
+        bad_setopt_tm = []
+        bad_setopt_ne = []
+
         for item in setopts:
-            k,v = item.split('=')
+            vals = item.split('=')
+            if len(vals) > 2:
+                bad_setopt_tm.append(item)
+                continue
+            if len(vals) < 2:
+                bad_setopt_ne.append(item)
+                continue
+            k,v = vals
             period = k.find('.') 
             if period != -1:
                 repo = k[:period]
@@ -180,13 +200,15 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
         
         self.main_setopts = mainopts
         self.repo_setopts = repoopts
-        
+
+        return bad_setopt_tm, bad_setopt_ne
         
     def getOptionsConfig(self, args):
-        """parses command line arguments, takes cli args:
-        sets up self.conf and self.cmds as well as logger objects 
-        in base instance"""
-       
+        """Parse command line arguments, and set up :attr:`self.conf` and
+        :attr:`self.cmds`, as well as logger objects in base instance.
+
+        :param args: a list of command line arguments
+        """
         self.optparser = YumOptionParser(base=self, usage=self._makeUsage())
         
         # Parse only command line options that affect basic yum setup
@@ -199,7 +221,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
             opts.verbose = False
 
         # go through all the setopts and set the global ones
-        self._parseSetOpts(opts.setopts)
+        bad_setopt_tm, bad_setopt_ne = self._parseSetOpts(opts.setopts)
         
         if self.main_setopts:
             for opt in self.main_setopts.items:
@@ -229,6 +251,12 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
             pc.releasever = opts.releasever
             self.conf
             
+            for item in  bad_setopt_tm:
+                msg = "Setopt argument has multiple values: %s"
+                self.logger.warning(msg % item)
+            for item in  bad_setopt_ne:
+                msg = "Setopt argument has no value: %s"
+                self.logger.warning(msg % item)
             # now set  all the non-first-start opts from main from our setopts
             if self.main_setopts:
                 for opt in self.main_setopts.items:
@@ -318,9 +346,11 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
         time.sleep(sleeptime)
         
     def parseCommands(self):
-        """reads self.cmds and parses them out to make sure that the requested 
-        base command + argument makes any sense at all""" 
-
+        """Read :attr:`self.cmds` and parse them out to make sure that
+        the requested base command and argument makes any sense at
+        all.  This function will also set :attr:`self.basecmd` and
+        :attr:`self.extcmds`.
+        """
         self.verbose_logger.debug('Yum Version: %s', yum.__version__)
         self.verbose_logger.log(yum.logginglevels.DEBUG_4,
                                 'COMMAND: %s', self.cmdstring)
@@ -365,7 +395,11 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
         self.history.write_addon_data('shell-cmds', data)
 
     def doShell(self):
-        """do a shell-like interface for yum commands"""
+        """Run a shell-like interface for yum commands.
+
+        :return: a tuple containing the shell result number, and the
+           shell result messages
+        """
 
         yumshell = shell.YumShell(base=self)
 
@@ -382,8 +416,12 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
         return yumshell.result, yumshell.resultmsgs
 
     def errorSummary(self, errstring):
-        """ parse the error string for 'interesting' errors which can
-            be grouped, such as disk space issues """
+        """Parse the error string for 'interesting' errors which can
+        be grouped, such as disk space issues.
+
+        :param errstring: the error string
+        :return: a string containing a summary of the errors
+        """
         summary = ''
         # do disk space report first
         p = re.compile('needs (\d+)MB on the (\S+) filesystem')
@@ -408,16 +446,17 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
 
 
     def doCommands(self):
-        """
-        Calls the base command passes the extended commands/args out to be
-        parsed (most notably package globs).
-        
-        Returns a numeric result code and an optional string
-           - 0 = we're done, exit
-           - 1 = we've errored, exit with error string
-           - 2 = we've got work yet to do, onto the next stage
-        """
-        
+        """Call the base command, and pass it the extended commands or
+           arguments.
+
+        :return: (exit_code, [ errors ])
+
+        exit_code is::
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+            2 = we've got work yet to do, onto the next stage
+        """       
         # at this point we know the args are valid - we don't know their meaning
         # but we know we're not being sent garbage
         
@@ -435,14 +474,18 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
             try:
                 self._getTs(needTsRemove)
             except yum.Errors.YumBaseError, e:
-                return 1, [str(e)]
+                return 1, [exception2msg(e)]
 
         return self.yum_cli_commands[self.basecmd].doCommand(self, self.basecmd, self.extcmds)
 
     def doTransaction(self):
-        """takes care of package downloading, checking, user confirmation and actually
-           RUNNING the transaction"""
-    
+        """Take care of package downloading, checking, user
+        confirmation and actually running the transaction.
+
+        :return: a numeric return code, and optionally a list of
+           errors.  A negative return code indicates that errors
+           occurred in the pre-transaction checks
+        """
         # just make sure there's not, well, nothing to do
         if len(self.tsInfo) == 0:
             self.verbose_logger.info(_('Trying to run the transaction but nothing to do. Exiting.'))
@@ -453,7 +496,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
         lsts = self.listTransaction()
         if self.verbose_logger.isEnabledFor(yum.logginglevels.INFO_1):
             self.verbose_logger.log(yum.logginglevels.INFO_1, lsts)
-        elif not self.conf.assumeyes:
+        elif self.conf.assumeno or not self.conf.assumeyes:
             #  If we are in quiet, and assumeyes isn't on we want to output
             # at least the transaction list anyway.
             self.logger.warn(lsts)
@@ -491,7 +534,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
         
         # confirm with user
         if self._promptWanted():
-            if not self.userconfirm():
+            if self.conf.assumeno or not self.userconfirm():
                 self.verbose_logger.info(_('Exiting on user Command'))
                 return -1
 
@@ -609,12 +652,14 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
         return resultobject.return_code
         
     def gpgsigcheck(self, pkgs):
-        '''Perform GPG signature verification on the given packages, installing
-        keys if possible
+        """Perform GPG signature verification on the given packages,
+        installing keys if possible.
 
-        Returns non-zero if execution should stop (user abort).
-        Will raise YumBaseError if there's a problem
-        '''
+        :param pkgs: a list of package objects to verify the GPG
+           signatures of
+        :return: non-zero if execution should stop due to an error
+        :raises: Will raise :class:`YumBaseError` if there's a problem
+        """
         for po in pkgs:
             result, errmsg = self.sigCheckPkg(po)
 
@@ -623,7 +668,8 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
                 continue            
 
             elif result == 1:
-                if not sys.stdin.isatty() and not self.conf.assumeyes:
+                ay = self.conf.assumeyes and not self.conf.assumeno
+                if not sys.stdin.isatty() and not ay:
                     raise yum.Errors.YumBaseError, \
                             _('Refusing to automatically import keys when running ' \
                             'unattended.\nUse "-y" to override.')
@@ -691,12 +737,59 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
                                      ", ".join(matches))
             self.verbose_logger.log(yum.logginglevels.INFO_2, msg)
 
+    def _install_upgraded_requires(self, txmbrs):
+        """Go through the given txmbrs, and for any to be installed packages
+        look for their installed deps. and try to upgrade them, if the
+        configuration is set. Returning any new transaction members to be
+        isntalled.
+
+        :param txmbrs: a list of :class:`yum.transactioninfo.TransactionMember` objects
+        :return: a list of :class:`yum.transactioninfo.TransactionMember` objects
+        """
+
+        if not self.conf.upgrade_requirements_on_install:
+            return []
+
+        ret = []
+        done = set()
+        def _pkg2ups(pkg, reqpo=None):
+            if pkg.name in done:
+                return []
+            if reqpo is None:
+                reqpo = pkg
+
+            done.add(pkg.name)
+
+            uret = []
+            for req in pkg.requires:
+                for npkg in self.returnInstalledPackagesByDep(req):
+                    uret += self.update(pattern=npkg.name, requiringPo=reqpo)
+                    uret += _pkg2ups(npkg, reqpo=reqpo)
+            return uret
+
+        for txmbr in txmbrs:
+            print "JDBG:", txmbr
+            ret += _pkg2ups(txmbr.po)
+
+        return ret
+
     def installPkgs(self, userlist):
-        """Attempts to take the user specified list of packages/wildcards
-           and install them, or if they are installed, update them to a newer
-           version. If a complete version number if specified, attempt to 
-           upgrade (or downgrade if they have been removed) them to the
-           specified version"""
+        """Attempt to take the user specified list of packages or
+        wildcards and install them, or if they are installed, update
+        them to a newer version. If a complete version number is
+        specified, attempt to upgrade (or downgrade if they have been
+        removed) them to the specified version.
+
+        :param userlist: a list of names or wildcards specifying
+           packages to install
+        :return: (exit_code, [ errors ])
+
+        exit_code is::
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+            2 = we've got work yet to do, onto the next stage
+        """
         # get the list of available packages
         # iterate over the user's list
         # add packages to Transaction holding class if they match.
@@ -710,11 +803,12 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
         for arg in userlist:
             if (arg.endswith('.rpm') and (yum.misc.re_remote_url(arg) or
                                           os.path.exists(arg))):
-                self.localInstall(filelist=[arg])
+                txmbrs = self.installLocal(arg)
+                self._install_upgraded_requires(txmbrs)
                 continue # it was something on disk and it ended in rpm 
                          # no matter what we don't go looking at repos
             try:
-                self.install(pattern=arg)
+                txmbrs = self.install(pattern=arg)
             except yum.Errors.InstallError:
                 self.verbose_logger.log(yum.logginglevels.INFO_2,
                                         _('No package %s%s%s available.'),
@@ -723,6 +817,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
                 self._maybeYouMeant(arg)
             else:
                 done = True
+                self._install_upgraded_requires(txmbrs)
         if len(self.tsInfo) > oldcount:
             change = len(self.tsInfo) - oldcount
             return 2, [P_('%d package to install', '%d packages to install', change) % change]
@@ -732,9 +827,27 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
         return 0, [_('Nothing to do')]
         
     def updatePkgs(self, userlist, quiet=0, update_to=False):
-        """take user commands and populate transaction wrapper with 
-           packages to be updated"""
-        
+        """Take user commands and populate transaction wrapper with
+        packages to be updated.
+
+        :param userlist: a list of names or wildcards specifying
+           packages to update.  If *userlist* is an empty list, yum
+           will perform a global update
+        :param quiet: unused
+        :param update_to: if *update_to* is True, the update will only
+           be run if it will update the given package to the given
+           version.  For example, if the package foo-1-2 is installed,
+           updatePkgs(["foo-1-2], update_to=False) will work
+           identically to updatePkgs(["foo"]), but
+           updatePkgs(["foo-1-2"], update_to=True) will do nothing
+        :return: (exit_code, [ errors ])
+
+        exit_code is::
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+            2 = we've got work yet to do, onto the next stage
+        """
         # if there is no userlist, then do global update below
         # this is probably 90% of the calls
         # if there is a userlist then it's for updating pkgs, not obsoleting
@@ -745,20 +858,18 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
 
         else:
             # go through the userlist - look for items that are local rpms. If we find them
-            # pass them off to localInstall() and then move on
+            # pass them off to installLocal() and then move on
             localupdates = []
             for item in userlist:
                 if (item.endswith('.rpm') and (yum.misc.re_remote_url(item) or
                                                os.path.exists(item))):
-                    localupdates.append(item)
-            
-            if len(localupdates) > 0:
-                self.localInstall(filelist=localupdates, updateonly=1)
-                for item in localupdates:
-                    userlist.remove(item)
-                
-            for arg in userlist:
-                if not self.update(pattern=arg, update_to=update_to):
+                    txmbrs = self.installLocal(item, updateonly=1)
+                    self._install_upgraded_requires(txmbrs)
+                    continue
+
+                txmbrs = self.update(pattern=item, update_to=update_to)
+                self._install_upgraded_requires(txmbrs)
+                if not txmbrs:
                     self._checkMaybeYouMeant(arg)
 
         if len(self.tsInfo) > oldcount:
@@ -770,9 +881,24 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
     #  Note that we aren't in __init__ yet for a couple of reasons, but we 
     # probably will get there for 3.2.28.
     def distroSyncPkgs(self, userlist):
-        """ This does either upgrade/downgrade, depending on if the latest
-            installed version is older or newer. We allow "selection" but not
-            local packages (use tmprepo, or something). """
+        """Upgrade or downgrade packages to match the latest versions
+        available in the enabled repositories.
+
+        :param userlist: list of names or wildcards specifying
+           packages to synchronize with the repositories.  If the
+           first string in *userlist* is "full", packages will also be
+           reinstalled if their checksums do not match the checksums
+           in the repositories.  If *userlist* is an empty list or
+           only contains "full", every installed package will be
+           synchronized
+        :return: (exit_code, [ errors ])
+
+        exit_code is::
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+            2 = we've got work yet to do, onto the next stage
+        """
 
         level = 'diff'
         if userlist and userlist[0] in ('full', 'diff', 'different'):
@@ -866,9 +992,19 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
             return 0, [_('No Packages marked for Distribution Synchronization')]
 
     def erasePkgs(self, userlist):
-        """take user commands and populate a transaction wrapper with packages
-           to be erased/removed"""
-        
+        """Take user commands and populate a transaction wrapper with
+        packages to be erased.
+
+        :param userlist: a list of names or wildcards specifying
+           packages to erase
+        :return: (exit_code, [ errors ])
+
+        exit_code is::
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+            2 = we've got work yet to do, onto the next stage
+        """
         oldcount = len(self.tsInfo)
 
         all_rms = []
@@ -884,9 +1020,20 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
             return 0, [_('No Packages marked for removal')]
     
     def downgradePkgs(self, userlist):
-        """Attempts to take the user specified list of packages/wildcards
-           and downgrade them. If a complete version number if specified,
-           attempt to downgrade them to the specified version"""
+        """Attempt to take the user specified list of packages or
+        wildcards and downgrade them. If a complete version number if
+        specified, attempt to downgrade them to the specified version
+
+        :param userlist: a list of names or wildcards specifying
+           packages to downgrade
+        :return: (exit_code, [ errors ])
+
+        exit_code is::
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+            2 = we've got work yet to do, onto the next stage
+        """
 
         oldcount = len(self.tsInfo)
         
@@ -911,20 +1058,32 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
         return 0, [_('Nothing to do')]
         
     def reinstallPkgs(self, userlist):
-        """Attempts to take the user specified list of packages/wildcards
-           and reinstall them. """
+        """Attempt to take the user specified list of packages or
+        wildcards and reinstall them.
+
+        :param userlist: a list of names or wildcards specifying
+           packages to reinstall
+        :return: (exit_code, [ errors ])
+
+        exit_code is::
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+            2 = we've got work yet to do, onto the next stage
+        """
 
         oldcount = len(self.tsInfo)
 
         for arg in userlist:
             if (arg.endswith('.rpm') and (yum.misc.re_remote_url(arg) or
                                           os.path.exists(arg))):
-                self.reinstallLocal(arg)
+                txmbrs = self.reinstallLocal(arg)
+                self._install_upgraded_requires(txmbrs)
                 continue # it was something on disk and it ended in rpm
                          # no matter what we don't go looking at repos
 
             try:
-                self.reinstall(pattern=arg)
+                txmbrs = self.reinstall(pattern=arg)
             except yum.Errors.ReinstallRemoveError:
                 self._checkMaybeYouMeant(arg, always_output=False)
             except yum.Errors.ReinstallInstallError, e:
@@ -940,15 +1099,27 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
             except yum.Errors.ReinstallError, e:
                 assert False, "Shouldn't happen, but just in case"
                 self.verbose_logger.log(yum.logginglevels.INFO_2, e)
+            else:
+                self._install_upgraded_requires(txmbrs)
+
         if len(self.tsInfo) > oldcount:
             change = len(self.tsInfo) - oldcount
             return 2, [P_('%d package to reinstall', '%d packages to reinstall', change) % change]
         return 0, [_('Nothing to do')]
 
     def localInstall(self, filelist, updateonly=0):
-        """handles installs/updates of rpms provided on the filesystem in a 
-           local dir (ie: not from a repo)"""
-           
+        """Install or update rpms provided on the file system in a
+        local directory (i.e. not from a repository).
+
+        :param filelist: a list of names specifying local rpms
+        :return: (exit_code, [ errors ])
+
+        exit_code is::
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+            2 = we've got work yet to do, onto the next stage
+        """           
         # read in each package into a YumLocalPackage Object
         # append it to self.localPackages
         # check if it can be installed or updated based on nevra versus rpmdb
@@ -972,20 +1143,25 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
         return 0, [_('Nothing to do')]
 
     def returnPkgLists(self, extcmds, installed_available=False):
-        """Returns packages lists based on arguments on the cli.returns a 
-           GenericHolder instance with the following lists defined:
-           available = list of packageObjects
-           installed = list of packageObjects
-           updates = tuples of packageObjects (updating, installed)
-           extras = list of packageObjects
-           obsoletes = tuples of packageObjects (obsoleting, installed)
-           recent = list of packageObjects
-
-           installed_available = that the available package list is present
-                                 as .hidden_available when doing any of:
-                                 all/available/installed
-           """
-        
+        """Return a :class:`yum.misc.GenericHolder` object containing
+        lists of package objects that match the given names or wildcards.
+
+        :param extcmds: a list of names or wildcards specifying
+           packages to list
+        :param installed_available: whether the available package list
+           is present as .hidden_available when doing all, available,
+           or installed
+
+        :return: a :class:`yum.misc.GenericHolder` instance with the
+           following lists defined::
+
+             available = list of packageObjects
+             installed = list of packageObjects
+             updates = tuples of packageObjects (updating, installed)
+             extras = list of packageObjects
+             obsoletes = tuples of packageObjects (obsoleting, installed)
+             recent = list of packageObjects
+        """
         special = ['available', 'installed', 'all', 'extras', 'updates', 'recent',
                    'obsoletes']
         
@@ -1017,8 +1193,25 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
         return ypl
 
     def search(self, args):
-        """cli wrapper method for module search function, searches simple
-           text tags in a package object"""
+        """Search for simple text tags in a package object. This is a
+        cli wrapper method for the module search function.
+
+        :param args: list of names or wildcards to search for.
+           Normally this method will begin by searching the package
+           names and summaries, and will only search urls and
+           descriptions if that fails.  However, if the first string
+           in *args* is "all", this method will always search
+           everything
+        :return: a tuple where the first item is an exit code, and
+           the second item is a generator if the search is a
+           successful, and a list of error messages otherwise
+
+        exit_code is::
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+            2 = we've got work yet to do, onto the next stage
+        """
         
         # call the yum module search function with lists of tags to search
         # and what to search for
@@ -1108,9 +1301,20 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
         return 0, matching
 
     def deplist(self, args):
-        """cli wrapper method for findDeps method takes a list of packages and 
-            returns a formatted deplist for that package"""
+        """Print out a formatted list of dependencies for a list of
+        packages.  This is a cli wrapper method for
+        :class:`yum.YumBase.findDeps`.
+
+        :param args: a list of names or wildcards specifying packages
+           that should have their dependenices printed
+        :return: (exit_code, [ errors ])
 
+        exit_code is::
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+            2 = we've got work yet to do, onto the next stage
+        """
         pkgs = []
         for arg in args:
             if (arg.endswith('.rpm') and (yum.misc.re_remote_url(arg) or
@@ -1131,10 +1335,19 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
         return 0, []
 
     def provides(self, args):
-        """use the provides methods in the rpmdb and pkgsack to produce a list 
-           of items matching the provides strings. This is a cli wrapper to the 
-           module"""
-        
+        """Print out a list of packages that provide the given file or
+        feature.  This a cli wrapper to the provides methods in the
+        rpmdb and pkgsack.
+
+        :param args: the name of a file or feature to search for
+        :return: (exit_code, [ errors ])
+
+        exit_code is::
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+            2 = we've got work yet to do, onto the next stage
+        """
         old_sdup = self.conf.showdupesfromrepos
         # For output, as searchPackageProvides() is always in showdups mode
         self.conf.showdupesfromrepos = True
@@ -1163,8 +1376,19 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
         return 0, []
     
     def resolveDepCli(self, args):
-        """returns a package (one per user arg) that provide the supplied arg"""
-        
+        """Print information about a package that provides the given
+        dependency.  Only one package will be printed per dependency.
+
+        :param args: a list of strings specifying dependencies to
+           search for
+        :return: (exit_code, [ errors ])
+
+        exit_code is::
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+            2 = we've got work yet to do, onto the next stage
+        """
         for arg in args:
             try:
                 pkg = self.returnPackageByDep(arg)
@@ -1177,6 +1401,34 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
         return 0, []
     
     def cleanCli(self, userlist):
+        """Remove data from the yum cache directory.  What data is
+        removed depends on the options supplied by the user.
+
+        :param userlist: a list of options.  The following are valid
+           options::
+
+             expire-cache = Eliminate the local data saying when the
+               metadata and mirror lists were downloaded for each
+               repository.
+             packages = Eliminate any cached packages
+             headers = Eliminate the header files, which old versions
+               of yum used for dependency resolution
+             metadata = Eliminate all of the files which yum uses to
+               determine the remote availability of packages
+             dbcache = Eliminate the sqlite cache used for faster
+               access to metadata
+             rpmdb = Eliminate any cached datat from the local rpmdb
+             plugins = Tell any enabled plugins to eliminate their
+               cached data
+             all = do all of the above
+        :return: (exit_code, [ errors ])
+
+        exit_code is::
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+            2 = we've got work yet to do, onto the next stage
+        """
         hdrcode = pkgcode = xmlcode = dbcode = expccode = 0
         pkgresults = hdrresults = xmlresults = dbresults = expcresults = []
         msg = self.fmtKeyValFill(_('Cleaning repos: '), 
@@ -1228,7 +1480,19 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
         return code, []
 
     def returnGroupLists(self, userlist):
+        """Print out a list of groups that match the given names or
+        wildcards.
+
+        :param extcmds: a list of names or wildcards specifying
+           groups to list
+        :return: (exit_code, [ errors ])
 
+        exit_code is::
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+            2 = we've got work yet to do, onto the next stage        
+        """
         uservisible=1
             
         if len(userlist) > 0:
@@ -1283,7 +1547,20 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
         return 0, [_('Done')]
 
     def returnGroupSummary(self, userlist):
+        """Print a summary of the groups that match the given names or
+        wildcards.
 
+        :param userlist: a list of names or wildcards specifying the
+           groups to summarise. If *userlist* is an empty list, all
+           installed and available packages will be summarised
+        :return: (exit_code, [ errors ])
+
+        exit_code is::
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+            2 = we've got work yet to do, onto the next stage
+        """
         uservisible=1
             
         if len(userlist) > 0:
@@ -1327,7 +1604,19 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
         return 0, [_('Done')]
     
     def returnGroupInfo(self, userlist):
-        """returns complete information on a list of groups"""
+        """Print complete information about the groups that match the
+        given names or wildcards.
+
+        :param userlist: a list of names or wildcards specifying the
+           groups to print information about
+        :return: (exit_code, [ errors ])
+
+        exit_code is::
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+            2 = we've got work yet to do, onto the next stage
+        """
         for strng in userlist:
             group_matched = False
             for group in self.comps.return_groups(strng):
@@ -1340,8 +1629,18 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
         return 0, []
         
     def installGroups(self, grouplist):
-        """for each group requested do 'selectGroup' on them."""
-        
+        """Mark the packages in the given groups for installation.
+
+        :param grouplist: a list of names or wildcards specifying
+           groups to be installed
+        :return: (exit_code, [ errors ])
+
+        exit_code is::
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+            2 = we've got work yet to do, onto the next stage
+        """
         pkgs_used = []
         
         for group_string in grouplist:
@@ -1368,8 +1667,18 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
             return 2, [P_('%d package to Install', '%d packages to Install', len(pkgs_used)) % len(pkgs_used)]
 
     def removeGroups(self, grouplist):
-        """Remove only packages of the named group(s). Do not recurse."""
+        """Mark the packages in the given groups for removal.
+
+        :param grouplist: a list of names or wildcards specifying
+           groups to be removed
+        :return: (exit_code, [ errors ])
 
+        exit_code is::
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+            2 = we've got work yet to do, onto the next stage
+        """
         pkgs_used = []
         for group_string in grouplist:
             try:
@@ -1389,7 +1698,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
 
     def _promptWanted(self):
         # shortcut for the always-off/always-on options
-        if self.conf.assumeyes:
+        if self.conf.assumeyes and not self.conf.assumeno:
             return False
         if self.conf.alwaysprompt:
             return True
@@ -1400,7 +1709,6 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
         #  package wasn't explictly given on the command line
         for txmbr in self.tsInfo.getMembers():
             if txmbr.isDep or \
-                   txmbr.ts_state == 'e' or \
                    txmbr.name not in self.extcmds:
                 return True
         
@@ -1408,11 +1716,11 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
         return False
 
     def usage(self):
-        ''' Print out command line usage '''
+        """Print out an explanation of command line usage."""
         sys.stdout.write(self.optparser.format_help())
 
     def shellUsage(self):
-        ''' Print out the shell usage '''
+        """Print out an explanation of the shell usage."""
         sys.stdout.write(self.optparser.get_usage())
     
     def _installable(self, pkg, ematch=False):
@@ -1468,9 +1776,9 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
         return False
 
 class YumOptionParser(OptionParser):
-    '''Subclass that makes some minor tweaks to make OptionParser do things the
+    """Subclass that makes some minor tweaks to make OptionParser do things the
     "yum way".
-    '''
+    """
 
     def __init__(self,base, **kwargs):
         # check if this is called with a utils=True/False parameter
@@ -1488,13 +1796,23 @@ class YumOptionParser(OptionParser):
         self._addYumBasicOptions()
 
     def error(self, msg):
-        '''This method is overridden so that error output goes to logger. '''
+        """Output an error message, and exit the program.  This method
+        is overridden so that error output goes to the logger.
+
+        :param msg: the error message to output
+        """
         self.print_usage()
         self.logger.critical(_("Command line error: %s"), msg)
         sys.exit(1)
 
     def firstParse(self,args):
-        # Parse only command line options that affect basic yum setup
+        """Parse only command line options that affect basic yum
+        setup.
+
+        :param args: a list of command line options to parse
+        :return: a dictionary containing the values of command line
+           options
+        """
         try:
             args = _filtercmdline(
                         ('--noplugins','--version','-q', '-v', "--quiet", "--verbose"), 
@@ -1521,7 +1839,15 @@ class YumOptionParser(OptionParser):
         return ret
         
     def setupYumConfig(self, args=None):
-        # Now parse the command line for real
+        """Parse command line options.
+
+        :param args: the command line arguments entered by the user
+        :return: (opts, cmds)  opts is a dictionary containing
+           the values of command line options.  cmds is a list of the
+           command line arguments that were not parsed as options.
+           For example, if args is ["install", "foo", "--verbose"],
+           cmds will be ["install", "foo"].
+        """
         if not args:
             (opts, cmds) = self.parse_args()
         else:
@@ -1536,7 +1862,9 @@ class YumOptionParser(OptionParser):
                 
             # Handle remaining options
             if opts.assumeyes:
-                self.base.conf.assumeyes =1
+                self.base.conf.assumeyes = 1
+            if opts.assumeno:
+                self.base.conf.assumeno  = 1
 
             #  Instead of going cache-only for a non-root user, try to use a
             # user writable cachedir. If that fails fall back to cache-only.
@@ -1640,6 +1968,14 @@ class YumOptionParser(OptionParser):
         sys.exit(1)
 
     def getRoot(self,opts):
+        """Return the root location to use for the yum operation.
+        This location can be changed by using the --installroot
+        option.
+
+        :param opts: a dictionary containing the values of the command
+           line options
+        :return: a string representing the root location
+        """
         self._checkAbsInstallRoot(opts)
         # If the conf file is inside the  installroot - use that.
         # otherwise look for it in the normal root
@@ -1713,6 +2049,10 @@ class YumOptionParser(OptionParser):
                         help=_("verbose operation"))
         group.add_option("-y", "--assumeyes", dest="assumeyes",
                 action="store_true", help=_("answer yes for all questions"))
+        group.add_option("--assumeno", dest="assumeno",
+                action="store_true", help=_("answer no for all questions"))
+        group.add_option("--nodeps", dest="assumeno", # easter egg :)
+                action="store_true", help=SUPPRESS_HELP)
         group.add_option("--version", action="store_true", 
                 help=_("show Yum version and exit"))
         group.add_option("--installroot", help=_("set install root"), 
diff --git a/docs/sphinxdocs/Makefile b/docs/sphinxdocs/Makefile
new file mode 100644
index 0000000..623c22b
--- /dev/null
+++ b/docs/sphinxdocs/Makefile
@@ -0,0 +1,130 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS    =
+SPHINXBUILD   = sphinx-build
+PAPER         =
+BUILDDIR      = _build
+
+# Internal variables.
+PAPEROPT_a4     = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
+
+help:
+	@echo "Please use \`make <target>' where <target> is one of"
+	@echo "  html       to make standalone HTML files"
+	@echo "  dirhtml    to make HTML files named index.html in directories"
+	@echo "  singlehtml to make a single large HTML file"
+	@echo "  pickle     to make pickle files"
+	@echo "  json       to make JSON files"
+	@echo "  htmlhelp   to make HTML files and a HTML help project"
+	@echo "  qthelp     to make HTML files and a qthelp project"
+	@echo "  devhelp    to make HTML files and a Devhelp project"
+	@echo "  epub       to make an epub"
+	@echo "  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+	@echo "  latexpdf   to make LaTeX files and run them through pdflatex"
+	@echo "  text       to make text files"
+	@echo "  man        to make manual pages"
+	@echo "  changes    to make an overview of all changed/added/deprecated items"
+	@echo "  linkcheck  to check all external links for integrity"
+	@echo "  doctest    to run all doctests embedded in the documentation (if enabled)"
+
+clean:
+	-rm -rf $(BUILDDIR)/*
+
+html:
+	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+	@echo
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml:
+	$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+	@echo
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+singlehtml:
+	$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+	@echo
+	@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+pickle:
+	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+	@echo
+	@echo "Build finished; now you can process the pickle files."
+
+json:
+	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+	@echo
+	@echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+	@echo
+	@echo "Build finished; now you can run HTML Help Workshop with the" \
+	      ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp:
+	$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+	@echo
+	@echo "Build finished; now you can run "qcollectiongenerator" with the" \
+	      ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+	@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Yum.qhcp"
+	@echo "To view the help file:"
+	@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Yum.qhc"
+
+devhelp:
+	$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+	@echo
+	@echo "Build finished."
+	@echo "To view the help file:"
+	@echo "# mkdir -p $$HOME/.local/share/devhelp/Yum"
+	@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Yum"
+	@echo "# devhelp"
+
+epub:
+	$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+	@echo
+	@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+latex:
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+	@echo
+	@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+	@echo "Run \`make' in that directory to run these through (pdf)latex" \
+	      "(use \`make latexpdf' here to do that automatically)."
+
+latexpdf:
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+	@echo "Running LaTeX files through pdflatex..."
+	make -C $(BUILDDIR)/latex all-pdf
+	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+text:
+	$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+	@echo
+	@echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+man:
+	$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+	@echo
+	@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+changes:
+	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+	@echo
+	@echo "The overview file is in $(BUILDDIR)/changes."
+
+linkcheck:
+	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+	@echo
+	@echo "Link check complete; look for any errors in the above output " \
+	      "or in $(BUILDDIR)/linkcheck/output.txt."
+
+doctest:
+	$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+	@echo "Testing of doctests in the sources finished, look at the " \
+	      "results in $(BUILDDIR)/doctest/output.txt."
diff --git a/docs/sphinxdocs/conf.py b/docs/sphinxdocs/conf.py
new file mode 100644
index 0000000..867d323
--- /dev/null
+++ b/docs/sphinxdocs/conf.py
@@ -0,0 +1,228 @@
+# -*- coding: utf-8 -*-
+#
+# Yum documentation build configuration file, created by
+# sphinx-quickstart on Mon Jun 27 14:01:20 2011.
+#
+# This file is execfile()d with the current directory set to its containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys, os
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+# sys.path.insert(0, os.path.abspath('.'))
+
+# -- General configuration -----------------------------------------------------
+
+#Tell sphinx where to look for all the files
+sys.path.insert(0, os.path.normpath(os.path.join(os.getcwd(), "../../test")))
+sys.path.insert(0, os.path.normpath(os.path.join(os.getcwd(), "../../po")))
+sys.path.insert(0, os.path.normpath(os.path.join(os.getcwd(), "../../bin")))
+sys.path.insert(0, os.path.normpath(os.path.join(os.getcwd(), "../..")))
+
+#Generate all the rst files
+sys.path.insert(1, os.getcwd())
+import rstgenerator
+rstgenerator.generateAll(sys.path[0], os.getcwd())
+import yum
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = ['sphinx.ext.autodoc']
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'Yum'
+copyright = None
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+# version = 
+# The full version, including alpha/beta/rc tags.
+release = yum.__version__
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = ['_build']
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+
+# -- Options for HTML output ---------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  See the documentation for
+# a list of builtin themes.
+html_theme = 'default'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further.  For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents.  If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar.  Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_domain_indices = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+html_show_copyright = False
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it.  The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'Yumdoc'
+
+
+# -- Options for LaTeX output --------------------------------------------------
+
+# The paper size ('letter' or 'a4').
+#latex_paper_size = 'letter'
+
+# The font size ('10pt', '11pt' or '12pt').
+#latex_font_size = '10pt'
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, documentclass [howto/manual]).
+latex_documents = [
+  ('index', 'Yum.tex', u'Yum Documentation',
+   u'', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# If true, show page references after internal links.
+#latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#latex_show_urls = False
+
+# Additional stuff for the LaTeX preamble.
+#latex_preamble = ''
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_domain_indices = True
+
+
+# -- Options for manual page output --------------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+    ('index', 'yum', u'Yum Documentation',
+     [u''], 1)
+]
diff --git a/docs/sphinxdocs/rstgenerator.py b/docs/sphinxdocs/rstgenerator.py
new file mode 100755
index 0000000..d2a0ed1
--- /dev/null
+++ b/docs/sphinxdocs/rstgenerator.py
@@ -0,0 +1,222 @@
+#! /usr/bin/python
+
+import sys, re, os
+
+def generateFile(input_directory, file_name, output_directory,
+                 package_heirarchy=None, module_name=None):
+    """Generate a rst file telling sphinx to just generate documentation
+    for the public interface automatically.  Output will be written to
+    *file_name*.rst in the current directory.
+    
+    :param input_directory: a string specifying the directory containing the 
+       source code file
+    :param file_name: the name of the python source code file to generate
+       a sphinx rst file describing
+    :param ouput_directory: a string specifying the directory where
+       the generated rst file should be placed.  If *output_directory* does
+       not already exist, it will be created
+    :param package_heirarchy: a list of strings, where each name is
+       the name of a package, in the order of the hierarchy
+    :param module_name: the name of the module. If not given, the .py is 
+       removed from *file_name* to produce the module_name
+    """
+    #Stick all output into a list of strings, then just join it and output
+    #it all in on go.
+    output = []
+    
+    # Create the output directory if it doesn't already exist. Note that
+    # if the directory is created between the check and the creation, it 
+    # might cause issues, but I don't think this likely at all to happen
+    if not os.path.exists(output_directory):
+        try:
+            os.makedirs(output_directory)
+        except OSError as e:
+            print "Error creating the output directory"
+            print e.args
+
+    try:
+        #Open the file
+        f = open(os.path.join(input_directory, file_name), 'r')
+    
+        #Do the module output
+        if not module_name:
+            module_name = re.search('(\w+).py$', file_name).group(1)
+
+        #Append the package names, if there are any
+        full_module_name = module_name
+        if package_heirarchy:
+            full_module_name = '.'.join(package_heirarchy) + '.' + module_name
+    
+        output.append(full_module_name)
+        output.append('=' * len(full_module_name))
+        output.append('.. automodule:: %s\n' % full_module_name)
+        
+        #Read the file, and do output for classes
+        class_reg = re.compile('^class (\w+)')
+        func_reg = re.compile('^def ((?:[a-zA-Z0-9]+_)*[a-zA-Z0-9]+)')
+        
+        #We don't need a blank line between autofunction directives, but we do
+        #need one between autofunctions and headings etc. for classes.  This
+        #keeps track if we're switching from autofunctions to classes, so we
+        #can add that blank line.
+        finding_functions = False
+        
+        for line in iter(f):
+            #Search for classes
+            match = class_reg.match(line)
+            if match is not None: 
+                if finding_functions:
+                    output.append('')
+                    finding_functions = False
+                class_name = match.group(1)
+                output.append(class_name)
+                output.append('-' * len(class_name))
+                output.append('''.. autoclass:: %s
+           :members:
+           :show-inheritance:
+        
+           ''' % class_name)
+        
+        
+            #Search for top level functions
+            else:
+                match = func_reg.match(line)
+                if match is not None:
+                    func_name = match.group(1)
+                    output.append('.. autofunction:: ' + func_name)
+                    finding_functions = True
+        f.close()
+
+    except IOError as e:
+        print "Error opening the input file : ", os.path.join(input_directory, file_name)
+        print e.args[1]
+
+    else:
+        #Write the output
+        try:
+            output_file_name = os.path.join(output_directory, module_name) + '.rst'
+            f = open(output_file_name, 'w')
+            f.write('\n'.join(output))
+            
+            
+        except IOError as e:
+            print "Error opening the output file : ", output_file_name
+            print e.args[1]
+
+                
+def generateIndex(module_list, output_directory):
+    """Create an index.rst file for sphinx in the given directory.
+
+    :param module_list: a list of the names of the modules to list in
+       the index file
+    :param output_directory: the directory to create the index file in
+    """
+
+    #Sort the module_list
+    module_list.sort()
+
+    try:
+        #open the file
+        f = open(os.path.join(output_directory, 'index.rst'), 'w')
+    
+        #Do the output
+        f.write(""".. Yum documentation master file, created by
+   sphinx-quickstart on Mon Jun 27 14:01:20 2011.
+   You can adapt this file completely to your liking, but it should at least
+   contain the root `toctree` directive.
+
+Welcome to Yum's documentation!
+===============================
+
+Contents:
+
+.. toctree::
+   :maxdepth: 2
+
+   """)
+        f.write('\n   '.join(module_list))
+        f.write("""
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
+""")
+
+    except IOError as e:
+        print "Error opening the output file."
+        print e.args[1]
+
+
+def generateAll(source_directory, output_directory):
+    #Verify that both the source and output directories exist
+
+
+    # Keep a set of file names that are packages.  This is 
+    # useful so that later we will be able to figure out full
+    # module names.
+    packages = set()
+
+    # Keep a list of tuples containing python module names and
+    # relative paths, so that we can build the index file later
+    modules = []
+
+    # Walk the directory tree
+    for dirpath, dirnames, filenames in os.walk(source_directory, topdown=True):
+        
+        # print dirpath
+        # print dirnames
+        # print filenames
+        # print
+
+        # Add the curent directory to packages if __init__.py exists
+        if '__init__.py' in filenames:
+            packages.add(dirpath)
+
+        # Find the heirarchy of packages that we are currently in
+        package_heirarchy = []
+        #Recurse up to the root
+        dirpath_i = dirpath
+        while dirpath_i != '/':
+            if dirpath_i in packages:
+                dirpath_i, tail = os.path.split(dirpath_i)
+                package_heirarchy.insert(0, tail)
+            else:
+                break
+
+        # Find the relative output directory, mirroring the input
+        # directory structure
+        relative_output_directory = ''
+        if not os.path.samefile(dirpath, source_directory):
+            relative_output_directory = os.path.relpath(dirpath, source_directory)
+        
+        # Don't recurse into directories that are hidden, or for docs
+        for directory in dirnames:
+            if directory == "docs" or directory.startswith("."):
+                dirnames.remove(directory)
+
+        # Generate the rst for a file if it is a python source code file
+        for file_name in filenames:
+            # Skip file names that contain dashes, since they're not
+            # valid module names, so we won't be able to import them
+            # to generate the documentation anyway
+            if '-' in file_name:
+                continue
+
+            if file_name.endswith('.py'):
+                module_name = file_name.partition('.')[0]
+                modules.append(os.path.join(relative_output_directory, 
+                                            module_name))
+                generateFile(dirpath, file_name, 
+                             os.path.join(output_directory, relative_output_directory),
+                             package_heirarchy, module_name)
+
+        
+    
+    # Create the index.rst file
+    generateIndex(modules, output_directory)
+
+if __name__ == "__main__":
+    generateAll(os.getcwd(), os.getcwd())
diff --git a/docs/yum.8 b/docs/yum.8
index 1a8202a..255c755 100644
--- a/docs/yum.8
+++ b/docs/yum.8
@@ -69,7 +69,9 @@ gnome\-packagekit application\&.
 .br
 .I \fR * version [ all | installed | available | group-* | nogroups* | grouplist | groupinfo ]
 .br
-.I \fR * history [info|list|packages-list|summary|addon-info|redo|undo|rollback|new] 
+.I \fR * history [info|list|packages-list|packages-info|summary|addon-info|redo|undo|rollback|new|sync|stats] 
+.br
+.I \fR * load-transaction [txfile]
 .br
 .I \fR * check
 .br 
@@ -321,15 +323,17 @@ and so takes sub-commands:
 .IP "\fBhistory\fP"
 The history command allows the user to view what has happened in past
 transactions (assuming the history_record config. option is set). You can use
-info/list/packages-list/summary to view what happened, undo/redo/rollback to act
-on that information and new to start a new history file.
+info/list/packages-list/packages-info/summary to view what happened,
+undo/redo/rollback to act on that information and new to start a new history
+file.
 
 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.
 
-The packages-list command takes a package  (with wildcards, as in
-\fBSpecifying package names\fP).
+The packages-list/packages-info commands takes a package  (with wildcards, as in
+\fBSpecifying package names\fP). And show data from the point of view of that
+package.
 
 The undo/redo/rollback commands take either a single transaction id or the
 keyword last and an offset from the last transaction (Eg. if you've done 250
@@ -349,6 +353,12 @@ transactions 1 and 4.
 The addon-info command takes a transaction ID, and the packages-list command
 takes a package (with wildcards).
 
+The stats command shows some statistics about the current history DB.
+
+The sync commands allows you to change the rpmdb/yumdb data stored for any
+installed packages, to whaever is in the current rpmdb/yumdb (this is mostly
+useful when this data was not stored when the package went into the history DB).
+
 In "history list" you can change the behaviour of the 2nd column via. the
 configuration option history_list_view.
 
@@ -371,6 +381,15 @@ end of the package column in the packages-list command).
 .I \fBs\fR - The transaction completed fine, but --skip-broken was enabled and had to skip some packages.
 .br
 
+
+.IP
+.IP "\fBload-transaction\fP"
+This command will re-load a saved yum transaction file, this allows you to
+run a transaction on one machine and then use it on another.
+The two common ways to get a saved yum transaction file are from
+"yum -q history addon-info last saved_tx" or via. the automatic saves in
+$TMPDIR/yum_save_tx.* when a transaction is solved but not run.
+
 .IP
 .IP "\fBcheck\fP"
 Checks the local rpmdb and produces information on any problems it finds. You
@@ -401,6 +420,11 @@ Assume yes; assume that the answer to any question which would be asked
 is yes\&.
 .br
 Configuration Option: \fBassumeyes\fP
+.IP "\fB\-\-assumeno\fP"
+Assume no; assume that the answer to any question which would be asked 
+is no\&. This option overrides assumeyes, but is still subject to alwaysprompt.
+.br
+Configuration Option: \fBassumeno\fP
 .IP "\fB\-c, \-\-config=[config file]\fP" 
 Specifies the config file location - can take HTTP and FTP URLs and local file
 paths\&.
diff --git a/docs/yum.conf.5 b/docs/yum.conf.5
index 515aa73..607e9fc 100644
--- a/docs/yum.conf.5
+++ b/docs/yum.conf.5
@@ -114,15 +114,27 @@ are causing problems from the transaction.
 Either `1' or `0'. Determines whether or not yum prompts for confirmation of
 critical actions. Default is `0' (do prompt).
 .br
-Command-line option: \fB\-y\fP
+Command-line option: \fB\-y\fP \fB\--assumeyes\fP
+
+.IP
+\fBassumeno\fR
+Either `1' or `0'. If yum would prompt for confirmation of critical actions, 
+assume the user chose no. This is basically the same as doing "echo | yum ..."
+but is a bit more usable. This option overrides \fBassumeyes\fP, but is still
+subject to \fBalwaysprompt\fP.
+Default is `0' (do prompt).
+.br
+Command-line option: \fB\--assumeno\fP
 
 .IP
 \fBalwaysprompt\fR
 Either `1' or `0'. Without this option, yum will not prompt for confirmation
 when the list of packages to be installed exactly matches those given on the
-command line. Unless \fBassumeyes\fR is enabled, it will still prompt for
-package removal, or when additional packages need to be installed to fulfill
-dependencies. Default is `1'.
+command line. Unless \fBassumeyes\fR is enabled, it will still prompt when
+additional packages need to be installed to fulfill dependencies. Note that
+older versions of yum would also always prompt for package removal, and that is
+no longer true.
+Default is `1'.
 .br
 
 .IP
@@ -131,6 +143,7 @@ Either `1' or `0'. If enabled, then yum will be tolerant of errors on the
 command line with regard to packages. For example: if you request to install
 foo, bar and baz and baz is installed; yum won't error out complaining that baz
 is already installed. Default to `0' (not tolerant).
+Note: This option currently does nothing.
 .br
 Command-line option: \fB\-t\fP
 
@@ -300,6 +313,14 @@ with the \fBthrottle\fR option (above). If \fBthrottle\fR is a percentage and
 ignored. Default is `0' (no bandwidth throttling). 
 
 .IP
+\fBip_resolve \fR
+Determines how yum resolves host names.
+
+`4' or `IPv4': resolve to IPv4 addresses only.
+
+`6' or `IPv6': resolve to IPv6 addresses only.
+
+.IP
 \fBsslcacert \fR
 Path to the directory containing the databases of the certificate authorities
 yum should use to verify SSL certificates. Defaults to none - uses system
@@ -595,6 +616,12 @@ package's dependencies. If any of them are no longer required by any other
 package then also mark them to be removed.
 Boolean (1, 0, True, False, yes,no) Defaults to False
 
+.IP
+\fBupgrade_requirements_on_install \fR
+When installing/reinstalling/upgrading packages go through each package's
+installed dependencies and check for an update.
+Boolean (1, 0, True, False, yes,no) Defaults to False
+
 
 
 .SH "[repository] OPTIONS"
@@ -755,6 +782,11 @@ repository.
 Overrides the \fBbandwidth\fR option from the [main] section for this
 repository.
 
+.IP
+\fBip_resolve \fR
+Overrides the \fBip_resolve\fR option from the [main] section for this
+repository.
+
 
 .IP
 \fBsslcacert \fR
diff --git a/etc/0yum.cron b/etc/0yum.cron
deleted file mode 100755
index 0cfaa4b..0000000
--- a/etc/0yum.cron
+++ /dev/null
@@ -1,141 +0,0 @@
-#!/bin/bash
-
-# Only run if this flag file is set (by /etc/rc.d/init.d/yum-cron)
-if [ ! -f /var/lock/subsys/yum-cron ]; then
-  exit 0
-fi
-
-DAILYSCRIPT=/etc/yum/yum-daily.yum
-WEEKLYSCRIPT=/etc/yum/yum-weekly.yum
-LOCKDIR=/var/lock/yum-cron.lock
-LOCKFILE=$LOCKDIR/pidfile
-TSLOCK=$LOCKDIR/ts.lock
-
-# Grab config settings
-if [ -f /etc/sysconfig/yum-cron ]; then
-  source /etc/sysconfig/yum-cron
-fi
-# set default for SYSTEMNAME
-[ -z "$SYSTEMNAME" ]  && SYSTEMNAME=$(hostname) 
-
-# Only run on certain days of the week 
-dow=`date +%w` 
-DAYS_OF_WEEK=${DAYS_OF_WEEK:-0123456} 
-if [ "${DAYS_OF_WEEK/$dow/}" == "${DAYS_OF_WEEK}" ]; then 
-  exit 0 
-fi 
-
-# if DOWNLOAD_ONLY is set then we force CHECK_ONLY too.
-# Gotta check before one can download!
-if [ "$DOWNLOAD_ONLY" == "yes" ]; then
-  CHECK_ONLY=yes
-fi
-
-YUMTMP=$(mktemp /var/run/yum-cron.XXXXXX)
-touch $YUMTMP 
-[ -x /sbin/restorecon ] && /sbin/restorecon $YUMTMP
-
-# Random wait function
-random_wait() {
-  sleep $(( $RANDOM % ($RANDOMWAIT * 60) + 1 ))
-}
-
-# Note - the lockfile code doesn't try and use YUMTMP to email messages nicely.
-# Too many ways to die, this gets handled by normal cron error mailing.
-# Try mkdir for the lockfile, will test for and make it in one atomic action
-if mkdir $LOCKDIR 2>/dev/null; then
-  # store the current process ID in there so we can check for staleness later
-  echo "$$" >"${LOCKFILE}"
-  # and clean up locks and tempfile if the script exits or is killed  
-  trap "{ rm -f $LOCKFILE $TSLOCK; rmdir $LOCKDIR 2>/dev/null; rm -f $YUMTMP; exit 255; }" INT TERM EXIT
-else
-  # lock failed, check if process exists.  First, if there's no PID file
-  # in the lock directory, something bad has happened, we can't know the
-  # process name, so clean up the old lockdir and restart
-  if [ ! -f $LOCKFILE ]; then
-    rmdir $LOCKDIR 2>/dev/null
-    echo "yum-cron: no lock PID, clearing and restarting myself" >&2
-    exec $0 "$@"
-  fi
-  OTHERPID="$(cat "${LOCKFILE}")"
-  # if cat wasn't able to read the file anymore, another instance probably is
-  # about to remove the lock -- exit, we're *still* locked
-    if [ $? != 0 ]; then
-      echo "yum-cron: lock failed, PID ${OTHERPID} is active" >&2
-      exit 0
-    fi
-    if ! kill -0 $OTHERPID &>/dev/null; then
-      # lock is stale, remove it and restart
-      echo "yum-cron: removing stale lock of nonexistant PID ${OTHERPID}" >&2
-      rm -rf "${LOCKDIR}"
-      echo "yum-cron: restarting myself" >&2
-      exec $0 "$@"
-    else
-      # Remove stale (more than a day old) lockfiles
-      find $LOCKDIR -type f -name 'pidfile' -amin +1440 -exec rm -rf $LOCKDIR \;
-      # if it's still there, it wasn't too old, bail
-      if [ -f $LOCKFILE ]; then
-        # lock is valid and OTHERPID is active - exit, we're locked!
-        echo "yum-cron: lock failed, PID ${OTHERPID} is active" >&2
-        exit 0
-      else
-        # lock was invalid, restart
-	echo "yum-cron: removing stale lock belonging to stale PID ${OTHERPID}" >&2
-        echo "yum-cron: restarting myself" >&2
-        exec $0 "$@"
-      fi
-    fi
-fi
-
-# Then check for updates and/or do them, as configured
-{
-  # First, if this is CLEANDAY, do so
-  CLEANDAY=${CLEANDAY:-0}
-  if [ ! "${CLEANDAY/$dow/}" == "${CLEANDAY}" ]; then
-      /usr/bin/yum $YUM_PARAMETER -e ${ERROR_LEVEL:-0} -d ${DEBUG_LEVEL:-0} -y shell $WEEKLYSCRIPT
-  fi
-
-  # Now continue to do the real work
-  if [ "$CHECK_ONLY" == "yes" ]; then
-    random_wait
-    touch $TSLOCK
-    /usr/bin/yum $YUM_PARAMETER -e 0 -d 0 -y check-update 1> /dev/null 2>&1
-    case $? in
-      1)   exit 1;;
-      100) echo "New updates available for host `/bin/hostname`";
-           /usr/bin/yum $YUM_PARAMETER -e ${ERROR_LEVEL:-0} -d ${DEBUG_LEVEL:-0} -y -C check-update
-           if [ "$DOWNLOAD_ONLY" == "yes" ]; then
-	       /usr/bin/yum $YUM_PARAMETER -e ${ERROR_LEVEL:-0} -d ${DEBUG_LEVEL:-0} -y --downloadonly update
-	       echo "Updates downloaded, use \"yum -C update\" manually to install them."
-	   fi
-	   ;;
-    esac
-  elif [ "$CHECK_FIRST" == "yes" ]; then
-    # Don't run if we can't access the repos
-    random_wait
-    touch $TSLOCK
-    /usr/bin/yum $YUM_PARAMETER -e 0 -d 0 check-update 2>&-
-    case $? in
-      1)   exit 1;;
-      100) /usr/bin/yum $YUM_PARAMETER -e ${ERROR_LEVEL:-0} -d ${DEBUG_LEVEL:-0} -y update yum
-           /usr/bin/yum $YUM_PARAMETER -e ${ERROR_LEVEL:-0} -d ${DEBUG_LEVEL:-0} -y shell $DAILYSCRIPT
-           ;;
-    esac
-  else
-    random_wait
-    touch $TSLOCK
-    /usr/bin/yum $YUM_PARAMETER -e ${ERROR_LEVEL:-0} -d ${DEBUG_LEVEL:-0} -y update yum
-    /usr/bin/yum $YUM_PARAMETER -e ${ERROR_LEVEL:-0} -d ${DEBUG_LEVEL:-0} -y shell $DAILYSCRIPT
-  fi
-} >> $YUMTMP 2>&1
-
-if [ ! -z "$MAILTO" ] && [ -x /bin/mail ]; then 
-# if MAILTO is set, use mail command (ie better than standard mail with cron output) 
-  [ -s "$YUMTMP" ] && mail -s "System update: $SYSTEMNAME" $MAILTO < $YUMTMP 
-else 
-# default behavior is to use cron's internal mailing of output from cron-script
-  cat $YUMTMP
-fi 
-rm -f $YUMTMP 
-
-exit 0
diff --git a/etc/Makefile b/etc/Makefile
index 29a7f95..a512cdf 100644
--- a/etc/Makefile
+++ b/etc/Makefile
@@ -29,10 +29,3 @@ install:
 
 	mkdir -p $(DESTDIR)/etc/bash_completion.d
 	install -m 644 yum.bash $(DESTDIR)/etc/bash_completion.d
-	mkdir -p $(DESTDIR)/etc/cron.daily
-	mkdir -p $(DESTDIR)/etc/sysconfig/
-	install -D -m 755 0yum.cron $(DESTDIR)/etc/cron.daily/0yum.cron
-	install -D -m 755 yum-cron $(DESTDIR)/etc/rc.d/init.d/yum-cron
-	install -D -m 644 yum-daily.yum $(YUMETC)/yum-daily.yum
-	install -D -m 644 yum-weekly.yum $(YUMETC)/yum-weekly.yum
-	install -D -m 644 yum-cron.sysconf $(DESTDIR)/etc/sysconfig/yum-cron
diff --git a/etc/yum-cron b/etc/yum-cron
deleted file mode 100755
index 63c5ec0..0000000
--- a/etc/yum-cron
+++ /dev/null
@@ -1,102 +0,0 @@
-#!/bin/bash
-#
-# yum-cron           This shell script enables the automatic use of YUM
-#
-# Author:       Seth Vidal <skvidal@phy.duke.edu>
-#
-# chkconfig:	- 50 01
-#
-# description:  Enable daily run of yum, a program updater.
-# processname:  yum-cron
-# config: /etc/yum/yum-daily.yum
-#
-
-# source function library
-. /etc/rc.d/init.d/functions
-
-test -f /etc/sysconfig/yum-cron && . /etc/sysconfig/yum-cron
-
-lockfile=/var/lock/subsys/yum-cron
-tslock=/var/lock/yum-cron.lock/ts.lock
-yumcronpid=/var/lock/yum-cron.lock/pidfile
-
-RETVAL=0
-
-start() {
-	echo -n $"Enabling nightly yum update: "
-	touch "$lockfile" && success || failure
-	RETVAL=$?
-	echo
-}
-
-stop() {
-	echo -n $"Disabling nightly yum update: "
-	if [ -f "$yumcronpid" -a "$SERVICE_WAITS" = "yes" ]; then
-	  yum_done=0
-	  if [ ! -f $tslock ]; then
-	    # No transaction yet in progress, just kill it
-	    kill `cat $yumcronpid > /dev/null 2>&1` > /dev/null 2>&1
-	    yum_done=1
-	  fi
-	  if [ $yum_done -eq 0 ]; then
-	    echo -n $"Waiting for yum "
-	    if [ -z "$SERVICE_WAIT_TIME" ]; then
-	      SERVICE_WAIT_TIME=300
-	    fi
-	    start=`date +%s`
-	    end=`expr $start + $SERVICE_WAIT_TIME`
-	    while [ `date +%s` -le $end ]
-	    do
-	      sleep 5
-	      if [ ! -f "$tslock" ]; then
-		yum_done=1
-	        break
-	      fi
-	    done
-	    if [ $yum_done -eq 1 ]; then
-	      echo -n " ok "
-	    else
-	      echo -n " failed "
-	    fi
-	  fi
-	fi
-	rm -f "$lockfile" && success || failure
-	RETVAL=$?
-	echo
-}
-
-restart() {
-	stop
-	start
-}
-
-case "$1" in
-  start)
-	start
-	;;
-  stop) 
-	stop
-	;;
-  restart|force-reload)
-	restart
-	;;
-  reload)
-	;;
-  condrestart)
-	[ -f "$lockfile" ] && restart
-	;;
-  status)
-	if [ -f $lockfile ]; then
-		echo $"Nightly yum update is enabled."
-		RETVAL=0
-	else
-		echo $"Nightly yum update is disabled."
-		RETVAL=3
-	fi
-	;;
-  *)
-	echo $"Usage: $0 {start|stop|status|restart|reload|force-reload|condrestart}"
-	exit 1
-esac
-
-exit $RETVAL
diff --git a/etc/yum-cron.sysconf b/etc/yum-cron.sysconf
deleted file mode 100644
index 930341c..0000000
--- a/etc/yum-cron.sysconf
+++ /dev/null
@@ -1,58 +0,0 @@
-# Pass any given paramter to yum, as run in all the scripts invoked
-# by this package.  Be aware that this is global, and yum is invoked in 
-# several modes by these scripts for which your own parameter might not
-# be appropriate
-YUM_PARAMETER=
-
-# Don't install, just check (valid: yes|no)
-CHECK_ONLY=no
-
-# Check to see if you can reach the repos before updating (valid: yes|no)
-CHECK_FIRST=no
-
-# Don't install, just check and download (valid: yes|no)
-# Implies CHECK_ONLY=yes (gotta check first to see what to download)
-DOWNLOAD_ONLY=no
-
-# Error level, practical range 0-10, 0 means print only critical errors which
-# you must be told, 1 means print all errors, even ones that are not important
-# Level 0 is the default
-# ERROR_LEVEL=0
-
-# Debug level, practical range 0-10, higher number means more output
-# Level 1 is a useful level if you want to see what's been done and
-# don't want to read /var/log/yum.log
-# Level 0 is the default
-# DEBUG_LEVEL=1
-
-# randomwait is used by yum to wait random time
-# default is 60 so yum waits random time from 1 to 60 minutes
-# the value must not be zero
-RANDOMWAIT="60"
-
-# if MAILTO is set and the mail command is available, the mail command 
-# is used to deliver yum output
-
-# by default MAILTO is unset, so crond mails the output by itself
-# example:  MAILTO=root
-MAILTO= 
-
-# you may set SYSTEMNAME if you want your yum emails tagged differently
-# default is output of hostname command 
-# this variable is used only if MAILTO is set too
-#SYSTEMNAME="" 
-
-# you may set DAYS_OF_WEEK to the days of the week you want to run 
-# default is every day 
-#DAYS_OF_WEEK="0123456" 
-
-# which day should it do cleanup on?  defaults to 0 (Sunday).  If this day isn't in the 
-# DAYS_OF_WEEK above, it'll never happen
-CLEANDAY="0"
-
-# set to yes to make the yum-cron service to wait for transactions to complete
-SERVICE_WAITS=yes
-
-# set maximum time period (in seconds) for the yum-cron service to wait for
-# transactions to complete.  The default is 300 seconds (5 minutes)
-SERVICE_WAIT_TIME=300
diff --git a/etc/yum-daily.yum b/etc/yum-daily.yum
deleted file mode 100644
index 5d4e874..0000000
--- a/etc/yum-daily.yum
+++ /dev/null
@@ -1,3 +0,0 @@
-update
-ts run
-exit
diff --git a/etc/yum-weekly.yum b/etc/yum-weekly.yum
deleted file mode 100644
index c60fa08..0000000
--- a/etc/yum-weekly.yum
+++ /dev/null
@@ -1,4 +0,0 @@
-clean packages
-clean expire-cache
-ts run
-exit
diff --git a/etc/yum.bash b/etc/yum.bash
index f1e06e8..2faeb59 100644
--- a/etc/yum.bash
+++ b/etc/yum.bash
@@ -45,7 +45,7 @@ _yum_grouplist()
 {
     local IFS=$'\n'
     # TODO: add -d 0 when http://yum.baseurl.org/ticket/29 is fixed
-    COMPREPLY=( $( compgen -W "$( ${yum:-yum} -C grouplist $1 "$2*" \
+    COMPREPLY=( $( compgen -W "$( ${yum:-yum} -C grouplist $1 \
         2>/dev/null | sed -ne 's/^[[:space:]]\{1,\}\(.\{1,\}\)/\1/p' )" \
         -- "$2" ) )
 }
@@ -75,7 +75,7 @@ _yum_baseopts()
 {
     local opts='--help --tolerant --cacheonly --config --randomwait
         --debuglevel --showduplicates --errorlevel --rpmverbosity --quiet
-        --verbose --assumeyes --version --installroot --enablerepo
+        --verbose --assumeyes --assumeno --version --installroot --enablerepo
         --disablerepo --exclude --disableexcludes --obsoletes --noplugins
         --nogpgcheck --skip-broken --color --releasever --setopt'
     [[ $COMP_LINE == *--noplugins* ]] || \
@@ -89,6 +89,16 @@ _yum_transactions()
         sed -ne 's/^[[:space:]]*\([0-9]\{1,\}\).*/\1/p' )" -- "$cur" ) )
 }
 
+_yum_atgroups()
+{
+    if [[ $1 == \@* ]]; then
+        _yum_grouplist "" "${1:1}"
+        COMPREPLY=( "${COMPREPLY[@]/#/@}" )
+        return 0
+    fi
+    return 1
+}
+
 # arguments:
 #   1 = current word to be completed
 #   2 = previous word
@@ -231,13 +241,15 @@ _yum()
             ;;
 
         downgrade|reinstall)
-            _yum_binrpmfiles "$cur"
-            _yum_list installed "$cur"
+            if ! _yum_atgroups "$cur" ; then
+                _yum_binrpmfiles "$cur"
+                _yum_list installed "$cur"
+            fi
             return 0
             ;;
 
         erase|remove)
-            _yum_list installed "$cur"
+            _yum_atgroups "$cur" || _yum_list installed "$cur"
             return 0
             ;;
 
@@ -288,8 +300,10 @@ _yum()
             ;;
 
         install)
-            _yum_binrpmfiles "$cur"
-            _yum_list available "$cur"
+            if ! _yum_atgroups "$cur" ; then
+                _yum_binrpmfiles "$cur"
+                _yum_list available "$cur"
+            fi
             return 0
             ;;
 
@@ -318,8 +332,10 @@ _yum()
             ;;
 
         update|upgrade)
-            _yum_binrpmfiles "$cur"
-            _yum_list updates "$cur"
+            if ! _yum_atgroups "$cur" ; then
+                _yum_binrpmfiles "$cur"
+                _yum_list updates "$cur"
+            fi
             return 0
             ;;
         version)
diff --git a/output.py b/output.py
index b6aa277..00e0e6f 100755
--- a/output.py
+++ b/output.py
@@ -1,6 +1,6 @@
 #!/usr/bin/python -t
 
-"""This handles actual output from the cli"""
+"""Handle actual output from the cli."""
 
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -47,6 +47,8 @@ import yum.history
 
 from yum.i18n import utf8_width, utf8_width_fill, utf8_text_fill
 
+import locale
+
 def _term_width():
     """ Simple terminal width, limit to 20 chars. and make 0 == 80. """
     if not hasattr(urlgrabber.progress, 'terminal_width_cached'):
@@ -60,17 +62,21 @@ def _term_width():
 
 
 class YumTextMeter(TextMeter):
-
-    """
-    Text progress bar output.
-    """
+    """A class to display text progress bar output."""
 
     def update(self, amount_read, now=None):
+        """Update the status of the text progress bar
+
+        :param amount_read: the amount of data, in bytes, that has been read
+        :param now: the current time in seconds since the epoch.  If
+           *now* is not given, the output of :func:`time.time()` will
+           be used.
+        """
         checkSignals()
         TextMeter.update(self, amount_read, now)
 
 class YumTerm:
-    """some terminal "UI" helpers based on curses"""
+    """A class to provide some terminal "UI" helpers based on curses."""
 
     # From initial search for "terminfo and python" got:
     # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/475116
@@ -145,6 +151,17 @@ class YumTerm:
         self.BG_COLOR = self.__ansi_forced_BG_COLOR
 
     def reinit(self, term_stream=None, color='auto'):
+        """Reinitializes the :class:`YumTerm`.
+
+        :param term_stream:  the terminal stream that the
+           :class:`YumTerm` should be initialized to use.  If
+           *term_stream* is not given, :attr:`sys.stdout` is used.
+        :param color: when to colorize output.  Valid values are
+           'always', 'auto', and 'never'.  'always' will use ANSI codes
+           to always colorize output, 'auto' will decide whether do
+           colorize depending on the terminal, and 'never' will never
+           colorize.
+        """
         self.__enabled = True
         if not hasattr(urlgrabber.progress, 'terminal_width_cached'):
             self.columns = 80
@@ -255,6 +272,37 @@ class YumTerm:
         return re.sub(r'\$<\d+>[/*]?', '', cap)
 
     def sub(self, haystack, beg, end, needles, escape=None, ignore_case=False):
+        """Search the string *haystack* for all occurrences of any
+        string in the list *needles*.  Prefix each occurrence with
+        *beg*, and postfix each occurrence with *end*, then return the
+        modified string.  For example::
+           
+           >>> yt = YumTerm()
+           >>> yt.sub('spam and eggs', 'x', 'z', ['and'])
+           'spam xandz eggs'
+
+        This is particularly useful for emphasizing certain words
+        in output: for example, calling :func:`sub` with *beg* =
+        MODE['bold'] and *end* = MODE['normal'] will return a string
+        that when printed to the terminal will appear to be *haystack*
+        with each occurrence of the strings in *needles* in bold
+        face.  Note, however, that the :func:`sub_mode`,
+        :func:`sub_bold`, :func:`sub_fg`, and :func:`sub_bg` methods
+        provide convenient ways to access this same emphasizing functionality. 
+
+        :param haystack: the string to be modified
+        :param beg: the string to be prefixed onto matches
+        :param end: the string to be postfixed onto matches
+        :param needles: a list of strings to add the prefixes and
+           postfixes to
+        :param escape: a function that accepts a string and returns
+           the same string with problematic characters escaped.  By
+           default, :func:`re.escape` is used.
+        :param ignore_case: whether case should be ignored when
+           searching for matches
+        :return: *haystack* with *beg* prefixing, and *end*
+          postfixing, occurrences of the strings in *needles*
+        """
         if not self.__enabled:
             return haystack
 
@@ -269,27 +317,106 @@ class YumTerm:
             haystack = re.sub(pat, render, haystack)
         return haystack
     def sub_norm(self, haystack, beg, needles, **kwds):
+        """Search the string *haystack* for all occurrences of any
+        string in the list *needles*.  Prefix each occurrence with
+        *beg*, and postfix each occurrence with self.MODE['normal'],
+        then return the modified string.  If *beg* is an ANSI escape
+        code, such as given by self.MODE['bold'], this method will
+        return *haystack* with the formatting given by the code only
+        applied to the strings in *needles*.
+
+        :param haystack: the string to be modified
+        :param beg: the string to be prefixed onto matches
+        :param end: the string to be postfixed onto matches
+        :param needles: a list of strings to add the prefixes and
+           postfixes to
+        :return: *haystack* with *beg* prefixing, and self.MODE['normal']
+          postfixing, occurrences of the strings in *needles*
+        """
         return self.sub(haystack, beg, self.MODE['normal'], needles, **kwds)
 
     def sub_mode(self, haystack, mode, needles, **kwds):
+        """Search the string *haystack* for all occurrences of any
+        string in the list *needles*.  Prefix each occurrence with
+        self.MODE[*mode*], and postfix each occurrence with
+        self.MODE['normal'], then return the modified string.  This
+        will return a string that when printed to the terminal will
+        appear to be *haystack* with each occurrence of the strings in
+        *needles* in the given *mode*.
+
+        :param haystack: the string to be modified
+        :param mode: the mode to set the matches to be in.  Valid
+           values are given by self.MODE.keys().
+        :param needles: a list of strings to add the prefixes and
+           postfixes to
+        :return: *haystack* with self.MODE[*mode*] prefixing, and
+          self.MODE['normal'] postfixing, occurrences of the strings
+          in *needles*
+        """
         return self.sub_norm(haystack, self.MODE[mode], needles, **kwds)
 
     def sub_bold(self, haystack, needles, **kwds):
+        """Search the string *haystack* for all occurrences of any
+        string in the list *needles*.  Prefix each occurrence with
+        self.MODE['bold'], and postfix each occurrence with
+        self.MODE['normal'], then return the modified string.  This
+        will return a string that when printed to the terminal will
+        appear to be *haystack* with each occurrence of the strings in
+        *needles* in bold face.
+
+        :param haystack: the string to be modified
+        :param needles: a list of strings to add the prefixes and
+           postfixes to
+        :return: *haystack* with self.MODE['bold'] prefixing, and
+          self.MODE['normal'] postfixing, occurrences of the strings
+          in *needles*
+        """
         return self.sub_mode(haystack, 'bold', needles, **kwds)
     
     def sub_fg(self, haystack, color, needles, **kwds):
+        """Search the string *haystack* for all occurrences of any
+        string in the list *needles*.  Prefix each occurrence with
+        self.FG_COLOR[*color*], and postfix each occurrence with
+        self.MODE['normal'], then return the modified string.  This
+        will return a string that when printed to the terminal will
+        appear to be *haystack* with each occurrence of the strings in
+        *needles* in the given color.
+
+        :param haystack: the string to be modified
+        :param color: the color to set the matches to be in.  Valid
+           values are given by self.FG_COLOR.keys().
+        :param needles: a list of strings to add the prefixes and
+           postfixes to
+        :return: *haystack* with self.FG_COLOR[*color*] prefixing, and
+          self.MODE['normal'] postfixing, occurrences of the strings
+          in *needles*
+        """
         return self.sub_norm(haystack, self.FG_COLOR[color], needles, **kwds)
 
     def sub_bg(self, haystack, color, needles, **kwds):
+        """Search the string *haystack* for all occurrences of any
+        string in the list *needles*.  Prefix each occurrence with
+        self.BG_COLOR[*color*], and postfix each occurrence with
+        self.MODE['normal'], then return the modified string.  This
+        will return a string that when printed to the terminal will
+        appear to be *haystack* with each occurrence of the strings in
+        *needles* highlighted in the given background color.
+
+        :param haystack: the string to be modified
+        :param color: the background color to set the matches to be in.  Valid
+           values are given by self.BG_COLOR.keys().
+        :param needles: a list of strings to add the prefixes and
+           postfixes to
+        :return: *haystack* with self.BG_COLOR[*color*] prefixing, and
+          self.MODE['normal'] postfixing, occurrences of the strings
+          in *needles*
+        """
         return self.sub_norm(haystack, self.BG_COLOR[color], needles, **kwds)
 
 
 
 class YumOutput:
-
-    """
-    Main output class for the yum command line.
-    """
+    """Main output class for the yum command line."""
 
     def __init__(self):
         self.logger = logging.getLogger("yum.cli")
@@ -304,6 +431,12 @@ class YumOutput:
 
     
     def printtime(self):
+        """Return a string representing the current time in the form::
+
+           Mon dd hh:mm:ss
+
+        :return: a string representing the current time
+        """
         months = [_('Jan'), _('Feb'), _('Mar'), _('Apr'), _('May'), _('Jun'),
                   _('Jul'), _('Aug'), _('Sep'), _('Oct'), _('Nov'), _('Dec')]
         now = time.localtime(time.time())
@@ -312,14 +445,27 @@ class YumOutput:
         return ret
          
     def failureReport(self, errobj):
-        """failure output for failovers from urlgrabber"""
-        
+        """Perform failure output for failovers from urlgrabber
+
+        :param errobj: :class:`urlgrabber.grabber.CallbackObject`
+           containing information about the error
+        :raises: *errobj*.exception
+        """
         self.logger.error('%s: %s', errobj.url, errobj.exception)
         self.logger.error(_('Trying other mirror.'))
         raise errobj.exception
     
         
     def simpleProgressBar(self, current, total, name=None):
+        """Output the current status to the terminal using a simple
+        status bar.
+
+        :param current: a number representing the amount of work
+           already done
+        :param total: a number representing the total amount of work
+           to be done
+        :param name: a name to label the progress bar with
+        """
         progressbar(current, total, name)
 
     def _highlight(self, highlight):
@@ -368,9 +514,29 @@ class YumOutput:
 
     def calcColumns(self, data, columns=None, remainder_column=0,
                     total_width=None, indent=''):
-        """ Dynamically calculate the width of the fields in the data, data is
-            of the format [column-number][field_length] = rows. """
-
+        """Dynamically calculate the widths of the columns that the
+        fields in data should be placed into for output.
+        
+        :param data: a list of dictionaries that represent the data to
+           be output.  Each dictionary in the list corresponds to a
+           column of output. The keys of the dictionary are the
+           lengths of the items to be output, and the value associated
+           with a key is the number of items of that length.
+        :param columns: a list containing the minimum amount of space
+           that must be allocated for each row. This can be used to
+           ensure that there is space available in a column if, for
+           example, the actual lengths of the items being output
+           cannot be given in *data*
+        :param remainder_column: number of the column to receive a few
+           extra spaces that may remain after other allocation has
+           taken place
+        :param total_width: the total width of the output.
+           self.term.columns is used by default
+        :param indent: string that will be prefixed to a line of
+           output to create e.g. an indent
+        :return: a list of the widths of the columns that the fields
+           in data should be placed into for output
+        """
         if total_width is None:
             total_width = self.term.columns
 
@@ -473,10 +639,20 @@ class YumOutput:
         return (val, width, hibeg, hiend)
 
     def fmtColumns(self, columns, msg=u'', end=u'', text_width=utf8_width):
-        """ Return a string for columns of data, which can overflow.
-            text_width parameter finds the width of columns, this defaults to
-            utf8 but can be changed to len() if you know it'll be fine. """
-
+        """Return a row of data formatted into a string for output.
+        Items can overflow their columns. 
+
+        :param columns: a list of tuples containing the data to
+           output.  Each tuple contains first the item to be output,
+           then the amount of space allocated for the column, and then
+           optionally a type of highlighting for the item
+        :param msg: a string to begin the line of output with
+        :param end: a string to end the line of output with
+        :param text_width: a function to find the width of the items
+           in the columns.  This defaults to utf8 but can be changed
+           to len() if you know it'll be fine
+        :return: a row of data formatted into a string for output
+        """
         total_width = len(msg)
         data = []
         for col_data in columns[:-1]:
@@ -513,8 +689,18 @@ class YumOutput:
 
     def simpleList(self, pkg, ui_overflow=False, indent='', highlight=False,
                    columns=None):
-        """ Simple to use function to print a pkg as a line. """
-
+        """Print a package as a line.
+
+        :param pkg: the package to be printed
+        :param ui_overflow: unused
+        :param indent: string to be prefixed onto the line to provide
+           e.g. an indent
+        :param highlight: highlighting options for the name of the
+           package
+        :param colums: tuple containing the space allocated for each
+           column of output.  The columns are the package name, version,
+           and repository
+        """
         if columns is None:
             columns = (-40, -22, -16) # Old default
         ver = pkg.printVer()
@@ -526,9 +712,19 @@ class YumOutput:
 
     def simpleEnvraList(self, pkg, ui_overflow=False,
                         indent='', highlight=False, columns=None):
-        """ Simple to use function to print a pkg as a line, with the pkg
-            itself in envra format so it can be pased to list/install/etc. """
-
+        """Print a package as a line, with the package itself in envra
+        format so it can be passed to list/install/etc. 
+
+        :param pkg: the package to be printed
+        :param ui_overflow: unused
+        :param indent: string to be prefixed onto the line to provide
+           e.g. an indent
+        :param highlight: highlighting options for the name of the
+           package
+        :param colums: tuple containing the space allocated for each
+           column of output.  The columns the are the package envra and
+           repository
+        """
         if columns is None:
             columns = (-63, -16) # Old default
         envra = '%s%s' % (indent, str(pkg))
@@ -538,7 +734,13 @@ class YumOutput:
         print self.fmtColumns(columns, text_width=len)
 
     def fmtKeyValFill(self, key, val):
-        """ Return a key value pair in the common two column output format. """
+        """Return a key value pair in the common two column output
+        format.
+
+        :param key: the key to be formatted
+        :param val: the value associated with *key*
+        :return: the key value pair formatted in two columns for output
+        """
         val = to_str(val)
         keylen = utf8_width(key)
         cols = self.term.columns
@@ -553,6 +755,15 @@ class YumOutput:
         return ret
     
     def fmtSection(self, name, fill='='):
+        """Format and return a section header.  The format of the
+        header is a line with *name* centred, and *fill* repeated on
+        either side to fill an entire line on the terminal.
+
+        :param name: the name of the section
+        :param fill: the character to repeat on either side of *name*
+          to fill an entire line.  *fill* must be a single character.
+        :return: a string formatted to be a section header
+        """
         name = to_str(name)
         cols = self.term.columns - 2
         name_len = utf8_width(name)
@@ -577,6 +788,12 @@ class YumOutput:
         return to_unicode(s)
 
     def infoOutput(self, pkg, highlight=False):
+        """Print information about the given package.
+
+        :param pkg: the package to print information about 
+        :param hightlight: highlighting options for the name of the
+           package
+        """
         (hibeg, hiend) = self._highlight(highlight)
         print _("Name        : %s%s%s") % (hibeg, to_unicode(pkg.name), hiend)
         print _("Arch        : %s") % to_unicode(pkg.arch)
@@ -617,9 +834,22 @@ class YumOutput:
         print ""
     
     def updatesObsoletesList(self, uotup, changetype, columns=None):
-        """takes an updates or obsoletes tuple of pkgobjects and
-           returns a simple printed string of the output and a string
-           explaining the relationship between the tuple members"""
+        """Print a simple string that explains the relationship
+        between the members of an update or obsoletes tuple.
+
+        :param uotup: an update or obsoletes tuple.  The first member
+           is the new package, and the second member is the old
+           package
+        :param changetype: a string indicating what the change between
+           the packages is, e.g. 'updates' or 'obsoletes'
+        :param columns: a tuple containing information about how to
+           format the columns of output.  The absolute value of each
+           number in the tuple indicates how much space has been
+           allocated for the corresponding column.  If the number is
+           negative, the text in the column will be left justified,
+           and if it is positive, the text will be right justified.
+           The columns of output are the package name, version, and repository
+        """
         (changePkg, instPkg) = uotup
 
         if columns is not None:
@@ -640,12 +870,44 @@ class YumOutput:
 
     def listPkgs(self, lst, description, outputType, highlight_na={},
                  columns=None, highlight_modes={}):
-        """outputs based on whatever outputType is. Current options:
-           'list' - simple pkg list
-           'info' - similar to rpm -qi output
-           ...also highlight_na can be passed, and we'll highlight
-           pkgs with (names, arch) in that set."""
-
+        """Prints information about the given list of packages.
+
+        :param lst: a list of packages to print information about
+        :param description: string describing what the list of
+           packages contains, e.g. 'Available Packages'
+        :param outputType: The type of information to be printed.
+           Current options::
+           
+              'list' - simple pkg list
+              'info' - similar to rpm -qi output
+        :param highlight_na: a dictionary containing information about
+              packages that should be highlighted in the output.  The
+              dictionary keys are (name, arch) tuples for the package,
+              and the associated values are the package objects
+              themselves.
+        :param columns: a tuple containing information about how to
+           format the columns of output.  The absolute value of each
+           number in the tuple indicates how much space has been
+           allocated for the corresponding column.  If the number is
+           negative, the text in the column will be left justified,
+           and if it is positive, the text will be right justified.
+           The columns of output are the package name, version, and
+           repository
+        :param highlight_modes: dictionary containing information
+              about to highlight the packages in *highlight_na*.
+              *highlight_modes* should contain the following keys::
+                 
+                 'not_in' - highlighting used for packages not in *highlight_na*
+                 '=' - highlighting used when the package versions are equal
+                 '<' - highlighting used when the package has a lower version number
+                 '>' - highlighting used when the package has a higher version number
+        :return: (exit_code, [errors])
+
+        exit_code is::
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+        """
         if outputType in ['list', 'info']:
             thingslisted = 0
             if len(lst) > 0:
@@ -679,8 +941,11 @@ class YumOutput:
     
         
     def userconfirm(self):
-        """gets a yes or no from the user, defaults to No"""
+        """Get a yes or no from the user, and default to No
 
+        :return: True if the user selects yes, and False if the user
+           selects no
+        """
         yui = (to_unicode(_('y')), to_unicode(_('yes')))
         nui = (to_unicode(_('n')), to_unicode(_('no')))
         aui = (yui[0], yui[1], nui[0], nui[1])
@@ -774,6 +1039,10 @@ class YumOutput:
                                          columns=columns)
     
     def displayPkgsInGroups(self, group):
+        """Output information about the packages in a given group
+        
+        :param group: a Group object to output information about
+        """
         print _('\nGroup: %s') % group.ui_name
 
         verb = self.verbose_logger.isEnabledFor(logginglevels.DEBUG_3)
@@ -807,8 +1076,11 @@ class YumOutput:
                                            columns=columns)
 
     def depListOutput(self, results):
-        """take a list of findDeps results and 'pretty print' the output"""
-        
+        """Format and output a list of findDeps results
+
+        :param results: a list of package dependency information as
+           returned by findDeps
+        """
         verb = self.verbose_logger.isEnabledFor(logginglevels.DEBUG_3)
         for pkg in sorted(results):
             print _("package: %s") % pkg.compactPrint()
@@ -832,7 +1104,18 @@ class YumOutput:
                     print "   provider: %s" % po.compactPrint()
 
     def format_number(self, number, SI=0, space=' '):
-        """Turn numbers into human-readable metric-like numbers"""
+        """Return a human-readable metric-like string representation
+        of a number.
+
+        :param number: the number to be converted to a human-readable form
+        :param SI: If is 0, this function will use the convention
+           that 1 kilobyte = 1024 bytes, otherwise, the convention
+           that 1 kilobyte = 1000 bytes will be used
+        :param space: string that will be placed between the number
+           and the SI prefix
+        :return: a human-readable metric-like string representation of
+           *number*
+        """
         symbols = [ ' ', # (none)
                     'k', # kilo
                     'M', # mega
@@ -870,16 +1153,31 @@ class YumOutput:
 
     @staticmethod
     def format_time(seconds, use_hours=0):
+        """Return a human-readable string representation of a number
+        of seconds.  The string will show seconds, minutes, and
+        optionally hours.
+
+        :param seconds: the number of seconds to convert to a
+           human-readable form
+        :param use_hours: If use_hours is 0, the representation will
+           be in minutes and seconds. Otherwise, it will be in hours,
+           minutes, and seconds
+        :return: a human-readable string representation of *seconds*
+        """
         return urlgrabber.progress.format_time(seconds, use_hours)
 
     def matchcallback(self, po, values, matchfor=None, verbose=None,
                       highlight=None):
-        """ Output search/provides type callback matches. po is the pkg object,
-            values are the things in the po that we've matched.
-            If matchfor is passed, all the strings in that list will be
-            highlighted within the output.
-            verbose overrides logginglevel, if passed. """
-
+        """Output search/provides type callback matches.
+
+        :param po: the package object that matched the search
+        :param values: the information associated with *po* that
+           matched the search
+        :param matchfor: a list of strings to be highlighted in the
+           output
+        :param verbose: whether to output extra verbose information
+        :param highlight: highlighting options for the highlighted matches
+        """
         if self.conf.showdupesfromrepos:
             msg = '%s : ' % po
         else:
@@ -935,10 +1233,23 @@ class YumOutput:
         print '\n\n'
 
     def matchcallback_verbose(self, po, values, matchfor=None):
+        """Output search/provides type callback matches.  This will
+        output more information than :func:`matchcallback`.
+
+        :param po: the package object that matched the search
+        :param values: the information associated with *po* that
+           matched the search
+        :param matchfor: a list of strings to be highlighted in the
+           output
+        """
         return self.matchcallback(po, values, matchfor, verbose=True)
         
     def reportDownloadSize(self, packages, installonly=False):
-        """Report the total download size for a set of packages"""
+        """Report the total download size for a set of packages
+        
+        :param packages: a list of package objects
+        :param installonly: whether the transaction consists only of installations
+        """
         totsize = 0
         locsize = 0
         insize  = 0
@@ -982,7 +1293,10 @@ class YumOutput:
                                         self.format_number(insize))
 
     def reportRemoveSize(self, packages):
-        """Report the total size of packages being removed. """
+        """Report the total size of packages being removed.
+
+        :param packages: a list of package objects
+        """
         totsize = 0
         error = False
         for pkg in packages:
@@ -1002,8 +1316,9 @@ class YumOutput:
                                     self.format_number(totsize))
             
     def listTransaction(self):
-        """returns a string rep of the  transaction in an easy-to-read way."""
-        
+        """Return a string representation of the transaction in an
+        easy-to-read format.
+        """
         self.tsInfo.makelists(True, True)
         pkglist_lines = []
         data  = {'n' : {}, 'v' : {}, 'r' : {}}
@@ -1115,6 +1430,12 @@ Transaction Summary
         return ''.join(out)
         
     def postTransactionOutput(self):
+        """Returns a human-readable summary of the results of the
+        transaction.
+        
+        :return: a string containing a human-readable summary of the
+           results of the transaction
+        """
         out = ''
         
         self.tsInfo.makelists()
@@ -1179,9 +1500,9 @@ Transaction Summary
         return out
 
     def setupProgressCallbacks(self):
-        """sets up the progress callbacks and various 
-           output bars based on debug level"""
-
+        """Set up the progress callbacks and various 
+           output bars based on debug level.
+        """
         # if we're below 2 on the debug level we don't need to be outputting
         # progress bars - this is hacky - I'm open to other options
         # One of these is a download
@@ -1216,10 +1537,12 @@ Transaction Summary
         self.dsCallback = dscb
     
     def setupProgessCallbacks(self):
-        # api purposes only to protect the typo
+        """This function is for API purposes only to protect the typo."""
         self.setupProgressCallbacks()
     
     def setupKeyImportCallbacks(self):
+        """Set up callbacks to import and confirm gpg public keys."""
+
         confirm_func = self._cli_confirm_gpg_key_import
         gpg_import_func = self.getKeyForRepo
         gpgca_import_func = self.getCAKeyForRepo
@@ -1233,14 +1556,12 @@ Transaction Summary
             self.repos.gpgca_import_func = gpgca_import_func
 
     def interrupt_callback(self, cbobj):
-        '''Handle CTRL-C's during downloads
-
-        If a CTRL-C occurs a URLGrabError will be raised to push the download
-        onto the next mirror.  
-        
-        If two CTRL-C's occur in quick succession then yum will exit.
+        '''Handle CTRL-C's during downloads.  If a CTRL-C occurs a
+        URLGrabError will be raised to push the download onto the next
+        mirror.  If two CTRL-C's occur in quick succession then yum
+        will exit.
 
-        @param cbobj: urlgrabber callback obj
+        :param cbobj: :class:`urlgrabber.grabber.CallbackObject`
         '''
         delta_exit_chk = 2.0      # Delta between C-c's so we treat as exit
         delta_exit_str = _("two") # Human readable version of above
@@ -1269,6 +1590,14 @@ to exit.
 
     def download_callback_total_cb(self, remote_pkgs, remote_size,
                                    download_start_timestamp):
+        """Outputs summary information about the download process.
+
+        :param remote_pkgs: a list of package objects that were downloaded
+        :param remote_size: the total amount of information that was
+           downloaded, in bytes
+        :param download_start_timestamp: the time when the download
+           process started, in seconds since the epoch
+        """
         if len(remote_pkgs) <= 1:
             return
         if not hasattr(urlgrabber.progress, 'TerminalLine'):
@@ -1434,8 +1763,17 @@ to exit.
         return tids, printall
 
     def historyListCmd(self, extcmds):
-        """ Shows the user a list of data about the history. """
+        """Output a list of information about the history of yum
+        transactions.
+
+        :param extcmds: list of extra command line arguments
+        :return: (exit_code, [errors])
+
+        exit_code is::
 
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+        """
         tids, printall = self._history_list_transactions(extcmds)
         if tids is None:
             return 1, ['Failed history list']
@@ -1564,6 +1902,16 @@ to exit.
         return old[0]
 
     def historyInfoCmd(self, extcmds):
+        """Output information about a transaction in history
+
+        :param extcmds: list of extra command line arguments
+        :return: (exit_code, [errors])
+
+        exit_code is::
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+        """
         def str2int(x):
             try:
                 return int(x)
@@ -1656,6 +2004,9 @@ to exit.
     def _hpkg2from_repo(self, hpkg):
         """ Given a pkg, find the ipkg.ui_from_repo ... if none, then
             get an apkg. ... and put a ? in there. """
+        if 'from_repo' in hpkg.yumdb_info:
+            return hpkg.ui_from_repo
+
         ipkgs = self.rpmdb.searchPkgTuple(hpkg.pkgtup)
         if not ipkgs:
             apkgs = self.pkgSack.searchPkgTuple(hpkg.pkgtup)
@@ -1833,6 +2184,13 @@ to exit.
                               'Updated'      : _('Updated'),
                               }
     def historyInfoCmdPkgsAltered(self, old, pats=[]):
+        """Print information about how packages are altered in a transaction.
+
+        :param old: the :class:`history.YumHistoryTransaction` to
+           print information about
+        :param pats: a list of patterns.  Packages that match a patten
+           in *pats* will be highlighted in the output
+        """
         last = None
         #  Note that these don't use _simple_pkg() because we are showing what
         # happened to them in the transaction ... not the difference between the
@@ -1886,6 +2244,10 @@ to exit.
                                         self._hpkg2from_repo(hpkg))
 
     def historySummaryCmd(self, extcmds):
+        """Print a summary of transactions in history.
+
+        :param extcmds: list of extra command line arguments
+        """
         tids, printall = self._history_list_transactions(extcmds)
         if tids is None:
             return 1, ['Failed history info']
@@ -1946,6 +2308,10 @@ to exit.
                              utf8_width_fill(uiacts, 16, 16), count)
 
     def historyAddonInfoCmd(self, extcmds):
+        """Print addon information about transaction in history.
+
+        :param extcmds: list of extra command line arguments
+        """
         tid = None
         if len(extcmds) > 1:
             tid = extcmds[1]
@@ -1983,16 +2349,19 @@ to exit.
         
         for item in extcmds[2:]:
             if item in addon_info:
-                print '%s:' % item
-                print self.history.return_addon_data(hist_data.tid, item)
+                self.verbose_logger.log(logginglevels.INFO_2, '%s:', item)
+                print self.history.return_addon_data(hist_data.tid, item),
+                self.verbose_logger.log(logginglevels.INFO_2, '')
             else:
                 print _('%s: No additional data found by this name') % item
-
-            print ''
+            self.verbose_logger.log(logginglevels.INFO_2, '')
 
     def historyPackageListCmd(self, extcmds):
-        """ Shows the user a list of data about the history, from the point
-            of a package(s) instead of via. transactions. """
+        """Print a list of information about transactions from history
+        that involve the given package or packages.
+
+        :param extcmds: list of extra command line arguments
+        """
         tids = self.history.search(extcmds)
         limit = None
         if extcmds and not tids:
@@ -2078,9 +2447,95 @@ to exit.
             if lastdbv.end_rpmdbversion != rpmdbv:
                 self._rpmdb_warn_checks()
 
+    def historyPackageInfoCmd(self, extcmds):
+        """Print information about packages in history transactions.
+
+        :param extcmds: list of extra command line arguments
+        """
+        tids = self.history.search(extcmds)
+        limit = None
+        if extcmds and not tids:
+            self.logger.critical(_('Bad transaction IDs, or package(s), given'))
+            return 1, ['Failed history packages-info']
+        if not tids:
+            limit = 20
+
+        all_uistates = self._history_state2uistate
+
+        num = 0
+        for old in self.history.old(tids, limit=limit):
+            if limit is not None and num and (num +len(old.trans_data)) > limit:
+                break
+            last = None
+
+            for hpkg in old.trans_data: # Find a pkg to go with each cmd...
+                if limit is None:
+                    x,m,u = yum.packages.parsePackages([hpkg], extcmds)
+                    if not x and not m:
+                        continue
+
+                uistate = all_uistates.get(hpkg.state, hpkg.state)
+                if num:
+                    print ""
+                print _("Transaction ID :"), old.tid
+                tm = time.ctime(old.beg_timestamp)
+                print _("Begin time     :"), tm
+                print _("Package        :"), hpkg.ui_nevra
+                print _("State          :"), uistate
+                if hpkg.size is not None:
+                    num = int(hpkg.size)
+                    print _("Size           :"), locale.format("%d", num, True)
+                if hpkg.buildhost is not None:
+                    print _("Build host     :"), hpkg.buildhost
+                if hpkg.buildtime is not None:
+                    tm = time.ctime(int(hpkg.buildtime))
+                    print _("Build time     :"), tm
+                if hpkg.packager is not None:
+                    print _("Packager       :"), hpkg.packager
+                if hpkg.vendor is not None:
+                    print _("Vendor         :"), hpkg.vendor
+                if hpkg.license is not None:
+                    print _("License        :"), hpkg.license
+                if hpkg.url is not None:
+                    print _("URL            :"), hpkg.url
+                if hpkg.sourcerpm is not None:
+                    print _("Source RPM     :"), hpkg.sourcerpm
+                if hpkg.committime is not None:
+                    tm = time.ctime(int(hpkg.committime))
+                    print _("Commit Time    :"), tm
+                if hpkg.committer is not None:
+                    print _("Committer      :"), hpkg.committer
+                if hpkg.yumdb_info.reason is not None:
+                    print _("Reason         :"), hpkg.yumdb_info.reason
+                if hpkg.yumdb_info.command_line is not None:
+                    print _("Command Line   :"), hpkg.yumdb_info.command_line
+                if hpkg.yumdb_info.from_repo is not None:
+                    print _("From repo      :"), hpkg.yumdb_info.from_repo
+                if hpkg.yumdb_info.installed_by is not None:
+                    uid = int(hpkg.yumdb_info.installed_by)
+                    name = self._pwd_ui_username(uid)
+                    print _("Installed by   :"), name
+                if hpkg.yumdb_info.changed_by is not None:
+                    uid = int(hpkg.yumdb_info.changed_by)
+                    name = self._pwd_ui_username(uid)
+                    print _("Changed by     :"), name
+
+                num += 1
+
+        # And, again, copy and paste...
+        lastdbv = self.history.last()
+        if lastdbv is None:
+            self._rpmdb_warn_checks(warn=False)
+        else:
+            #  If this is the last transaction, is good and it doesn't
+            # match the current rpmdb ... then mark it as bad.
+            rpmdbv  = self.rpmdb.simpleVersion(main_only=True)[0]
+            if lastdbv.end_rpmdbversion != rpmdbv:
+                self._rpmdb_warn_checks()
+
 
 class DepSolveProgressCallBack:
-    """provides text output callback functions for Dependency Solver callback"""
+    """A class to provide text output callback functions for Dependency Solver callback."""
     
     def __init__(self, ayum=None):
         """requires yum-cli log and errorlog functions as arguments"""
@@ -2089,6 +2544,25 @@ class DepSolveProgressCallBack:
         self.ayum = ayum
 
     def pkgAdded(self, pkgtup, mode):
+        """Print information about a package being added to the
+        transaction set.
+
+        :param pkgtup: tuple containing the package name, arch,
+           version, and repository
+        :param mode: a short string indicating why the package is
+           being added to the transaction set.
+
+        Valid current values for *mode* are::
+        
+           i = the package will be installed
+           u = the package will be an update
+           e = the package will be erased
+           r = the package will be reinstalled
+           d = the package will be a downgrade
+           o = the package will be obsoleting another package
+           ud = the package will be updated
+           od = the package will be obsoleted
+        """
         modedict = { 'i': _('installed'),
                      'u': _('an update'),
                      'e': _('erased'),
@@ -2104,43 +2578,85 @@ class DepSolveProgressCallBack:
             modeterm)
         
     def start(self):
+        """Perform setup at the beginning of the dependency solving
+        process.
+        """
         self.loops += 1
         
     def tscheck(self):
+        """Output a message stating that a transaction check is beginning."""
         self.verbose_logger.log(logginglevels.INFO_2, _('--> Running transaction check'))
         
     def restartLoop(self):
+        """Output a message stating that dependency resolution is restarting."""
         self.loops += 1
         self.verbose_logger.log(logginglevels.INFO_2,
             _('--> Restarting Dependency Resolution with new changes.'))
         self.verbose_logger.debug('---> Loop Number: %d', self.loops)
     
     def end(self):
+        """Output a message stating that dependency resolution has finished."""
         self.verbose_logger.log(logginglevels.INFO_2,
             _('--> Finished Dependency Resolution'))
 
     
     def procReq(self, name, formatted_req):
+        """Output a message stating that the package *formatted_req*
+        is being processed as a dependency for the package *name*.
+
+        :param name: the name of the package that *formatted_req* is a
+           dependency of
+        :param formatted_req: a string representing the package that
+           is being processed as a dependency of *name*
+        """
         self.verbose_logger.log(logginglevels.INFO_2,
             _('--> Processing Dependency: %s for package: %s'), formatted_req,
             name)
 
     def procReqPo(self, po, formatted_req):
+        """Output a message stating that the package *formatted_req*
+        is being processed as a dependency for the package *po*.
+
+        :param po: the package object that *formatted_req* is a
+           dependency of
+        :param formatted_req: a string representing the package that
+           is being processed as a dependency of *po*
+        """
         self.verbose_logger.log(logginglevels.INFO_2,
             _('--> Processing Dependency: %s for package: %s'), formatted_req,
             po)
     
     def groupRemoveReq(self, po, hits):
+        """Output a message stating that the given package will not be
+        removed. This method is used during leaf-only group remove
+        commands to indicate that the package will be kept.
+
+        :param po: the :class:`yum.packages.PackageObject` that will
+           not be removed
+        :param hits: unused
+        """
         self.verbose_logger.log(logginglevels.INFO_2,
             _('---> Keeping package: %s'), po)
 
     def unresolved(self, msg):
+        """Output a message stating that there is an unresolved
+        dependency.
+
+        :param msg: string giving information about the unresolved
+        dependency
+        """
         self.verbose_logger.log(logginglevels.INFO_2, _('--> Unresolved Dependency: %s'),
             msg)
 
     def format_missing_requires(self, reqPo, reqTup):
-        """ Create a message for errorlist, non-cli users could also store this
-            data somewhere and not print errorlist. """
+        """Return an error message stating that a package required to
+        fulfill a dependency is missing.
+
+        :param reqPo: the package object that has a dependency that
+           cannot be fulfilled
+        :param reqTup: the name, flags, and version of the package
+           needed to fulfil the dependency
+        """
         needname, needflags, needversion = reqTup
 
         yb = self.ayum
@@ -2225,45 +2741,89 @@ class DepSolveProgressCallBack:
         return msg
     
     def procConflict(self, name, confname):
+        """Print a message stating that two packages in the
+        transaction conflict.
+
+        :param name: the name of the first package involved in the
+           conflict 
+        :param confname: the name of the second package involved in
+           the conflict
+        """
         self.verbose_logger.log(logginglevels.INFO_2,
             _('--> Processing Conflict: %s conflicts %s'),
                                 name, confname)
 
     def procConflictPo(self, po, confname):
+        """Print a message stating that two packages in the
+        transaction conflict.
+
+        :param name: the first package object involved in the
+           conflict 
+        :param confname: the second package object involved in
+           the conflict
+        """
         self.verbose_logger.log(logginglevels.INFO_2,
             _('--> Processing Conflict: %s conflicts %s'),
                                 po, confname)
 
     def transactionPopulation(self):
+        """Output a message stating that the transaction set is being populated."""
+
         self.verbose_logger.log(logginglevels.INFO_2, _('--> Populating transaction set '
             'with selected packages. Please wait.'))
     
     def downloadHeader(self, name):
+        """Output a message stating that the header for the given
+        package is being downloaded.
+
+        :param name: the name of the package
+        """
         self.verbose_logger.log(logginglevels.INFO_2, _('---> Downloading header for %s '
             'to pack into transaction set.'), name)
        
 
 class CacheProgressCallback:
+    """A class to handle text output callbacks during metadata cache updates."""
 
-    '''
-    The class handles text output callbacks during metadata cache updates.
-    '''
-    
     def __init__(self):
         self.logger = logging.getLogger("yum.cli")
         self.verbose_logger = logging.getLogger("yum.verbose.cli")
         self.file_logger = logging.getLogger("yum.filelogging.cli")
 
     def log(self, level, message):
+        """Output a log message.
+
+        :param level: the logging level for the message
+        :param message: the message
+        """
         self.verbose_logger.log(level, message)
 
     def errorlog(self, level, message):
+        """Output an errorlog message.
+
+        :param level: the logging level for the message
+        :param message: the message
+        """
         self.logger.log(level, message)
 
     def filelog(self, level, message):
+        """Output a file log message.
+
+        :param level: the logging level for the message
+        :param message: the message
+        """
         self.file_logger.log(level, message)
 
     def progressbar(self, current, total, name=None):
+        """Output the current status to the terminal using a progress
+        status bar.
+
+        :param current: a number representing the amount of work
+           already done
+        :param total: a number representing the total amount of work
+           to be done
+        :param name: a name to label the progress bar with
+        """
         progressbar(current, total, name)
 
 def _pkgname_ui(ayum, pkgname, ts_states=None):
@@ -2316,10 +2876,7 @@ def _pkgname_ui(ayum, pkgname, ts_states=None):
     return pkgname
 
 class YumCliRPMCallBack(RPMBaseCallback):
-
-    """
-    Yum specific callback class for RPM operations.
-    """
+    """A Yum specific callback class for RPM operations."""
 
     width = property(lambda x: _term_width())
 
@@ -2337,11 +2894,31 @@ class YumCliRPMCallBack(RPMBaseCallback):
     #  Installing things have pkg objects passed to the events, so only need to
     # lookup for erased/obsoleted.
     def pkgname_ui(self, pkgname, ts_states=('e', 'od', 'ud', None)):
-        """ Get more information on a simple pkgname, if we can. """
+        """Return more information on a simple pkgname, if possible.
+
+        :param pkgname: the name of the package to find information about
+        :param ts_states: a tuple containing the states where the
+           package might be found
+        """
         return _pkgname_ui(self.ayum, pkgname, ts_states)
 
     def event(self, package, action, te_current, te_total, ts_current, ts_total):
-        # this is where a progress bar would be called
+        """Output information about an rpm operation.  This may
+        include a text progress bar.
+
+        :param package: the package involved in the event
+        :param action: the type of action that is taking place.  Valid
+           values are given by
+           :func:`rpmtrans.RPMBaseCallback.action.keys()`
+        :param te_current: a number representing the amount of work
+           already done in the current transaction
+        :param te_total: a number representing the total amount of work
+           to be done in the current transaction
+        :param ts_current: the number of the current transaction in
+           transaction set
+        :param ts_total: the total number of transactions in the
+           transaction set
+        """
         process = self.action[action]
 
         if not hasattr(self, '_max_action_wid'):
@@ -2366,6 +2943,7 @@ class YumCliRPMCallBack(RPMBaseCallback):
         
         if self.output and (sys.stdout.isatty() or te_current == te_total):
             (fmt, wid1, wid2) = self._makefmt(percent, ts_current, ts_total,
+                                              progress=sys.stdout.isatty(),
                                               pkgname=pkgname, wid1=wid1)
             msg = fmt % (utf8_width_fill(process, wid1, wid1),
                          utf8_width_fill(pkgname, wid2, wid2))
@@ -2377,6 +2955,11 @@ class YumCliRPMCallBack(RPMBaseCallback):
                 print " "
 
     def scriptout(self, package, msgs):
+        """Print messages originating from a package script.
+
+        :param package: unused
+        :param msgs: the messages coming from the script
+        """
         if msgs:
             sys.stdout.write(to_unicode(msgs))
             sys.stdout.flush()
@@ -2431,6 +3014,15 @@ class YumCliRPMCallBack(RPMBaseCallback):
 
 
 def progressbar(current, total, name=None):
+    """Output the current status to the terminal using a simple
+    text progress bar consisting of 50 # marks.
+
+    :param current: a number representing the amount of work
+       already done
+    :param total: a number representing the total amount of work
+       to be done
+    :param name: a name to label the progress bar with
+    """
     """simple progress bar 50 # marks"""
     
     mark = '#'
diff --git a/po/pygettext.py b/po/pygettext.py
index 276a433..7f13138 100644
--- a/po/pygettext.py
+++ b/po/pygettext.py
@@ -1,4 +1,5 @@
 #!/usr/bin/python
+# coding=utf-8
 # Originally written by Barry Warsaw <bwarsaw@python.org>
 #
 # minimally patched to make it even more xgettext compatible 
diff --git a/rpmUtils/arch.py b/rpmUtils/arch.py
index 6082005..ad7bbb1 100644
--- a/rpmUtils/arch.py
+++ b/rpmUtils/arch.py
@@ -3,6 +3,8 @@
 
 import os
 
+_ppc64_native_is_best = False
+
 # dict mapping arch -> ( multicompat, best personality, biarch personality )
 multilibArches = { "x86_64":  ( "athlon", "x86_64", "athlon" ),
                    "sparc64v": ( "sparcv9v", "sparcv9v", "sparc64v" ),
@@ -10,6 +12,8 @@ multilibArches = { "x86_64":  ( "athlon", "x86_64", "athlon" ),
                    "ppc64":   ( "ppc", "ppc", "ppc64" ),
                    "s390x":   ( "s390", "s390x", "s390" ),
                    }
+if _ppc64_native_is_best:
+    multilibArches["ppc64"] = ( "ppc", "ppc64", "ppc64" )
 
 arches = {
     # ia32
@@ -339,7 +343,7 @@ def getBestArch(myarch=None):
     if arch.startswith("sparc64"):
         arch = multilibArches[arch][1]
 
-    if arch.startswith("ppc64"):
+    if arch.startswith("ppc64") and not _ppc64_native_is_best:
         arch = 'ppc'
 
     return arch
@@ -357,7 +361,7 @@ def getBaseArch(myarch=None):
 
     if myarch.startswith("sparc64"):
         return "sparc"
-    elif myarch.startswith("ppc64"):
+    elif myarch.startswith("ppc64") and not _ppc64_native_is_best:
         return "ppc"
     elif myarch.startswith("arm"):
         return "arm"
diff --git a/shell.py b/shell.py
index 999bffc..34a492e 100644
--- a/shell.py
+++ b/shell.py
@@ -23,16 +23,14 @@ import cmd
 import shlex
 import logging
 
-from yum import Errors
+from yum import Errors, _
 from yum.constants import *
 import yum.logginglevels as logginglevels
-
+from yum.i18n import to_utf8
+import __builtin__
 
 class YumShell(cmd.Cmd):
-
-    """
-    Interactive yum shell.
-    """
+    """A class to implement an interactive yum shell."""
 
     def __init__(self, base):
         cmd.Cmd.__init__(self)
@@ -75,8 +73,39 @@ class YumShell(cmd.Cmd):
                 raise Errors.YumBaseError, "Fatal error in script, exiting"
         
         return inputs
-        
+
+    def cmdloop(self, *args, **kwargs):
+        """ Sick hack for readline. """
+
+        oraw_input = raw_input
+        owriter    = sys.stdout
+        _ostdout   = owriter.stream
+
+        def _sick_hack_raw_input(prompt):
+            sys.stdout = _ostdout
+            rret = oraw_input(to_utf8(prompt))
+            sys.stdout = owriter
+
+            return rret
+
+        __builtin__.raw_input = _sick_hack_raw_input
+
+        try:
+            cret = cmd.Cmd.cmdloop(self, *args, **kwargs)
+        except:
+            __builtin__.raw_input  = oraw_input
+            raise
+
+        __builtin__.raw_input = oraw_input
+
+        return cret
+
     def script(self):
+        """Execute a script file in the yum shell.  The location of
+        the script file is supplied by the :class:`cli.YumBaseCli`
+        object that is passed as a parameter to the :class:`YumShell`
+        object when it is created.
+        """
         try:
             fd = open(self.file, 'r')
         except IOError:
@@ -90,6 +119,13 @@ class YumShell(cmd.Cmd):
         return True
             
     def default(self, line):
+        """Handle the next line of input if there is not a dedicated
+        method of :class:`YumShell` to handle it.  This method will
+        handle yum commands that are not unique to the shell, such as
+        install, erase, etc.
+
+        :param line: the next line of input
+        """
         if len(line) > 0 and line.strip()[0] == '#':
             pass
         else:
@@ -117,9 +153,15 @@ class YumShell(cmd.Cmd):
                 self.base.doCommands()
     
     def emptyline(self):
+        """Do nothing on an empty line of input."""
         pass
 
     def completenames(self, text, line, begidx, endidx):
+        """Return a list of possible completions of a command.
+
+        :param text: the command to be completed
+        :return: a list of possible completions of the command
+        """
         ret = cmd.Cmd.completenames(self, text, line, begidx, endidx)
         for command in self.base.yum_cli_commands:
             if command.startswith(text) and command != "shell":
@@ -127,6 +169,11 @@ class YumShell(cmd.Cmd):
         return ret
 
     def do_help(self, arg):
+        """Output help information.
+
+        :param arg: the command to ouput help information about. If
+           *arg* is an empty string, general help will be output.
+        """
         msg = """
     Shell specific arguments:
       config - set config options
@@ -166,21 +213,47 @@ class YumShell(cmd.Cmd):
         self.verbose_logger.info(msg)
         
     def do_EOF(self, line):
+        """Exit the shell when EOF is reached.
+
+        :param line: unused
+        """
         self.resultmsgs = ['Leaving Shell']
         return True
     
     def do_quit(self, line):
+        """Exit the shell.
+
+        :param line: unused
+        """
         self.resultmsgs = ['Leaving Shell']
         return True
     
     def do_exit(self, line):
+        """Exit the shell.
+
+        :param line: unused
+        """
         self.resultmsgs = ['Leaving Shell']
         return True
     
     def do_ts(self, line):
+        """Handle the ts alias of the :func:`do_transaction` method.
+
+        :param line: the remainder of the line, containing the name of
+           a subcommand.  If no subcommand is given, run the list subcommand.
+        """
         self.do_transaction(line)
 
     def do_transaction(self, line):
+        """Execute the given transaction subcommand.  The list
+        subcommand outputs the contents of the transaction, the reset
+        subcommand clears the transaction, the solve subcommand solves
+        dependencies for the transaction, and the run subcommand
+        executes the transaction.
+
+        :param line: the remainder of the line, containing the name of
+           a subcommand.  If no subcommand is given, run the list subcommand.
+        """
         (cmd, args, line) = self.parseline(line)
         if cmd in ['list', None]:
             self.verbose_logger.log(logginglevels.INFO_2,
@@ -210,6 +283,15 @@ class YumShell(cmd.Cmd):
             self.do_help('transaction')
     
     def do_config(self, line):
+        """Configure yum shell options.
+        
+        :param line: the remainder of the line, containing an option,
+           and then optionally a value in the form [option] [value].
+           Valid options are one of the following: debuglevel,
+           errorlevel, obsoletes, gpgcheck, assumeyes, exclude.  If no
+           value is given, print the current value.  If a value is
+           supplied, set the option to the given value.
+        """
         (cmd, args, line) = self.parseline(line)
         # logs
         if cmd in ['debuglevel', 'errorlevel']:
@@ -264,9 +346,23 @@ class YumShell(cmd.Cmd):
             self.do_help('config')
 
     def do_repository(self, line):
+        """Handle the repository alias of the :func:`do_repo` method.
+
+        :param line: the remainder of the line, containing the name of
+           a subcommand.
+        """
         self.do_repo(line)
         
     def do_repo(self, line):
+        """Execute the given repo subcommand.  The list subcommand
+        lists repositories and their statuses, the enable subcommand
+        enables the given repository, and the disable subcommand
+        disables the given repository.
+
+        :param line: the remainder of the line, containing the name of
+           a subcommand and other parameters if required.  If no
+           subcommand is given, run the list subcommand.
+        """
         (cmd, args, line) = self.parseline(line)
         if cmd in ['list', None]:
             # Munge things to run the repolist command
@@ -338,6 +434,10 @@ class YumShell(cmd.Cmd):
         print line
         
     def do_run(self, line):
+        """Run the transaction.
+
+        :param line: unused
+        """
         if len(self.base.tsInfo) > 0:
             try:
                 (code, msgs) = self.base.buildTransaction()
diff --git a/test/check-po-yes-no.py b/test/check-po-yes-no.py
index e22318e..b9cb8aa 100755
--- a/test/check-po-yes-no.py
+++ b/test/check-po-yes-no.py
@@ -16,6 +16,8 @@ def trans(msg, default):
         msg = msg[:-2]
     return unicode(msg, encoding='utf-8')
 
+allow_plain_yn = True
+
 for fname in glob.glob("po/*.po"):
     next = None
     is_this_ok  = None
@@ -32,7 +34,8 @@ for fname in glob.glob("po/*.po"):
         if next is not None:
             if next == 'is_this_ok':
                 sis_this_ok = line
-                if line == 'msgstr ""\n' or line.find('[y/N]') != -1:
+                if line == 'msgstr ""\n' or (not allow_plain_yn and
+                                             line.find('[y/N]') != -1):
                     is_this_ok = False
                 else:
                     is_this_ok = True
@@ -62,9 +65,9 @@ for fname in glob.glob("po/*.po"):
             next = 'n'
     if (is_this_ok is None or
         yes is None or
-        y   is None or
+        (not allow_plain_yn and y   is None) or
         no  is None or
-        n   is None):
+        (not allow_plain_yn and n   is None)):
         print >>sys.stderr, """\
 ERROR: Can't find all the msg id's in %s
 is_this_ok %s
@@ -96,6 +99,10 @@ n          %5s: %s
        to_utf8(is_this_ok), to_utf8(sis_this_ok),
        to_utf8(yes), to_utf8(syes), to_utf8(y), to_utf8(sy),
        to_utf8(no), to_utf8(sno), to_utf8(n), to_utf8(sn))
+
+    if allow_plain_yn:
+        continue
+
     if syes[0] != sy:
         print >>sys.stderr, """\
 ERROR: yes/y translations don't match in: %s
diff --git a/test/simpleobsoletestests.py b/test/simpleobsoletestests.py
index 97a9923..70dde98 100644
--- a/test/simpleobsoletestests.py
+++ b/test/simpleobsoletestests.py
@@ -244,6 +244,42 @@ class SimpleObsoletesTests(OperationsTests):
         self.assert_(res=='ok', msg)
         self.assertResult((p.obsoletes_noarch,))
 
+    def testObsoletesOffPostInst1(self):
+        p = self.pkgs
+        res, msg = self.runOperation(['install', 'zsh'], [p.obsoletes_i386], [p.installed_i386])
+        self.assert_(res=='ok', msg)
+        self.assertResult((p.obsoletes_i386,))
+
+    def testObsoletesOffPostInst2(self):
+        p = self.pkgs
+        res, msg = self.runOperation(['install', 'zsh'], [p.obsoletes_i386], [p.installed_i386], {'obsoletes' : False})
+        self.assert_(res=='ok', msg)
+        self.assertResult((p.obsoletes_i386,))
+
+    def testObsoletesOffPostAvail1(self):
+        p = self.pkgs
+        res, msg = self.runOperation(['install', 'zsh-ng', 'zsh'], [], [p.obsoletes_i386, p.installed_i386])
+        self.assert_(res=='ok', msg)
+        self.assertResult((p.obsoletes_i386,))
+
+    def testObsoletesOffPostAvail2(self):
+        p = self.pkgs
+        res, msg = self.runOperation(['install', 'zsh-ng', 'zsh'], [], [p.obsoletes_i386, p.installed_i386], {'obsoletes' : False})
+        self.assert_(res=='ok', msg)
+        self.assertResult((p.obsoletes_i386,))
+
+    def testObsoletesOffPostAvail3(self):
+        p = self.pkgs
+        res, msg = self.runOperation(['install', 'zsh', 'zsh-ng'], [], [p.obsoletes_i386, p.installed_i386])
+        self.assert_(res=='ok', msg)
+        self.assertResult((p.obsoletes_i386,))
+
+    def testObsoletesOffPostAvail4(self):
+        p = self.pkgs
+        res, msg = self.runOperation(['install', 'zsh', 'zsh-ng'], [], [p.obsoletes_i386, p.installed_i386], {'obsoletes' : False})
+        self.assert_(res=='ok', msg)
+        self.assertResult((p.obsoletes_i386,))
+
     def _MultiObsHelper(self):
         ret = {'zsh'  : FakePackage('zsh', '1', '1', '0', 'noarch'),
                'ksh'  : FakePackage('ksh', '1', '1', '0', 'noarch'),
diff --git a/utils.py b/utils.py
old mode 100644
new mode 100755
index ced6ba0..99533a6
--- a/utils.py
+++ b/utils.py
@@ -13,6 +13,8 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
+"""Various utility functions, and a utility class."""
+
 import sys
 import time
 import exceptions
@@ -21,7 +23,7 @@ import yum
 from cli import *
 from yum import Errors
 from yum import _
-from yum.i18n import utf8_width
+from yum.i18n import utf8_width, exception2msg
 from yum import logginglevels
 from optparse import OptionGroup
 
@@ -29,6 +31,9 @@ import yum.plugins as plugins
 from urlgrabber.progress import format_number
 
 def suppress_keyboard_interrupt_message():
+    """Change settings so that nothing will be printed to the
+    terminal after an uncaught :class:`exceptions.KeyboardInterrupt`.
+    """
     old_excepthook = sys.excepthook
 
     def new_hook(type, value, traceback):
@@ -40,10 +45,23 @@ def suppress_keyboard_interrupt_message():
     sys.excepthook = new_hook
 
 def jiffies_to_seconds(jiffies):
+    """Convert a number of jiffies to seconds, using the convention
+    that 100 jiffies = 1 second.
+
+    :param jiffies: a number of jiffies
+    :return: the equivalent number of seconds
+    """
     Hertz = 100 # FIXME: Hack, need to get this, AT_CLKTCK elf note *sigh*
     return int(jiffies) / Hertz
 
 def seconds_to_ui_time(seconds):
+    """Return a human-readable string representation of the length of
+    a time interval given in seconds.
+
+    :param seconds: the length of the time interval in seconds
+    :return: a human-readable string representation of the length of
+    the time interval
+    """
     if seconds >= 60 * 60 * 24:
         return "%d day(s) %d:%02d:%02d" % (seconds / (60 * 60 * 24),
                                            (seconds / (60 * 60)) % 24,
@@ -55,6 +73,12 @@ def seconds_to_ui_time(seconds):
     return "%02d:%02d" % ((seconds / 60), seconds % 60)
 
 def get_process_info(pid):
+    """Return information about a process taken from
+    /proc/*pid*/status, /proc/stat/, and /proc/*pid*/stat.
+
+    :param pid: the process id number
+    :return: a dictionary containing information about the process
+    """
     if not pid:
         return
 
@@ -106,6 +130,16 @@ def get_process_info(pid):
     return ps
 
 def show_lock_owner(pid, logger):
+    """Output information about another process that is holding the
+    yum lock.
+
+    :param pid: the process id number of the process holding the yum
+       lock
+    :param logger: the logger to output the information to
+    :return: a dictionary containing information about the process.
+       This is the same as the dictionary returned by
+       :func:`get_process_info`.
+    """
     ps = get_process_info(pid)
     if not ps:
         return None
@@ -129,28 +163,9 @@ def show_lock_owner(pid, logger):
     return ps
 
 
-def exception2msg(e):
-    """ DIE python DIE! Which one works:
-        to_unicode(e.value); unicode(e); str(e); 
-        Call this so you don't have to care. """
-    try:
-        return to_unicode(e.value)
-    except:
-        pass
-
-    try:
-        return unicode(e)
-    except:
-        pass
-
-    try:
-        return str(e)
-    except:
-        pass
-    return "<exception failed to convert to text>"
-
-
 class YumUtilBase(YumBaseCli):
+    """A class to extend the yum cli for utilities."""
+    
     def __init__(self,name,ver,usage):
         YumBaseCli.__init__(self)
         self._parser = YumOptionParser(base=self,utils=True,usage=usage)
@@ -167,11 +182,22 @@ class YumUtilBase(YumBaseCli):
             self.run_with_package_names.add("yum-utils")
 
     def exUserCancel(self):
+        """Output a message stating that the operation was cancelled
+        by the user.
+
+        :return: the exit code
+        """
         self.logger.critical(_('\n\nExiting on user cancel'))
         if self.unlock(): return 200
         return 1
 
     def exIOError(self, e):
+        """Output a message stating that the program is exiting due to
+        an IO exception.
+
+        :param e: the IO exception
+        :return: the exit code
+        """
         if e.errno == 32:
             self.logger.critical(_('\n\nExiting on Broken Pipe'))
         else:
@@ -180,10 +206,13 @@ class YumUtilBase(YumBaseCli):
         return 1
 
     def exPluginExit(self, e):
-        '''Called when a plugin raises PluginYumExit.
+        """Called when a plugin raises
+           :class:`yum.plugins.PluginYumExit`.  Log the plugin's exit
+           message if one was supplied.
 
-        Log the plugin's exit message if one was supplied.
-        ''' # ' xemacs hack
+        :param e: the exception
+        :return: the exit code
+        """ # ' xemacs hack
         exitmsg = exception2msg(e)
         if exitmsg:
             self.logger.warn('\n\n%s', exitmsg)
@@ -191,11 +220,20 @@ class YumUtilBase(YumBaseCli):
         return 1
 
     def exFatal(self, e):
+        """Output a message stating that a fatal error has occurred.
+
+        :param e: the exception
+        :return: the exit code
+        """
         self.logger.critical('\n\n%s', exception2msg(e))
         if self.unlock(): return 200
         return 1
         
     def unlock(self):
+        """Release the yum lock.
+
+        :return: the exit code
+        """
         try:
             self.closeRpmDB()
             self.doUnlock()
@@ -205,13 +243,27 @@ class YumUtilBase(YumBaseCli):
         
         
     def getOptionParser(self):
+        """Return the :class:`cli.YumOptionParser` for this object.
+
+        :return: the :class:`cli.YumOptionParser` for this object
+        """
         return self._parser        
 
     def getOptionGroup(self):
-        """ Get an option group to add non inherited options"""
+        """Return an option group to add non inherited options.
+
+        :return: a :class:`optparse.OptionGroup` for adding options
+           that are not inherited from :class:`YumBaseCli`.
+        """
         return self._option_group    
     
     def waitForLock(self):
+        """Establish the yum lock.  If another process is already
+        holding the yum lock, by default this method will keep trying
+        to establish the lock until it is successful.  However, if
+        :attr:`self.conf.exit_on_lock` is set to True, it will
+        raise a :class:`Errors.YumBaseError`.
+        """
         lockerr = ""
         while True:
             try:
@@ -233,6 +285,13 @@ class YumUtilBase(YumBaseCli):
         print "%s - %s (yum - %s)" % (self._utilName,self._utilVer,yum.__version__)
         
     def doUtilConfigSetup(self,args = sys.argv[1:],pluginsTypes=(plugins.TYPE_CORE,)):
+        """Parse command line options, and perform configuration.
+
+        :param args: list of arguments to use for configuration
+        :param pluginsTypes: a sequence specifying the types of
+           plugins to load
+        :return: a dictionary containing the values of command line options
+        """
         # Parse only command line options that affect basic yum setup
         opts = self._parser.firstParse(args)
 
@@ -305,8 +364,9 @@ class YumUtilBase(YumBaseCli):
         return opts
 
     def doUtilYumSetup(self):
-        """do a default setup for all the normal/necessary yum components,
-           really just a shorthand for testing"""
+        """Do a default setup for all the normal or necessary yum components;
+           this method is mostly just a used for testing.
+        """
         # FIXME - we need another way to do this, I think.
         try:
             self.waitForLock()
@@ -319,6 +379,11 @@ class YumUtilBase(YumBaseCli):
             sys.exit(1)
 
     def doUtilBuildTransaction(self, unfinished_transactions_check=True):
+        """Build the transaction.
+
+        :param unfinished_transactions_check: whether to check if an
+           unfinished transaction has been saved
+        """
         try:
             (result, resultmsgs) = self.buildTransaction(unfinished_transactions_check = unfinished_transactions_check)
         except plugins.PluginYumExit, e:
@@ -361,6 +426,7 @@ class YumUtilBase(YumBaseCli):
         self.verbose_logger.log(logginglevels.INFO_2, _('\nDependencies Resolved'))
         
     def doUtilTransaction(self):
+        """Perform the transaction."""
 
         try:
             return_code = self.doTransaction()
diff --git a/yum-cron/Makefile b/yum-cron/Makefile
new file mode 100644
index 0000000..d68659a
--- /dev/null
+++ b/yum-cron/Makefile
@@ -0,0 +1,19 @@
+all:
+	echo "Nothing to do"
+
+clean:
+	rm -f *~
+
+install:
+	mkdir -p $(DESTDIR)/etc/cron.daily
+	mkdir -p $(DESTDIR)/etc/rc.d/init.d
+	mkdir -p $(DESTDIR)/usr/sbin
+	mkdir -p $(DESTDIR)/etc/sysconfig
+	mkdir -p $(DESTDIR)/usr/share/yum-cron
+	install -D -m 755 yum-update.cron.sh $(DESTDIR)/etc/cron.daily/yum-update.cron
+	install -D -m 755 yum-cleanup.cron.sh $(DESTDIR)/etc/cron.daily/yum-cleanup.cron
+	install -D -m 755 yum-cron.sysvinit $(DESTDIR)/etc/rc.d/init.d/yum-cron
+	install -D -m 755 yum-cron.sh $(DESTDIR)/usr/sbin/yum-cron
+	install -D -m 644 yum-cron.sysconfig $(DESTDIR)/etc/sysconfig/yum-cron
+	install -D -m 644 update.yum $(DESTDIR)/usr/share/yum-cron/update.yum
+	install -D -m 644 cleanup.yum $(DESTDIR)/usr/share/yum-cron/cleanup.yum
diff --git a/yum-cron/TODO b/yum-cron/TODO
new file mode 100644
index 0000000..28e1964
--- /dev/null
+++ b/yum-cron/TODO
@@ -0,0 +1,19 @@
+Logging:
+- Add basic syslog support (success or failure; number of updates)
+
+Documentation:
+- Write a man page
+- Write a readme file
+
+Mail:
+- Use a different subject for success and failure mail
+- Make receiving mail when nothing happens optional
+
+General:
+- Break out check-updates and download-updates into their own actions;
+  move that logic to the cron scripts
+- Re-do the lockfile bit so failures can be reported without resorting to
+  relying on cron's output handler.
+- Figure out what to do with systemd
+- Update rpm, glibc, yum, python, specially?
+- Check if we're running as root; exit nicely.
diff --git a/yum-cron/cleanup.yum b/yum-cron/cleanup.yum
new file mode 100644
index 0000000..c60fa08
--- /dev/null
+++ b/yum-cron/cleanup.yum
@@ -0,0 +1,4 @@
+clean packages
+clean expire-cache
+ts run
+exit
diff --git a/yum-cron/update.yum b/yum-cron/update.yum
new file mode 100644
index 0000000..5d4e874
--- /dev/null
+++ b/yum-cron/update.yum
@@ -0,0 +1,3 @@
+update
+ts run
+exit
diff --git a/yum-cron/yum-cleanup.cron.sh b/yum-cron/yum-cleanup.cron.sh
new file mode 100755
index 0000000..e38e80f
--- /dev/null
+++ b/yum-cron/yum-cleanup.cron.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+
+# Only run if this flag is set. The flag is created by the yum-cron init
+# script when the service is started -- this allows one to use chkconfig and
+# the standard "service stop|start" commands to enable or disable yum-cron.
+if [[ ! -f /var/lock/subsys/yum-cron ]]; then
+  exit 0
+fi
+
+# Read configuration settings from the sysconfig directory.
+if [[ -f /etc/sysconfig/yum-cron ]]; then
+  source /etc/sysconfig/yum-cron
+fi
+
+# Only run on certain days of the week, based on the
+# settings in the above-mentioned sysconfig file.
+dow=`date +%w` 
+DAYS_OF_WEEK=${DAYS_OF_WEEK:-0123456} 
+if [[ "${DAYS_OF_WEEK/$dow/}" == "${DAYS_OF_WEEK}" ]]; then 
+  exit 0 
+fi 
+
+# And only _clean_ on a subset of the configured days.
+CLEANDAY=${CLEANDAY:-0}
+if [[ "${CLEANDAY/$dow/}" == "${CLEANDAY}" ]]; then
+  exit 0
+fi
+
+# Action!
+exec /usr/sbin/yum-cron cleanup
diff --git a/yum-cron/yum-cron.sh b/yum-cron/yum-cron.sh
new file mode 100755
index 0000000..d549471
--- /dev/null
+++ b/yum-cron/yum-cron.sh
@@ -0,0 +1,172 @@
+#!/bin/bash
+
+# This script is designed to be run from cron to automatically keep your
+# system up to date with the latest security patches and bug fixes. It
+# can download and/or apply package updates as configured in
+# /etc/sysconfig/yum-cron.
+
+
+# These are used by /etc/init.d/yum-cron on shutdown to protect against
+# abruptly shutting down mid-transaction. Therefore, you shouldn't change
+# them without changing that.
+LOCKDIR=/var/lock/yum-cron.lock
+PIDFILE=$LOCKDIR/pidfile
+TSLOCK=$LOCKDIR/ts.lock
+
+
+# This is the home of the yum scripts which power the various actions the
+# yum-cron system performs.
+SCRIPTDIR=/usr/share/yum-cron/
+
+# If no command line options were given, exit with a usage message.
+if [[ -z "$1" ]]; then
+  echo "Usage: yum-cron {update|cleanup|...}"
+  exit 1
+else
+  ACTION=$1
+fi
+
+# If a command line option was given, it must match a yum script.
+YUMSCRIPT=${SCRIPTDIR}/${ACTION}.yum
+if [[ ! -r $YUMSCRIPT ]]; then
+  echo "Script for action \"$ACTION\" is not readable in $SCRIPTDIR."
+  exit 1
+fi  
+
+# Read the settings from our config file.
+if [[ -f /etc/sysconfig/yum-cron ]]; then
+  source /etc/sysconfig/yum-cron
+fi
+
+# If no system name is set, use the hostname.
+[[ -z "$SYSTEMNAME" ]] && SYSTEMNAME=$( hostname ) 
+
+# If DOWNLOAD_ONLY is set, then we force CHECK_ONLY too.
+# Gotta check for updates before we can possibly download them.
+[[ "$DOWNLOAD_ONLY" == "yes" ]] && CHECK_ONLY=yes
+
+# This holds the output from the "meat" of this script, so that it can
+# be nicely mailed to the configured destination when we're done.
+YUMTMP=$(mktemp /var/run/yum-cron.XXXXXX)
+touch $YUMTMP 
+[[ -x /sbin/restorecon ]] && /sbin/restorecon $YUMTMP
+
+# Here is the gigantic block of lockfile logic.
+#
+# Note: the lockfile code doesn't currently try and use YUMTMP to email
+# messages nicely, so this gets handled by normal cron error mailing.
+#
+
+# We use mkdir for the lockfile, as this will test for and if possible
+# create the lock in one atomic action. (So there's no race condition.)
+if mkdir $LOCKDIR 2>/dev/null; then
+  # Store the current process ID in the lock directory so we can check for
+  # staleness later.
+  echo "$$" >"${PIDFILE}"
+  # And, clean up locks and tempfile when the script exits or is killed.
+  trap "{ rm -f $PIDFILE $TSLOCK; rmdir $LOCKDIR 2>/dev/null; rm -f $YUMTMP; exit 255; }" INT TERM EXIT
+else
+  # Lock failed -- check if a running process exists.  
+  # First, if there's no PID file in the lock directory, something bad has
+  # happened.  We can't know the process name, so, clean up the old lockdir
+  # and restart.
+  if [[ ! -f $PIDFILE ]]; then
+    rm -rf "${LOCKDIR}"
+    echo "yum-cron: no lock PID, clearing and restarting myself" >&2
+    exec $0 "$@"
+  fi
+  OTHERPID="$(cat "${PIDFILE}")"
+  # if cat wasn't able to read the file anymore, another instance probably is
+  # about to remove the lock -- exit, we're *still* locked
+  if [[ $? != 0 ]]; then
+    echo "yum-cron: lock failed, PID ${OTHERPID} is active" >&2
+    exit 0
+  fi
+  if ! kill -0 $OTHERPID &>/dev/null; then
+    # Lock is stale. Remove it and restart.
+    echo "yum-cron: removing stale lock of nonexistant PID ${OTHERPID}" >&2
+    rm -rf "${LOCKDIR}"
+    echo "yum-cron: restarting myself" >&2
+    exec $0 "$@"
+  else
+    # Remove lockfiles more than a day old -- they must be stale.
+    find $LOCKDIR -type f -name 'pidfile' -amin +1440 -exec rm -rf $LOCKDIR \;
+    # If it's still there, it *wasn't* too old. Bail!
+    if [[ -f $PIDFILE ]]; then
+      # Lock is valid and OTHERPID is active -- exit, we're locked!
+      echo "yum-cron: lock failed, PID ${OTHERPID} is active" >&2
+      exit 0
+    else
+      # Lock was invalid. Restart.
+      echo "yum-cron: removing stale lock belonging to stale PID ${OTHERPID}" >&2
+      echo "yum-cron: restarting myself" >&2
+      exec $0 "$@"
+    fi
+  fi
+fi
+
+# Now, do the actual work.
+
+# We special case "update" because it has complicated conditionals; for
+# everything else we just run yum with the right parameters and
+# corresponding script.  Right now, that's just "cleanup" but theoretically
+# there could be other actions.
+{
+  case "$ACTION" in
+    update)
+        # There's three broad possibilties here:
+        #   CHECK_ONLY (possibly with DOWNLOAD_ONLY)
+        #   CHECK_FIRST (exits _silently_ if we can't access the repos)
+        #   nothing special -- just do it
+        # Note that in all cases, yum is updated first, and then 
+        # everything else.
+        if [[ "$CHECK_ONLY" == "yes" ]]; then
+          # TSLOCK is used by the safe-shutdown code in the init script.
+          touch $TSLOCK
+          /usr/bin/yum $YUM_PARAMETER -e 0 -d 0 -y check-update 1> /dev/null 2>&1
+          case $? in
+            1)   exit 1;;
+            100) echo "New updates available for host $SYSTEMNAME";
+                 /usr/bin/yum $YUM_PARAMETER -e ${ERROR_LEVEL:-0} -d ${DEBUG_LEVEL:-0} -y -C check-update
+                 if [[ "$DOWNLOAD_ONLY" == "yes" ]]; then
+                     /usr/bin/yum $YUM_PARAMETER -e ${ERROR_LEVEL:-0} -d ${DEBUG_LEVEL:-0} -y --downloadonly update
+                     echo "Updates downloaded. Use \"yum -C update\" manually to install them."
+                 fi
+                 ;;
+          esac
+        elif [[ "$CHECK_FIRST" == "yes" ]]; then
+          # Don't run if we can't access the repos -- if this is set, 
+          # and there's a problem, we exit silently (but return an error
+          # code).
+          touch $TSLOCK
+          /usr/bin/yum $YUM_PARAMETER -e 0 -d 0 check-update 2>&-
+          case $? in
+            1)   exit 1;;
+            100) /usr/bin/yum $YUM_PARAMETER -e ${ERROR_LEVEL:-0} -d ${DEBUG_LEVEL:-0} -y update yum
+                 /usr/bin/yum $YUM_PARAMETER -e ${ERROR_LEVEL:-0} -d ${DEBUG_LEVEL:-0} -y shell $YUMSCRIPT
+                 ;;
+          esac
+        else
+          # and here's the "just do it".
+          touch $TSLOCK
+          /usr/bin/yum $YUM_PARAMETER -e ${ERROR_LEVEL:-0} -d ${DEBUG_LEVEL:-0} -y update yum
+          /usr/bin/yum $YUM_PARAMETER -e ${ERROR_LEVEL:-0} -d ${DEBUG_LEVEL:-0} -y shell $YUMSCRIPT
+        fi
+        ;;
+    *)
+        /usr/bin/yum $YUM_PARAMETER -e ${ERROR_LEVEL:-0} -d ${DEBUG_LEVEL:-0} -y shell $YUMSCRIPT
+        ;;
+  esac       
+
+} >> $YUMTMP 2>&1
+
+if [[ ! -z "$MAILTO" && -x /bin/mail ]]; then 
+# If MAILTO is set, use mail command for prettier output.
+  [[ -s "$YUMTMP" ]] && mail -s "System update: $SYSTEMNAME" $MAILTO < $YUMTMP 
+else 
+# The default behavior is to use cron's internal mailing of output.
+  cat $YUMTMP
+fi 
+rm -f $YUMTMP 
+
+exit 0
diff --git a/yum-cron/yum-cron.sysconfig b/yum-cron/yum-cron.sysconfig
new file mode 100644
index 0000000..4c8c40d
--- /dev/null
+++ b/yum-cron/yum-cron.sysconfig
@@ -0,0 +1,92 @@
+# This is the configuration file for yum-cron, a simple system for
+# keeping your machine up to date. These options are used variously by
+# the main script, by the cron scripts, and by the init script.
+
+# Main Options
+#--------------------------------------------------------------------------
+
+# Pass any given parameter to yum, as run in all the scripts invoked by
+# this package. Be aware that this is global, and yum is invoked in
+# several modes by these scripts, and your parameter might not be
+# appropriate in all cases.
+YUM_PARAMETER=
+
+# Don't install; just check and report. 
+# (Valid options: yes|no)
+CHECK_ONLY=no
+
+# Don't install; just check for and download any pending updates. This
+# implies CHECK_ONLY=yes, as we've gotta check first to see what to
+# download.
+# (Valid options: yes|no)
+DOWNLOAD_ONLY=no
+
+# Check to see if we can reach the repos before attempting an update.
+# If there is an error, exit silently with no output. You might want
+# this if you know your network connectivity is sporadic.
+# (Valid options: yes|no)
+CHECK_FIRST=no
+
+
+# Yum error level. The practical range is 0-10, where 0 means print
+# only critical errors, and 10 means print all errors, even ones that
+# are not important. Level 0 is the default if nothing is set.
+ERROR_LEVEL=0
+
+# Yum debug level. The practical range is 0-10; a higher number means
+# more output. Level 1 is a useful level if you want to see what's been
+# done and don't want to read /var/log/yum.log. Level 0 is the default
+# if no value is set here.
+DEBUG_LEVEL=0
+
+# If MAILTO is set and the /bin/mail command is available, the mail
+# command is used to deliver yum output. If MAILTO is unset, crond will
+# send the output by itself, usually to root (but with a less useful
+# subject line).
+MAILTO=root
+
+# The reports generated by this command generally use the hostname of
+# the system as reported by the hostname command. If you'd prefer to
+# use something else, you can set that here.
+#SYSTEMNAME="" 
+
+# Scheduling Options (used by the default cron scripts,
+# /etc/cron.daily/yum-cleanup.cron and /etc/cron.daily/yum-update.cron)
+# 
+#   Note that if you use a different cron configuration (for example,
+#   removing the default scripts and adding an entry in /etc/cron.d),
+#   these values will have no effect -- unless you read and act on them
+#   in your new configuration.
+#--------------------------------------------------------------------------
+
+# Wait for a random time up to the given number of minutes before
+# applying updates. With a value of 60, yum-cron will delay between 1
+# and 60 minutes. A value of 0 will result in no delay, which is handy
+# if you want to ensure that updates happen at a known time, but could
+# be bad for update servers to be hit by all clients at exactly the
+# same time.
+RANDOMWAIT=60
+
+# You may set DAYS_OF_WEEK to the numeric days of the week you want to
+# run, where 0 is Sunday and 6 is Saturday. The default is to run every
+# day.
+#DAYS_OF_WEEK="0123456" 
+
+# The cleanup task (which clears the package cache) can run on a subset
+# of the days above. (If the value chosen here doesn't appear in
+# DAYS_OF_WEEK, the cleanup task will never happen.)
+CLEANDAY="0"
+
+# Init System Options (used by /etc/init.d/yum-cron)
+#--------------------------------------------------------------------------
+
+# If SERVICE_WAITS is set to "yes", and a transaction is in progress
+# when the yum-cron service is stopped, the init script will wait 
+# up to SERVICE_WAIT_TIME seconds before killing the task. Without
+# this, system shutdown continues as normal, potentially breaking
+# in-progress transactions.
+# (Valid options: yes|no)
+SERVICE_WAITS=yes
+
+# 300 is the default.
+SERVICE_WAIT_TIME=300
diff --git a/yum-cron/yum-cron.sysvinit b/yum-cron/yum-cron.sysvinit
new file mode 100755
index 0000000..084dd32
--- /dev/null
+++ b/yum-cron/yum-cron.sysvinit
@@ -0,0 +1,103 @@
+#!/bin/bash
+#
+# yum-cron      Enable or disable scheduled yum system updates.
+#
+# chkconfig:	- 50 01
+#
+# description:  This controls whether yum-cron runs. If this service is \
+#               off, the yum-cron scripts in /etc/cron.daily exit \
+#               immediately; otherwise, they download and/or apply package \
+#               updates as configured in /etc/sysconfig/yum-cron.
+# processname:  yum-cron
+# config: /etc/yum/yum-daily.yum
+#
+
+# source function library
+. /etc/rc.d/init.d/functions
+
+test -f /etc/sysconfig/yum-cron && . /etc/sysconfig/yum-cron
+
+lockfile=/var/lock/subsys/yum-cron
+tslock=/var/lock/yum-cron.lock/ts.lock
+yumcronpid=/var/lock/yum-cron.lock/pidfile
+
+RETVAL=0
+
+start() {
+	echo -n $"Enabling scheduled yum updates: "
+	touch "$lockfile" && success || failure
+	RETVAL=$?
+	echo
+}
+
+stop() {
+	echo -n $"Disabling scheduled yum updates: "
+	if [ -f "$yumcronpid" -a "$SERVICE_WAITS" = "yes" ]; then
+	  yum_done=0
+	  if [ ! -f $tslock ]; then
+	    # No transaction yet in progress, just kill it
+	    kill `cat $yumcronpid > /dev/null 2>&1` > /dev/null 2>&1
+	    yum_done=1
+	  fi
+	  if [ $yum_done -eq 0 ]; then
+	    echo -n $"Waiting for in-progress yum transaction "
+	    if [ -z "$SERVICE_WAIT_TIME" ]; then
+	      SERVICE_WAIT_TIME=300
+	    fi
+	    start=`date +%s`
+	    end=`expr $start + $SERVICE_WAIT_TIME`
+	    while [ `date +%s` -le $end ]
+	    do
+	      sleep 5
+	      if [ ! -f "$tslock" ]; then
+		yum_done=1
+	        break
+	      fi
+	    done
+	    if [ $yum_done -eq 1 ]; then
+	      echo -n " ok "
+	    else
+	      echo -n " failed "
+	    fi
+	  fi
+	fi
+	rm -f "$lockfile" && success || failure
+	RETVAL=$?
+	echo
+}
+
+restart() {
+	stop
+	start
+}
+
+case "$1" in
+  start)
+	start
+	;;
+  stop) 
+	stop
+	;;
+  restart|force-reload)
+	restart
+	;;
+  reload)
+	;;
+  condrestart)
+	[ -f "$lockfile" ] && restart
+	;;
+  status)
+	if [ -f $lockfile ]; then
+		echo $"Scheduled yum updates are enabled."
+		RETVAL=0
+	else
+		echo $"Scheduled yum updates are disabled."
+		RETVAL=3
+	fi
+	;;
+  *)
+	echo $"Usage: $0 {start|stop|status|restart|reload|force-reload|condrestart}"
+	exit 1
+esac
+
+exit $RETVAL
diff --git a/yum-cron/yum-update.cron.sh b/yum-cron/yum-update.cron.sh
new file mode 100755
index 0000000..c439ad3
--- /dev/null
+++ b/yum-cron/yum-update.cron.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+# Only run if this flag is set. The flag is created by the yum-cron init
+# script when the service is started -- this allows one to use chkconfig and
+# the standard "service stop|start" commands to enable or disable yum-cron.
+if [[ ! -f /var/lock/subsys/yum-cron ]]; then
+  exit 0
+fi
+
+# Read configuration settings from the sysconfig directory.
+if [[ -f /etc/sysconfig/yum-cron ]]; then
+  source /etc/sysconfig/yum-cron
+fi
+
+# Only run on certain days of the week, based on the
+# settings in the above-mentioned sysconfig file.
+dow=`date +%w` 
+DAYS_OF_WEEK=${DAYS_OF_WEEK:-0123456} 
+if [[ "${DAYS_OF_WEEK/$dow/}" == "${DAYS_OF_WEEK}" ]]; then 
+  exit 0 
+fi 
+
+# Wait a random number of minutes, again based on
+# the setting in the sysconfig file.
+[[ $RANDOMWAIT -gt 0 ]] && sleep $(( $RANDOM % ($RANDOMWAIT * 60) + 1 ))
+
+# Action!
+exec /usr/sbin/yum-cron update
diff --git a/yum-updatesd.py b/yum-updatesd.py
old mode 100644
new mode 100755
index 1ce4720..25e3022
--- a/yum-updatesd.py
+++ b/yum-updatesd.py
@@ -28,6 +28,11 @@
 # $ dbus-send --system --print-reply --type=method_call \
 #   --dest=edu.duke.linux.yum /Updatesd edu.duke.linux.yum.CheckNow
 
+"""
+Daemon to periodically check for updates to installed packages, and
+associated classes and methods.
+"""
+
 import os
 import sys
 import time
@@ -60,34 +65,78 @@ config_file = '/etc/yum/yum-updatesd.conf'
 initial_directory = os.getcwd()
 
 class UpdateEmitter(object):
-    """Abstract object for implementing different types of emitters."""
+    """Abstract class for implementing different types of
+       emitters.
+    """
+
     def __init__(self):
         pass
     def updatesAvailable(self, updateInfo):
         """Emitted when there are updates available to be installed.
         If not doing the download here, then called immediately on finding
         new updates.  If we do the download here, then called after the
-        updates have been downloaded."""
+        updates have been downloaded.
+
+        :param updateInfo: a list of tuples of dictionaries.  Each
+           dictionary contains information about a package, and each
+           tuple specifies an available upgrade: the second dictionary
+           in the tuple has information about a package that is
+           currently installed, and the first dictionary has
+           information what the package can be upgraded to
+        """
         pass
+
     def updatesDownloading(self, updateInfo):
-        """Emitted to give feedback of update download starting."""
+        """Emitted to give feedback of update download starting.
+
+        :param updateInfo: a list of tuples of dictionaries.  Each
+           dictionary contains information about a package, and each
+           tuple specifies an available upgrade: the second dictionary
+           in the tuple has information about a package that is
+           currently installed, and the first dictionary has
+           information what the package can be upgraded to
+        """
         pass
+
     def updatesApplied(self, updateInfo):
-        """Emitted on successful installation of updates."""
+        """Emitted on successful installation of updates.
+
+        :param updateInfo: a list of tuples of dictionaries.  Each
+           dictionary contains information about a package, and each
+           tuple specifies an available upgrade: the second dictionary
+           in the tuple has information about a package that is
+           currently installed, and the first dictionary has
+           information what the package can be upgraded to
+        """
         pass
+
     def updatesFailed(self, errmsgs):
-        """Emitted when an update has failed to install."""
+        """Emitted when an update has failed to install.
+
+        :param errmsgs: a list of error messages
+        """
         pass
+
     def checkFailed(self, error):
-        """Emitted when checking for updates failed."""
+        """Emitted when checking for updates failed.
+
+        :param error: an error message
+        """
         pass
 
     def setupFailed(self, error, translation_domain):
-        """Emitted when plugin initialization failed."""
+        """Emitted when plugin initialization failed.
+
+        :param error: an error message
+        :param translation_domain: the translation domain supplied by
+           the plugin
+        """
         pass
  
 
 class SyslogUpdateEmitter(UpdateEmitter):
+    """Emitter class to send messages to syslog."""
+
     def __init__(self, syslog_facility, ident = "yum-updatesd",
                  level = "WARN"):
         UpdateEmitter.__init__(self)
@@ -95,6 +144,15 @@ class SyslogUpdateEmitter(UpdateEmitter):
         self.level = level
         
     def updatesAvailable(self, updateInfo):
+        """Emit a message stating that updates are available.
+
+        :param updateInfo: a list of tuples of dictionaries.  Each
+           dictionary contains information about a package, and each
+           tuple specifies an available upgrade: the second dictionary
+           in the tuple has information about a package that is
+           currently installed, and the first dictionary has
+           information what the package can be upgraded to
+        """
         num = len(updateInfo)
         level = self.level
         if num > 1:
@@ -144,12 +202,23 @@ class SyslogUpdateEmitter(UpdateEmitter):
 
 
 class EmailUpdateEmitter(UpdateEmitter):
+    """Emitter class to send messages via email."""
+
     def __init__(self, sender, rcpt):
         UpdateEmitter.__init__(self)        
         self.sender = sender
         self.rcpt = rcpt
 
     def updatesAvailable(self, updateInfo):
+        """Emit a message stating that updates are available.
+
+        :param updateInfo: a list of tuples of dictionaries.  Each
+           dictionary contains information about a package, and each
+           tuple specifies an available upgrade: the second dictionary
+           in the tuple has information about a package that is
+           currently installed, and the first dictionary has
+           information what the package can be upgraded to
+        """
         num = len(updateInfo)
         if num < 1:
             return
@@ -173,6 +242,8 @@ class EmailUpdateEmitter(UpdateEmitter):
         s.close()
 
 class DbusUpdateEmitter(UpdateEmitter):
+    """Emitter class to send messages to the dbus message system."""
+
     def __init__(self):
         UpdateEmitter.__init__(self)        
         bus = dbus.SystemBus()
@@ -181,6 +252,15 @@ class DbusUpdateEmitter(UpdateEmitter):
         self.dbusintf = yum_dbus
 
     def updatesAvailable(self, updateInfo):
+        """Emit a message stating that updates are available.
+
+        :param updateInfo: a list of tuples of dictionaries.  Each
+           dictionary contains information about a package, and each
+           tuple specifies an available upgrade: the second dictionary
+           in the tuple has information about a package that is
+           currently installed, and the first dictionary has
+           information what the package can be upgraded to
+        """
         num = len(updateInfo)
         msg = "%d" %(num,)
         if num > 0:
@@ -189,44 +269,102 @@ class DbusUpdateEmitter(UpdateEmitter):
             self.dbusintf.NoUpdatesAvailableSignal(msg)
 
     def updatesFailed(self, errmsgs):
+        """Emit a message stating that an update has failed to install.
+
+        :param errmsgs: a list of error messages
+        """
         self.dbusintf.UpdatesFailedSignal(errmsgs)
 
     def updatesApplied(self, updinfo):
+        """Emit a message stating that updates were installed
+        successfully.
+
+        :param updinfo: a list of tuples of dictionaries.  Each
+           dictionary contains information about a package, and each
+           tuple specifies an available upgrade: the second dictionary
+           in the tuple has information about a package that is
+           currently installed, and the first dictionary has
+           information what the package can be upgraded to
+        """
         self.dbusintf.UpdatesAppliedSignal(updinfo)
 
     def checkFailed(self, error):
+        """Emit a message stating that checking for updates failed.
+
+        :param error: an error message
+        """
         self.dbusintf.CheckFailedSignal(error)
 
     def setupFailed(self, error, translation_domain):
+        """Emit a message stating that plugin initialization failed.
+
+        :param error: an error message
+        :param translation_domain: the translation domain supplied by
+           the plugin
+        """
         self.dbusintf.SetupFailedSignal(error, translation_domain)
 
 
 class YumDbusInterface(dbus.service.Object):
+    """Interface class for sending signals to the dbus."""
+
     def __init__(self, bus_name, object_path='/UpdatesAvail'):
         dbus.service.Object.__init__(self, bus_name, object_path)
 
     @dbus.service.signal('edu.duke.linux.yum')
     def UpdatesAvailableSignal(self, message):
+        """Send a signal stating that updates are available.
+        
+        :param message: the message to send in the signal
+        """
         pass
 
     @dbus.service.signal('edu.duke.linux.yum')
     def NoUpdatesAvailableSignal(self, message):
+        """Send a signal stating that no updates are available.
+
+        :param message: the message to send in the signal
+        """
         pass
         
     @dbus.service.signal('edu.duke.linux.yum')
     def UpdatesFailedSignal(self, errmsgs):
+        """Send a signal stating that the update has failed.
+
+        :param errmsgs: a list of error messages
+        """
         pass
 
     @dbus.service.signal('edu.duke.linux.yum')
     def UpdatesAppliedSignal(self, updinfo):
+        """Send a signal stating that updates were applied
+        successfully.
+
+        :param updinfo: a list of tuples of dictionaries.  Each
+           dictionary contains information about a package, and each
+           tuple specifies an available upgrade: the second dictionary
+           in the tuple has information about a package that is
+           currently installed, and the first dictionary has
+           information what the package can be upgraded to
+        """
         pass
 
     @dbus.service.signal('edu.duke.linux.yum')
     def CheckFailedSignal(self, message):
+        """Send a signal stating that checking for updates failed.
+
+        :param message: the message to send in the signal
+        """
         pass
 
     @dbus.service.signal('edu.duke.linux.yum')
     def SetupFailedSignal(self, message, translation_domain=""):
+        """Send a signal stating that plugin initialization failed.
+
+        :param message: the message to send in the signal
+        :param translation_domain: the translation domain supplied by
+           the plugin
+        """
         pass
 
 
@@ -249,11 +387,18 @@ class UDConfig(BaseConfig):
 
 
 class UpdateBuildTransactionThread(threading.Thread):
+    """Class to build the update transaction in a new thread."""
+
     def __init__(self, updd, name):
         self.updd = updd
         threading.Thread.__init__(self, name=name)
 
     def run(self):
+        """Build the transaction, and download the packages to be
+        updated.  Finally, call self.processPkgs. This method must be
+        provided by a subclass, and will determine what, if any,
+        action is taken with the packages after they are downloaded.
+        """
         self.updd.tsInfo.makelists()
         try:
             (result, msgs) = self.updd.buildTransaction()
@@ -270,25 +415,41 @@ class UpdateBuildTransactionThread(threading.Thread):
 
 
 class UpdateDownloadThread(UpdateBuildTransactionThread):
+    """Class to download the packages in the update in a new thread."""
+
     def __init__(self, updd):
         UpdateBuildTransactionThread.__init__(self, updd,
             name="UpdateDownloadThread")
 
     def processPkgs(self, dlpkgs):
+        """Output that there are updates available via the emitters,
+        and release the yum locks.
+
+        :param dlpkgs: unused
+        """
         self.updd.emitAvailable()
         self.updd.releaseLocks()
 
 
 class UpdateInstallThread(UpdateBuildTransactionThread):
+    """Class to install updates in a new thread."""
+
     def __init__(self, updd):
         UpdateBuildTransactionThread.__init__(self, updd,
             name="UpdateInstallThread")
 
     def failed(self, msgs):
+        """Output that the update failed via the emitters.
+
+        :param msgs: a list or error messages to emit
+        """
         self.updd.emitUpdateFailed(msgs)
         self.updd.releaseLocks()
 
     def success(self):
+        """Output that the update has completed successfully via the
+        emitters, and perform clean up.
+        """
         self.updd.emitUpdateApplied()
         self.updd.releaseLocks()
 
@@ -296,6 +457,10 @@ class UpdateInstallThread(UpdateBuildTransactionThread):
         self.updd.updateInfoTime = None        
         
     def processPkgs(self, dlpkgs):
+        """Apply the available updates.
+
+        :param dlpkgs: a list of package objecs to update
+        """
         for po in dlpkgs:
             result, err = self.updd.sigCheckPkg(po)
             if result == 0:
@@ -323,6 +488,8 @@ class UpdateInstallThread(UpdateBuildTransactionThread):
         self.success()
 
 class UpdatesDaemon(yum.YumBase):
+    """Class to implement the update checking daemon."""
+
     def __init__(self, opts):
         yum.YumBase.__init__(self)
         self.opts = opts
@@ -343,6 +510,9 @@ class UpdatesDaemon(yum.YumBase):
         self.updateInfoTime = None
 
     def doSetup(self):
+        """Perform set up, including setting up directories and
+        parsing options.
+        """
         # if we are not root do the special subdir thing
         if os.geteuid() != 0:
             if not os.path.exists(self.opts.nonroot_workdir):
@@ -352,6 +522,8 @@ class UpdatesDaemon(yum.YumBase):
         self.doConfigSetup(fn=self.opts.yum_config)
 
     def refreshUpdates(self):
+        """Retrieve information about what updates are available."""
+
         self.doLock()
         try:
             self.doRepoSetup()
@@ -366,6 +538,8 @@ class UpdatesDaemon(yum.YumBase):
         return True
 
     def populateUpdateMetadata(self):
+        """Populate the metadata for the packages in the update."""
+
         self.updateMetadata = UpdateMetadata()
         repos = []
 
@@ -383,6 +557,9 @@ class UpdatesDaemon(yum.YumBase):
                 md.close()
 
     def populateUpdates(self):
+        """Retrieve and set up information about the updates available
+        for installed packages.
+        """
         def getDbusPackageDict(pkg):
             """Returns a dictionary corresponding to the package object
             in the form that we can send over the wire for dbus."""
@@ -425,6 +602,8 @@ class UpdatesDaemon(yum.YumBase):
         self.updateInfoTime = time.time()
 
     def populateTsInfo(self):
+        """Set up information about the update in the tsInfo object."""
+
         # figure out the updates
         for (new, old) in self.up.getUpdatesTuples():
             updating = self.getPackageObject(new)
@@ -442,6 +621,13 @@ class UpdatesDaemon(yum.YumBase):
                 self.tsInfo.addObsoleted(installed, obsoleting)
 
     def updatesCheck(self):
+        """Check to see whether updates are available for any
+        installed packages. If updates are available, install them,
+        download them, or just emit a message, depending on what
+        options are selected in the configuration file.
+ 
+        :return: whether the daemon should continue looping
+        """
         if not self.didSetup:
             try:
                 self.doSetup()
@@ -491,6 +677,18 @@ class UpdatesDaemon(yum.YumBase):
         return True
 
     def getUpdateInfo(self):
+        """Return information about the update.  This may be
+        previously cached information if the configured time interval
+        between update retrievals has not yet elapsed, or there is an
+        error in trying to retrieve the update information.
+
+        :return: a list of tuples of dictionaries.  Each
+           dictionary contains information about a package, and each
+           tuple specifies an available upgrade: the second dictionary
+           in the tuple has information about a package that is
+           currently installed, and the first dictionary has
+           information what the package can be upgraded to        
+        """
         # if we have a cached copy, use it
         if self.updateInfoTime and (time.time() - self.updateInfoTime <
                                     self.opts.updaterefresh):
@@ -522,40 +720,47 @@ class UpdatesDaemon(yum.YumBase):
         return self.updateInfo
 
     def updateCheckSetup(self):
+        """Set up the transaction set, rpm database, and prepare to
+        get updates.
+        """
         self.doTsSetup()
         self.doRpmDBSetup()
         self.doUpdateSetup()
 
     def releaseLocks(self):
+        """Close the rpm database, and release the yum lock."""
         self.closeRpmDB()
         self.doUnlock()
 
     def emitAvailable(self):
-        """method to emit a notice about updates"""
+        """Emit a notice stating whether updates are available."""
         map(lambda x: x.updatesAvailable(self.updateInfo), self.emitters)
 
     def emitDownloading(self):
-        """method to emit a notice about updates downloading"""
+        """Emit a notice stating that updates are downloading."""
         map(lambda x: x.updatesDownloading(self.updateInfo), self.emitters)
 
     def emitUpdateApplied(self):
-        """method to emit a notice when automatic updates applied"""
+        """Emit a notice stating that automatic updates have been applied."""
         map(lambda x: x.updatesApplied(self.updateInfo), self.emitters)
 
     def emitUpdateFailed(self, errmsgs):
-        """method to emit a notice when automatic updates failed"""
+        """Emit a notice stating that automatic updates failed."""
         map(lambda x: x.updatesFailed(errmsgs), self.emitters)
 
     def emitCheckFailed(self, error):
-        """method to emit a notice when checking for updates failed"""
+        """Emit a notice stating that checking for updates failed."""
         map(lambda x: x.checkFailed(error), self.emitters)
 
     def emitSetupFailed(self, error, translation_domain=""):
-        """method to emit a notice when checking for updates failed"""
+        """Emit a notice stating that checking for updates failed."""
         map(lambda x: x.setupFailed(error, translation_domain), self.emitters)
 
 
 class YumDbusListener(dbus.service.Object):
+    """Class to export methods that control the daemon over the
+    dbus.
+    """
     def __init__(self, updd, bus_name, object_path='/Updatesd',
                  allowshutdown = False):
         dbus.service.Object.__init__(self, bus_name, object_path)
@@ -563,11 +768,19 @@ class YumDbusListener(dbus.service.Object):
         self.allowshutdown = allowshutdown
 
     def doCheck(self):
+        """Check whether updates are available.
+
+        :return: False
+        """
         self.updd.updatesCheck()
         return False
 
     @dbus.service.method("edu.duke.linux.yum", in_signature="")
     def CheckNow(self):
+        """Check whether updates are available.
+
+        :return: a message stating that a check has been queued
+        """
         # make updating checking asynchronous since we discover whether
         # or not there are updates via a callback signal anyway
         gobject.idle_add(self.doCheck)
@@ -575,6 +788,11 @@ class YumDbusListener(dbus.service.Object):
 
     @dbus.service.method("edu.duke.linux.yum", in_signature="")
     def ShutDown(self):
+        """Shut down the daemon.
+        
+        :return: whether remote callers have permission to shut down
+           the daemon
+        """
         if not self.allowshutdown:
             return False
         
@@ -585,19 +803,34 @@ class YumDbusListener(dbus.service.Object):
 
     @dbus.service.method("edu.duke.linux.yum", in_signature="", out_signature="a(a{ss}a{ss})")
     def GetUpdateInfo(self):
+        """Return information about the available updates
+
+        :return: a list of tuples of dictionaries.  Each
+           dictionary contains information about a package, and each
+           tuple specifies an available upgrade: the second dictionary
+           in the tuple has information about a package that is
+           currently installed, and the first dictionary has
+           information what the package can be upgraded to        
+        """
         # FIXME: should this be async?
         upds = self.updd.getUpdateInfo()
         return upds
         
 
 def shutDown():
+    """Shut down the daemon."""
+
     sys.exit(0)
 
 def restart():
+    """Restart the daemon, reloading configuration files."""
+
     os.chdir(initial_directory)
     os.execve(sys.argv[0], sys.argv, os.environ)  
 
 def main(options = None):
+    """Configure and start the daemon."""
+
     # we'll be threading for downloads/updates
     gobject.threads_init()
     dbus.glib.threads_init()
diff --git a/yum.spec b/yum.spec
index abd203f..b78a9f6 100644
--- a/yum.spec
+++ b/yum.spec
@@ -1,24 +1,51 @@
-Summary: RPM installer/updater
+%define move_yum_conf_back 1
+%define auto_sitelib 1
+%define yum_updatesd 0
+%define disable_check 0
+
+%if %{auto_sitelib}
+
+%{!?python_sitelib: %define python_sitelib %(python -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")}
+
+%else
+%define python_sitelib /usr/lib/python?.?/site-packages
+%endif
+
+# We always used /usr/lib here, even on 64bit ... so it's a bit meh.
+%define yum_pluginslib   /usr/lib/yum-plugins
+%define yum_pluginsshare /usr/share/yum-plugins
+
+Summary: RPM package installer/updater/manager
 Name: yum
-Version: 3.4.2
+Version: 3.4.3
 Release: 0
 License: GPLv2+
 Group: System Environment/Base
 Source: %{name}-%{version}.tar.gz
 URL: http://yum.baseurl.org/
-BuildRoot: %{_tmppath}/%{name}-%{version}root
 BuildArchitectures: noarch
 BuildRequires: python
 BuildRequires: gettext
 BuildRequires: intltool
-
+# This is really CheckRequires ...
+BuildRequires: python-nose
+BuildRequires: python >= 2.4
+BuildRequires: rpm-python, rpm >= 0:4.4.2
+BuildRequires: python-iniparse
+BuildRequires: python-sqlite
+BuildRequires: python-urlgrabber >= 3.9.0-8
+BuildRequires: yum-metadata-parser >= 1.1.0
+BuildRequires: pygpgme
+# End of CheckRequires
+Conflicts: pirut < 1.1.4
 Requires: python >= 2.4
 Requires: rpm-python, rpm >= 0:4.4.2
+Requires: python-iniparse
 Requires: python-sqlite
-Requires: urlgrabber >= 3.9.2
+Requires: python-urlgrabber >= 3.9.0-8
 Requires: yum-metadata-parser >= 1.1.0
-Requires: python-iniparse
 Requires: pygpgme
+
 Conflicts: rpm >= 5-0
 # Zif is a re-implementation of yum in C, however:
 #
@@ -34,18 +61,26 @@ Conflicts: rpm >= 5-0
 # zif experts).
 #
 # ...so we have two sane choices: i) Conflict with it. 2) Stop developing yum.
-Conflicts: zif
+#
+#  Upstream says that #2 will no longer be true after this release.
+Conflicts: zif <= 0.1.3-3.fc15
+
 Obsoletes: yum-skip-broken <= 1.1.18
+Provides: yum-skip-broken = 1.1.18.yum
 Obsoletes: yum-basearchonly <= 1.1.9
+Obsoletes: yum-plugin-basearchonly <= 1.1.9
+Provides: yum-basearchonly = 1.1.9.yum
+Provides: yum-plugin-basearchonly = 1.1.9.yum
 Obsoletes: yum-allow-downgrade < 1.1.20-0
 Obsoletes: yum-plugin-allow-downgrade < 1.1.22-0
+Provides: yum-allow-downgrade = 1.1.20-0.yum
+Provides: yum-plugin-allow-downgrade = 1.1.22-0.yum
 Obsoletes: yum-plugin-protect-packages < 1.1.27-0
-Provides: yum-skip-broken
-Provides: yum-basearchonly
-Provides: yum-allow-downgrade
-Provides: yum-plugin-allow-downgrade
-Provides: yum-protect-packages
-Provides: yum-plugin-protect-packages
+Provides: yum-protect-packages = 1.1.27-0.yum
+Provides: yum-plugin-protect-packages = 1.1.27-0.yum
+Obsoletes: yum-plugin-download-order <= 0.2-2
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+
 
 %description
 Yum is a utility that can check for and automatically download and
@@ -58,9 +93,11 @@ Group: Applications/System
 Requires: yum = %{version}-%{release}
 Requires: dbus-python
 Requires: pygobject2
-Requires(preun): /sbin/chkconfig 
+Requires(preun): /sbin/chkconfig
+Requires(post): /sbin/chkconfig
 Requires(preun): /sbin/service
-Requires(postun): /sbin/chkconfig 
+Requires(post): /sbin/service
+Requires(postun): /sbin/chkconfig
 Requires(postun): /sbin/service
 
 
@@ -83,18 +120,46 @@ Requires(postun): /sbin/service
 These are the files needed to run yum updates as a cron job.
 Install this package if you want auto yum updates nightly via cron.
 
+
+
 %prep
 %setup -q
 
 %build
 make
 
+%if !%{disable_check}
+%check
+make check
+%endif
+
 
 %install
 [ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT
 make DESTDIR=$RPM_BUILD_ROOT install
-# install -m 644 %{SOURCE1} $RPM_BUILD_ROOT/etc/yum/yum.conf
-# install -m 755 %{SOURCE2} $RPM_BUILD_ROOT/etc/cron.daily/yum.cron
+install -m 644 %{SOURCE1} $RPM_BUILD_ROOT/%{_sysconfdir}/yum.conf
+mkdir -p $RPM_BUILD_ROOT/%{_sysconfdir}/yum/pluginconf.d $RPM_BUILD_ROOT/%{yum_pluginslib}
+mkdir -p $RPM_BUILD_ROOT/%{yum_pluginsshare}
+
+%if %{move_yum_conf_back}
+# for now, move repodir/yum.conf back
+mv $RPM_BUILD_ROOT/%{_sysconfdir}/yum/repos.d $RPM_BUILD_ROOT/%{_sysconfdir}/yum.repos.d
+rm -f $RPM_BUILD_ROOT/%{_sysconfdir}/yum/yum.conf
+%endif
+
+%if %{yum_updatesd}
+echo Keeping local yum-updatesd
+%else
+
+# yum-updatesd has moved to the separate source version
+rm -f $RPM_BUILD_ROOT/%{_sysconfdir}/yum/yum-updatesd.conf 
+rm -f $RPM_BUILD_ROOT/%{_sysconfdir}/rc.d/init.d/yum-updatesd
+rm -f $RPM_BUILD_ROOT/%{_sysconfdir}/dbus-1/system.d/yum-updatesd.conf
+rm -f $RPM_BUILD_ROOT/%{_sbindir}/yum-updatesd
+rm -f $RPM_BUILD_ROOT/%{_mandir}/man*/yum-updatesd*
+rm -f $RPM_BUILD_ROOT/%{_datadir}/yum-cli/yumupd.py*
+
+%endif
 
 # Ghost files:
 mkdir -p $RPM_BUILD_ROOT/var/lib/yum/history
@@ -102,12 +167,18 @@ mkdir -p $RPM_BUILD_ROOT/var/lib/yum/plugins
 mkdir -p $RPM_BUILD_ROOT/var/lib/yum/yumdb
 touch $RPM_BUILD_ROOT/var/lib/yum/uuid
 
+# rpmlint bogus stuff...
+chmod +x $RPM_BUILD_ROOT/%{_datadir}/yum-cli/*.py
+chmod +x $RPM_BUILD_ROOT/%{python_sitelib}/yum/*.py
+chmod +x $RPM_BUILD_ROOT/%{python_sitelib}/rpmUtils/*.py
+
 %find_lang %name
 
 %clean
 [ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT
 
 
+%if %{yum_updatesd}
 %post updatesd
 /sbin/chkconfig --add yum-updatesd
 /sbin/service yum-updatesd condrestart >/dev/null 2>&1
@@ -119,6 +190,7 @@ if [ $1 = 0 ]; then
  /sbin/service yum-updatesd stop >/dev/null 2>&1
 fi
 exit 0
+%endif
 
 
 %post cron
@@ -165,21 +237,29 @@ exit 0
 
 
 %files -f %{name}.lang
-%defattr(-, root, root)
+%defattr(-, root, root, -)
 %doc README AUTHORS COPYING TODO INSTALL ChangeLog PLUGINS
+%if %{move_yum_conf_back}
+%config(noreplace) %{_sysconfdir}/yum.conf
+%dir %{_sysconfdir}/yum.repos.d
+%else
 %config(noreplace) %{_sysconfdir}/yum/yum.conf
+%dir %{_sysconfdir}/yum/repos.d
+%endif
 %config(noreplace) %{_sysconfdir}/yum/version-groups.conf
 %dir %{_sysconfdir}/yum
 %dir %{_sysconfdir}/yum/protected.d
-%dir %{_sysconfdir}/yum/repos.d
 %dir %{_sysconfdir}/yum/vars
-%config %{_sysconfdir}/logrotate.d/%{name}
+%config(noreplace) %{_sysconfdir}/logrotate.d/%{name}
 %{_sysconfdir}/bash_completion.d
+%dir %{_datadir}/yum-cli
 %{_datadir}/yum-cli/*
+%if %{yum_updatesd}
 %exclude %{_datadir}/yum-cli/yumupd.py*
+%endif
 %{_bindir}/yum
-/usr/lib/python?.?/site-packages/yum
-/usr/lib/python?.?/site-packages/rpmUtils
+%{python_sitelib}/yum
+%{python_sitelib}/rpmUtils
 %dir /var/cache/yum
 %dir /var/lib/yum
 %ghost /var/lib/yum/uuid
@@ -188,20 +268,24 @@ exit 0
 %ghost /var/lib/yum/yumdb
 %{_mandir}/man*/yum.*
 %{_mandir}/man*/yum-shell*
-
+# plugin stuff
+%dir %{_sysconfdir}/yum/pluginconf.d 
+%dir %{yum_pluginslib}
+%dir %{yum_pluginsshare}
 
 %files cron
 %defattr(-,root,root)
 %doc COPYING
-%{_sysconfdir}/cron.daily/0yum.cron
-%config(noreplace) %{_sysconfdir}/yum/yum-daily.yum
-%config(noreplace) %{_sysconfdir}/yum/yum-weekly.yum
+%config(noreplace) %{_sysconfdir}/cron.daily/yum-update.cron
+%config(noreplace) %{_sysconfdir}/cron.daily/yum-cleanup.cron
 %{_sysconfdir}/rc.d/init.d/yum-cron
+%{_sbindir}/yum-cron
 %config(noreplace) %{_sysconfdir}/sysconfig/yum-cron
+%dir %{_datadir}/yum-cron   
+%{_datadir}/yum-cron/update.yum
+%{_datadir}/yum-cron/cleanup.yum
 
-
-
-
+%if %{yum_updatesd}
 %files updatesd
 %defattr(-, root, root)
 %config(noreplace) %{_sysconfdir}/yum/yum-updatesd.conf
@@ -210,8 +294,12 @@ exit 0
 %{_datadir}/yum-cli/yumupd.py*
 %{_sbindir}/yum-updatesd
 %{_mandir}/man*/yum-updatesd*
+%endif
 
 %changelog
+* Thu Jul 28 2011 Matthew Miller <mattdm at mattdm.org>
+- reorganize yum-cron to allow more flexible scheduling
+
 * Wed Apr 20 2011 James Antill <james at fedoraproject.org>
 - 3.4.1
 - umask bug fix.
diff --git a/yum/__init__.py b/yum/__init__.py
index 99039e0..5fb7c00 100644
--- a/yum/__init__.py
+++ b/yum/__init__.py
@@ -82,7 +82,7 @@ from packages import YumAvailablePackage, YumLocalPackage, YumInstalledPackage
 from packages import YumUrlPackage, YumNotFoundPackage
 from constants import *
 from yum.rpmtrans import RPMTransaction,SimpleCliCallBack
-from yum.i18n import to_unicode, to_str
+from yum.i18n import to_unicode, to_str, exception2msg
 
 import string
 import StringIO
@@ -102,10 +102,12 @@ default_grabber.opts.user_agent += " yum/" + __version__
 
 
 class _YumPreBaseConf:
-    """This is the configuration interface for the YumBase configuration.
-       So if you want to change if plugins are on/off, or debuglevel/etc.
-       you tweak it here, and when yb.conf does it's thing ... it happens. """
-
+    """This is the configuration interface for the :class:`YumBase`
+    configuration.  To change configuration settings such as whether
+    plugins are on or off, or the value of debuglevel, change the
+    values here. Later, when :func:`YumBase.conf` is first called, all
+    of the options will be automatically configured.
+    """
     def __init__(self):
         self.fn = '/etc/yum/yum.conf'
         self.root = '/'
@@ -125,10 +127,12 @@ class _YumPreBaseConf:
 
 
 class _YumPreRepoConf:
-    """This is the configuration interface for the repos configuration.
-       So if you want to change callbacks etc. you tweak it here, and when
-       yb.repos does it's thing ... it happens. """
-
+    """This is the configuration interface for the repos configuration
+    configuration.  To change configuration settings such what
+    callbacks are used, change the values here. Later, when
+    :func:`YumBase.repos` is first called, all of the options will be
+    automatically configured.
+    """
     def __init__(self):
         self.progressbar = None
         self.callback = None
@@ -164,11 +168,11 @@ class _YumCostExclude:
         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
-       class in that you will need to add your own class above it for most
-       real use."""
-    
+    """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 class in that you will need to add your own
+    class above it for most real use.
+    """
     def __init__(self):
         depsolve.Depsolve.__init__(self)
         self._conf = None
@@ -213,6 +217,8 @@ class YumBase(depsolve.Depsolve):
         for cb in self._cleanup: cb()
 
     def close(self):
+        """Close the history and repo objects."""
+
         # We don't want to create the object, so we test if it's been created
         if self._history is not None:
             self.history.close()
@@ -225,15 +231,33 @@ class YumBase(depsolve.Depsolve):
         return transactioninfo.TransactionData()
 
     def doGenericSetup(self, cache=0):
-        """do a default setup for all the normal/necessary yum components,
-           really just a shorthand for testing"""
+        """Do a default setup for all the normal or necessary yum
+        components.  This function is really just a shorthand for
+        testing purposes.
 
+        :param cache: whether to run in cache only mode, which will
+           run only from the system cache
+        """
         self.preconf.init_plugins = False
         self.conf.cache = cache
 
     def doConfigSetup(self, fn='/etc/yum/yum.conf', root='/', init_plugins=True,
             plugin_types=(plugins.TYPE_CORE,), optparser=None, debuglevel=None,
             errorlevel=None):
+        """Deprecated.  Perform configuration setup.
+
+        :param fn: the name of the configuration file to use
+        :param root: the root directory to use
+        :param init_plugins: whether to initialize plugins before
+           running yum
+        :param plugin_types: a tuple containing the types to plugins
+           to load
+        :param optparser: the option parser to use for configuration
+        :param debuglevel: the minimum debug logging level to output
+           messages from
+        :param errorlevel: the minimum error logging level to output
+           messages from
+        """
         warnings.warn(_('doConfigSetup() will go away in a future version of Yum.\n'),
                 Errors.YumFutureDeprecationWarning, stacklevel=2)
 
@@ -297,7 +321,7 @@ class YumBase(depsolve.Depsolve):
             # Try the old default
             fn = '/etc/yum.conf'
 
-        startupconf = config.readStartupConfig(fn, root)
+        startupconf = config.readStartupConfig(fn, root, releasever)
         startupconf.arch = arch
         startupconf.basearch = self.arch.basearch
         if uuid:
@@ -367,22 +391,36 @@ class YumBase(depsolve.Depsolve):
     def doLoggingSetup(self, debuglevel, errorlevel,
                        syslog_ident=None, syslog_facility=None,
                        syslog_device='/dev/log'):
-        '''
-        Perform logging related setup.
-
-        @param debuglevel: Debug logging level to use.
-        @param errorlevel: Error logging level to use.
-        '''
+        """Perform logging related setup.
+
+        :param debuglevel: the minimum debug logging level to output
+           messages from
+        :param errorlevel: the minimum error logging level to output
+           messages from
+        :param syslog_ident: the ident of the syslog to use
+        :param syslog_facility: the name of the syslog facility to use
+        :param syslog_device: the syslog device to use
+        """
         logginglevels.doLoggingSetup(debuglevel, errorlevel,
                                      syslog_ident, syslog_facility,
                                      syslog_device)
 
     def doFileLogSetup(self, uid, logfile):
+        """Set up the logging file.
+
+        :param uid: the user id of the current user
+        :param logfile: the name of the file to use for logging
+        """
         logginglevels.setFileLog(uid, logfile, self._cleanup)
 
     def getReposFromConfigFile(self, repofn, repo_age=None, validate=None):
-        """read in repositories from a config .repo file"""
+        """Read in repositories from a config .repo file.
 
+        :param repofn: a string specifying the path of the .repo file
+           to read
+        :param repo_age: the last time that the .repo file was
+           modified, in seconds since the epoch
+        """
         if repo_age is None:
             repo_age = os.stat(repofn)[8]
         
@@ -448,8 +486,11 @@ class YumBase(depsolve.Depsolve):
                 self.logger.warning(e)
         
     def getReposFromConfig(self):
-        """read in repositories from config main and .repo files"""
-
+        """Read in repositories from the main yum conf file, and from
+        .repo files.  The location of the main yum conf file is given
+        by self.conf.config_file_path, and the location of the
+        directory of .repo files is given by self.conf.reposdir.
+        """
         # Read .repo files from directories specified by the reposdir option
         # (typically /etc/yum/repos.d)
         repo_config_age = self.conf.config_file_age
@@ -472,12 +513,13 @@ class YumBase(depsolve.Depsolve):
                     self.getReposFromConfigFile(repofn, repo_age=thisrepo_age)
 
     def readRepoConfig(self, parser, section):
-        '''Parse an INI file section for a repository.
+        """Parse an INI file section for a repository.
 
-        @param parser: ConfParser or similar to read INI file values from.
-        @param section: INI file section to read.
-        @return: YumRepository instance.
-        '''
+        :param parser: :class:`ConfigParser` or similar object to read
+           INI file values from
+        :param section: INI file section to read
+        :return: :class:`yum.yumRepo.YumRepository` instance
+        """
         repo = yumRepo.YumRepository(section)
         try:
             repo.populate(parser, section, self.conf)
@@ -500,31 +542,31 @@ class YumBase(depsolve.Depsolve):
         return repo
 
     def disablePlugins(self):
-        '''Disable yum plugins
-        '''
+        """Disable yum plugins."""
+
         self.plugins = plugins.DummyYumPlugins()
     
     def doPluginSetup(self, optparser=None, plugin_types=None, searchpath=None,
             confpath=None,disabled_plugins=None,enabled_plugins=None):
-        '''Initialise and enable yum plugins. 
-
-        Note: _getConfig() will initialise plugins if instructed to. Only
-        call this method directly if not calling _getConfig() or calling
-        doConfigSetup(init_plugins=False).
-
-        @param optparser: The OptionParser instance for this run (optional)
-        @param plugin_types: A sequence specifying the types of plugins to load.
-            This should be a sequence containing one or more of the
-            yum.plugins.TYPE_...  constants. If None (the default), all plugins
-            will be loaded.
-        @param searchpath: A list of directories to look in for plugins. A
-            default will be used if no value is specified.
-        @param confpath: A list of directories to look in for plugin
-            configuration files. A default will be used if no value is
-            specified.
-        @param disabled_plugins: Plugins to be disabled    
-        @param enabled_plugins: Plugins to be enabled
-        '''
+        """Initialise and enable yum plugins.
+        Note: _getConfig() will also initialise plugins if instructed
+        to. Only call this method directly if not calling _getConfig()
+        or calling doConfigSetup(init_plugins=False).
+
+        :param optparser: the :class:`OptionParser` instance to use
+           for this run
+        :param plugin_types: a sequence specifying the types of plugins to load.
+           This should be a sequence containing one or more of the
+           yum.plugins.TYPE_...  constants. If None (the default), all plugins
+           will be loaded
+        :param searchpath: a list of directories to look in for plugins. A
+           default will be used if no value is specified
+        :param confpath: a list of directories to look in for plugin
+           configuration files. A default will be used if no value is
+           specified
+        :param disabled_plugins: a list of plugins to be disabled    
+        :param enabled_plugins: a list plugins to be enabled
+        """
         if isinstance(self.plugins, plugins.YumPlugins):
             raise RuntimeError(_("plugins already initialised"))
 
@@ -533,6 +575,8 @@ class YumBase(depsolve.Depsolve):
 
     
     def doRpmDBSetup(self):
+        """Deprecated.  Set up the rpm database."""
+
         warnings.warn(_('doRpmDBSetup() will go away in a future version of Yum.\n'),
                 Errors.YumFutureDeprecationWarning, stacklevel=2)
 
@@ -552,7 +596,8 @@ class YumBase(depsolve.Depsolve):
         return self._rpmdb
 
     def closeRpmDB(self):
-        """closes down the instances of the rpmdb we have wangling around"""
+        """Closes down the instances of rpmdb that could be open."""
+
         if self._rpmdb is not None:
             self._rpmdb.ts = None
             self._rpmdb.dropCachedData()
@@ -567,6 +612,12 @@ class YumBase(depsolve.Depsolve):
         self._ts = None
 
     def doRepoSetup(self, thisrepo=None):
+        """Deprecated. Set up the yum repositories.
+
+        :param thisrepo: the repository to set up.  If None, all
+           repositories will be set up
+        :return: the set up repos
+        """
         warnings.warn(_('doRepoSetup() will go away in a future version of Yum.\n'),
                 Errors.YumFutureDeprecationWarning, stacklevel=2)
 
@@ -630,6 +681,14 @@ class YumBase(depsolve.Depsolve):
         self._repos = RepoStorage(self)
     
     def doSackSetup(self, archlist=None, thisrepo=None):
+        """Deprecated. Populate the package sacks with information
+        from our repositories.
+
+        :param archlist: a list of the names of archs to include.  If
+           None, all arches are set up
+        :param thisrepo: the repository to use.  If None, all enabled
+           repositories are used
+        """
         warnings.warn(_('doSackSetup() will go away in a future version of Yum.\n'),
                 Errors.YumFutureDeprecationWarning, stacklevel=2)
 
@@ -711,6 +770,9 @@ class YumBase(depsolve.Depsolve):
             
            
     def doUpdateSetup(self):
+        """Deprecated. Set up the update object in the base class and populate the
+        updates, obsoletes, and other lists.
+        """
         warnings.warn(_('doUpdateSetup() will go away in a future version of Yum.\n'),
                 Errors.YumFutureDeprecationWarning, stacklevel=2)
 
@@ -765,6 +827,8 @@ class YumBase(depsolve.Depsolve):
         return self._up
     
     def doGroupSetup(self):
+        """Deprecated. Create and populate the groups object."""
+
         warnings.warn(_('doGroupSetup() will go away in a future version of Yum.\n'),
                 Errors.YumFutureDeprecationWarning, stacklevel=2)
 
@@ -881,7 +945,8 @@ class YumBase(depsolve.Depsolve):
         if self._history is None:
             pdb_path = self.conf.persistdir + "/history"
             self._history = yum.history.YumHistory(root=self.conf.installroot,
-                                                   db_path=pdb_path)
+                                                   db_path=pdb_path,
+                                                   releasever=self.conf.yumvar['releasever'])
         return self._history
     
     # properties so they auto-create themselves with defaults
@@ -928,9 +993,10 @@ class YumBase(depsolve.Depsolve):
     
     
     def doSackFilelistPopulate(self):
-        """convenience function to populate the repos with the filelist metadata
-           it also is simply to only emit a log if anything actually gets populated"""
-        
+        """Convenience function to populate the repositories with the
+        filelist metadata, and emit a log message only if new
+        information is actually populated.
+        """
         necessary = False
         
         # I can't think of a nice way of doing this, we have to have the sack here
@@ -951,8 +1017,12 @@ class YumBase(depsolve.Depsolve):
             self.repos.populateSack(mdtype='filelists')
            
     def yumUtilsMsg(self, func, prog):
-        """ Output a message that the tool requires the yum-utils package,
-            if not installed. """
+        """Output a message that the given tool requires the yum-utils
+        package, if it not installed.
+
+        :param func: the function to output the message
+        :param prog: the name of the tool that requires yum-utils
+        """
         if self.rpmdb.contains(name="yum-utils"):
             return
 
@@ -964,8 +1034,17 @@ class YumBase(depsolve.Depsolve):
              (hibeg, prog, hiend))
 
     def buildTransaction(self, unfinished_transactions_check=True):
-        """go through the packages in the transaction set, find them in the
-           packageSack or rpmdb, and pack up the ts accordingly"""
+        """Go through the packages in the transaction set, find them
+        in the packageSack or rpmdb, and pack up the transaction set
+        accordingly.
+
+        :param unfinished_transactions_check: whether to check for
+           unfinished transactions before building the new transaction
+        """
+        # FIXME: This is horrible, see below and yummain. Maybe create a real
+        #        rescode object? :(
+        self._depsolving_failed = False
+
         if (unfinished_transactions_check and
             misc.find_unfinished_transactions(yumlibpath=self.conf.persistdir)):
             msg = _('There are unfinished transactions remaining. You might ' \
@@ -1242,13 +1321,15 @@ class YumBase(depsolve.Depsolve):
         if None in pkgtup:
             return None
         return pkgtup
-    def _add_not_found_a(self, pkgs, nevra_dict):
-        pkgtup = self._add_not_found(pkgs, nevra_dict)
+    def _add_not_found_a(self, pkgs, nevra_dict={}, pkgtup=None):
+        if pkgtup is None and nevra_dict:
+            pkgtup = self._add_not_found(pkgs, nevra_dict)
         if pkgtup is None:
             return
         self._not_found_a[pkgtup] = YumNotFoundPackage(pkgtup)
-    def _add_not_found_i(self, pkgs, nevra_dict):
-        pkgtup = self._add_not_found(pkgs, nevra_dict)
+    def _add_not_found_i(self, pkgs, nevra_dict={}, pkgtup=None):
+        if pkgtup is None and nevra_dict:
+            pkgtup = self._add_not_found(pkgs, nevra_dict)
         if pkgtup is None:
             return
         self._not_found_i[pkgtup] = YumNotFoundPackage(pkgtup)
@@ -1454,8 +1535,14 @@ class YumBase(depsolve.Depsolve):
         return probs
 
     def runTransaction(self, cb):
-        """takes an rpm callback object, performs the transaction"""
+        """Perform the transaction.
 
+        :param cb: an rpm callback object to use in the transaction
+        :return: a :class:`yum.misc.GenericHolder` containing
+           information about the results of the transaction
+        :raises: :class:`yum.Errors.YumRPMTransError` if there is a
+           transaction cannot be completed
+        """
         self.plugins.run('pretrans')
 
         #  We may want to put this other places, eventually, but for now it's
@@ -1571,9 +1658,14 @@ class YumBase(depsolve.Depsolve):
         return resultobject
 
     def verifyTransaction(self, resultobject=None):
-        """checks that the transaction did what we expected it to do. Also 
-           propagates our external yumdb info"""
-        
+        """Check that the transaction did what was expected, and 
+        propagate external yumdb information.  Output error messages
+        if the transaction did not do what was expected.
+
+        :param resultobject: the :class:`yum.misc.GenericHolder`
+           object returned from the :func:`runTransaction` call that
+           ran the transaction
+        """
         # check to see that the rpmdb and the tsInfo roughly matches
         # push package object metadata outside of rpmdb into yumdb
         # delete old yumdb metadata entries
@@ -1645,6 +1737,9 @@ class YumBase(depsolve.Depsolve):
                 elif loginuid is not None:
                     po.yumdb_info.installed_by = str(loginuid)
 
+                if self.conf.history_record:
+                    self.history.sync_alldb(po)
+
         # Remove old ones after installing new ones, so we can copy values.
         for txmbr in self.tsInfo:
             if txmbr.output_state in TS_INSTALL_STATES:
@@ -1680,10 +1775,11 @@ class YumBase(depsolve.Depsolve):
         self.verbose_logger.debug('VerifyTransaction time: %0.3f' % (time.time() - vt_st))
 
     def costExcludePackages(self):
-        """ 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."""
-        
+        """Create an excluder for repositories with higher costs. For
+        example, if repo-A:cost=1 and repo-B:cost=2, this function
+        will set up an excluder on repo-B that looks for packages in
+        repo-B.
+        """
         # if all the repo.costs are equal then don't bother running things
         costs = {}
         for r in self.repos.listEnabled():
@@ -1705,10 +1801,12 @@ class YumBase(depsolve.Depsolve):
             done = True
 
     def excludePackages(self, repo=None):
-        """removes packages from packageSacks based on global exclude lists,
-           command line excludes and per-repository excludes, takes optional 
-           repo object to use."""
+        """Remove packages from packageSacks based on global exclude
+        lists, command line excludes and per-repository excludes.
 
+        :param repo: a repo object to use.  If not given, all
+           repositories are used
+        """
         if "all" in self.conf.disable_excludes:
             return
         
@@ -1735,9 +1833,11 @@ class YumBase(depsolve.Depsolve):
             self.pkgSack.addPackageExcluder(repoid, exid,'exclude.match', match)
 
     def includePackages(self, repo):
-        """removes packages from packageSacks based on list of packages, to include.
-           takes repoid as a mandatory argument."""
-        
+        """Remove packages from packageSacks based on list of
+        packages to include.  
+
+        :param repo: the repository to use
+        """
         includelist = repo.getIncludePkgList()
         
         if len(includelist) == 0:
@@ -1757,8 +1857,11 @@ class YumBase(depsolve.Depsolve):
         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"""
-        
+        """Acquire the yum lock.
+
+        :param lockfile: the file to use for the lock
+        :raises: :class:`yum.Errors.LockError`
+        """
         if self.conf.uid != 0:
             #  If we are a user, assume we are using the root cache ... so don't
             # bother locking.
@@ -1804,8 +1907,12 @@ class YumBase(depsolve.Depsolve):
         self._lockfile = lockfile
     
     def doUnlock(self, lockfile=None):
-        """do the unlock for yum"""
-        
+        """Release the yum lock.
+
+        :param lockfile: the lock file to use.  If not given, the file
+           that was given as a parameter to the :func:`doLock` call
+           that closed the lock is used
+        """
         # if we're not root then we don't lock - just return nicely
         #  Note that we can get here from __del__, so if we haven't created
         # YumBase.conf we don't want to do so here as creating stuff inside
@@ -1836,25 +1943,36 @@ class YumBase(depsolve.Depsolve):
             if not os.path.exists(lockdir):
                 os.makedirs(lockdir, mode=0755)
             fd = os.open(filename, os.O_EXCL|os.O_CREAT|os.O_WRONLY, mode)    
+            os.write(fd, contents)
+            os.close(fd)
+            return 1
         except OSError, msg:
             if not msg.errno == errno.EEXIST: 
                 # Whoa. What the heck happened?
                 errmsg = _('Could not create lock at %s: %s ') % (filename, str(msg))
                 raise Errors.LockError(msg.errno, errmsg, int(contents))
             return 0
-        else:
-            os.write(fd, contents)
-            os.close(fd)
-            return 1
     
     def _unlock(self, filename):
         misc.unlink_f(filename)
 
     def verifyPkg(self, fo, po, raiseError):
-        """verifies the package is what we expect it to be
-           raiseError  = defaults to 0 - if 1 then will raise
-           a URLGrabError if the file does not check out.
-           otherwise it returns false for a failure, true for success"""
+        """Check that the checksum of a remote package matches what we
+        expect it to be.  If the checksum of the package file is
+        wrong, and the file is also larger than expected, it cannot be
+        redeemed, so delete it.
+
+        :param fo: the file object of the package
+        :param po: the package object to verify
+        :param raiseError: if *raiseError* is 1, and the package
+           does not check out, a :class:`URLGrabError will be raised.
+           Defaults to 0
+        :return: True if the package is verified successfully.
+           Otherwise, False will be returned, unless *raiseError* is
+           1, in which case a :class:`URLGrabError` will be raised
+        :raises: :class:`URLGrabError` if verification fails, and
+           *raiseError* is 1
+        """
         failed = False
 
         if type(fo) is types.InstanceType:
@@ -1894,9 +2012,16 @@ class YumBase(depsolve.Depsolve):
         
         
     def verifyChecksum(self, fo, checksumType, csum):
-        """Verify the checksum of the file versus the 
-           provided checksum"""
-
+        """Verify that the checksum of the given file matches the
+        given checksum.
+
+        :param fo: the file object to verify the checksum of
+        :param checksumType: the type of checksum to use
+        :parm csum: the checksum to check against
+        :return: 0 if the checksums match
+        :raises: :class:`URLGrabError` if there is an error performing
+           the checksums, or the checksums do not match
+        """
         try:
             filesum = misc.checksum(checksumType, fo)
         except Errors.MiscError, e:
@@ -1908,6 +2033,17 @@ class YumBase(depsolve.Depsolve):
         return 0
 
     def downloadPkgs(self, pkglist, callback=None, callback_total=None):
+        """Download the packages specified by the given list of
+        package objects.
+
+        :param pkglist: a list of package objects specifying the
+           packages to download
+        :param callback: unused
+        :param callback_total: a callback to output messages about the
+           download operation
+        :return: a dictionary containing errors from the downloading process
+        :raises: :class:`URLGrabError`
+        """
         def mediasort(apo, bpo):
             # FIXME: we should probably also use the mediaid; else we
             # could conceivably ping-pong between different disc1's
@@ -1998,16 +2134,6 @@ class YumBase(depsolve.Depsolve):
                     os.unlink(local)
 
             checkfunc = (self.verifyPkg, (po, 1), {})
-            dirstat = os.statvfs(po.repo.pkgdir)
-            if (dirstat.f_bavail * dirstat.f_bsize) <= long(po.size):
-                adderror(po, _('Insufficient space in download directory %s\n'
-                        "    * free   %s\n"
-                        "    * needed %s") %
-                         (po.repo.pkgdir,
-                          format_number(dirstat.f_bavail * dirstat.f_bsize),
-                          format_number(po.size)))
-                continue
-            
             try:
                 if i == 1 and not local_size and remote_size == po.size:
                     text = os.path.basename(po.relativepath)
@@ -2032,7 +2158,7 @@ class YumBase(depsolve.Depsolve):
                 done_repos.add(po.repoid)
 
             except Errors.RepoError, e:
-                adderror(po, str(e))
+                adderror(po, exception2msg(e))
             else:
                 po.localpath = mylocal
                 if po in errors:
@@ -2052,7 +2178,22 @@ class YumBase(depsolve.Depsolve):
         return errors
 
     def verifyHeader(self, fo, po, raiseError):
-        """check the header out via it's naevr, internally"""
+        """Check that the header of the given file object and matches
+        the given package.
+
+        :param fo: the file object to check
+        :param po: the package object to check
+        :param raiseError: if *raiseError* is True, a
+           :class:`URLGrabError` will be raised if the header matches
+           the package object, or cannot be read from the file.  If
+           *raiseError* is False, 0 will be returned in the above
+           cases
+        :return: 1 if the header matches the package object, and 0 if
+           they do not match, and *raiseError* is False
+        :raises: :class:`URLGrabError` if *raiseError* is True, and
+           the header does not match the package object or cannot be
+           read from the file
+        """
         if type(fo) is types.InstanceType:
             fo = fo.filename
             
@@ -2076,9 +2217,12 @@ class YumBase(depsolve.Depsolve):
         return 1
         
     def downloadHeader(self, po):
-        """download a header from a package object.
-           output based on callback, raise yum.Errors.YumBaseError on problems"""
+        """Download a header from a package object.
 
+        :param po: the package object to download the header from
+        :raises: :class:`yum.Errors.RepoError` if there are errors
+           obtaining the header
+        """
         if hasattr(po, 'pkgtype') and po.pkgtype == 'local':
             return
                 
@@ -2122,15 +2266,17 @@ class YumBase(depsolve.Depsolve):
             return
 
     def sigCheckPkg(self, po):
-        '''
-        Take a package object and attempt to verify GPG signature if required
+        """Verify the GPG signature of the given package object.
 
-        Returns (result, error_string) where result is:
-            - 0 - GPG signature verifies ok or verification is not required.
-            - 1 - GPG verification failed but installation of the right GPG key
-                  might help.
-            - 2 - Fatal GPG verification error, give up.
-        '''
+        :param po: the package object to verify the signature of
+        :return: (result, error_string) 
+           where result is::
+
+              0 = GPG signature verifies ok or verification is not required.
+              1 = GPG verification failed but installation of the right GPG key
+                    might help.
+              2 = Fatal GPG verification error, give up.
+        """
         if self._override_sigchecks:
             check = False
             hasgpgkey = 0
@@ -2181,6 +2327,9 @@ class YumBase(depsolve.Depsolve):
         return result, msg
 
     def cleanUsedHeadersPackages(self):
+        """Delete the header and package files used in the
+        transaction from the yum cache.
+        """
         filelist = []
         for txmbr in self.tsInfo:
             if txmbr.po.state not in TS_INSTALL_STATES:
@@ -2218,27 +2367,40 @@ class YumBase(depsolve.Depsolve):
                     _('%s removed'), fn)
         
     def cleanHeaders(self):
+        """Delete the header files from the yum cache."""
+
         exts = ['hdr']
         return self._cleanFiles(exts, 'hdrdir', 'header')
 
     def cleanPackages(self):
+        """Delete the package files from the yum cache."""
+
         exts = ['rpm']
         return self._cleanFiles(exts, 'pkgdir', 'package')
 
     def cleanSqlite(self):
+        """Delete the sqlite files from the yum cache."""
+
         exts = ['sqlite', 'sqlite.bz2', 'sqlite-journal']
         return self._cleanFiles(exts, 'cachedir', 'sqlite')
 
     def cleanMetadata(self):
+        """Delete the metadata files from the yum cache."""
+
         exts = ['xml.gz', 'xml', 'cachecookie', 'mirrorlist.txt', 'asc']
         # Metalink is also here, but is a *.xml file
         return self._cleanFiles(exts, 'cachedir', 'metadata') 
 
     def cleanExpireCache(self):
+        """Delete the local data saying when the metadata and mirror
+           lists were downloaded for each repository."""
+
         exts = ['cachecookie', 'mirrorlist.txt']
         return self._cleanFiles(exts, 'cachedir', 'metadata')
 
     def cleanRpmDB(self):
+        """Delete any cached data from the local rpmdb."""
+
         cachedir = self.conf.persistdir + "/rpmdb-indexes/"
         if not os.path.exists(cachedir):
             filelist = []
@@ -2272,8 +2434,29 @@ class YumBase(depsolve.Depsolve):
 
     def doPackageLists(self, pkgnarrow='all', patterns=None, showdups=None,
                        ignore_case=False):
-        """generates lists of packages, un-reduced, based on pkgnarrow option"""
-
+        """Return a :class:`yum.misc.GenericHolder` containing
+        lists of package objects.  The contents of the lists are
+        specified in various ways by the arguments.
+
+        :param pkgnarrow: a string specifying which types of packages
+           lists to produces, such as updates, installed, available,
+           etc.
+        :param patterns: a list of names or wildcards specifying
+           packages to list
+        :param showdups: whether to include duplicate packages in the
+           lists
+        :param ignore_case: whether to ignore case when searching by
+           package names
+        :return: a :class:`yum.misc.GenericHolder` instance with the
+           following lists defined::
+
+             available = list of packageObjects
+             installed = list of packageObjects
+             updates = tuples of packageObjects (updating, installed)
+             extras = list of packageObjects
+             obsoletes = tuples of packageObjects (obsoleting, installed)
+             recent = list of packageObjects
+        """
         if showdups is None:
             showdups = self.conf.showdupesfromrepos
         ygh = misc.GenericHolder(iter=pkgnarrow)
@@ -2461,14 +2644,13 @@ class YumBase(depsolve.Depsolve):
 
         
     def findDeps(self, pkgs):
-        """
-        Return the dependencies for a given package object list, as well
-        possible solutions for those dependencies.
+        """Return the dependencies for a given package object list, as well
+        as possible solutions for those dependencies.
            
-        Returns the deps as a dict of dicts::
-            packageobject = [reqs] = [list of satisfying pkgs]
+        :param pkgs: a list of package objects
+        :return: the dependencies as a dictionary of dictionaries:
+           packageobject = [reqs] = [list of satisfying pkgs]
         """
-        
         results = {}
 
         for pkg in pkgs:
@@ -2495,10 +2677,22 @@ class YumBase(depsolve.Depsolve):
     # pre 3.2.10 API used to always showdups, so that's the default atm.
     def searchGenerator(self, fields, criteria, showdups=True, keys=False, 
                                              searchtags=True, searchrpmdb=True):
-        """Generator method to lighten memory load for some searches.
-           This is the preferred search function to use. Setting keys to True
-           will use the search keys that matched in the sorting, and return
-           the search keys in the results. """
+        """Yield the packages that match the given search criteria.
+        This generator method will lighten memory load for some
+        searches, and is the preferred search function to use.
+
+        :param fields: the fields to search
+        :param criteria: a list of strings specifying the criteria to
+           search for
+        :param showdups: whether to yield duplicate packages from
+           different repositories
+        :param keys: setting *keys* to True will use the search keys
+           that matched in the sorting, and return the search keys in
+           the results
+        :param searchtags: whether to search the package tags
+        :param searchrpmdb: whether to search the rmpdb
+           
+        """
         sql_fields = []
         for f in fields:
             sql_fields.append(RPM_TO_SQLITE.get(f, f))
@@ -2661,6 +2855,14 @@ class YumBase(depsolve.Depsolve):
                     yield (po, vs)
 
     def searchPackageTags(self, criteria):
+        """Search for and return a list packages that have tags
+        matching the given criteria.
+
+        :param criteria: a list of strings specifying the criteria to
+           search for
+        :return: a list of package objects that have tags matching the
+           given criteria
+        """
         results = {} # name = [(criteria, taglist)]
         for c in criteria:
             c = c.lower()
@@ -2677,11 +2879,16 @@ class YumBase(depsolve.Depsolve):
         return results
         
     def searchPackages(self, fields, criteria, callback=None):
-        """Search specified fields for matches to criteria
-           optional callback specified to print out results
-           as you go. Callback is a simple function of:
-           callback(po, matched values list). It will 
-           just return a dict of dict[po]=matched values list"""
+        """Deprecated.  Search the specified fields for packages that
+        match the given criteria, and return a list of the results.
+
+        :param fields: the fields to seach
+        :param criteria: a list of strings specifying the criteria to
+           search for
+        :param callback: a function to print out the results as they
+           are found.  *callback* should have the form callback(po,
+           matched values list)
+        """
         warnings.warn(_('searchPackages() will go away in a future version of Yum.\
                       Use searchGenerator() instead. \n'),
                 Errors.YumFutureDeprecationWarning, stacklevel=2)           
@@ -2700,6 +2907,19 @@ class YumBase(depsolve.Depsolve):
     
     def searchPackageProvides(self, args, callback=None,
                               callback_has_matchfor=False):
+        """Search for and return a list package objects that provide
+        the given files or features.
+
+        :param args: a list of strings specifying the files and
+           features to search for the packages that provide
+        :param callback: a callback function to print out the results
+           as they are found
+        :param callback_has_matchfor: whether the callback function
+           will accept a list of strings to highlight in its output.
+           If this is true, *args* will be passed to *callback* so
+           that the files or features that were searched for can be
+           highlighted
+        """
         def _arg_data(arg):
             if not misc.re_glob(arg):
                 isglob = False
@@ -2818,11 +3038,17 @@ class YumBase(depsolve.Depsolve):
         return matches
 
     def doGroupLists(self, uservisible=0, patterns=None, ignore_case=True):
-        """returns two lists of groups, installed groups and available groups
-           optional 'uservisible' bool to tell it whether or not to return
-           only groups marked as uservisible"""
-        
-        
+        """Return two lists of groups: installed groups and available
+        groups.
+
+        :param uservisible: If True, only groups marked as uservisible
+           will be returned. Otherwise, all groups will be returned
+        :param patterns: a list of stings.  If given, only groups
+           with names that match the patterns will be included in the
+           lists.  If not given, all groups will be included
+        :param ignore_case: whether to ignore case when determining
+           whether group names match the strings in *patterns*
+        """
         installed = []
         available = []
 
@@ -2852,8 +3078,13 @@ class YumBase(depsolve.Depsolve):
     
     
     def groupRemove(self, grpid):
-        """mark all the packages in this group to be removed"""
-        
+        """Mark all the packages in the given group to be removed.
+
+        :param grpid: the name of the group containing the packages to
+           mark for removal
+        :return: a list of transaction members added to the
+           transaction set by this function
+        """
         txmbrs_used = []
         
         thesegroups = self.comps.return_groups(grpid)
@@ -2872,9 +3103,10 @@ class YumBase(depsolve.Depsolve):
         return txmbrs_used
 
     def groupUnremove(self, grpid):
-        """unmark any packages in the group from being removed"""
-        
+        """Unmark any packages in the given group from being removed.
 
+        :param grpid: the name of the group to unmark the packages of
+        """
         thesegroups = self.comps.return_groups(grpid)
         if not thesegroups:
             raise Errors.GroupsError, _("No Group named %s exists") % to_unicode(grpid)
@@ -2899,12 +3131,16 @@ class YumBase(depsolve.Depsolve):
         
         
     def selectGroup(self, grpid, group_package_types=[], enable_group_conditionals=None):
-        """mark all the packages in the group to be installed
-           returns a list of transaction members it added to the transaction 
-           set
-           Optionally take:
-           group_package_types=List - overrides self.conf.group_package_types
-           enable_group_conditionals=Bool - overrides self.conf.enable_group_conditionals
+        """Mark all the packages in the given group to be installed.
+
+        :param grpid: the name of the group containing the packages to
+           mark for installation
+        :param group_package_types: a list of the types of groups to
+           work with.  This overrides self.conf.group_package_types
+        :param enable_group_conditionals: overrides
+           self.conf.enable_group_conditionals
+        :return: a list of transaction members added to the
+           transaction set by this function
         """
 
         if not self.comps.has_group(grpid):
@@ -2939,7 +3175,7 @@ class YumBase(depsolve.Depsolve):
                 self.verbose_logger.log(logginglevels.DEBUG_2,
                     _('Adding package %s from group %s'), pkg, thisgroup.groupid)
                 try:
-                    txmbrs = self.install(name = pkg)
+                    txmbrs = self.install(name=pkg, pkg_warning_level='debug2')
                 except Errors.InstallError, e:
                     self.verbose_logger.debug(_('No package named %s available to be installed'),
                         pkg)
@@ -2997,10 +3233,14 @@ class YumBase(depsolve.Depsolve):
         return txmbrs_used
 
     def deselectGroup(self, grpid, force=False):
-        """ Without the force option set, this removes packages from being
-            installed that were added as part of installing one of the
-            group(s). If the force option is set, then all installing packages
-            in the group(s) are force removed from the transaction. """
+        """Unmark the packages in the given group from being
+        installed.
+
+        :param grpid: the name of the group containing the packages to
+           unmark from installation
+        :param force: if True, force remove all the packages in the
+           given group from the transaction
+        """
         
         if not self.comps.has_group(grpid):
             raise Errors.GroupsError, _("No Group named %s exists") % to_unicode(grpid)
@@ -3035,12 +3275,21 @@ class YumBase(depsolve.Depsolve):
                             self.tsInfo.remove(pkg.pkgtup)
         
     def getPackageObject(self, pkgtup, allow_missing=False):
-        """retrieves a packageObject from a pkgtuple - if we need
-           to pick and choose which one is best we better call out
-           to some method from here to pick the best pkgobj if there are
-           more than one response - right now it's more rudimentary."""
-           
-        
+        """Return a package object that corresponds to the given
+        package tuple.
+
+        :param pkgtup: the package tuple specifying the package object
+           to return
+
+        :param allow_missing: If no package corresponding to the given
+           package tuple can be found, None is returned if
+           *allow_missing* is True, and a :class:`yum.Errors.DepError` is
+           raised if *allow_missing* is False.
+        :return: a package object corresponding to the given package tuple
+        :raises: a :class:`yum.Errors.DepError` if no package
+           corresponding to the given package tuple can be found, and
+           *allow_missing* is False
+        """
         # look it up in the self.localPackages first:
         for po in self.localPackages:
             if po.pkgtup == pkgtup:
@@ -3049,7 +3298,7 @@ class YumBase(depsolve.Depsolve):
         pkgs = self.pkgSack.searchPkgTuple(pkgtup)
 
         if len(pkgs) == 0:
-            self._add_not_found_a(pkgs, pkgtup)
+            self._add_not_found_a(pkgs, pkgtup=pkgtup)
             if allow_missing: #  This can happen due to excludes after .up has
                 return None   # happened.
             raise Errors.DepError, _('Package tuple %s could not be found in packagesack') % str(pkgtup)
@@ -3065,13 +3314,21 @@ class YumBase(depsolve.Depsolve):
         return result
 
     def getInstalledPackageObject(self, pkgtup):
-        """ Returns a YumInstalledPackage object for the pkgtup specified, or
-            raises an exception. You should use this instead of
-            searchPkgTuple() if you are assuming there is a value. """
-
+        """Return a :class:`yum.packages.YumInstalledPackage` object that
+        corresponds to the given package tuple.  This function should
+        be used instead of :func:`searchPkgTuple` if you are assuming
+        that the package object exists.
+
+        :param pkgtup: the package tuple specifying the package object
+           to return
+        :return: a :class:`yum.packages.YumInstalledPackage` object corresponding
+           to the given package tuple
+        :raises: a :class:`yum.Errors.RpmDBError` if the specified package
+           object cannot be found
+        """
         pkgs = self.rpmdb.searchPkgTuple(pkgtup)
         if len(pkgs) == 0:
-            self._add_not_found_i(pkgs, pkgtup)
+            self._add_not_found_i(pkgs, pkgtup=pkgtup)
             raise Errors.RpmDBError, _('Package tuple %s could not be found in rpmdb') % str(pkgtup)
 
         # Dito. FIXME from getPackageObject() for len() > 1 ... :)
@@ -3079,9 +3336,11 @@ class YumBase(depsolve.Depsolve):
         return po
         
     def gpgKeyCheck(self):
-        """checks for the presence of gpg keys in the rpmdb
-           returns 0 if no keys returns 1 if keys"""
+        """Checks for the presence of GPG keys in the rpmdb.
 
+        :return: 0 if there are no GPG keys in the rpmdb, and 1 if
+           there are keys
+        """
         gpgkeyschecked = self.conf.cachedir + '/.gpgkeyschecked.yum'
         if os.path.exists(gpgkeyschecked):
             return 1
@@ -3106,9 +3365,13 @@ class YumBase(depsolve.Depsolve):
             return 1
 
     def returnPackagesByDep(self, depstring):
-        """Pass in a generic [build]require string and this function will 
-           pass back the packages it finds providing that dep."""
+        """Return a list of package objects that provide the given
+        dependencies. 
 
+        :param depstring: a string specifying the dependency to return
+           the packages that fulfil
+        :return: a list of packages that fulfil the given dependency
+        """
         if not depstring:
             return []
 
@@ -3135,9 +3398,16 @@ class YumBase(depsolve.Depsolve):
         return self.pkgSack.getProvides(depname, depflags, depver).keys()
 
     def returnPackageByDep(self, depstring):
-        """Pass in a generic [build]require string and this function will 
-           pass back the best(or first) package it finds providing that dep."""
-        
+        """Return the best, or first, package object that provides the
+        given dependencies.
+
+        :param depstring: a string specifying the dependency to return
+           the package that fulfils
+        :return: the best, or first, package that fulfils the given
+           dependency
+        :raises: a :class:`yum.Errors.YumBaseError` if no packages that
+           fulfil the given dependency can be found
+        """
         # we get all sorts of randomness here
         errstring = depstring
         if type(depstring) not in types.StringTypes:
@@ -3156,9 +3426,14 @@ class YumBase(depsolve.Depsolve):
         return result
 
     def returnInstalledPackagesByDep(self, depstring):
-        """Pass in a generic [build]require string and this function will 
-           pass back the installed packages it finds providing that dep."""
-        
+        """Return a list of installed package objects that provide the
+        given dependencies.
+
+        :param depstring: a string specifying the dependency to return
+           the packages that fulfil
+        :return: a list of installed packages that fulfil the given
+           dependency
+        """
         if not depstring:
             return []
 
@@ -3202,10 +3477,17 @@ class YumBase(depsolve.Depsolve):
         return bestlist[0][0]
 
     def bestPackagesFromList(self, pkglist, arch=None, single_name=False):
-        """Takes a list of packages, returns the best packages.
-           This function is multilib aware so that it will not compare
-           multilib to singlelib packages""" 
-    
+        """Return the best packages from a list of packages.  This
+        function is multilib aware, so that it will not compare
+        multilib to singlelib packages.
+
+        :param pkglist: the list of packages to return the best
+           packages from
+        :param arch: packages will be selected that are compatible
+           with the architecture specified by *arch*
+        :param single_name: whether to return a single package name
+        :return: a list of the best packages from *pkglist*
+        """
         returnlist = []
         compatArchList = self.arch.get_arch_list(arch)
         multiLib = []
@@ -3438,13 +3720,35 @@ class YumBase(depsolve.Depsolve):
                 self.tsInfo.probFilterFlags.append(flag)
 
     def install(self, po=None, **kwargs):
-        """try to mark for install the item specified. Uses provided package 
-           object, if available. If not it uses the kwargs and gets the best
-           packages from the keyword options provided 
-           returns the list of txmbr of the items it installs
-           
-           """
+        """Mark the specified item for installation.  If a package
+        object is given, mark it for installation.  Otherwise, mark
+        the best package specified by the key word arguments for
+        installation.
+
+        :param po: a package object to install
+        :param kwargs: if *po* is not specified, these keyword
+           arguments will be used to find the best package to install
+        :return: a list of the transaction members added to the
+           transaction set by this function
+        :raises: :class:`yum.Errors.InstallError` if there is a problem
+           installing the package
+        """
         
+
+        #  This is kind of hacky, we really need a better way to do errors than
+        # doing them directly from .install/etc. ... but this is easy. *sigh*.
+        #  We are only using this in "groupinstall" atm. ... so we don't have
+        # a long list of "blah already installed." messages when people run
+        # "groupinstall mygroup" in yum-cron etc.
+        pkg_warn = kwargs.get('pkg_warning_level', 'flibble')
+        def _dbg2(*args, **kwargs):
+            self.verbose_logger.log(logginglevels.DEBUG_2, *args, **kwargs)
+        level2func = {'debug2' : _dbg2,
+                      'warning' : self.verbose_logger.warning}
+        if pkg_warn not in level2func:
+            pkg_warn = 'warning'
+        pkg_warn = level2func[pkg_warn]
+
         pkgs = []
         was_pattern = False
         if po:
@@ -3600,23 +3904,23 @@ class YumBase(depsolve.Depsolve):
                     already_obs = pkgs[0]
 
                 if already_obs:
-                    self.verbose_logger.warning(_('Package %s is obsoleted by %s which is already installed'), 
-                                                po, already_obs)
+                    pkg_warn(_('Package %s is obsoleted by %s which is already installed'), 
+                             po, already_obs)
                 else:
                     if 'provides_for' in kwargs:
                         if not obsoleting_pkg.provides_for(kwargs['provides_for']):
-                            self.verbose_logger.warning(_('Package %s is obsoleted by %s, but obsoleting package does not provide for requirements'),
-                                                  po.name, obsoleting_pkg.name)
+                            pkg_warn(_('Package %s is obsoleted by %s, but obsoleting package does not provide for requirements'),
+                                     po.name, obsoleting_pkg.name)
                             continue
-                    self.verbose_logger.warning(_('Package %s is obsoleted by %s, trying to install %s instead'),
-                        po.name, obsoleting_pkg.name, obsoleting_pkg)
+                    pkg_warn(_('Package %s is obsoleted by %s, trying to install %s instead'),
+                             po.name, obsoleting_pkg.name, obsoleting_pkg)
                     tx_return.extend(self.install(po=obsoleting_pkg))
                 continue
             
             # make sure it's not already installed
             if self.rpmdb.contains(po=po):
                 if not self.tsInfo.getMembersWithState(po.pkgtup, TS_REMOVE_STATES):
-                    self.verbose_logger.warning(_('Package %s already installed and latest version'), po)
+                    pkg_warn(_('Package %s already installed and latest version'), po)
                     continue
 
             # make sure we don't have a name.arch of this already installed
@@ -3630,7 +3934,7 @@ class YumBase(depsolve.Depsolve):
                         found = True
                         break
                 if not found:
-                    self.verbose_logger.warning(_('Package matching %s already installed. Checking for update.'), po)            
+                    pkg_warn(_('Package matching %s already installed. Checking for update.'), po)            
                     txmbrs = self.update(po=po)
                     tx_return.extend(txmbrs)
                     continue
@@ -3719,14 +4023,33 @@ class YumBase(depsolve.Depsolve):
         return txmbr
 
     def update(self, po=None, requiringPo=None, update_to=False, **kwargs):
-        """try to mark for update the item(s) specified. 
-            po is a package object - if that is there, mark it for update,
-            if possible
-            else use **kwargs to match the package needing update
-            if nothing is specified at all then attempt to update everything
+        """Mark the specified items to be updated.  If a package
+        object is given, mark it.  Else, if a package is specified by
+        the keyword arguments, mark it.  Finally, if nothing is given,
+        mark all installed packages to be updated.
+
+
+        :param po: the package object to be marked for updating
+        :param requiringPo: the package object that requires the
+           upgrade
+        :param update_to: if *update_to* is True, the update will only
+           be run if it will update the given package to the given
+           version.  For example, if the package foo-1-2 is installed,::
+
+             updatePkgs(["foo-1-2], update_to=False)
+           will work identically to::
             
-            returns the list of txmbr of the items it marked for update"""
-        
+             updatePkgs(["foo"])
+           but::
+
+             updatePkgs(["foo-1-2"], update_to=True)
+           
+           will do nothing
+        :param kwargs: if *po* is not given, the names or wildcards in
+           *kwargs* will be used to find the packages to update
+        :return: a list of transaction members added to the
+           transaction set by this function
+        """
         # check for args - if no po nor kwargs, do them all
         # if po, do it, ignore all else
         # if no po do kwargs
@@ -3985,11 +4308,18 @@ class YumBase(depsolve.Depsolve):
         return tx_return
         
     def remove(self, po=None, **kwargs):
-        """try to find and mark for remove the specified package(s) -
-            if po is specified then that package object (if it is installed) 
-            will be marked for removal.
-            if no po then look at kwargs, if neither then raise an exception"""
-
+        """Mark the specified packages for removal. If a package
+        object is given, mark it for removal.  Otherwise, mark the
+        package specified by the keyword arguments.
+
+        :param po: the package object to mark for installation
+        :param kwargs: If *po* is not given, the keyword arguments
+           will be used to specify a package to mark for installation
+        :return: a list of the transaction members that were added to
+           the transaction set by this method
+        :raises: :class:`yum.Errors.RemoveError` if nothing is specified
+           to mark for removal
+        """
         if not po and not kwargs:
             raise Errors.RemoveError, 'Nothing specified to remove'
         
@@ -4055,17 +4385,19 @@ class YumBase(depsolve.Depsolve):
         return tx_return
 
     def installLocal(self, pkg, po=None, updateonly=False):
+        """Mark a package on the local filesystem (i.e. not from a
+        repository) for installation. 
+        
+        :param pkg: a string specifying the path to an rpm file in the
+           local filesystem to be marked for installation
+        :param po: a :class:`yum.packages.YumLocalPackage` 
+        :param updateonly: if True, the given package will only be
+           marked for installation if it is an upgrade for a package
+           that is already installed.  If False, this restriction is
+           not enforced
+        :return: a list of the transaction members added to the
+           transaction set by this method
         """
-        handles installs/updates of rpms provided on the filesystem in a
-        local dir (ie: not from a repo)
-
-        Return the added transaction members.
-
-        @param pkg: a path to an rpm file on disk.
-        @param po: A YumLocalPackage
-        @param updateonly: Whether or not true installs are valid.
-        """
-
         # read in the package into a YumLocalPackage Object
         # append it to self.localPackages
         # check if it can be installed or updated based on nevra versus rpmdb
@@ -4183,16 +4515,15 @@ class YumBase(depsolve.Depsolve):
         return tx_return
 
     def reinstallLocal(self, pkg, po=None):
+        """Mark a package on the local filesystem (i.e. not from a
+        repository) for reinstallation. 
+        
+        :param pkg: a string specifying the path to an rpm file in the
+           local filesystem to be marked for reinstallation
+        :param po: a :class:`yum.packages.YumLocalPackage` 
+        :return: a list of the transaction members added to the
+           transaction set by this method
         """
-        handles reinstall of rpms provided on the filesystem in a
-        local dir (ie: not from a repo)
-
-        Return the added transaction members.
-
-        @param pkg: a path to an rpm file on disk.
-        @param po: A YumLocalPackage
-        """
-
         if not po:
             try:
                 po = YumUrlPackage(self, ts=self.rpmdb.readOnlyTS(), url=pkg,
@@ -4215,9 +4546,19 @@ class YumBase(depsolve.Depsolve):
         return self.reinstall(po=po)
 
     def reinstall(self, po=None, **kwargs):
-        """Setup the problem filters to allow a reinstall to work, then
-           pass everything off to install"""
-           
+        """Mark the given package for reinstallation.  This is
+        accomplished by setting problem filters to allow a reinstall
+        take place, then calling :func:`install`.
+
+        :param po: the package object to mark for reinstallation
+        :param kwargs: if po is not given, the keyword will be used to
+           specify a package for reinstallation
+        :return: a list of the transaction members added to the
+           transaction set by this method
+        :raises: :class:`yum.Errors.ReinstallRemoveError` or
+           :class:`yum.Errors.ReinstallInstallError` depending the nature
+           of the error that is encountered
+        """
         self._add_prob_flags(rpm.RPMPROB_FILTER_REPLACEPKG,
                              rpm.RPMPROB_FILTER_REPLACENEWFILES,
                              rpm.RPMPROB_FILTER_REPLACEOLDFILES)
@@ -4259,16 +4600,15 @@ class YumBase(depsolve.Depsolve):
         return tx_mbrs
         
     def downgradeLocal(self, pkg, po=None):
+        """Mark a package on the local filesystem (i.e. not from a
+        repository) to be downgraded.
+        
+        :param pkg: a string specifying the path to an rpm file in the
+           local filesystem to be marked to be downgraded
+        :param po: a :class:`yum.packages.YumLocalPackage` 
+        :return: a list of the transaction members added to the
+           transaction set by this method
         """
-        handles downgrades of rpms provided on the filesystem in a
-        local dir (ie: not from a repo)
-
-        Return the added transaction members.
-
-        @param pkg: a path to an rpm file on disk.
-        @param po: A YumLocalPackage
-        """
-
         if not po:
             try:
                 po = YumUrlPackage(self, ts=self.rpmdb.readOnlyTS(), url=pkg,
@@ -4309,13 +4649,19 @@ class YumBase(depsolve.Depsolve):
         return False
         
     def downgrade(self, po=None, **kwargs):
-        """ Try to downgrade a package. Works like:
-            % yum shell <<EOL
-            remove  abcd
-            install abcd-<old-version>
-            run
-            EOL """
-
+        """Mark a package to be downgraded.  This is equivalent to
+        first removing the currently installed package, and then
+        installing the older version.
+
+        :param po: the package object to be marked to be downgraded
+        :param kwargs: if a package object is not given, the keyword
+           arguments will be used to specify a package to be marked to
+           be downgraded
+        :return: a list of the transaction members added to the
+           transaction set by this method
+        :raises: :class:`yum.Errors.DowngradeError` if no packages are
+           specified or available for downgrade
+        """
         if not po and not kwargs:
             raise Errors.DowngradeError, 'Nothing specified to downgrade'
 
@@ -4501,8 +4847,14 @@ class YumBase(depsolve.Depsolve):
         return returndict
 
     def history_redo(self, transaction):
-        """ Given a valid historical transaction object, try and repeat
-            that transaction. """
+        """Repeat the transaction represented by the given
+        :class:`yum.history.YumHistoryTransaction` object.
+
+        :param transaction: a
+           :class:`yum.history.YumHistoryTransaction` object
+           representing the transaction to be repeated
+        :return: whether the transaction was repeated successfully
+        """
         # NOTE: This is somewhat basic atm. ... see comment in undo.
         #  Also note that redo doesn't force install Dep-Install packages,
         # which is probably what is wanted the majority of the time.
@@ -4538,8 +4890,14 @@ class YumBase(depsolve.Depsolve):
         return done
 
     def history_undo(self, transaction):
-        """ Given a valid historical transaction object, try and undo
-            that transaction. """
+        """Undo the transaction represented by the given
+        :class:`yum.history.YumHistoryTransaction` object.
+
+        :param transaction: a
+           :class:`yum.history.YumHistoryTransaction` object
+           representing the transaction to be undone
+        :return: whether the transaction was undone successfully
+        """
         # NOTE: This is somewhat basic atm. ... for instance we don't check
         #       that we are going from the old new version. However it's still
         #       better than the RHN rollback code, and people pay for that :).
@@ -4689,19 +5047,18 @@ class YumBase(depsolve.Depsolve):
         self.logger.critical("%s", msg)
 
     def getKeyForPackage(self, po, askcb = None, fullaskcb = None):
-        """
-        Retrieve a key for a package. If needed, prompt for if the key should
-        be imported using askcb.
-        
-        @param po: Package object to retrieve the key of.
-        @param askcb: Callback function to use for asking for permission to
-                      import a key. This is verification, but also "choice".
-                      Takes arguments of the po, the userid for the key, and
-                      the keyid.
-        @param fullaskcb: Callback function to use for asking for permission to
-                          import a key. This is verification, but also "choice".
-                          Differs from askcb in that it gets passed a
-                          dictionary so that we can expand the values passed.
+        """Retrieve a key for a package. If needed, use the given
+        callback to prompt whether the key should be imported.
+
+        :param po: the package object to retrieve the key of
+        :param askcb: Callback function to use to ask permission to
+           import a key.  The arguments *askck* should take are the
+           package object, the userid of the key, and the keyid
+        :param fullaskcb: Callback function to use to ask permission to
+           import a key.  This differs from *askcb* in that it gets
+           passed a dictionary so that we can expand the values passed.
+        :raises: :class:`yum.Errors.YumBaseError` if there are errors
+           retrieving the keys
         """
         repo = self.repos.getRepo(po.repoid)
         keyurls = repo.gpgkey
@@ -4725,7 +5082,9 @@ class YumBase(depsolve.Depsolve):
                     # Try installing/updating GPG key
                     self._getKeyImportMessage(info, keyurl)
                     rc = False
-                    if self.conf.assumeyes:
+                    if self.conf.assumeno:
+                        rc = False
+                    elif self.conf.assumeyes:
                         rc = True
                         
                     # grab the .sig/.asc for the keyurl, if it exists
@@ -4819,8 +5178,11 @@ class YumBase(depsolve.Depsolve):
                 if not key_installed:
                     self._getKeyImportMessage(info, keyurl, keytype)
                     rc = False
-                    if self.conf.assumeyes:
+                    if self.conf.assumeno:
+                        rc = False
+                    elif self.conf.assumeyes:
                         rc = True
+
                     elif callback:
                         rc = callback({"repo": repo, "userid": info['userid'],
                                         "hexkeyid": info['hexkeyid'], "keyurl": keyurl,
@@ -4861,26 +5223,23 @@ class YumBase(depsolve.Depsolve):
                   'this repository.') % (repo.name)
 
     def getKeyForRepo(self, repo, callback=None):
-        """
-        Retrieve a key for a repository If needed, prompt for if the key should
-        be imported using callback
-        
-        @param repo: Repository object to retrieve the key of.
-        @param callback: Callback function to use for asking for verification
-                          of a key. Takes a dictionary of key info.
+        """Retrieve a key for a repository.  If needed, use the given
+        callback to prompt whether the key should be imported.
+
+        :param repo: repository object to retrieve the key of
+        :param callback: callback function to use for asking for
+           verification of key information
         """
         self._getAnyKeyForRepo(repo, repo.gpgdir, repo.gpgkey, is_cakey=False, callback=callback)
 
     def getCAKeyForRepo(self, repo, callback=None):
-        """
-        Retrieve a key for a repository If needed, prompt for if the key should
-        be imported using callback
-        
-        @param repo: Repository object to retrieve the key of.
-        @param callback: Callback function to use for asking for verification
-                          of a key. Takes a dictionary of key info.
-        """
+        """Retrieve a key for a repository.  If needed, use the given
+        callback to prompt whether the key should be imported.
 
+        :param repo: repository object to retrieve the key of
+        :param callback: callback function to use for asking for
+           verification of key information
+        """
         self._getAnyKeyForRepo(repo, repo.gpgcadir, repo.gpgcakey, is_cakey=True, callback=callback)
 
     def _limit_installonly_pkgs(self):
@@ -4959,19 +5318,22 @@ class YumBase(depsolve.Depsolve):
             txmbr.depends_on.append(rel)
 
     def processTransaction(self, callback=None,rpmTestDisplay=None, rpmDisplay=None):
-        '''
-        Process the current Transaction
-        - Download Packages
-        - Check GPG Signatures.
-        - Run Test RPM Transaction
-        - Run RPM Transaction
-        
-        callback.event method is called at start/end of each process.
-        
-        @param callback: callback object (must have an event method)
-        @param rpmTestDisplay: Name of display class to use in RPM Test Transaction 
-        @param rpmDisplay: Name of display class to use in RPM Transaction 
-        '''
+        """Process the current transaction.  This involves the
+        following steps:
+          - Download the packages
+          - Check the GPG signatures of the packages
+          - Run the test RPM transaction
+          - Run the RPM Transaction
+        The *callback*.event method is called at the start, and
+        between each step.
+
+        :param callback: a callback object, which must have an event
+           method
+        :param rpmTestDisplay: name of the display class to use in the
+           RPM test transaction
+        :param rpmDisplay: name of the display class to use in the rpm
+           transaction
+        """
         
         if not callback:
             callback = callbacks.ProcessTransNoOutputCallback()
@@ -5114,13 +5476,19 @@ class YumBase(depsolve.Depsolve):
         return results
 
     def add_enable_repo(self, repoid, baseurls=[], mirrorlist=None, **kwargs):
-        """add and enable a repo with just a baseurl/mirrorlist and repoid
-           requires repoid and at least one of baseurl and mirrorlist
-           additional optional kwargs are:
-           variable_convert=bool (defaults to true)
-           and any other attribute settable to the normal repo setup
-           ex: metadata_expire, enable_groups, gpgcheck, cachedir, etc
-           returns the repo object it added"""
+        """Add and enable a repository.
+
+        :param repoid: a string specifying the name of the repository
+        :param baseurls: a list of strings specifying the urls for
+           the repository.  At least one base url, or one mirror, must
+           be given
+        :param mirrorlist: a list of strings specifying a list of
+           mirrors for the repository.  At least one base url, or one
+           mirror must be given
+        :param kwargs: key word arguments to set any normal repository
+           attribute
+        :return: the new repository that has been added and enabled
+        """
         # out of place fixme - maybe we should make this the default repo addition
         # routine and use it from getReposFromConfigFile(), etc.
         newrepo = yumRepo.YumRepository(repoid)
@@ -5167,9 +5535,15 @@ class YumBase(depsolve.Depsolve):
 
     def setCacheDir(self, force=False, tmpdir=None, reuse=True,
                     suffix='/$basearch/$releasever'):
-        ''' Set a new cache dir, using misc.getCacheDir() and var. replace
-            on suffix. '''
-
+        """Set a new cache directory.
+
+        :param force: whether to force the cache directory to be
+           changed
+        :param tmpdir: a temporary directory
+        :param reuse: whether the temporary directory can be reused
+        :param suffix: suffix to attach to the directory name
+        :return: whether the new cache directory is successfully set
+        """
         if not force and os.geteuid() == 0:
             return True # We are root, not forced, so happy with the global dir.
         if tmpdir is None:
@@ -5220,13 +5594,24 @@ class YumBase(depsolve.Depsolve):
         self.history.write_addon_data('config-repos', myrepos)
         
     def verify_plugins_cb(self, verify_package):
-        """ Callback to call a plugin hook for pkg.verify(). """
+        """Callback to call a plugin hook for pkg.verify().
+
+        :param verify_package: a conduit for the callback
+        :return: *verify_package*
+        """
         self.plugins.run('verify_package', verify_package=verify_package)
         return verify_package
 
     def save_ts(self, filename=None, auto=False):
-        """saves out a transaction to .yumtx file to be loaded later"""
-        
+        """Save out a transaction to a .yumtx file to be loaded later.
+
+        :param filename: the name of the file to save the transaction
+           in.  If *filename* is not given, a name will be generated
+        :param auto: whether to output errors to the logger, rather
+           than raising exceptions
+        :raises: :class:`yum.Errors.YumBaseError` if there are errors
+           saving the transaction
+        """
         if self.tsInfo._unresolvedMembers:
             if auto:
                 self.logger.critical(_("Dependencies not solved. Will not save unresolved transaction."))
@@ -5234,7 +5619,7 @@ class YumBase(depsolve.Depsolve):
             raise Errors.YumBaseError(_("Dependencies not solved. Will not save unresolved transaction."))
         
         if not filename:
-            prefix = 'yum_save_tx-%s' % time.strftime('%Y-%m-%d-%H-%M')
+            prefix = 'yum_save_tx.%s' % time.strftime('%Y-%m-%d.%H-%M.')
             fd,filename = tempfile.mkstemp(suffix='.yumtx', prefix=prefix)
             f = os.fdopen(fd, 'w')
         else:
@@ -5266,7 +5651,17 @@ class YumBase(depsolve.Depsolve):
 
         
     def load_ts(self, filename, ignorerpm=None, ignoremissing=None):
-        """loads a transaction from a .yumtx file"""
+        """Load a transaction from a .yumtx file.
+
+        :param filename: the name of the file to load the transaction
+           from
+        :param ignorerpm: whether to ignore messages from rpm
+        :param ignoremissing: whether to ignore that there may be
+           transaction members missing
+        :return: the members of the loaded transaction
+        :raises: :class:`yum.Errors.YumBaseError` if there are problems
+           loading the transaction
+        """
         # check rpmversion - if not match throw a fit
         # check repoversions  (and repos)- if not match throw a fit
         # load each txmbr - if pkgs being updated don't exist, bail w/error
@@ -5292,6 +5687,16 @@ class YumBase(depsolve.Depsolve):
         # 3+numrepos = num pkgs
         # 3+numrepos+1 -> EOF= txmembers
         
+        if data[0] == 'saved_tx:\n':
+            #  Old versions of yum would put "saved_tx:" at the begining and
+            # two blank lines at the end when you used:
+            # "yum -q history addon-info saved_tx".
+            if data[-1] == 'history addon-info\n':
+                # Might as well also DTRT if they hand removed the plugins line
+                data = data[1:-3]
+            else:
+                data = data[1:-2]
+
         # rpm db ver
         rpmv = data[0].strip()
         if rpmv != str(self.rpmdb.simpleVersion(main_only=True)[0]):
diff --git a/yum/callbacks.py b/yum/callbacks.py
index 7ad25ce..a9a8e53 100644
--- a/yum/callbacks.py
+++ b/yum/callbacks.py
@@ -13,6 +13,8 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
+"""Classes for handling various callbacks."""
+
 # imports
 
 import logging 
@@ -35,59 +37,79 @@ PT_MESSAGES = { PT_DOWNLOAD    : "Downloading Packages",
 
 
 class ProcessTransBaseCallback:
-    
+    """A class to handle callbacks from
+    :func:`YumBase.processTransaction`.
+    """
     def __init__(self):
         self.logger = logging.getLogger('yum.verbose.ProcessTrasactionBaseCallback')
         
     def event(self,state,data=None):
+        """Handle an event by logging it.
+
+        :param state: a number indicating the type of callback
+        :param data: data associated with the callback
+        """
         if state in PT_MESSAGES.keys():
             self.logger.info(PT_MESSAGES[state])
 
 class ProcessTransNoOutputCallback:
+    """A class to handle callbacks from
+    :func:`YumBase.processTransaction`, without logging them.
+    """
     def __init__(self):
         pass
          
     def event(self,state,data=None):
+        """Handle an event.
+
+        :param state: a number indicating the type of callback
+        :param data: data associated with the callback
+        """
         pass
     
 class DownloadBaseCallback( BaseMeter ):
-    """ 
-    This is class is a base class to use by implement a download progress
-    handler to be used with YumBase.repos.setProgressBar.
+    """This is a base class that can be extended to implement a custom
+    download progress handler to be used with
+    :func:`YumBase.repos.setProgressBar`.
     
-    Example:
-    
-    from yum.callbacks import DownloadBaseCallback
-    
-    class MyDownloadCallback(  DownloadBaseCallback ):
-
-        def updateProgress(self,name,frac,fread,ftime):
-            '''
-            Update the progressbar
-            @param name: filename
-            @param frac: Progress fracment (0 -> 1)
-            @param fread: formated string containing BytesRead
-            @param ftime : formated string containing remaining or elapsed time
-            '''
-            pct = int( frac*100 )
-            print " %s : %s " % (name,pct)
-
-
-    if __name__ == '__main__':
-        my = YumBase()
-        my.doConfigSetup()
-        dnlcb = MyDownloadCallback()
-        my.repos.repos.setProgressBar( dnlcb )
-        for pkg in my.pkgSack:
-            print pkg.name
-
-    """
+    Example::
     
+       from yum.callbacks import DownloadBaseCallback
+       
+       class MyDownloadCallback(  DownloadBaseCallback ):
+   
+           def updateProgress(self,name,frac,fread,ftime):
+               '''
+               Update the progressbar
+               @param name: filename
+               @param frac: Progress fracment (0 -> 1)
+               @param fread: formated string containing BytesRead
+               @param ftime : formated string containing remaining or elapsed time
+               '''
+               pct = int( frac*100 )
+               print " %s : %s " % (name,pct)
+   
+   
+       if __name__ == '__main__':
+           my = YumBase()
+           my.doConfigSetup()
+           dnlcb = MyDownloadCallback()
+           my.repos.setProgressBar( dnlcb )
+           for pkg in my.pkgSack:
+               print pkg.name
+       """
     def __init__(self):
         BaseMeter.__init__( self )
         self.totSize = ""   # Total size to download in a formatted string (Kb, MB etc)
         
     def update( self, amount_read, now=None ):
+        """Update the status bar.
+
+        :param amount_read: the amount of data, in bytes, that has been read
+        :param now: the current time in seconds since the epoch.  If
+           *now* is not given, the output of :func:`time.time()` will
+           be used.
+        """
         BaseMeter.update( self, amount_read, now )
 
     def _do_start( self, now=None ):
@@ -130,12 +152,15 @@ class DownloadBaseCallback( BaseMeter ):
         return name
 
     def updateProgress(self,name,frac,fread,ftime):
-        '''
-         Update the progressbar (Overload in child class)
-        @param name: filename
-        @param frac: Progress fracment (0 -> 1)
-        @param fread: formated string containing BytesRead
-        @param ftime : formated string containing remaining or elapsed time
-        '''
+        """Update the progressbar.  This method should be overridden
+        by subclasses to implement the handler.
+
+        :param name: the name of the filed being downloaded
+        :param frac: number between 0 and 1 representing the fraction
+            fraction of the file that has been downloaded
+        :param fread: formatted string containing the number of bytes read
+        :param ftime: formatted string containing remaining or elapsed time
+
+        """
         pass
         
diff --git a/yum/config.py b/yum/config.py
index d09511f..ef1b9e1 100644
--- a/yum/config.py
+++ b/yum/config.py
@@ -47,13 +47,12 @@ __pkgs_gpgcheck_default__ = False
 __repo_gpgcheck_default__ = False
 
 class Option(object):
-    '''
+    """
     This class handles a single Yum configuration file option. Create
     subclasses for each type of supported configuration option.
-    
     Python descriptor foo (__get__ and __set__) is used to make option
-    definition easy and consise.
-    '''
+    definition easy and concise.
+    """
 
     def __init__(self, default=None, parse_default=False):
         self._setattrname()
@@ -63,19 +62,19 @@ class Option(object):
         self.default = default
 
     def _setattrname(self):
-        '''Calculate the internal attribute name used to store option state in
+        """Calculate the internal attribute name used to store option state in
         configuration instances.
-        '''
+        """
         self._attrname = '__opt%d' % id(self)
 
     def __get__(self, obj, objtype):
-        '''Called when the option is read (via the descriptor protocol). 
+        """Called when the option is read (via the descriptor protocol). 
 
-        @param obj: The configuration instance to modify.
-        @param objtype: The type of the config instance (not used).
-        @return: The parsed option value or the default value if the value
-            wasn't set in the configuration file.
-        '''
+        :param obj: The configuration instance to modify.
+        :param objtype: The type of the config instance (not used).
+        :return: The parsed option value or the default value if the value
+           wasn't set in the configuration file.
+        """
         # xemacs highlighting hack: '
         if obj is None:
             return self
@@ -83,12 +82,11 @@ class Option(object):
         return getattr(obj, self._attrname, None)
 
     def __set__(self, obj, value):
-        '''Called when the option is set (via the descriptor protocol). 
+        """Called when the option is set (via the descriptor protocol). 
 
-        @param obj: The configuration instance to modify.
-        @param value: The value to set the option to.
-        @return: Nothing.
-        '''
+        :param obj: The configuration instance to modify.
+        :param value: The value to set the option to.
+        """
         # Only try to parse if it's a string
         if isinstance(value, basestring):
             try:
@@ -100,62 +98,59 @@ class Option(object):
         setattr(obj, self._attrname, value)
 
     def setup(self, obj, name):
-        '''Initialise the option for a config instance. 
+        """Initialise the option for a config instance. 
         This must be called before the option can be set or retrieved. 
 
-        @param obj: BaseConfig (or subclass) instance.
-        @param name: Name of the option.
-        '''
+        :param obj: :class:`BaseConfig` (or subclass) instance.
+        :param name: Name of the option.
+        """
         self._optname = name
         setattr(obj, self._attrname, copy.copy(self.default))
 
     def clone(self):
-        '''Return a safe copy of this Option instance
-        '''
+        """Return a safe copy of this :class:`Option` instance.
+
+        :return: a safe copy of this :class:`Option` instance
+        """
         new = copy.copy(self)
         new._setattrname()
         return new
 
     def parse(self, s):
-        '''Parse the string value to the Option's native value.
+        """Parse the string value to the :class:`Option`'s native value.
 
-        @param s: Raw string value to parse.
-        @return: Validated native value.
-    
-        Will raise ValueError if there was a problem parsing the string.
-        Subclasses should override this.
-        '''
+        :param s: raw string value to parse
+        :return: validated native value
+        :raise: ValueError if there was a problem parsing the string.
+           Subclasses should override this
+        """
         return s
 
     def tostring(self, value):
-        '''Convert the Option's native value to a string value.
-
-        @param value: Native option value.
-        @return: String representation of input.
-
-        This does the opposite of the parse() method above.
+        """Convert the :class:`Option`'s native value to a string value.  This
+        does the opposite of the :func:`parse` method above.
         Subclasses should override this.
-        '''
+
+        :param value: native option value
+        :return: string representation of input
+        """
         return str(value)
 
 def Inherit(option_obj):
-    '''Clone an Option instance for the purposes of inheritance. The returned
-    instance has all the same properties as the input Option and shares items
+    """Clone an :class:`Option` instance for the purposes of inheritance. The returned
+    instance has all the same properties as the input :class:`Option` and shares items
     such as the default value. Use this to avoid redefinition of reused
     options.
 
-    @param option_obj: Option instance to inherit.
-    @return: New Option instance inherited from the input.
-    '''
+    :param option_obj: :class:`Option` instance to inherit
+    :return: New :class:`Option` instance inherited from the input
+    """
     new_option = option_obj.clone()
     new_option.inherit = True
     return new_option
 
 class ListOption(Option):
-
-    """
-    An option containing a list of strings.
-    """
+    """An option containing a list of strings."""
 
     def __init__(self, default=None, parse_default=False):
         if default is None:
@@ -163,10 +158,12 @@ class ListOption(Option):
         super(ListOption, self).__init__(default, parse_default)
 
     def parse(self, s):
-        """Converts a string from the config file to a workable list, parses
-           globdir: paths as foo.d-style dirs
+        """Convert a string from the config file to a workable list, parses
+        globdir: paths as foo.d-style dirs.
 
-        Commas and spaces are used as separators for the list
+        :param s: The string to be converted to a list. Commas and
+           whitespace are used as separators for the list
+        :return: *s* converted to a list
         """
         # we need to allow for the '\n[whitespace]' continuation - easier
         # to sub the \n with a space and then read the lines
@@ -183,12 +180,18 @@ class ListOption(Option):
         return results
 
     def tostring(self, value):
+        """Convert a list of to a string value.  This does the
+        opposite of the :func:`parse` method above.
+
+        :param value: a list of values
+        :return: string representation of input
+        """
         return '\n '.join(value)
 
 class UrlOption(Option):
-    '''
-    This option handles lists of URLs with validation of the URL scheme.
-    '''
+    """This option handles lists of URLs with validation of the URL
+    scheme.
+    """
 
     def __init__(self, default=None, schemes=('http', 'ftp', 'file', 'https'), 
             allow_none=False):
@@ -197,6 +200,13 @@ class UrlOption(Option):
         self.allow_none = allow_none
 
     def parse(self, url):
+        """Parse a url to make sure that it is valid, and in a scheme
+        that can be used.
+
+        :param url: a string containing the url to parse
+        :return: *url* if it is valid
+        :raises: :class:`ValueError` if there is an error parsing the url
+        """
         url = url.strip()
 
         # Handle the "_none_" special case
@@ -224,10 +234,9 @@ class UrlOption(Option):
             return '%s or %s' % (', '.join(self.schemes[:-1]), self.schemes[-1])
 
 class UrlListOption(ListOption):
-    '''
-    Option for handling lists of URLs with validation of the URL scheme.
-    '''
-
+    """Option for handling lists of URLs with validation of the URL
+    scheme.
+    """
     def __init__(self, default=None, schemes=('http', 'ftp', 'file', 'https'),
                  parse_default=False):
         super(UrlListOption, self).__init__(default, parse_default)
@@ -236,6 +245,13 @@ class UrlListOption(ListOption):
         self._urloption = UrlOption(schemes=schemes)
         
     def parse(self, s):
+        """Parse a string containing multiple urls into a list, and
+        ensure that they are in a scheme that can be used.
+
+        :param s: the string to parse
+        :return: a list of strings containing the urls in *s*
+        :raises: :class:`ValueError` if there is an error parsing the urls
+        """
         out = []
         s = s.replace('\n', ' ')
         s = s.replace(',', ' ')
@@ -254,10 +270,7 @@ class UrlListOption(ListOption):
 
 
 class IntOption(Option):
-
-    """
-    An option representing an integer value.
-    """
+    """An option representing an integer value."""
 
     def __init__(self, default=None, range_min=None, range_max=None):
         super(IntOption, self).__init__(default)
@@ -265,6 +278,13 @@ class IntOption(Option):
         self._range_max = range_max
         
     def parse(self, s):
+        """Parse a string containing an integer.
+
+        :param s: the string to parse
+        :return: the integer in *s*
+        :raises: :class:`ValueError` if there is an error parsing the
+           integer
+        """
         try:
             val = int(s)
         except (ValueError, TypeError), e:
@@ -276,39 +296,56 @@ class IntOption(Option):
         return val
 
 class PositiveIntOption(IntOption):
-
-    """
-    An option representing a positive integer value, where 0 can have a special
-    represention.
+    """An option representing a positive integer value, where 0 can
+    have a special representation.
     """
-
     def __init__(self, default=None, range_min=0, range_max=None,
                  names_of_0=None):
         super(PositiveIntOption, self).__init__(default, range_min, range_max)
         self._names0 = names_of_0
 
     def parse(self, s):
+        """Parse a string containing a positive integer, where 0 can
+           have a special representation.
+
+        :param s: the string to parse
+        :return: the integer in *s*
+        :raises: :class:`ValueError` if there is an error parsing the
+           integer
+        """
         if s in self._names0:
             return 0
         return super(PositiveIntOption, self).parse(s)
 
 class SecondsOption(Option):
+    """An option representing an integer value of seconds, or a human
+    readable variation specifying days, hours, minutes or seconds
+    until something happens. Works like :class:`BytesOption`.  Note
+    that due to historical president -1 means "never", so this accepts
+    that and allows the word never, too.
 
-    """
-    An option representing an integer value of seconds, or a human readable
-    variation specifying days, hours, minutes or seconds until something
-    happens. Works like BytesOption.
-    Note that due to historical president -1 means "never", so this accepts
-    that and allows the word never too.
-
-    Valid inputs: 100, 1.5m, 90s, 1.2d, 1d, 0xF, 0.1, -1, never
-    Invalid inputs: -10, -0.1, 45.6Z, 1d6h, 1day, 1y
+    Valid inputs: 100, 1.5m, 90s, 1.2d, 1d, 0xF, 0.1, -1, never.
+    Invalid inputs: -10, -0.1, 45.6Z, 1d6h, 1day, 1y.
 
     Return value will always be an integer
     """
     MULTS = {'d': 60 * 60 * 24, 'h' : 60 * 60, 'm' : 60, 's': 1}
 
     def parse(self, s):
+        """Parse a string containing an integer value of seconds, or a human
+        readable variation specifying days, hours, minutes or seconds
+        until something happens. Works like :class:`BytesOption`.  Note
+        that due to historical president -1 means "never", so this accepts
+        that and allows the word never, too.
+    
+        Valid inputs: 100, 1.5m, 90s, 1.2d, 1d, 0xF, 0.1, -1, never.
+        Invalid inputs: -10, -0.1, 45.6Z, 1d6h, 1day, 1y.
+    
+        :param s: the string to parse
+        :return: an integer representing the number of seconds
+           specified by *s*
+        :raises: :class:`ValueError` if there is an error parsing the string
+        """
         if len(s) < 1:
             raise ValueError("no value specified")
 
@@ -335,14 +372,20 @@ class SecondsOption(Option):
         return int(n * mult)
 
 class BoolOption(Option):
-
-    """
-    An option representing a boolean value.
-
-    The value can be one of 0, 1, yes, no, true, or false.
+    """An option representing a boolean value.  The value can be one
+    of 0, 1, yes, no, true, or false.
     """
 
     def parse(self, s):
+        """Parse a string containing a boolean value.  1, yes, and
+        true will evaluate to True; and 0, no, and false will evaluate
+        to False.  Case is ignored.
+        
+        :param s: the string containing the boolean value
+        :return: the boolean value contained in *s*
+        :raises: :class:`ValueError` if there is an error in parsing
+           the boolean value
+        """
         s = s.lower()
         if s in ('0', 'no', 'false'):
             return False
@@ -352,30 +395,49 @@ class BoolOption(Option):
             raise ValueError('invalid boolean value')
 
     def tostring(self, value):
+        """Convert a boolean value to a string value.  This does the
+        opposite of the :func:`parse` method above.
+        
+        :param value: the boolean value to convert
+        :return: a string representation of *value*
+        """
         if value:
             return "1"
         else:
             return "0"
 
 class FloatOption(Option):
-    """
-    An option representing a numeric float value.
-    """
+    """An option representing a numeric float value."""
+
     def parse(self, s):
+        """Parse a string containing a numeric float value.
+
+        :param s: a string containing a numeric float value to parse
+        :return: the numeric float value contained in *s*
+        :raises: :class:`ValueError` if there is an error parsing
+           float value
+        """
         try:
             return float(s.strip())
         except (ValueError, TypeError):
             raise ValueError('invalid float value')
 
 class SelectionOption(Option):
-    '''Handles string values where only specific values are allowed
-    '''
+    """Handles string values where only specific values are
+    allowed.
+    """
     def __init__(self, default=None, allowed=(), mapper={}):
         super(SelectionOption, self).__init__(default)
         self._allowed = allowed
         self._mapper  = mapper
         
     def parse(self, s):
+        """Parse a string for specific values.
+
+        :param s: the string to parse
+        :return: *s* if it contains a valid value
+        :raises: :class:`ValueError` if there is an error parsing the values
+        """
         if s in self._mapper:
             s = self._mapper[s]
         if s not in self._allowed:
@@ -383,18 +445,21 @@ class SelectionOption(Option):
         return s
 
 class CaselessSelectionOption(SelectionOption):
-    ''' Mainly for compat. with BoolOption, works like SelectionOption but
-        lowers input case. '''
-
+    """Mainly for compatibility with :class:`BoolOption`, works like
+    :class:`SelectionOption` but lowers input case.
+    """
     def parse(self, s):
+        """Parse a string for specific values.
+
+        :param s: the string to parse
+        :return: *s* if it contains a valid value
+        :raises: :class:`ValueError` if there is an error parsing the values
+        """
         return super(CaselessSelectionOption, self).parse(s.lower())
 
 class BytesOption(Option):
-
-    """
-    An option representing a value in bytes.
-
-    The value may be given in bytes, kilobytes, megabytes, or gigabytes.
+    """An option representing a value in bytes. The value may be given
+    in bytes, kilobytes, megabytes, or gigabytes.
     """
     # Multipliers for unit symbols
     MULTS = {
@@ -404,20 +469,18 @@ class BytesOption(Option):
     }
 
     def parse(self, s):
-        """Parse a friendly bandwidth option to bytes
-
-        The input should be a string containing a (possibly floating point)
-        number followed by an optional single character unit. Valid units are
-        'k', 'M', 'G'. Case is ignored.
+        """Parse a friendly bandwidth option to bytes.  The input
+        should be a string containing a (possibly floating point)
+        number followed by an optional single character unit. Valid
+        units are 'k', 'M', 'G'. Case is ignored. The convention that
+        1k = 1024 bytes is used.
        
-        Valid inputs: 100, 123M, 45.6k, 12.4G, 100K, 786.3, 0
-        Invalid inputs: -10, -0.1, 45.6L, 123Mb
-
-        Return value will always be an integer
-
-        1k = 1024 bytes.
+        Valid inputs: 100, 123M, 45.6k, 12.4G, 100K, 786.3, 0.
+        Invalid inputs: -10, -0.1, 45.6L, 123Mb.
 
-        ValueError will be raised if the option couldn't be parsed.
+        :param s: the string to parse
+        :return: the number of bytes represented by *s*
+        :raises: :class:`ValueError` if the option can't be parsed
         """
         if len(s) < 1:
             raise ValueError("no value specified")
@@ -443,25 +506,23 @@ class BytesOption(Option):
         return int(n * mult)
 
 class ThrottleOption(BytesOption):
-
-    """
-    An option representing a bandwidth throttle value. See
-    ThrottleOption.parse for acceptable input values.
+    """An option representing a bandwidth throttle value. See
+    :func:`parse` for acceptable input values.
     """
 
     def parse(self, s):
-        """Get a throttle option. 
-
-        Input may either be a percentage or a "friendly bandwidth value" as
-        accepted by the BytesOption.
-
-        Valid inputs: 100, 50%, 80.5%, 123M, 45.6k, 12.4G, 100K, 786.0, 0
-        Invalid inputs: 100.1%, -4%, -500
-
-        Return value will be a int if a bandwidth value was specified or a
-        float if a percentage was given.
-
-        ValueError will be raised if input couldn't be parsed.
+        """Get a throttle option. Input may either be a percentage or
+        a "friendly bandwidth value" as accepted by the
+        :class:`BytesOption`.
+
+        Valid inputs: 100, 50%, 80.5%, 123M, 45.6k, 12.4G, 100K, 786.0, 0.
+        Invalid inputs: 100.1%, -4%, -500.
+
+        :param s: the string to parse
+        :return: the bandwidth represented by *s*. The return value
+           will be an int if a bandwidth value was specified, and a
+           float if a percentage was given
+        :raises: :class:`ValueError` if input can't be parsed
         """
         if len(s) < 1:
             raise ValueError("no value specified")
@@ -479,10 +540,9 @@ class ThrottleOption(BytesOption):
             return BytesOption.parse(self, s)
 
 class BaseConfig(object):
-    '''
-    Base class for storing configuration definitions. Subclass when creating
-    your own definitons.
-    '''
+    """Base class for storing configuration definitions. Subclass when
+    creating your own definitions.
+    """
 
     def __init__(self):
         self._section = None
@@ -499,13 +559,14 @@ class BaseConfig(object):
         return '\n'.join(out)
 
     def populate(self, parser, section, parent=None):
-        '''Set option values from a INI file section.
+        """Set option values from an INI file section.
 
-        @param parser: ConfParser instance (or subclass)
-        @param section: INI file section to read use.
-        @param parent: Optional parent BaseConfig (or subclass) instance to use
-            when doing option value inheritance.
-        '''
+        :param parser: :class:`ConfigParser` instance (or subclass)
+        :param section: INI file section to read use
+        :param parent: Optional parent :class:`BaseConfig` (or
+            subclass) instance to use when doing option value
+            inheritance
+        """
         self.cfg = parser
         self._section = section
 
@@ -527,8 +588,19 @@ class BaseConfig(object):
                 setattr(self, name, value)
 
     def optionobj(cls, name, exceptions=True):
-        '''Return the Option instance for the given name
-        '''
+        """Return the :class:`Option` instance for the given name.
+
+        :param cls: the class to return the :class:`Option` instance from
+        :param name: the name of the :class:`Option` instance to return
+        :param exceptions: defines what action to take if the
+           specified :class:`Option` instance does not exist. If *exceptions* is
+           True, a :class:`KeyError` will be raised. If *exceptions*
+           is False, None will be returned
+        :return: the :class:`Option` instance specified by *name*, or None if
+           it does not exist and *exceptions* is False
+        :raises: :class:`KeyError` if the specified :class:`Option` does not
+           exist, and *exceptions* is True
+        """
         obj = getattr(cls, name, None)
         if isinstance(obj, Option):
             return obj
@@ -539,37 +611,41 @@ class BaseConfig(object):
     optionobj = classmethod(optionobj)
 
     def isoption(cls, name):
-        '''Return True if the given name refers to a defined option 
-        '''
+        """Return True if the given name refers to a defined option.
+
+        :param cls: the class to find the option in
+        :param name: the name of the option to search for
+        :return: whether *name* specifies a defined option
+        """
         return cls.optionobj(name, exceptions=False) is not None
     isoption = classmethod(isoption)
 
     def iterkeys(self):
-        '''Yield the names of all defined options in the instance.
-        '''
+        """Yield the names of all defined options in the instance."""
+
         for name in dir(self):
             if self.isoption(name):
                 yield name
 
     def iteritems(self):
-        '''Yield (name, value) pairs for every option in the instance.
-
-        The value returned is the parsed, validated option value.
-        '''
+        """Yield (name, value) pairs for every option in the
+        instance. The value returned is the parsed, validated option
+        value.
+        """
         # Use dir() so that we see inherited options too
         for name in self.iterkeys():
             yield (name, getattr(self, name))
 
     def write(self, fileobj, section=None, always=()):
-        '''Write out the configuration to a file-like object
+        """Write out the configuration to a file-like object.
 
-        @param fileobj: File-like object to write to
-        @param section: Section name to use. If not-specified the section name
-            used during parsing will be used.
-        @param always: A sequence of option names to always write out.
+        :param fileobj: File-like object to write to
+        :param section: Section name to use. If not specified, the section name
+            used during parsing will be used
+        :param always: A sequence of option names to always write out.
             Options not listed here will only be written out if they are at
-            non-default values. Set to None to dump out all options.
-        '''
+            non-default values. Set to None to dump out all options
+        """
         # Write section heading
         if section is None:
             if self._section is None:
@@ -586,6 +662,14 @@ class BaseConfig(object):
         self.cfg.write(fileobj)
 
     def getConfigOption(self, option, default=None):
+        """Return the current value of the given option.
+
+        :param option: string specifying the option to return the
+           value of
+        :param default: the value to return if the option does not exist
+        :return: the value of the option specified by *option*, or
+           *default* if it does not exist
+        """
         warnings.warn('getConfigOption() will go away in a future version of Yum.\n'
                 'Please access option values as attributes or using getattr().',
                 DeprecationWarning)
@@ -594,6 +678,12 @@ class BaseConfig(object):
         return default
 
     def setConfigOption(self, option, value):
+        """Set the value of the given option to the given value.
+
+        :param option: string specifying the option to set the value
+           of
+        :param value: the value to set the option to
+        """        
         warnings.warn('setConfigOption() will go away in a future version of Yum.\n'
                 'Please set option values as attributes or using setattr().',
                 DeprecationWarning)
@@ -603,11 +693,10 @@ class BaseConfig(object):
             raise Errors.ConfigError, 'No such option %s' % option
 
 class StartupConf(BaseConfig):
-    '''
-    Configuration option definitions for yum.conf's [main] section that are
-    required early in the initialisation process or before the other [main]
-    options can be parsed. 
-    '''
+    """Configuration option definitions for yum.conf's [main] section
+    that are required early in the initialisation process or before
+    the other [main] options can be parsed.
+    """
     # xemacs highlighting hack: '
     debuglevel = IntOption(2, 0, 10)
     errorlevel = IntOption(2, 0, 10)
@@ -625,11 +714,10 @@ class StartupConf(BaseConfig):
     persistdir = Option('/var/lib/yum')
     
 class YumConf(StartupConf):
-    '''
-    Configuration option definitions for yum.conf\'s [main] section.
+    """Configuration option definitions for yum.conf's [main] section.
 
-    Note: see also options inherited from StartupConf
-    '''
+    Note: see also options inherited from :class:`StartupConf`
+    """
     retries = PositiveIntOption(10, names_of_0=["<forever>"])
     recent = IntOption(7, range_min=0)
 
@@ -664,6 +752,7 @@ class YumConf(StartupConf):
     tsflags = ListOption()
 
     assumeyes = BoolOption(False)
+    assumeno  = BoolOption(False)
     alwaysprompt = BoolOption(True)
     exactarch = BoolOption(True)
     tolerant = BoolOption(True)
@@ -686,6 +775,9 @@ class YumConf(StartupConf):
 
     bandwidth = BytesOption(0)
     throttle = ThrottleOption(0)
+    ip_resolve = CaselessSelectionOption(
+            allowed = ('ipv4', 'ipv6', 'whatever'),
+            mapper  = {'4': 'ipv4', '6': 'ipv6'})
 
     http_caching = SelectionOption('all', ('none', 'packages', 'all'))
     metadata_expire = SecondsOption(60 * 60 * 6) # Time in seconds (6h).
@@ -747,6 +839,7 @@ class YumConf(StartupConf):
     
     clean_requirements_on_remove = BoolOption(False)
 
+    upgrade_requirements_on_install = BoolOption(False)
 
     history_list_view = SelectionOption('single-user-commands',
                                         ('single-user-commands', 'users',
@@ -756,6 +849,12 @@ class YumConf(StartupConf):
     _reposlist = []
 
     def dump(self):
+        """Return a string representing the values of all the
+        configuration options.
+
+        :return: a string representing the values of all the
+           configuration options
+        """
         output = '[main]\n'
         # we exclude all vars which start with _ or are in this list:
         excluded_vars = ('cfg', 'uid', 'yumvar', 'progress_obj', 'failure_obj',
@@ -778,14 +877,12 @@ class YumConf(StartupConf):
         return output
 
 class RepoConf(BaseConfig):
-    '''
-    Option definitions for repository INI file sections.
-    '''
+    """Option definitions for repository INI file sections."""
 
     __cached_keys = set()
     def iterkeys(self):
-        '''Yield the names of all defined options in the instance.
-        '''
+        """Yield the names of all defined options in the instance."""
+
         ck = self.__cached_keys
         if not isinstance(self, RepoConf):
             ck = set()
@@ -823,6 +920,8 @@ class RepoConf(BaseConfig):
     bandwidth = Inherit(YumConf.bandwidth)
     throttle = Inherit(YumConf.throttle)
     timeout = Inherit(YumConf.timeout)
+    ip_resolve = Inherit(YumConf.ip_resolve)
+
     http_caching = Inherit(YumConf.http_caching)
     metadata_expire = Inherit(YumConf.metadata_expire)
     mirrorlist_expire = Inherit(YumConf.mirrorlist_expire)
@@ -839,23 +938,23 @@ class RepoConf(BaseConfig):
     skip_if_unavailable = BoolOption(False)
     
 class VersionGroupConf(BaseConfig):
+    """Option definitions for version groups."""
+
     pkglist = ListOption()
     run_with_packages = BoolOption(False)
 
 
-def readStartupConfig(configfile, root):
-    '''
-    Parse Yum's main configuration file and return a StartupConf instance.
-    
-    This is required in order to access configuration settings required as Yum
-    starts up.
+def readStartupConfig(configfile, root, releasever=None):
+    """Parse Yum's main configuration file and return a
+    :class:`StartupConf` instance.  This is required in order to
+    access configuration settings required as Yum starts up.
 
-    @param configfile: The path to yum.conf.
-    @param root: The base path to use for installation (typically '/')
-    @return: A StartupConf instance.
+    :param configfile: the path to yum.conf
+    :param root: the base path to use for installation (typically '/')
+    :return: A :class:`StartupConf` instance
 
-    May raise Errors.ConfigError if a problem is detected with while parsing.
-    '''
+    :raises: :class:`Errors.ConfigError` if a problem is detected with while parsing.
+    """
 
     # ' xemacs syntax hack
 
@@ -876,20 +975,24 @@ def readStartupConfig(configfile, root):
             raise Errors.ConfigError("All plugin search paths must be absolute")
     # Stuff this here to avoid later re-parsing
     startupconf._parser = parser
+
     # setup the release ver here
-    startupconf.releasever = _getsysver(startupconf.installroot, startupconf.distroverpkg)
+    if releasever is None:
+        releasever = _getsysver(startupconf.installroot,
+                                startupconf.distroverpkg)
+    startupconf.releasever = releasever
+
     uuidfile = '%s/%s/uuid' % (startupconf.installroot, startupconf.persistdir)
     startupconf.uuid = get_uuid(uuidfile)
 
     return startupconf
 
 def readMainConfig(startupconf):
-    '''
-    Parse Yum's main configuration file
+    """Parse Yum's main configuration file
 
-    @param startupconf: StartupConf instance as returned by readStartupConfig()
-    @return: Populated YumConf instance.
-    '''
+    :param startupconf: :class:`StartupConf` instance as returned by readStartupConfig()
+    :return: Populated :class:`YumConf` instance
+    """
     
     # ' xemacs syntax hack
 
@@ -956,6 +1059,12 @@ def readMainConfig(startupconf):
     return yumconf
 
 def readVersionGroupsConfig(configfile="/etc/yum/version-groups.conf"):
+    """Parse the configuration file for version groups.
+    
+    :param configfile: the configuration file to read
+    :return: a dictionary containing the parsed options
+    """
+
     parser = ConfigParser()
     confpp_obj = ConfigPreProcessor(configfile)
     try:
@@ -970,17 +1079,16 @@ def readVersionGroupsConfig(configfile="/etc/yum/version-groups.conf"):
 
 
 def getOption(conf, section, name, option):
-    '''Convenience function to retrieve a parsed and converted value from a
-    ConfigParser.
-
-    @param conf: ConfigParser instance or similar
-    @param section: Section name
-    @param name: Option name
-    @param option: Option instance to use for conversion.
-    @return: The parsed value or default if value was not present.
-
-    Will raise ValueError if the option could not be parsed.
-    '''
+    """Convenience function to retrieve a parsed and converted value from a
+    :class:`ConfigParser`.
+
+    :param conf: ConfigParser instance or similar
+    :param section: Section name
+    :param name: :class:`Option` name
+    :param option: :class:`Option` instance to use for conversion
+    :return: The parsed value or default if value was not present
+    :raises: :class:`ValueError` if the option could not be parsed
+    """
     try: 
         val = conf.get(section, name)
     except (NoSectionError, NoOptionError):
@@ -1028,7 +1136,10 @@ def _getsysver(installroot, distroverpkg):
     if idx.count() == 0:
         releasever = '$releasever'
     else:
-        hdr = idx.next()
+        try:
+            hdr = idx.next()
+        except StopIteration:
+            raise Errors.YumBaseError("Error: rpmdb failed release provides. Try: rpm --rebuilddb")
         releasever = hdr['version']
         del hdr
     del idx
@@ -1036,13 +1147,12 @@ def _getsysver(installroot, distroverpkg):
     return releasever
 
 def writeRawRepoFile(repo,only=None):
-    """
-    Writes changes in a repo object back to a .repo file.
-    @param repo: Repo Object
-    @param only: List of attributes to work on (None = All)
-    It work by reading the repo file, changes the values there shall be changed and write it back to disk.
-    """
+    """Write changes in a repo object back to a .repo file.
 
+    :param repo: the Repo Object to write back out
+    :param only: list of attributes to work on. If *only* is None, all
+       options will be written out   
+    """
     if not _use_iniparse:
         return
 
@@ -1069,7 +1179,7 @@ def writeRawRepoFile(repo,only=None):
         #  If the value is the same, but just interpreted ... when we don't want
         # to keep the interpreted values.
         if (name in ini[section_id] and
-            ovalue == varReplace(ini[section_id][name], yumvar)):
+            ovalue == varReplace(ini[section_id][name], repo.yumvar)):
             ovalue = ini[section_id][name]
 
         if name not in cfgOptions and option.default == value:
diff --git a/yum/depsolve.py b/yum/depsolve.py
index 6d744c0..8fe4952 100644
--- a/yum/depsolve.py
+++ b/yum/depsolve.py
@@ -60,10 +60,7 @@ flags = {"GT": rpm.RPMSENSE_GREATER,
          None: 0 }
 
 class Depsolve(object):
-
-    """
-    Dependency resolving class.
-    """
+    """A class for resolving dependencies."""
 
     def __init__(self):
         self._ts = None
@@ -81,6 +78,8 @@ class Depsolve(object):
         self.installedUnresolvedFileRequires = None
 
     def doTsSetup(self):
+        """Sets up the transaction set before it is used."""
+
         warnings.warn(_('doTsSetup() will go away in a future version of Yum.\n'),
                 Errors.YumFutureDeprecationWarning, stacklevel=2)
         return self._getTs()
@@ -131,7 +130,7 @@ class Depsolve(object):
         
 
     def initActionTs(self):
-        """sets up the ts we'll use for all the work"""
+        """Set up the transaction set that will be used for all the work."""
         
         self._ts = rpmUtils.transaction.TransactionWrapper(self.conf.installroot)
         ts_flags_to_rpm = { 'noscripts': rpm.RPMTRANS_FLAG_NOSCRIPTS,
@@ -158,19 +157,31 @@ class Depsolve(object):
         self._ts.setProbFilter(probfilter)
 
     def whatProvides(self, name, flags, version):
-        """searches the packageSacks for what provides the arguments
-           returns a ListPackageSack of providing packages, possibly empty"""
-
+        """Search the packageSacks for what provides the specified
+        feature or file.
+      
+        :param name: a string specifying the file or feature to search
+           for
+        :param flags: flags related to the search
+        :param version: the version to search for
+        :return: a :class:`ListPackagaSack` containing the packages
+           that match the arguments, and may be empty
+        """
         self.verbose_logger.log(logginglevels.DEBUG_1, _('Searching pkgSack for dep: %s'),
             name)
         defSack = ListPackageSack(self.pkgSack.searchProvides((name, flags, version)))
         return defSack
         
     def allowedMultipleInstalls(self, po):
-        """takes a packageObject, returns 1 or 0 depending on if the package 
-           should/can be installed multiple times with different vers
-           like kernels and kernel modules, for example"""
-
+        """Return whether the given package object can be installed
+        multiple times with different versions.  For example, this
+        would be true of kernels and kernel modules.
+
+        :param po: the package object that this function will
+           determine whether can be install multiple times
+        :return: a boolean specifying whether *po* can be installed
+           multiple times
+        """
         iopkgs = set(self.conf.installonlypkgs)
         if po.name in iopkgs:
             return True
@@ -182,8 +193,11 @@ class Depsolve(object):
         return False
 
     def populateTs(self, test=0, keepold=1):
-        """take transactionData class and populate transaction set"""
+        """Populate the transaction set.
 
+        :param test: unused
+        :param keepold: whether to keep old packages
+        """
         if self.dsCallback: self.dsCallback.transactionPopulation()
         ts_elem = {}
         
@@ -696,6 +710,13 @@ class Depsolve(object):
                 self.tsInfo.remove(txmbr.pkgtup)
 
     def prof_resolveDeps(self):
+        """Call :func:`resolveDeps`, and profile the call using the
+        hotshot profiler.
+
+        :return: a tuple containing the result code and a list of
+           result strings.  This is simply the return value of
+           :func:`resolveDeps` being passed up.
+        """
         fn = "anaconda.prof.0"
         import hotshot, hotshot.stats
         prof = hotshot.Profile(fn)
@@ -709,6 +730,13 @@ class Depsolve(object):
         return rc
 
     def cprof_resolveDeps(self):
+        """Call :func:`resolveDeps`, and profile the call using the
+        cprof profiler.
+
+        :return: a tuple containing the result code and a list of
+           result strings.  This is simply the return value of
+           :func:`resolveDeps` being passed up.
+        """
         import cProfile, pstats
         prof = cProfile.Profile()
         rc = prof.runcall(self.resolveDeps)
@@ -722,7 +750,17 @@ class Depsolve(object):
         return rc
 
     def resolveDeps(self, full_check=True, skipping_broken=False):
-
+        """Resolve dependencies for the packages in the current
+        trasaction set.
+        
+        :param full_check: whether to also check removes and installs,
+           as well as dependencies
+        :param skipping_broken: if this is true, a message saying that
+           dependency solving has finished successfully will not be output.
+           This is useful since if there are broken packages that are
+           being skipped, :func:`resolveDeps` will need to be called
+           multiple times before dependency solving is completely finished
+        """
         if not len(self.tsInfo):
             return (0, [_('Success - empty transaction')])
 
@@ -1150,6 +1188,11 @@ class Depsolve(object):
         return ret
 
     def isPackageInstalled(self, pkgname):
+        """Return whether the given package in installed.
+
+        :param pkgname: the name of the package
+        :return: whether the package in installed
+        """
         lst = self.tsInfo.matchNaevr(name = pkgname)
         for txmbr in lst:
             if txmbr.output_state in TS_INSTALL_STATES:
@@ -1393,42 +1436,52 @@ class Depsolve(object):
 
 
 class DepCheck(object):
-    """object that YumDepsolver uses to see what things are needed to close
-       the transaction set. attributes: requires, conflicts are a list of 
-       requires are conflicts in the current transaction set. Each item in the
-       lists are a requires or conflicts object"""
+    """Object that :class:`YumDepsolver` uses to see what things are
+    needed to close the transaction set. The attributes requires and
+    conflicts are lists of requires and conflicts in the current
+    transaction set. Each item in the lists is a :class:`Requires` or
+    :class:`Conflicts` object.
+    """
     def __init__(self):
         self.requires = []
         self.conflicts = []
 
     def addRequires(self, po, req_tuple_list):
+        """Create and add a :class:`Requires` object to the list of
+        requires.
+
+        :param po: the package object involved in the requires
+        :param req_tuple_list: a list of required by *po* that are
+           un-closed in the transaction set
+        """
         # fixme - do checking for duplicates or additions in here to zip things along
         reqobj = Requires(po, req_tuple_list)
         self.requires.append(reqobj)
     
     def addConflicts(self, conflict_po_list, conflict_item):
+        """Create and add a :class:`Conflicts` object to the list of
+        conflicts.
+
+        :param conflict_po_list: a list of conflicting package objects
+        :param conflict_item: what the conflict between the members of
+           *conflict_po_list is.
+        """
         confobj = Conflicts(conflict_po_list, conflict_item)
         self.conflicts.append(confobj)
 
 class Requires(object):
-
+    """A pure data class for holding a package and the list of things
+    it requires.
     """
-    A pure data class for holding a package and the list of things it
-    requires.
-    """
-
     def __init__(self, pkg,requires):
         self.pkg = pkg # po of requiring pkg
         self.requires = requires # list of things it requires that are un-closed in the ts
 
 
 class Conflicts(object):
-
+    """A pure data class for holding a list packages and what the
+    conflict between them is.
     """
-    A pure data class for holding a package and the list of things it
-    conflicts.
-    """
-
     def __init__(self, pkglist, conflict):
         self.pkglist = pkglist # list of conflicting package objects
         self.conflict = conflict # what the conflict was between them
diff --git a/yum/failover.py b/yum/failover.py
index bca9651..00c17ad 100644
--- a/yum/failover.py
+++ b/yum/failover.py
@@ -19,51 +19,80 @@
 # worry about calling get_serverurl() and server_failed() and these classes will 
 # figure out which URL to cough up based on the failover method.
 
+"""Classes for handling failovers for server URLs."""
+
 import random
 
 class baseFailOverMethod:
-
+    """A base class to provide a failover to switch to a new server if
+    the current one fails.
+    """
     def __init__(self, repo):
         self.repo = repo
         self.failures = 0
     
     def get_serverurl(self, i=None):
-        """Returns a serverurl based on this failover method or None 
-           if complete failure.  If i is given it is a direct index
-           to pull a server URL from instead of using the failures 
-           counter."""
+        """Return a server URL based on this failover method, or None
+        if there is a complete failure.  This method should always be
+        used to translate an index into a URL, as this object may
+        change how indexes map.
+
+        :param i: if given, this is the index of the server URL to
+           return, instead of using the failures counter
+        :return: the next server URL
+        """
         return None
         
     def server_failed(self):
-        "Tells the failover method that the current server is failed."
+        """Notify the failover method that the current server has
+        failed.
+        """
         self.failures = self.failures + 1
         
     def reset(self, i=0):
-        "Reset the failures counter to a given index."
+        """Reset the failures counter to the given index.
+
+        :param i: the index to reset the failures counter to
+        """
         self.failures = i
 
     def get_index(self):
-        """Returns the current number of failures which is also the
-           index into the list this object represents.  ger_serverurl()
-           should always be used to translate an index into a URL
-           as this object may change how indexs map.  (See RoundRobin)"""
+        """Return the current number of failures, which is also the
+        current index into the list of URLs that this object
+        represents.  :fun:`get_serverurl` should always be used to
+        translate an index into a URL, as this object may change how
+        indexes map.
 
+        :return: the current number of failures, which is also the
+           current index   
+        """
         return self.failures
    
     def len(self):
-        """Returns the how many URLs we've got to cycle through."""
+        """Return the total number of URLs available to cycle through
+        in this object.
 
+        :return: the total number of URLs available
+        """
         return len(self.repo.urls)
         
             
 
 class priority(baseFailOverMethod):
-
-    """Chooses server based on the first success in the list."""
-    
+    """A class to provide a failover to switch to a new server
+    if the current one fails.  This classes chooses the next server
+    based on the first success in the list of servers.
+    """
     def get_serverurl(self, i=None):
-        "Returns a serverurl based on this failover method or None if complete failure."
-        
+        """Return the next successful server URL in the list, or None
+        if there is a complete failure.  This method should always be
+        used to translate an index into a URL, as this object may
+        change how indexes map.
+
+        :param i: if given, this is the index of the server URL to
+           return, instead of using the failures counter
+        :return: the next server URL
+        """
         if i == None:
             index = self.failures
         else:
@@ -77,17 +106,28 @@ class priority(baseFailOverMethod):
         
     
 class roundRobin(baseFailOverMethod):
-
-    """Chooses server based on a round robin."""
-    
+    """A class to provide a failover to switch to a new server
+    if the current one fails.  When an object of this class is
+    created, it selects a random place in the list of URLs to begin
+    with, then each time :func:`get_serveurl` is called, the next URL
+    in the list is returned, cycling back to the beginning of the list
+    after the end is reached.
+    """
     def __init__(self, repo):
         baseFailOverMethod.__init__(self, repo)
         random.seed()
         self.offset = random.randint(0, 37)
     
     def get_serverurl(self, i=None):
-        "Returns a serverurl based on this failover method or None if complete failure."
+        """Return the next successful server URL in the list, using
+        the round robin scheme, or None if there is a complete
+        failure.  This method should always be used to translate an
+        index into a URL, as this object may change how indexes map.
 
+        :param i: if given, this is the index of the server URL to
+           return, instead of using the failures counter
+        :return: the next server URL
+        """
         if i == None:
             index = self.failures
         else:
diff --git a/yum/history.py b/yum/history.py
index 5385bd1..a9b12cf 100644
--- a/yum/history.py
+++ b/yum/history.py
@@ -97,9 +97,58 @@ def _setupHistorySearchSQL(patterns=None, ignore_case=False):
     return (need_full, patterns, fields, False)
 # ---- horrible Copy and paste from sqlitesack ----
 
+class _YumHistPackageYumDB:
+    """ Class to pretend to be yumdb_info for history packages. """
+
+    def __init__(self, pkg):
+        self._pkg = pkg
+
+    _valid_yumdb_keys = set(["command_line",
+                             "from_repo", "from_repo_revision",
+                             "from_repo_timestamp",
+                             "installed_by", "changed_by",
+                             "reason", "releasever"])
+    def __getattr__(self, attr):
+        """ Load yumdb attributes from the history sqlite. """
+        pkg = self._pkg
+        if attr.startswith('_'):
+            raise AttributeError, "%s has no yum attribute %s" % (pkg, attr)
+
+        if attr not in self._valid_yumdb_keys:
+            raise AttributeError, "%s has no yum attribute %s" % (pkg, attr)
+
+        val = pkg._history._load_yumdb_key(pkg, attr)
+        if False and val is None:
+            raise AttributeError, "%s has no yum attribute %s" % (pkg, attr)
+
+        if val is None:
+            return None
+
+        val = str(val) or ""
+        setattr(self, attr, val)
+
+        return val
+
+    def __contains__(self, attr):
+        #  This is faster than __iter__ and it makes things fail in a much more
+        # obvious way in weird FS corruption cases like: BZ 593436
+        x = self.get(attr)
+        return x is not None
+
+    def get(self, attr, default=None):
+        """retrieve an add'l data obj"""
+
+        try:
+            res = getattr(self, attr)
+        except AttributeError:
+            return default
+        return res
+
+
 class YumHistoryPackage(PackageObject):
 
-    def __init__(self, name, arch, epoch, version, release, checksum=None):
+    def __init__(self, name, arch, epoch, version, release, checksum=None,
+                 history=None):
         self.name    = name
         self.version = version
         self.release = release
@@ -111,21 +160,69 @@ class YumHistoryPackage(PackageObject):
             self._checksums = [] # (type, checksum, id(0,1)
         else:
             chk = checksum.split(':')
-            self._checksums = [(chk[0], chk[1], 0)] # (type, checksum, id(0,1))
+            self._checksums = [(chk[0], chk[1], 1)] # (type, checksum, id(0,1))
         # Needed for equality comparisons in PackageObject
         self.repoid = "<history>"
 
+        self._history = history
+        self.yumdb_info = _YumHistPackageYumDB(self)
+
+    _valid_rpmdb_keys = set(["buildtime", "buildhost",
+                             "license", "packager",
+                             "size", "sourcerpm", "url", "vendor",
+                             # ?
+                             "committer", "committime"])
+    def __getattr__(self, attr):
+        """ Load rpmdb attributes from the history sqlite. """
+        if attr.startswith('_'):
+            raise AttributeError, "%s has no attribute %s" % (self, attr)
+
+        if attr not in self._valid_rpmdb_keys:
+            raise AttributeError, "%s has no attribute %s" % (self, attr)
+
+        val = self._history._load_rpmdb_key(self, attr)
+        if False and val is None:
+            raise AttributeError, "%s has no attribute %s" % (self, attr)
+
+        if val is None:
+            return None
+
+        val = str(val) or ""
+        setattr(self, attr, val)
+
+        return val
+
+    def _ui_from_repo(self):
+        """ This reports the repo the package is from, we integrate YUMDB info.
+            for RPM packages so a package from "fedora" that is installed has a
+            ui_from_repo of "@fedora". Note that, esp. with the --releasever
+            option, "fedora" or "rawhide" isn't authoritive.
+            So we also check against the current releasever and if it is
+            different we also print the YUMDB releasever. This means that
+            installing from F12 fedora, while running F12, would report as
+            "@fedora/13". """
+        if 'from_repo' in self.yumdb_info:
+            self._history.releasever
+            end = ''
+            if (self._history.releasever is not None and
+                'releasever' in self.yumdb_info and
+                self.yumdb_info.releasever != self._history.releasever):
+                end = '/' + self.yumdb_info.releasever
+            return '@' + self.yumdb_info.from_repo + end
+        return self.repoid
+    ui_from_repo = property(fget=lambda self: self._ui_from_repo())
+
+
 class YumHistoryPackageState(YumHistoryPackage):
-    def __init__(self, name,arch, epoch,version,release, state, checksum=None):
+    def __init__(self, name,arch, epoch,version,release, state, checksum=None,
+                 history=None):
         YumHistoryPackage.__init__(self, name,arch, epoch,version,release,
-                                   checksum)
+                                   checksum, history)
         self.done  = None
         self.state = state
 
-        self.repoid = '<history>'
-
 
-class YumHistoryRpmdbProblem(PackageObject):
+class YumHistoryRpmdbProblem:
     """ Class representing an rpmdb problem that existed at the time of the
         transaction. """
 
@@ -328,7 +425,8 @@ class YumMergedHistoryTransaction(YumHistoryTransaction):
     @staticmethod
     def _conv_pkg_state(pkg, state):
         npkg = YumHistoryPackageState(pkg.name, pkg.arch,
-                                      pkg.epoch,pkg.version,pkg.release, state)
+                                      pkg.epoch,pkg.version,pkg.release, state,
+                                      pkg._history)
         npkg._checksums = pkg._checksums
         npkg.done = pkg.done
         if _sttxt2stcode[npkg.state] in TS_INSTALL_STATES:
@@ -557,7 +655,7 @@ class YumMergedHistoryTransaction(YumHistoryTransaction):
 class YumHistory:
     """ API for accessing the history sqlite data. """
 
-    def __init__(self, root='/', db_path=_history_dir):
+    def __init__(self, root='/', db_path=_history_dir, releasever=None):
         self._conn = None
         
         self.conf = yum.misc.GenericHolder()
@@ -568,6 +666,8 @@ class YumHistory:
         self.conf.writable = False
         self.conf.readable = True
 
+        self.releasever = releasever
+
         if not os.path.exists(self.conf.db_path):
             try:
                 os.makedirs(self.conf.db_path)
@@ -644,7 +744,7 @@ class YumHistory:
             self._conn.close()
             self._conn = None
 
-    def _pkgtup2pid(self, pkgtup, checksum=None):
+    def _pkgtup2pid(self, pkgtup, checksum=None, create=True):
         cur = self._get_cursor()
         executeSQL(cur, """SELECT pkgtupid, checksum FROM pkgtups
                            WHERE name=? AND arch=? AND
@@ -659,6 +759,9 @@ class YumHistory:
             if checksum == sql_checksum:
                 return sql_pkgtupid
         
+        if not create:
+            return None
+
         (n,a,e,v,r) = pkgtup
         (n,a,e,v,r) = (to_unicode(n),to_unicode(a),
                        to_unicode(e),to_unicode(v),to_unicode(r))
@@ -674,23 +777,28 @@ class YumHistory:
                                 (name, arch, epoch, version, release)
                                 VALUES (?, ?, ?, ?, ?)""", (n,a,e,v,r))
         return cur.lastrowid
-    def _apkg2pid(self, po):
+    def _apkg2pid(self, po, create=True):
         csum = po.returnIdSum()
         if csum is not None:
             csum = "%s:%s" % (str(csum[0]), str(csum[1]))
-        return self._pkgtup2pid(po.pkgtup, csum)
-    def _ipkg2pid(self, po):
+        return self._pkgtup2pid(po.pkgtup, csum, create)
+    def _ipkg2pid(self, po, create=True):
         csum = None
         yumdb = po.yumdb_info
         if 'checksum_type' in yumdb and 'checksum_data' in yumdb:
             csum = "%s:%s" % (yumdb.checksum_type, yumdb.checksum_data)
-        return self._pkgtup2pid(po.pkgtup, csum)
-    def pkg2pid(self, po):
+        return self._pkgtup2pid(po.pkgtup, csum, create)
+    def _hpkg2pid(self, po, create=False):
+        return self._apkg2pid(po, create)
+
+    def pkg2pid(self, po, create=True):
         if isinstance(po, YumInstalledPackage):
-            return self._ipkg2pid(po)
+            return self._ipkg2pid(po, create)
         if isinstance(po, YumAvailablePackage):
-            return self._apkg2pid(po)
-        return self._pkgtup2pid(po.pkgtup, None)
+            return self._apkg2pid(po, create)
+        if isinstance(po, YumHistoryPackage):
+            return self._hpkg2pid(po, create)
+        return self._pkgtup2pid(po.pkgtup, None, create)
 
     @staticmethod
     def txmbr2state(txmbr):
@@ -984,7 +1092,8 @@ class YumHistory:
                       ORDER BY name ASC, epoch ASC""", (tid,))
         ret = []
         for row in cur:
-            obj = YumHistoryPackage(row[0],row[1],row[2],row[3],row[4], row[5])
+            obj = YumHistoryPackage(row[0],row[1],row[2],row[3],row[4], row[5],
+                                    history=self)
             ret.append(obj)
         return ret
     def _old_data_pkgs(self, tid):
@@ -998,7 +1107,7 @@ class YumHistory:
         ret = []
         for row in cur:
             obj = YumHistoryPackageState(row[0],row[1],row[2],row[3],row[4],
-                                         row[7], row[5])
+                                         row[7], row[5], history=self)
             obj.done     = row[6] == 'TRUE'
             obj.state_installed = None
             if _sttxt2stcode[obj.state] in TS_INSTALL_STATES:
@@ -1018,7 +1127,8 @@ class YumHistory:
                       ORDER BY name ASC, epoch ASC""", (tid,))
         ret = []
         for row in cur:
-            obj = YumHistoryPackage(row[0],row[1],row[2],row[3],row[4], row[5])
+            obj = YumHistoryPackage(row[0],row[1],row[2],row[3],row[4], row[5],
+                                    history=self)
             ret.append(obj)
         return ret
     def _old_prob_pkgs(self, rpid):
@@ -1032,7 +1142,8 @@ class YumHistory:
                       ORDER BY name ASC, epoch ASC""", (rpid,))
         ret = []
         for row in cur:
-            obj = YumHistoryPackage(row[0],row[1],row[2],row[3],row[4], row[5])
+            obj = YumHistoryPackage(row[0],row[1],row[2],row[3],row[4], row[5],
+                                    history=self)
             obj.main = row[6] == 'TRUE'
             ret.append(obj)
         return ret
@@ -1151,6 +1262,127 @@ class YumHistory:
         assert len(ret) == 1
         return ret[0]
 
+    def _load_anydb_key(self, pkg, db, attr):
+        cur = self._get_cursor()
+        if cur is None or not self._update_db_file_3():
+            return None
+
+        pid = self.pkg2pid(pkg, create=False)
+        if pid is None:
+            return None
+
+        sql = """SELECT %(db)sdb_val FROM pkg_%(db)sdb
+                  WHERE pkgtupid=? and %(db)sdb_key=? """ % {'db' : db}
+        executeSQL(cur, sql, (pid, attr))
+        for row in cur:
+            return row[0]
+
+        return None
+
+    def _load_rpmdb_key(self, pkg, attr):
+        return self._load_anydb_key(pkg, "rpm", attr)
+    def _load_yumdb_key(self, pkg, attr):
+        return self._load_anydb_key(pkg, "yum", attr)
+
+    def _save_anydb_key(self, pkg, db, attr, val):
+        cur = self._get_cursor()
+        if cur is None or not self._update_db_file_3():
+            return None
+
+        pid = self.pkg2pid(pkg, create=False)
+        if pid is None:
+            return None
+
+        sql = """INSERT INTO pkg_%(db)sdb (pkgtupid, %(db)sdb_key, %(db)sdb_val)
+                        VALUES (?, ?, ?)""" % {'db' : db}
+        executeSQL(cur, sql, (pid, attr, to_unicode(val)))
+        for row in cur:
+            return row[0]
+
+        return None
+
+    def _save_rpmdb_key(self, pkg, attr, val):
+        return self._save_anydb_key(pkg, "rpm", attr, val)
+    def _save_yumdb_key(self, pkg, attr, val):
+        return self._save_anydb_key(pkg, "yum", attr, val)
+
+    def _save_rpmdb(self, ipkg):
+        """ Save all the data for rpmdb for this installed pkg, assumes
+            there is no data currently. """
+        for attr in YumHistoryPackage._valid_rpmdb_keys:
+            val = getattr(ipkg, attr, None)
+            if val is None:
+                continue
+            if not self._save_anydb_key(ipkg, "rpm", attr, val):
+                return False
+        return True
+
+    def _save_yumdb(self, ipkg):
+        """ Save all the data for yumdb for this installed pkg, assumes
+            there is no data currently. """
+        for attr in _YumHistPackageYumDB._valid_yumdb_keys:
+            val = ipkg.yumdb_info.get(attr)
+            if val is None:
+                continue
+            if not self._save_anydb_key(ipkg, "yum", attr, val):
+                return False
+        return True
+
+    def _wipe_anydb(self, pkg, db):
+        """ Delete all the data for rpmdb/yumdb for this installed pkg. """
+        cur = self._get_cursor()
+        if cur is None or not self._update_db_file_3():
+            return False
+
+        pid = self.pkg2pid(pkg, create=False)
+        if pid is None:
+            return False
+
+        sql = """DELETE FROM pkg_%(db)sdb WHERE pkgtupid=?""" % {'db' : db}
+        executeSQL(cur, sql, (pid,))
+
+        return True
+
+    def sync_alldb(self, ipkg):
+        """ Sync. all the data for rpmdb/yumdb for this installed pkg. """
+        if not self._wipe_anydb(ipkg, "rpm"):
+            return False
+        self._wipe_anydb(ipkg, "yum")
+        if not self._save_rpmdb(ipkg):
+            return False
+        self._save_yumdb(ipkg)
+        self._commit()
+        return True
+
+    def _pkg_stats(self):
+        """ Some stats about packages in the DB. """
+
+        ret = {'nevrac' : 0,
+               'nevra'  : 0,
+               'nevr'   : 0,
+               'na'     : 0,
+               'rpmdb'  : 0,
+               'yumdb'  : 0,
+               }
+        cur = self._get_cursor()
+        if cur is None or not self._update_db_file_3():
+            return False
+
+        data = (('nevrac', "COUNT(*)",                      "pkgtups"),
+                ('na',     "COUNT(DISTINCT(name || arch))", "pkgtups"),
+                ('nevra',"COUNT(DISTINCT(name||version||epoch||release||arch))",
+                 "pkgtups"),
+                ('nevr',   "COUNT(DISTINCT(name||version||epoch||release))",
+                 "pkgtups"),
+                ('rpmdb',  "COUNT(DISTINCT(pkgtupid))", "pkg_rpmdb"),
+                ('yumdb',  "COUNT(DISTINCT(pkgtupid))", "pkg_yumdb"))
+
+        for key, bsql, esql in data:
+            executeSQL(cur, "SELECT %s FROM %s" % (bsql, esql))
+            for row in cur:
+                ret[key] = row[0]
+        return ret
+
     def _yieldSQLDataList(self, patterns, fields, ignore_case):
         """Yields all the package data for the given params. """
 
@@ -1220,6 +1452,47 @@ class YumHistory:
             tids.add(row[0])
         return tids
 
+    _update_ops_3 = ['''\
+\
+ CREATE TABLE pkg_rpmdb (
+     pkgtupid INTEGER NOT NULL REFERENCES pkgtups,
+     rpmdb_key TEXT NOT NULL,
+     rpmdb_val TEXT NOT NULL);
+''', '''\
+ CREATE INDEX i_pkgkey_rpmdb ON pkg_rpmdb (pkgtupid, rpmdb_key);
+''', '''\
+ CREATE TABLE pkg_yumdb (
+     pkgtupid INTEGER NOT NULL REFERENCES pkgtups,
+     yumdb_key TEXT NOT NULL,
+     yumdb_val TEXT NOT NULL);
+''', '''\
+ CREATE INDEX i_pkgkey_yumdb ON pkg_yumdb (pkgtupid, yumdb_key);
+''']
+
+    def _update_db_file_3(self):
+        """ Update to version 3 of history, rpmdb/yumdb data. """
+        if not self._update_db_file_2():
+            return False
+
+        if hasattr(self, '_cached_updated_3'):
+            return self._cached_updated_3
+
+        cur = self._get_cursor()
+        if cur is None:
+            return False
+
+        executeSQL(cur, "PRAGMA table_info(pkg_yumdb)")
+        #  If we get anything, we're fine. There might be a better way of
+        # saying "anything" but this works.
+        for ob in cur:
+            break
+        else:
+            for op in self._update_ops_3:
+                cur.execute(op)
+            self._commit()
+        self._cached_updated_3 = True
+        return True
+
     _update_ops_2 = ['''\
 \
  CREATE TABLE trans_skip_pkgs (
@@ -1374,6 +1647,8 @@ class YumHistory:
             cur.execute(op)
         for op in self._update_ops_2:
             cur.execute(op)
+        for op in self._update_ops_3:
+            cur.execute(op)
         self._commit()
 
 # Pasted from sqlitesack
diff --git a/yum/i18n.py b/yum/i18n.py
index 9889bf6..85ad15e 100755
--- a/yum/i18n.py
+++ b/yum/i18n.py
@@ -462,6 +462,34 @@ def str_eq(a, b):
     
     return False
     
+def exception2msg(e):
+    """Convert an exception to a message.  This function will convert
+    the exception using to_unicode, unicode, or str, whichever works correctly.
+
+    :param e: an exception
+    :return: a string representation of the exception
+    """
+
+    # DIE python DIE! Which one works:
+    # to_unicode(e.value); unicode(e); str(e); 
+    # Call this so you don't have to care.
+    try:
+        return to_unicode(e.value)
+    except:
+        pass
+
+    try:
+        return unicode(e)
+    except:
+        pass
+
+    try:
+        return str(e)
+    except:
+        pass
+    return "<exception failed to convert to text>"
+
+
 try: 
     '''
     Setup the yum translation domain and make _() and P_() translation wrappers
diff --git a/yum/misc.py b/yum/misc.py
index 2f6ddfe..37c572b 100644
--- a/yum/misc.py
+++ b/yum/misc.py
@@ -940,14 +940,16 @@ def unlink_f(filename):
         if e.errno != errno.ENOENT:
             raise
 
-def stat_f(filename):
+def stat_f(filename, ignore_EACCES=False):
     """ Call os.stat(), but don't die if the file isn't there. Returns None. """
     try:
         return os.stat(filename)
     except OSError, e:
-        if e.errno not in (errno.ENOENT, errno.ENOTDIR):
-            raise
-        return None
+        if e.errno in (errno.ENOENT, errno.ENOTDIR):
+            return None
+        if ignore_EACCES and e.errno == errno.EACCES:
+            return None
+        raise
 
 def _getloginuid():
     """ Get the audit-uid/login-uid, if available. None is returned if there
diff --git a/yum/packages.py b/yum/packages.py
index 5ef9951..f72c068 100644
--- a/yum/packages.py
+++ b/yum/packages.py
@@ -271,6 +271,14 @@ class PackageObject(object):
         return out
     ui_nevra = property(fget=lambda self: self._ui_nevra())
 
+    def _ui_evr(self):
+        if self.epoch == '0':
+            out = '%s-%s' % (self.version, self.release)
+        else:
+            out = '%s:%s-%s' % (self.epoch, self.version, self.release)
+        return out
+    ui_evr = property(fget=lambda self: self._ui_evr())
+
     def __str__(self):
         return self.ui_envra
 
@@ -1083,7 +1091,7 @@ class YumAvailablePackage(PackageObject, RpmBase):
          misc.to_unicode(misc.to_xml(self.summary)), 
          misc.to_unicode(misc.to_xml(self.description)), 
          packager, url, self.filetime, 
-         self.buildtime, self.packagesize, self.size, self.archivesize)
+         self.buildtime, self.packagesize, self.installedsize, self.archivesize)
         
         msg += self._return_remote_location()
         return msg
@@ -1133,7 +1141,7 @@ class YumAvailablePackage(PackageObject, RpmBase):
         msg = ""
         mylist = getattr(self, pcotype)
         if mylist: msg = "\n    <rpm:%s>\n" % pcotype
-        for (name, flags, (e,v,r)) in mylist:
+        for (name, flags, (e,v,r)) in sorted(mylist):
             pcostring = '''      <rpm:entry name="%s"''' % misc.to_xml(name, attrib=True)
             if flags:
                 pcostring += ''' flags="%s"''' % misc.to_xml(flags, attrib=True)
@@ -1161,11 +1169,11 @@ class YumAvailablePackage(PackageObject, RpmBase):
             dirs = self.returnFileEntries('dir', primary_only=True)
             ghosts = self.returnFileEntries('ghost', primary_only=True)
                 
-        for fn in files:
+        for fn in sorted(files):
             msg += """    <file>%s</file>\n""" % misc.to_xml(fn)
-        for fn in dirs:
+        for fn in sorted(dirs):
             msg += """    <file type="dir">%s</file>\n""" % misc.to_xml(fn)
-        for fn in ghosts:
+        for fn in sorted(ghosts):
             msg += """    <file type="ghost">%s</file>\n""" % misc.to_xml(fn)
         
         return msg
@@ -1194,8 +1202,8 @@ class YumAvailablePackage(PackageObject, RpmBase):
                         continue
                     newlist.append(i)
                 mylist = newlist
-        
-        for (name, flags, (e,v,r),pre) in mylist:
+        used = 0
+        for (name, flags, (e,v,r),pre) in sorted(mylist):
             if name.startswith('rpmlib('):
                 continue
             # this drops out requires that the pkg provides for itself.
@@ -1217,13 +1225,16 @@ class YumAvailablePackage(PackageObject, RpmBase):
                     prcostring += ''' ver="%s"''' % misc.to_xml(v, attrib=True)
                 if r:
                     prcostring += ''' rel="%s"''' % misc.to_xml(r, attrib=True)
-            if pre:
+            if pre not in ("0", 0):
                 prcostring += ''' pre="%s"''' % pre
-                    
+
             prcostring += "/>\n"
             msg += prcostring
+            used += 1
             
         if mylist: msg += "    </rpm:requires>"
+        if used == 0:
+            return ""
         return msg
 
     def _dump_changelog(self, clog_limit):
@@ -1299,7 +1310,8 @@ class YumHeaderPackage(YumAvailablePackage):
         self.pkgid = self.hdr[rpm.RPMTAG_SHA1HEADER]
         if not self.pkgid:
             self.pkgid = "%s.%s" %(self.hdr['name'], self.hdr['buildtime'])
-        self.packagesize = self.hdr['size']
+        self.packagesize = self.hdr['archivesize']
+        self.installedsize = self.hdr['size']
         self.__mode_cache = {}
         self.__prcoPopulated = False
 
diff --git a/yum/plugins.py b/yum/plugins.py
index bfc49b7..9ddcae6 100644
--- a/yum/plugins.py
+++ b/yum/plugins.py
@@ -66,9 +66,9 @@ from yum.i18n import utf8_width
 API_VERSION = '2.6'
 
 class DeprecatedInt(int):
-    '''
-    A simple int subclass that used to check when a deprecated constant is used.
-    '''
+    """A simple int subclass that is used to check when a deprecated
+    constant is used.
+    """
 
 # Plugin types
 TYPE_CORE = 0
@@ -105,8 +105,8 @@ SLOT_TO_CONDUIT = {
 SLOTS = sorted(SLOT_TO_CONDUIT.keys())
 
 class PluginYumExit(Exception):
-    '''Used by plugins to signal that yum should stop
-    '''
+    """Exception that can be raised by plugins to signal that yum should stop."""
+
     def __init__(self, value="", translation_domain=""):
         self.value = value
         self.translation_domain = translation_domain
@@ -117,9 +117,7 @@ class PluginYumExit(Exception):
             return self.value
     
 class YumPlugins:
-    '''
-    Manager class for Yum plugins.
-    '''
+    """Manager class for Yum plugins."""
 
     def __init__(self, base, searchpath, optparser=None, types=None, 
             pluginconfpath=None,disabled=None,enabled=None):
@@ -167,8 +165,13 @@ class YumPlugins:
         self.run('config')
 
     def run(self, slotname, **kwargs):
-        '''Run all plugin functions for the given slot.
-        '''
+        """Run all plugin functions for the given slot.
+
+        :param slotname: a string representing the name of the slot to
+           run the plugins for
+        :param kwargs: keyword arguments that will be simply passed on
+           to the plugins
+        """
         # Determine handler class to use
         conduitcls = SLOT_TO_CONDUIT.get(slotname, None)
         if conduitcls is None:
@@ -356,23 +359,35 @@ class YumPlugins:
         return parser
 
     def setCmdLine(self, opts, commands):
-        '''Set the parsed command line options so that plugins can access them
-        '''
+        """Set the parsed command line options so that plugins can
+        access them.
+
+        :param opts: a dictionary containing the values of the command
+           line options
+        :param commands: a list of command line arguments passed to yum
+        """
         self.cmdline = (opts, commands)
 
 
 class DummyYumPlugins:
-    '''
-    This class provides basic emulation of the YumPlugins class. It exists so
-    that calls to plugins.run() don't fail if plugins aren't in use.
-    '''
+    """This class provides basic emulation of the :class:`YumPlugins`
+    class. It exists so that calls to plugins.run() don't fail if
+    plugins aren't in use.
+    """
     def run(self, *args, **kwargs):
+        """Do nothing.  All arguments are unused."""
+
         pass
 
     def setCmdLine(self, *args, **kwargs):
+        """Do nothing.  All arguments are unused."""
+
         pass
 
 class PluginConduit:
+    """A conduit class to transfer information between yum and the
+    plugin.
+    """
     def __init__(self, parent, base, conf):
         self._parent = parent
         self._base = base
@@ -382,14 +397,30 @@ class PluginConduit:
         self.verbose_logger = logging.getLogger("yum.verbose.plugin")
 
     def info(self, level, msg):
+        """Send an info message to the logger.
+
+        :param level: the level of the message to send
+        :param msg: the message to send
+        """
         converted_level = logginglevels.logLevelFromDebugLevel(level)
         self.verbose_logger.log(converted_level, msg)
 
     def error(self, level, msg):
+        """Send an error message to the logger.
+
+        :param level: the level of the message to send
+        :param msg: the message to send
+        """
         converted_level = logginglevels.logLevelFromErrorLevel(level)
         self.logger.log(converted_level, msg)
 
     def promptYN(self, msg):
+        """Return a yes or no response, either from assumeyes already
+        being set, or from prompting the user.
+
+        :param msg: the message to prompt the user with
+        :return: 1 if the response is yes, and 0 if the response is no
+        """
         self.info(2, msg)
         if self._base.conf.assumeyes:
             return 1
@@ -397,88 +428,96 @@ class PluginConduit:
             return self._base.userconfirm()
 
     def getYumVersion(self):
+        """Return a string representing the current version of yum."""
+
         import yum
         return yum.__version__
 
     def getOptParser(self):
-        '''Return the optparse.OptionParser instance for this execution of Yum
-
-        In the "config" and "init" slots a plugin may add extra options to this
-        instance to extend the command line options that Yum exposes.
-
-        In all other slots a plugin may only read the OptionParser instance.
-        Any modification of the instance at this point will have no effect. 
-        
-        See the getCmdLine() method for details on how to retrieve the parsed
-        values of command line options.
-
-        @return: the global optparse.OptionParser instance used by Yum. May be
-            None if an OptionParser isn't in use.
-        '''
+        """Return the :class:`optparse.OptionParser` instance for this
+        execution of Yum.  In the "config" and "init" slots a plugin
+        may add extra options to this instance to extend the command
+        line options that Yum exposes.  In all other slots a plugin
+        may only read the :class:`OptionParser` instance.  Any
+        modification of the instance at this point will have no
+        effect.  See the
+        :func:`PreRepoSetupPluginConduit.getCmdLine` method for
+        details on how to retrieve the parsed values of command line
+        options.
+
+        :return: the global :class:`optparse.OptionParser` instance used by
+           Yum. May be None if an OptionParser isn't in use
+        """
         # ' xemacs highlighting hack
         # This isn't API compatible :(
         # return self._parent.optparser.plugin_option_group
         return self._parent.optparser
 
     def confString(self, section, opt, default=None):
-        '''Read a string value from the plugin's own configuration file
+        """Read a string value from the plugin's own configuration file.
 
-        @param section: Configuration file section to read.
-        @param opt: Option name to read.
-        @param default: Value to read if option is missing.
-        @return: String option value read, or default if option was missing.
-        '''
+        :param section: configuration file section to read
+        :param opt: option name to read
+        :param default: value to read if the option is missing
+        :return: string option value read, or default if option was missing
+        """
         # ' xemacs highlighting hack
         return config.getOption(self._conf, section, opt, config.Option(default))
 
     def confInt(self, section, opt, default=None):
-        '''Read an integer value from the plugin's own configuration file
+        """Read an integer value from the plugin's own configuration file.
 
-        @param section: Configuration file section to read.
-        @param opt: Option name to read.
-        @param default: Value to read if option is missing.
-        @return: Integer option value read, or default if option was missing or
-            could not be parsed.
-        '''
+        :param section: configuration file section to read
+        :param opt: option name to read
+        :param default: value to read if the option is missing
+
+        :return: the integer option value read, or *default* if the
+            option was missing or could not be parsed
+        """
         return config.getOption(self._conf, section, opt, config.IntOption(default))
 
     def confFloat(self, section, opt, default=None):
-        '''Read a float value from the plugin's own configuration file
-
-        @param section: Configuration file section to read.
-        @param opt: Option name to read.
-        @param default: Value to read if option is missing.
-        @return: Float option value read, or default if option was missing or
-            could not be parsed.
-        '''
+        """Read a float value from the plugin's own configuration file.
+
+        :param section: configuration file section to read
+        :param opt: option name to read
+        :param default: value to read if the option is missing
+        :return: float option value read, or *default* if the option was
+            missing or could not be parsed
+        """
         return config.getOption(self._conf, section, opt, config.FloatOption(default))
 
     def confBool(self, section, opt, default=None):
-        '''Read a boolean value from the plugin's own configuration file
-
-        @param section: Configuration file section to read.
-        @param opt: Option name to read.
-        @param default: Value to read if option is missing.
-        @return: Boolean option value read, or default if option was missing or
-            could not be parsed.
-        '''
+        """Read a boolean value from the plugin's own configuration file
+
+        :param section: configuration file section to read
+        :param opt: option name to read
+        :param default: value to read if the option is missing
+        :return: boolean option value read, or *default* if the option
+            was missing or could not be parsed
+        """
         return config.getOption(self._conf, section, opt, config.BoolOption(default))
 
     def registerPackageName(self, name):
+        """Register the name of a package to use.
+
+        :param name: the name of the package to register
+        """
         self._base.run_with_package_names.add(name)
 
 
 class ConfigPluginConduit(PluginConduit):
+    """A conduit for use in the config slot."""
 
     def registerOpt(self, name, valuetype, where, default):
-        '''Register a yum configuration file option.
-
-        @param name: Name of the new option.
-        @param valuetype: Option type (PLUG_OPT_BOOL, PLUG_OPT_STRING ...)
-        @param where: Where the option should be available in the config file.
-            (PLUG_OPT_WHERE_MAIN, PLUG_OPT_WHERE_REPO, ...)
-        @param default: Default value for the option if not set by the user.
-        '''
+        """Deprecated.  Register a yum configuration file option.
+
+        :param name: name of the new option
+        :param valuetype: option type (PLUG_OPT_BOOL, PLUG_OPT_STRING, etc.)
+        :param where: where the option should be available in the config file
+            (PLUG_OPT_WHERE_MAIN, PLUG_OPT_WHERE_REPO, etc)
+        :param default: default value for the option if it is not set by the user
+        """
         warnings.warn('registerOpt() will go away in a future version of Yum.\n'
                 'Please manipulate config.YumConf and config.RepoConf directly.',
                 DeprecationWarning)
@@ -502,64 +541,93 @@ class ConfigPluginConduit(PluginConduit):
             setattr(config.RepoConf, name, config.Inherit(option))
 
     def registerCommand(self, command):
+        """Register a new command.
+
+        :param command: the command to register
+        :raises: :class:`yum.Errors.ConfigError` if the registration
+           of commands is not supported
+        """
         if hasattr(self._base, 'registerCommand'):
             self._base.registerCommand(command)
         else:
             raise Errors.ConfigError(_('registration of commands not supported'))
 
 class PostConfigPluginConduit(ConfigPluginConduit):
+    """Conduit for use in the postconfig slot."""
 
     def getConf(self):
+        """Return a dictionary containing the values of the
+        configuration options.
+
+        :return: a dictionary containing the values of the
+           configuration options
+        """
         return self._base.conf
 
 class InitPluginConduit(PluginConduit):
+    """Conduit for use in the init slot."""
 
     def getConf(self):
+        """Return a dictionary containing the values of the
+        configuration options.
+
+        :return: a dictionary containing the values of the
+           configuration options
+        """
         return self._base.conf
 
     def getRepos(self):
-        '''Return Yum's container object for all configured repositories.
+        """Return Yum's container object for all configured repositories.
 
-        @return: Yum's RepoStorage instance
-        '''
+        :return: Yum's :class:`yum.repos.RepoStorage` instance
+        """
         return self._base.repos
 
 class ArgsPluginConduit(InitPluginConduit):
+    """Conduit for dealing with command line arguments."""
 
     def __init__(self, parent, base, conf, args):
         InitPluginConduit.__init__(self, parent, base, conf)
         self._args = args
 
     def getArgs(self):
+        """Return a list of the command line arguments passed to yum.
+
+        :return: a list of the command line arguments passed to yum
+        """
         return self._args
 
 class PreRepoSetupPluginConduit(InitPluginConduit):
+    """Conduit for use in the prererosetup slot."""
+
 
     def getCmdLine(self):
-        '''Return parsed command line options.
+        """Return parsed command line options.
 
-        @return: (options, commands) as returned by OptionParser.parse_args()
-        '''
+        :return: (options, commands) as returned by :class:`OptionParser.parse_args()`
+        """
         return self._parent.cmdline
 
     def getRpmDB(self):
-        '''Return a representation of local RPM database. This allows querying
-        of installed packages.
+        """Return a representation of the local RPM database. This
+        allows querying of installed packages.
 
-        @return: rpmUtils.RpmDBHolder instance
-        '''
+        :return: a :class:`yum.rpmUtils.RpmDBHolder` instance
+        """
         return self._base.rpmdb
 
 class PostRepoSetupPluginConduit(PreRepoSetupPluginConduit):
+    """Conduit for use in the postreposetup slot."""
 
     def getGroups(self):
-        '''Return group information.
+        """Return group information.
 
-        @return: yum.comps.Comps instance
-        '''
+        :return: :class:`yum.comps.Comps` instance
+        """
         return self._base.comps
 
 class DownloadPluginConduit(PostRepoSetupPluginConduit):
+    """Conduit for use in the download slots."""
 
     def __init__(self, parent, base, conf, pkglist, errors=None):
         PostRepoSetupPluginConduit.__init__(self, parent, base, conf)
@@ -567,24 +635,35 @@ class DownloadPluginConduit(PostRepoSetupPluginConduit):
         self._errors = errors
 
     def getDownloadPackages(self):
-        '''Return a list of package objects representing packages to be
+        """Return a list of package objects representing packages to be
         downloaded.
-        '''
+
+        :return: a list of package object representing packages to be
+           downloaded
+        """
         return self._pkglist
 
     def getErrors(self):
-        '''Return a dictionary of download errors. 
+        """Return a dictionary of download errors. 
         
-        The returned dictionary is indexed by package object. Each element is a
-        list of strings describing the error.
-        '''
+        :return: a dictionary of download errors. This dictionary is
+           indexed by package object. Each element is a list of
+           strings describing the error
+        """
         if not self._errors:
             return {}
         return self._errors
 
 class MainPluginConduit(PostRepoSetupPluginConduit):
-
+    """Main conduit class for plugins.  Many other conduit classes
+    will inherit from this class.
+    """
     def getPackages(self, repo=None):
+        """Return a list of packages.
+
+        :param repo: the repo to return a packages from
+        :return: a list of package objects
+        """
         if repo:
             arg = repo.id
         else:
@@ -592,50 +671,76 @@ class MainPluginConduit(PostRepoSetupPluginConduit):
         return self._base.pkgSack.returnPackages(arg)
 
     def getPackageByNevra(self, nevra):
-        '''Retrieve a package object from the packages loaded by Yum using
-        nevra information 
+        """Retrieve a package object from the packages loaded by Yum using
+        nevra information.
         
-        @param nevra: A tuple holding (name, epoch, version, release, arch)
+        :param nevra: a tuple holding (name, epoch, version, release, arch)
             for a package
-        @return: A PackageObject instance (or subclass)
-        '''
+        :return: a :class:`yum.packages.PackageObject` instance (or subclass)
+        """
         return self._base.getPackageObject(nevra)
 
     def delPackage(self, po):
+        """Delete the given package from the package sack.
+
+        :param po: the package object to delete
+        """
         po.repo.sack.delPackage(po)
 
     def getTsInfo(self):
+        """Return transaction set.
+
+        :return: the transaction set
+        """
         return self._base.tsInfo
 
 class DepsolvePluginConduit(MainPluginConduit):
+    """Conduit for use in solving dependencies."""
+
     def __init__(self, parent, base, conf, rescode=None, restring=[]):
         MainPluginConduit.__init__(self, parent, base, conf)
         self.resultcode = rescode
         self.resultstring = restring
 
 class CompareProvidersPluginConduit(MainPluginConduit):
+    """Conduit to compare different providers of packages."""
+
     def __init__(self, parent, base, conf, providers_dict={}, reqpo=None):
         MainPluginConduit.__init__(self, parent, base, conf)
         self.packages = providers_dict
         self.reqpo = reqpo
 
 class HistoryPluginConduit(MainPluginConduit):
+    """Conduit to access information about the yum history."""
+
     def __init__(self, parent, base, conf, rescode=None, restring=[]):
         MainPluginConduit.__init__(self, parent, base, conf)
         self.history = self._base.history
 
 class VerifyPluginConduit(MainPluginConduit):
+    """Conduit to verify packages."""
+
     def __init__(self, parent, base, conf, verify_package):
         MainPluginConduit.__init__(self, parent, base, conf)
         self.verify_package = verify_package
 
 def parsever(apiver):
+    """Parse a string representing an api version.
+
+    :param apiver: a string representing an api version
+    :return: a tuple containing the major and minor version numbers
+    """
     maj, min = apiver.split('.')
     return int(maj), int(min)
 
 def apiverok(a, b):
-    '''Return true if API version "a" supports API version "b"
-    '''
+    """Return true if API version "a" supports API version "b"
+
+    :param a: a string representing an api version
+    :param b: a string representing an api version
+
+    :return: whether version *a* supports version *b*
+    """
     a = parsever(a)
     b = parsever(b)
 
diff --git a/yum/rpmsack.py b/yum/rpmsack.py
index e289a7a..635f03f 100644
--- a/yum/rpmsack.py
+++ b/yum/rpmsack.py
@@ -48,6 +48,17 @@ def _open_no_umask(*args):
 
     return ret
 
+def _makedirs_no_umask(*args):
+    """ Annoying people like to set umask's for root, which screws everything
+        up for user readable stuff. """
+    oumask = os.umask(022)
+    try:
+        ret = os.makedirs(*args)
+    finally:
+        os.umask(oumask)
+
+    return ret
+
 def _iopen(*args):
     """ IOError wrapper BS for open, stupid exceptions. """
     try:
@@ -364,7 +375,8 @@ class RPMDBPackageSack(PackageSackBase):
             pkg = self.searchNevra(n, e, v, r, a)
             if not pkg:
                 # Wibble?
-                self._deal_with_bad_rpmdbcache("dCDPT(pkg checksums)")
+                self._deal_with_bad_rpmdbcache("dCDPT(pkg checksums): %s" %
+                                               txmbr)
                 continue
 
             pkg = pkg[0]
@@ -1002,7 +1014,8 @@ class RPMDBPackageSack(PackageSackBase):
             (n, a, e, v, r) = pkgtup
             pkg = self.searchNevra(n, e, v, r, a)
             if not pkg:
-                self._deal_with_bad_rpmdbcache("pkg checksums")
+                self._deal_with_bad_rpmdbcache("pkg checksums: %s-%s:%s-%s.%s" %
+                                               (n, e, v, r, a))
                 continue
             pkg = pkg[0]
             (T, D) = checksum_data[pkgtup]
@@ -1088,7 +1101,7 @@ class RPMDBPackageSack(PackageSackBase):
                 return
 
             try:
-                os.makedirs(self._cachedir)
+                _makedirs_no_umask(self._cachedir)
             except (IOError, OSError), e:
                 return
 
@@ -1562,11 +1575,11 @@ class RPMDBAdditionalData(object):
         self._packages = {} # pkgid = dir
         if not os.path.exists(self.conf.db_path):
             try:
-                os.makedirs(self.conf.db_path)
+                _makedirs_no_umask(self.conf.db_path)
+                self.conf.writable = True
             except (IOError, OSError), e:
                 # some sort of useful thing here? A warning?
-                return
-            self.conf.writable = True
+                pass
         else:
             if os.access(self.conf.db_path, os.W_OK):
                 self.conf.writable = True
@@ -1708,7 +1721,7 @@ class RPMDBAdditionalDataPackage(object):
     def _write(self, attr, value):
         # check for self._conf.writable before going on?
         if not os.path.exists(self._mydir):
-            os.makedirs(self._mydir)
+            _makedirs_no_umask(self._mydir)
 
         attr = _sanitize(attr)
         if attr in self._read_cached_data:
@@ -1753,7 +1766,7 @@ class RPMDBAdditionalDataPackage(object):
         if attr.endswith('.tmp'):
             raise AttributeError, "%s has no attribute %s" % (self, attr)
 
-        info = misc.stat_f(fn)
+        info = misc.stat_f(fn, ignore_EACCES=True)
         if info is None:
             raise AttributeError, "%s has no attribute %s" % (self, attr)
 
diff --git a/yum/sqlitesack.py b/yum/sqlitesack.py
index 8a6f6f3..19193ad 100644
--- a/yum/sqlitesack.py
+++ b/yum/sqlitesack.py
@@ -406,7 +406,7 @@ class YumAvailablePackageSqlite(YumAvailablePackage, PackageObject, RpmBase):
         requires = []
         for ob in cur:
             pre = "0"
-            if ob['pre'].lower() in ['TRUE', 1]:
+            if ob['pre'].lower() in ['true', 1]:
                 pre = "1"
             prco_set = (_share_data(ob['name']), _share_data(ob['flags']),
                         (_share_data(ob['epoch']),
diff --git a/yum/yumRepo.py b/yum/yumRepo.py
index e5e9ece..91b7dde 100644
--- a/yum/yumRepo.py
+++ b/yum/yumRepo.py
@@ -24,6 +24,7 @@ urlparse.uses_fragment.append("media")
 import Errors
 from urlgrabber.grabber import URLGrabber
 from urlgrabber.grabber import default_grabber
+from urlgrabber.progress import format_number
 import urlgrabber.mirror
 from urlgrabber.grabber import URLGrabError
 import repoMDObject
@@ -35,6 +36,7 @@ import sqlitesack
 from yum import config
 from yum import misc
 from yum import comps
+from yum import _
 from constants import *
 import metalink
 
@@ -499,6 +501,7 @@ class YumRepository(Repository, config.RepoConf):
                  'throttle': self.throttle,
                  'proxies': self.proxy_dict,
                  'timeout': self.timeout,
+                 'ip_resolve': self.ip_resolve,
                  'http_headers': tuple(self.__headersListFromDict(cache=cache)),
                  'ssl_verify_peer': self.sslverify,
                  'ssl_verify_host': self.sslverify,
@@ -796,6 +799,16 @@ class YumRepository(Repository, config.RepoConf):
             except Errors.MediaError, e:
                 verbose_logger.log(logginglevels.DEBUG_2, "Error getting package from media; falling back to url %s" %(e,))
 
+        if size:
+            dirstat = os.statvfs(os.path.dirname(local))
+            avail = dirstat.f_bavail * dirstat.f_bsize
+            if avail < long(size):
+                raise Errors.RepoError, _('''\
+Insufficient space in download directory %s
+    * free   %s
+    * needed %s'''
+                ) % (os.path.dirname(local), format_number(avail), format_number(long(size)))
+
         if url and scheme != "media":
             ugopts = self._default_grabopts(cache=cache)
             ug = URLGrabber(progress_obj = self.callback,
@@ -1020,7 +1033,7 @@ class YumRepository(Repository, config.RepoConf):
             if grab_can_fail:
                 return None
             raise Errors.RepoError, 'Error downloading file %s: %s' % (local, e)
-        except (Errors.NoMoreMirrorsRepoError, Errors.RepoError):
+        except Errors.RepoError:
             misc.unlink_f(tfname)
             if grab_can_fail:
                 return None
@@ -1614,7 +1627,7 @@ class YumRepository(Repository, config.RepoConf):
                                   text=text,
                                   cache=self.http_caching == 'all',
                                   size=thisdata.size)
-        except (Errors.NoMoreMirrorsRepoError, Errors.RepoError):
+        except Errors.RepoError:
             if retrieve_can_fail:
                 return None
             raise
diff --git a/yumcommands.py b/yumcommands.py
index 4dcbea7..2ab9a28 100644
--- a/yumcommands.py
+++ b/yumcommands.py
@@ -43,16 +43,22 @@ def _err_mini_usage(base, basecmd):
     base.logger.critical(txt)
 
 def checkRootUID(base):
-    """
-    Verify that the program is being run by the root user.
+    """Verify that the program is being run by the root user.
 
-    @param base: a YumBase object.
+    :param base: a :class:`yum.Yumbase` object.
+    :raises: :class:`cli.CliError`
     """
     if base.conf.uid != 0:
         base.logger.critical(_('You need to be root to perform this command.'))
         raise cli.CliError
 
 def checkGPGKey(base):
+    """Verify that there are gpg keys for the enabled repositories in the
+    rpm database.
+
+    :param base: a :class:`yum.Yumbase` object.
+    :raises: :class:`cli.CliError`
+    """
     if not base.gpgKeyCheck():
         for repo in base.repos.listEnabled():
             if (repo.gpgcheck or repo.repo_gpgcheck) and not repo.gpgkey:
@@ -75,6 +81,14 @@ For more information contact your distribution or package provider.
                 raise cli.CliError
 
 def checkPackageArg(base, basecmd, extcmds):
+    """Verify that *extcmds* contains the name of at least one package for
+    *basecmd* to act on.
+
+    :param base: a :class:`yum.Yumbase` object.
+    :param basecmd: the name of the command being checked for
+    :param extcmds: a list of arguments passed to *basecmd*
+    :raises: :class:`cli.CliError`
+    """
     if len(extcmds) == 0:
         base.logger.critical(
                 _('Error: Need to pass a list of pkgs to %s') % basecmd)
@@ -82,18 +96,44 @@ def checkPackageArg(base, basecmd, extcmds):
         raise cli.CliError
 
 def checkItemArg(base, basecmd, extcmds):
+    """Verify that *extcmds* contains the name of at least one item for
+    *basecmd* to act on.  Generally, the items are command-line
+    arguments that are not the name of a package, such as a file name
+    passed to provides.
+
+    :param base: a :class:`yum.Yumbase` object.
+    :param basecmd: the name of the command being checked for
+    :param extcmds: a list of arguments passed to *basecmd*
+    :raises: :class:`cli.CliError`
+    """
     if len(extcmds) == 0:
         base.logger.critical(_('Error: Need an item to match'))
         _err_mini_usage(base, basecmd)
         raise cli.CliError
 
 def checkGroupArg(base, basecmd, extcmds):
+    """Verify that *extcmds* contains the name of at least one group for
+    *basecmd* to act on.
+
+    :param base: a :class:`yum.Yumbase` object.
+    :param basecmd: the name of the command being checked for
+    :param extcmds: a list of arguments passed to *basecmd*
+    :raises: :class:`cli.CliError`
+    """
     if len(extcmds) == 0:
         base.logger.critical(_('Error: Need a group or list of groups'))
         _err_mini_usage(base, basecmd)
         raise cli.CliError    
 
 def checkCleanArg(base, basecmd, extcmds):
+    """Verify that *extcmds* contains at least one argument, and that all
+    arguments in *extcmds* are valid options for clean.
+
+    :param base: a :class:`yum.Yumbase` object
+    :param basecmd: the name of the command being checked for
+    :param extcmds: a list of arguments passed to *basecmd*
+    :raises: :class:`cli.CliError`
+    """
     VALID_ARGS = ('headers', 'packages', 'metadata', 'dbcache', 'plugins',
                   'expire-cache', 'rpmdb', 'all')
 
@@ -108,12 +148,14 @@ def checkCleanArg(base, basecmd, extcmds):
             raise cli.CliError
 
 def checkShellArg(base, basecmd, extcmds):
-    """
-    Verify that the arguments given to 'yum shell' are valid.
-
-    yum shell can be given either no args, or exactly one argument,
-    which is the name of a file. If these are not met,
-    raise cli.CliError.
+    """Verify that the arguments given to 'yum shell' are valid.  yum
+    shell can be given either no argument, or exactly one argument,
+    which is the name of a file.
+
+    :param base: a :class:`yum.Yumbase` object.
+    :param basecmd: the name of the command being checked for
+    :param extcmds: a list of arguments passed to *basecmd*
+    :raises: :class:`cli.CliError`
     """
     if len(extcmds) == 0:
         base.verbose_logger.debug(_("No argument to shell"))
@@ -133,10 +175,12 @@ def checkShellArg(base, basecmd, extcmds):
         raise cli.CliError
 
 def checkEnabledRepo(base, possible_local_files=[]):
-    """
-    Verify that there is at least one enabled repo.
+    """Verify that there is at least one enabled repo.
 
-    @param base: a YumBase object.
+    :param base: a :class:`yum.Yumbase` object.
+    :param basecmd: the name of the command being checked for
+    :param extcmds: a list of arguments passed to *basecmd*
+    :raises: :class:`cli.CliError`:
     """
     if base.repos.listEnabled():
         return
@@ -152,63 +196,145 @@ def checkEnabledRepo(base, possible_local_files=[]):
     raise cli.CliError
 
 class YumCommand:
-        
+    """An abstract base class that defines the methods needed by the cli
+    to execute a specific command.  Subclasses must override at least
+    :func:`getUsage` and :func:`getSummary`.
+    """
+
     def __init__(self):
         self.done_command_once = False
         self.hidden = False
 
     def doneCommand(self, base, msg, *args):
+        """ Output *msg* the first time that this method is called, and do
+        nothing on subsequent calls.  This is to prevent duplicate
+        messages from being printed for the same command.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param msg: the message to be output
+        :param *args: additional arguments associated with the message
+        """
         if not self.done_command_once:
             base.verbose_logger.info(msg, *args)
         self.done_command_once = True
 
     def getNames(self):
+        """Return a list of strings that are the names of the command.
+        The command can be called from the command line by using any
+        of these names.
+
+        :return: a list containing the names of the command
+        """
         return []
 
     def getUsage(self):
-        """
-        @return: A usage string for the command, including arguments.
+        """Return a usage string for the command, including arguments.
+
+        :return: a usage string for the command
         """
         raise NotImplementedError
 
     def getSummary(self):
-        """
-        @return: A one line summary of what the command does.
+        """Return a one line summary of what the command does.
+
+        :return: a one line summary of what the command does
         """
         raise NotImplementedError
     
     def doCheck(self, base, basecmd, extcmds):
+        """Verify that various conditions are met so that the command
+        can run.
+
+        :param base: a :class:`yum.Yumbase` object.
+        :param basecmd: the name of the command being checked for
+        :param extcmds: a list of arguments passed to *basecmd*
+        """
         pass
 
     def doCommand(self, base, basecmd, extcmds):
-        """
-        @return: (exit_code, [ errors ]) where exit_code is:
-           0 = we're done, exit
-           1 = we've errored, exit with error string
-           2 = we've got work yet to do, onto the next stage
+        """Execute the command
+
+        :param base: a :class:`yum.Yumbase` object.
+        :param basecmd: the name of the command being executed
+        :param extcmds: a list of arguments passed to *basecmd*
+        :return: (exit_code, [ errors ])
+
+        exit_code is::
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+            2 = we've got work yet to do, onto the next stage
         """
         return 0, [_('Nothing to do')]
     
     def needTs(self, base, basecmd, extcmds):
+        """Return whether a transaction set must be set up before the
+        command can run
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: a list of arguments passed to *basecmd*
+        :return: True if a transaction set is needed, False otherwise
+        """
         return True
         
 class InstallCommand(YumCommand):
+    """A class containing methods needed by the cli to execute the
+    install command.
+    """
+
     def getNames(self):
+        """Return a list containing the names of this command.  This
+        command can be called from the command line by using any of
+        these names.
+
+        :return: a list containing the names of this command
+        """
         return ['install']
 
     def getUsage(self):
+        """Return a usage string for this command.
+
+        :return: a usage string for this command
+        """
         return _("PACKAGE...")
 
     def getSummary(self):
+        """Return a one line summary of this command.
+
+        :return: a one line summary of this command
+        """
         return _("Install a package or packages on your system")
     
     def doCheck(self, base, basecmd, extcmds):
+        """Verify that conditions are met so that this command can run.
+        These include that the program is being run by the root user,
+        that there are enabled repositories with gpg keys, and that
+        this command is called with appropriate arguments.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: the command line arguments passed to *basecmd*
+        """
         checkRootUID(base)
         checkGPGKey(base)
         checkPackageArg(base, basecmd, extcmds)
         checkEnabledRepo(base, extcmds)
 
     def doCommand(self, base, basecmd, extcmds):
+        """Execute this command.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: the command line arguments passed to *basecmd*
+        :return: (exit_code, [ errors ])
+
+        exit_code is::
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+            2 = we've got work yet to do, onto the next stage
+        """
         self.doneCommand(base, _("Setting up Install Process"))
         try:
             return base.installPkgs(extcmds)
@@ -216,21 +342,60 @@ class InstallCommand(YumCommand):
             return 1, [str(e)]
 
 class UpdateCommand(YumCommand):
+    """A class containing methods needed by the cli to execute the
+    update command.
+    """
+
     def getNames(self):
+        """Return a list containing the names of this command.  This
+        command can by called from the command line by using any of
+        these names.
+
+        :return: a list containing the names of this command
+        """
         return ['update', 'update-to']
 
     def getUsage(self):
+        """Return a usage string for this command.
+
+        :return: a usage string for this command
+        """
         return _("[PACKAGE...]")
 
     def getSummary(self):
+        """Return a one line summary of this command.
+
+        :return: a one line summary of this command
+        """
         return _("Update a package or packages on your system")
 
     def doCheck(self, base, basecmd, extcmds):
+        """Verify that conditions are met so that this command can run.
+        These include that there are enabled repositories with gpg
+        keys, and that this command is being run by the root user.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: the command line arguments passed to *basecmd*
+        """
         checkRootUID(base)
         checkGPGKey(base)
         checkEnabledRepo(base, extcmds)
 
     def doCommand(self, base, basecmd, extcmds):
+        """Execute this command.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: the command line arguments passed to *basecmd*
+        :return: (exit_code, [ errors ])
+
+        exit_code is::
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+            2 = we've got work yet to do, onto the next stage
+        """
         self.doneCommand(base, _("Setting up Update Process"))
         try:
             return base.updatePkgs(extcmds, update_to=(basecmd == 'update-to'))
@@ -238,21 +403,59 @@ class UpdateCommand(YumCommand):
             return 1, [str(e)]
 
 class DistroSyncCommand(YumCommand):
+    """A class containing methods needed by the cli to execute the
+    distro-synch command.
+    """
+
     def getNames(self):
+        """Return a list containing the names of this command.  This
+        command can be called from the command line by using any of these names.
+
+        :return: a list containing the names of this command
+        """
         return ['distribution-synchronization', 'distro-sync']
 
     def getUsage(self):
+        """Return a usage string for this command.
+
+        :return: a usage string for this command
+        """
         return _("[PACKAGE...]")
 
     def getSummary(self):
+        """Return a one line summary of this command.
+
+        :return: a one line summary of this command
+        """
         return _("Synchronize installed packages to the latest available versions")
 
     def doCheck(self, base, basecmd, extcmds):
+        """Verify that conditions are met so that this command can run.
+        These include that the program is being run by the root user,
+        and that there are enabled repositories with gpg keys.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: the command line arguments passed to *basecmd*
+        """
         checkRootUID(base)
         checkGPGKey(base)
         checkEnabledRepo(base, extcmds)
 
     def doCommand(self, base, basecmd, extcmds):
+        """Execute this command.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: the command line arguments passed to *basecmd*
+        :return: (exit_code, [ errors ])
+
+        exit_code is::
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+            2 = we've got work yet to do, onto the next stage
+        """
         self.doneCommand(base, _("Setting up Distribution Synchronization Process"))
         try:
             base.conf.obsoletes = 1
@@ -289,16 +492,46 @@ def _list_cmd_calc_columns(base, ypl):
     return (-columns[0], -columns[1], -columns[2])
 
 class InfoCommand(YumCommand):
+    """A class containing methods needed by the cli to execute the
+    update command.
+    """
+
     def getNames(self):
+        """Return a list containing the names of this command.  This
+        command can be called from the command line by using any of these names.
+
+        :return: a list containing the names of this command
+        """
         return ['info']
 
     def getUsage(self):
+        """Return a usage string for this command.
+
+        :return: a usage string for this command
+        """
         return "[PACKAGE|all|available|installed|updates|extras|obsoletes|recent]"
 
     def getSummary(self):
+        """Return a one line summary of this command.
+
+        :return: a one line summary of this command
+        """
         return _("Display details about a package or group of packages")
 
     def doCommand(self, base, basecmd, extcmds):
+        """Execute this command.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: the command line arguments passed to *basecmd*
+        :return: (exit_code, [ errors ])
+
+        exit_code is::
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+            2 = we've got work yet to do, onto the next stage
+        """
         try:
             highlight = base.term.MODE['bold']
             ypl = base.returnPkgLists(extcmds, installed_available=highlight)
@@ -389,35 +622,95 @@ class InfoCommand(YumCommand):
             return 0, []
 
     def needTs(self, base, basecmd, extcmds):
+        """Return whether a transaction set must be set up before this
+        command can run.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: a list of arguments passed to *basecmd*
+        :return: True if a transaction set is needed, False otherwise
+        """
         if len(extcmds) and extcmds[0] == 'installed':
             return False
         
         return True
 
 class ListCommand(InfoCommand):
+    """A class containing methods needed by the cli to execute the
+    list command.
+    """
+
     def getNames(self):
+        """Return a list containing the names of this command.  This
+        command can be called from the command line by using any of these names.
+
+        :return: a list containing the names of this command
+        """
         return ['list']
 
     def getSummary(self):
+        """Return a one line summary of this command.
+
+        :return: a one line summary of this command
+        """
         return _("List a package or groups of packages")
 
 
 class EraseCommand(YumCommand):
+    """A class containing methods needed by the cli to execute the
+    erase command.
+    """
+
         
     def getNames(self):
+        """Return a list containing the names of this command.  This
+        command can be called from the command line by using any of these names.
+
+        :return: a list containing the names of this command
+        """
         return ['erase', 'remove']
 
     def getUsage(self):
+        """Return a usage string for this command.
+
+        :return: a usage string for this command
+        """
         return "PACKAGE..."
 
     def getSummary(self):
+        """Return a one line summary of this command.
+
+        :return: a one line summary of this command
+        """
         return _("Remove a package or packages from your system")
 
     def doCheck(self, base, basecmd, extcmds):
+        """Verify that conditions are met so that this command can
+        run.  These include that the program is being run by the root
+        user, and that this command is called with appropriate
+        arguments.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: the command line arguments passed to *basecmd*
+        """
         checkRootUID(base)
         checkPackageArg(base, basecmd, extcmds)
 
     def doCommand(self, base, basecmd, extcmds):
+        """Execute this command.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: the command line arguments passed to *basecmd*
+        :return: (exit_code, [ errors ])
+
+        exit_code is::
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+            2 = we've got work yet to do, onto the next stage
+        """
         self.doneCommand(base, _("Setting up Remove Process"))
         try:
             return base.erasePkgs(extcmds)
@@ -425,9 +718,25 @@ class EraseCommand(YumCommand):
             return 1, [str(e)]
 
     def needTs(self, base, basecmd, extcmds):
+        """Return whether a transaction set must be set up before this
+        command can run.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: a list of arguments passed to *basecmd*
+        :return: True if a transaction set is needed, False otherwise
+        """
         return False
 
     def needTsRemove(self, base, basecmd, extcmds):
+        """Return whether a transaction set for removal only must be
+        set up before this command can run.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: a list of arguments passed to *basecmd*
+        :return: True if a remove-only transaction set is needed, False otherwise
+        """
         return True
 
  
@@ -442,12 +751,25 @@ class GroupsCommand(YumCommand):
                        'groupinfo'    : 'info'}
 
     def getNames(self):
+        """Return a list containing the names of this command.  This
+        command can be called from the command line by using any of these names.
+
+        :return: a list containing the names of this command
+        """
         return ['groups', 'group'] + self.direct_commands.keys()
 
     def getUsage(self):
+        """Return a usage string for this command.
+
+        :return: a usage string for this command
+        """
         return "[list|info|summary|install|upgrade|remove|mark] [GROUP]"
 
     def getSummary(self):
+        """Return a one line summary of this command.
+
+        :return: a one line summary of this command
+        """
         return _("Display, or use, the groups information")
     
     def _grp_setup_doCommand(self, base):
@@ -479,6 +801,14 @@ class GroupsCommand(YumCommand):
         return cmd, extcmds
 
     def doCheck(self, base, basecmd, extcmds):
+        """Verify that conditions are met so that this command can run.
+        The exact conditions checked will vary depending on the
+        subcommand that is being called.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: the command line arguments passed to *basecmd*
+        """
         cmd, extcmds = self._grp_cmd(basecmd, extcmds)
 
         checkEnabledRepo(base)
@@ -505,6 +835,19 @@ class GroupsCommand(YumCommand):
             raise cli.CliError
 
     def doCommand(self, base, basecmd, extcmds):
+        """Execute this command.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: the command line arguments passed to *basecmd*
+        :return: (exit_code, [ errors ])
+
+        exit_code is::
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+            2 = we've got work yet to do, onto the next stage
+        """
         cmd, extcmds = self._grp_cmd(basecmd, extcmds)
 
         self._grp_setup_doCommand(base)
@@ -529,6 +872,14 @@ class GroupsCommand(YumCommand):
 
 
     def needTs(self, base, basecmd, extcmds):
+        """Return whether a transaction set must be set up before this
+        command can run.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: a list of arguments passed to *basecmd*
+        :return: True if a transaction set is needed, False otherwise
+        """
         cmd, extcmds = self._grp_cmd(basecmd, extcmds)
 
         if cmd in ('list', 'info', 'remove', 'summary'):
@@ -536,6 +887,14 @@ class GroupsCommand(YumCommand):
         return True
 
     def needTsRemove(self, base, basecmd, extcmds):
+        """Return whether a transaction set for removal only must be
+        set up before this command can run.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: a list of arguments passed to *basecmd*
+        :return: True if a remove-only transaction set is needed, False otherwise
+        """
         cmd, extcmds = self._grp_cmd(basecmd, extcmds)
 
         if cmd in ('remove',):
@@ -543,20 +902,56 @@ class GroupsCommand(YumCommand):
         return False
 
 class MakeCacheCommand(YumCommand):
+    """A class containing methods needed by the cli to execute the
+    makecache command.
+    """
 
     def getNames(self):
+        """Return a list containing the names of this command.  This
+        command can be called from the command line by using any of these names.
+
+        :return: a list containing the names of this command
+        """
         return ['makecache']
 
     def getUsage(self):
+        """Return a usage string for this command.
+
+        :return: a usage string for this command
+        """
         return ""
 
     def getSummary(self):
+        """Return a one line summary of this command.
+
+        :return: a one line summary of this command
+        """
         return _("Generate the metadata cache")
 
     def doCheck(self, base, basecmd, extcmds):
+        """Verify that conditions are met so that this command can
+        run; namely that there is an enabled repository.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: the command line arguments passed to *basecmd*
+        """
         checkEnabledRepo(base)
 
     def doCommand(self, base, basecmd, extcmds):
+        """Execute this command.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: the command line arguments passed to *basecmd*
+        :return: (exit_code, [ errors ])
+
+        exit_code is::
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+            2 = we've got work yet to do, onto the next stage
+        """
         base.logger.debug(_("Making cache files for all metadata files."))
         base.logger.debug(_("This may take a while depending on the speed of this computer"))
         try:
@@ -582,44 +977,134 @@ class MakeCacheCommand(YumCommand):
         return 0, [_('Metadata Cache Created')]
 
     def needTs(self, base, basecmd, extcmds):
+        """Return whether a transaction set must be set up before this
+        command can run.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: a list of arguments passed to *basecmd*
+        :return: True if a transaction set is needed, False otherwise
+        """
         return False
 
 class CleanCommand(YumCommand):
+    """A class containing methods needed by the cli to execute the
+    clean command.
+    """
     
     def getNames(self):
+        """Return a list containing the names of this command.  This
+        command can be called from the command line by using any of these names.
+
+        :return: a list containing the names of this command
+        """
         return ['clean']
 
     def getUsage(self):
+        """Return a usage string for this command.
+
+        :return: a usage string for this command
+        """
         return "[headers|packages|metadata|dbcache|plugins|expire-cache|all]"
 
     def getSummary(self):
+        """Return a one line summary of this command.
+
+        :return: a one line summary of this command
+        """
         return _("Remove cached data")
 
     def doCheck(self, base, basecmd, extcmds):
+        """Verify that conditions are met so that this command can run.
+        These include that there is at least one enabled repository,
+        and that this command is called with appropriate arguments.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: the command line arguments passed to *basecmd*
+        """
         checkCleanArg(base, basecmd, extcmds)
         checkEnabledRepo(base)
         
     def doCommand(self, base, basecmd, extcmds):
+        """Execute this command.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: the command line arguments passed to *basecmd*
+        :return: (exit_code, [ errors ])
+
+        exit_code is::
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+            2 = we've got work yet to do, onto the next stage
+        """
         base.conf.cache = 1
         return base.cleanCli(extcmds)
 
     def needTs(self, base, basecmd, extcmds):
+        """Return whether a transaction set must be set up before this
+        command can run.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: a list of arguments passed to *basecmd*
+        :return: True if a transaction set is needed, False otherwise
+        """
         return False
 
 class ProvidesCommand(YumCommand):
+    """A class containing methods needed by the cli to execute the
+    provides command.
+    """
+
     def getNames(self):
+        """Return a list containing the names of this command.  This
+        command can be called from the command line by using any of these names.
+
+        :return: a list containing the names of this command
+        """
         return ['provides', 'whatprovides']
 
     def getUsage(self):
+        """Return a usage string for this command.
+
+        :return: a usage string for this command
+        """
         return "SOME_STRING"
     
     def getSummary(self):
+        """Return a one line summary of this command.
+
+        :return: a one line summary of this command
+        """
         return _("Find what package provides the given value")
 
     def doCheck(self, base, basecmd, extcmds):
+        """Verify that conditions are met so that this command can
+        run; namely that this command is called with appropriate arguments.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: the command line arguments passed to *basecmd*
+        """
         checkItemArg(base, basecmd, extcmds)
 
     def doCommand(self, base, basecmd, extcmds):
+        """Execute this command.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: the command line arguments passed to *basecmd*
+        :return: (exit_code, [ errors ])
+
+        exit_code is::
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+            2 = we've got work yet to do, onto the next stage
+        """
         base.logger.debug("Searching Packages: ")
         try:
             return base.provides(extcmds)
@@ -627,19 +1112,56 @@ class ProvidesCommand(YumCommand):
             return 1, [str(e)]
 
 class CheckUpdateCommand(YumCommand):
+    """A class containing methods needed by the cli to execute the
+    update command.
+    """
+
     def getNames(self):
+        """Return a list containing the names of this command.  This
+        command can be called from the command line by using any of these names.
+
+        :return: a list containing the names of this command
+        """
         return ['check-update']
 
     def getUsage(self):
+        """Return a usage string for this command.
+
+        :return: a usage string for this command
+        """
         return "[PACKAGE...]"
 
     def getSummary(self):
+        """Return a one line summary of this command.
+
+        :return: a one line summary of this command
+        """
         return _("Check for available package updates")
 
     def doCheck(self, base, basecmd, extcmds):
+        """Verify that conditions are met so that this command can
+        run; namely that there is at least one enabled repository.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: the command line arguments passed to *basecmd*
+        """
         checkEnabledRepo(base)
 
     def doCommand(self, base, basecmd, extcmds):
+        """Execute this command.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: the command line arguments passed to *basecmd*
+        :return: (exit_code, [ errors ])
+
+        exit_code is::
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+            2 = we've got work yet to do, onto the next stage
+        """
         obscmds = ['obsoletes'] + extcmds
         base.extcmds.insert(0, 'updates')
         result = 0
@@ -681,19 +1203,56 @@ class CheckUpdateCommand(YumCommand):
             return result, []
 
 class SearchCommand(YumCommand):
+    """A class containing methods needed by the cli to execute the
+    search command.
+    """
+
     def getNames(self):
+        """Return a list containing the names of this command.  This
+        command can be called from the command line by using any of these names.
+
+        :return: a list containing the names of this command
+        """
         return ['search']
 
     def getUsage(self):
+        """Return a usage string for this command.
+
+        :return: a usage string for this command
+        """
         return "SOME_STRING"
 
     def getSummary(self):
+        """Return a one line summary of this command.
+
+        :return: a one line summary of this command
+        """
         return _("Search package details for the given string")
 
     def doCheck(self, base, basecmd, extcmds):
+        """Verify that conditions are met so that this command can
+        run; namely that this command is called with appropriate arguments.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: the command line arguments passed to *basecmd*
+        """
         checkItemArg(base, basecmd, extcmds)
 
     def doCommand(self, base, basecmd, extcmds):
+        """Execute this command.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: the command line arguments passed to *basecmd*
+        :return: (exit_code, [ errors ])
+
+        exit_code is::
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+            2 = we've got work yet to do, onto the next stage
+        """
         base.logger.debug(_("Searching Packages: "))
         try:
             return base.search(extcmds)
@@ -701,24 +1260,70 @@ class SearchCommand(YumCommand):
             return 1, [str(e)]
 
     def needTs(self, base, basecmd, extcmds):
+        """Return whether a transaction set must be set up before this
+        command can run.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: a list of arguments passed to *basecmd*
+        :return: True if a transaction set is needed, False otherwise
+        """
         return False
 
 class UpgradeCommand(YumCommand):
+    """A class containing methods needed by the cli to execute the
+    update command.
+    """
+
     def getNames(self):
+        """Return a list containing the names of this command.  This
+        command can be called from the command line by using any of these names.
+
+        :return: a list containing the names of this command
+        """
         return ['upgrade', 'upgrade-to']
 
     def getUsage(self):
+        """Return a usage string for this command.
+
+        :return: a usage string for this command
+        """
         return 'PACKAGE...'
 
     def getSummary(self):
+        """Return a one line summary of this command.
+
+        :return: a one line summary of this command
+        """
         return _("Update packages taking obsoletes into account")
 
     def doCheck(self, base, basecmd, extcmds):
+        """Verify that conditions are met so that this command can
+         run.  These include that the program is being run by the root
+         user, and that there are enabled repositories with gpg.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: the command line arguments passed to *basecmd*
+        """
         checkRootUID(base)
         checkGPGKey(base)
         checkEnabledRepo(base, extcmds)
 
     def doCommand(self, base, basecmd, extcmds):
+        """Execute this command.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: the command line arguments passed to *basecmd*
+        :return: (exit_code, [ errors ])
+
+        exit_code is::
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+            2 = we've got work yet to do, onto the next stage
+        """
         base.conf.obsoletes = 1
         self.doneCommand(base, _("Setting up Upgrade Process"))
         try:
@@ -727,25 +1332,64 @@ class UpgradeCommand(YumCommand):
             return 1, [str(e)]
 
 class LocalInstallCommand(YumCommand):
+    """A class containing methods needed by the cli to execute the
+    localinstall command.
+    """
+
     def __init__(self):
         YumCommand.__init__(self)
         self.hidden = True
 
     def getNames(self):
+        """Return a list containing the names of this command.  This
+        command can be called from the command line by using any of these names.
+
+        :return: a list containing the names of this command
+        """
         return ['localinstall', 'localupdate']
 
     def getUsage(self):
+        """Return a usage string for this command.
+
+        :return: a usage string for this command
+        """
         return "FILE"
 
     def getSummary(self):
+        """Return a one line summary of this command.
+
+        :return: a one line summary of this command
+        """
         return _("Install a local RPM")
 
     def doCheck(self, base, basecmd, extcmds):
+        """Verify that conditions are met so that this command can
+        run.  These include that there are enabled repositories with
+        gpg keys, and that this command is called with appropriate
+        arguments.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: the command line arguments passed to *basecmd*
+        """
         checkRootUID(base)
         checkGPGKey(base)
         checkPackageArg(base, basecmd, extcmds)
         
     def doCommand(self, base, basecmd, extcmds):
+        """Execute this command.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: the command line arguments passed to *basecmd*
+        :return: (exit_code, [ errors ])
+
+        exit_code is:
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+            2 = we've got work yet to do, onto the next stage
+        """
         self.doneCommand(base, _("Setting up Local Package Process"))
 
         updateonly = basecmd == 'localupdate'
@@ -755,19 +1399,57 @@ class LocalInstallCommand(YumCommand):
             return 1, [str(e)]
 
     def needTs(self, base, basecmd, extcmds):
+        """Return whether a transaction set must be set up before this
+        command can run.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: a list of arguments passed to *basecmd*
+        :return: True if a transaction set is needed, False otherwise
+        """
         return False
 
 class ResolveDepCommand(YumCommand):
+    """A class containing methods needed by the cli to execute the
+    resolvedep command.
+    """
+
     def getNames(self):
+        """Return a list containing the names of this command.  This
+        command can be called from the command line by using any of these names.
+
+        :return: a list containing the names of this command
+        """
         return ['resolvedep']
 
     def getUsage(self):
+        """Return a usage string for this command.
+
+        :return: a usage string for this command
+        """
         return "DEPENDENCY"
 
     def getSummary(self):
+        """Return a one line summary of this command.
+
+        :return: a one line summary of this command
+        """
         return _("Determine which package provides the given dependency")
 
     def doCommand(self, base, basecmd, extcmds):
+        """Execute this command.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: the command line arguments passed to *basecmd*
+        :return: (exit_code, [ errors ])
+
+        exit_code is::
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+            2 = we've got work yet to do, onto the next stage
+        """
         base.logger.debug(_("Searching Packages for Dependency:"))
         try:
             return base.resolveDepCli(extcmds)
@@ -775,19 +1457,56 @@ class ResolveDepCommand(YumCommand):
             return 1, [str(e)]
 
 class ShellCommand(YumCommand):
+    """A class containing methods needed by the cli to execute the
+    shell command.
+    """
+
     def getNames(self):
+        """Return a list containing the names of this command.  This
+        command can be called from the command line by using any of these names.
+
+        :return: a list containing the names of this command
+        """
         return ['shell']
 
     def getUsage(self):
+        """Return a usage string for this command.
+
+        :return: a usage string for this command
+        """
         return "[FILENAME]"
 
     def getSummary(self):
+        """Return a one line summary of this command.
+
+        :return: a one line summary of this command
+        """
         return _("Run an interactive yum shell")
 
     def doCheck(self, base, basecmd, extcmds):
+        """Verify that conditions are met so that this command can
+        run; namely that this command is called with appropriate arguments.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: the command line arguments passed to *basecmd*
+        """
         checkShellArg(base, basecmd, extcmds)
 
     def doCommand(self, base, basecmd, extcmds):
+        """Execute this command.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: the command line arguments passed to *basecmd*
+        :return: (exit_code, [ errors ])
+
+        exit_code is::
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+            2 = we've got work yet to do, onto the next stage
+        """
         self.doneCommand(base, _('Setting up Yum Shell'))
         try:
             return base.doShell()
@@ -795,23 +1514,69 @@ class ShellCommand(YumCommand):
             return 1, [str(e)]
 
     def needTs(self, base, basecmd, extcmds):
+        """Return whether a transaction set must be set up before this
+        command can run.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: a list of arguments passed to *basecmd*
+        :return: True if a transaction set is needed, False otherwise
+        """
         return False
 
 
 class DepListCommand(YumCommand):
+    """A class containing methods needed by the cli to execute the
+    deplist command.
+    """
+
     def getNames(self):
+        """Return a list containing the names of this command.  This
+        command can be called from the command line by using any of these names.
+
+        :return: a list containing the names of this command
+        """
         return ['deplist']
 
     def getUsage(self):
+        """Return a usage string for this command.
+
+        :return: a usage string for this command
+        """
         return 'PACKAGE...'
 
     def getSummary(self):
+        """Return a one line summary of this command.
+
+        :return: a one line summary of this command
+        """
         return _("List a package's dependencies")
 
     def doCheck(self, base, basecmd, extcmds):
+        """Verify that conditions are met so that this command can
+        run; namely that this command is called with appropriate
+        arguments.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: the command line arguments passed to *basecmd*
+        """
         checkPackageArg(base, basecmd, extcmds)
 
     def doCommand(self, base, basecmd, extcmds):
+        """Execute this command.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: the command line arguments passed to *basecmd*
+        :return: (exit_code, [ errors ])
+
+        exit_code is::
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+            2 = we've got work yet to do, onto the next stage
+        """
         self.doneCommand(base, _("Finding dependencies: "))
         try:
             return base.deplist(extcmds)
@@ -820,17 +1585,46 @@ class DepListCommand(YumCommand):
 
 
 class RepoListCommand(YumCommand):
+    """A class containing methods needed by the cli to execute the
+    repolist command.
+    """
     
     def getNames(self):
+        """Return a list containing the names of this command.  This
+        command can be called from the command line by using any of these names.
+
+        :return: a list containing the names of this command
+        """
         return ('repolist',)
 
     def getUsage(self):
+        """Return a usage string for this command.
+
+        :return: a usage string for this command
+        """
         return '[all|enabled|disabled]'
 
     def getSummary(self):
+        """Return a one line summary of this command.
+
+        :return: a one line summary of this command
+        """
         return _('Display the configured software repositories')
 
     def doCommand(self, base, basecmd, extcmds):
+        """Execute this command.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: the command line arguments passed to *basecmd*
+        :return: (exit_code, [ errors ])
+
+        exit_code is::
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+            2 = we've got work yet to do, onto the next stage
+        """
         def _repo_size(repo):
             ret = 0
             for pkg in repo.sack.returnPackages():
@@ -1088,21 +1882,54 @@ class RepoListCommand(YumCommand):
         return 0, ['repolist: ' +to_unicode(locale.format("%d", tot_num, True))]
 
     def needTs(self, base, basecmd, extcmds):
+        """Return whether a transaction set must be set up before this
+        command can run.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: a list of arguments passed to *basecmd*
+        :return: True if a transaction set is needed, False otherwise
+        """
         return False
 
 
 class HelpCommand(YumCommand):
+    """A class containing methods needed by the cli to execute the
+    help command.
+    """
+
 
     def getNames(self):
+        """Return a list containing the names of this command.  This
+        command can be called from the command line by using any of these names.
+
+        :return: a list containing the names of this command
+        """
         return ['help']
 
     def getUsage(self):
+        """Return a usage string for this command.
+
+        :return: a usage string for this command
+        """
         return "COMMAND"
 
     def getSummary(self):
+        """Return a one line summary of this command.
+
+        :return: a one line summary of this command
+        """
         return _("Display a helpful usage message")
 
     def doCheck(self, base, basecmd, extcmds):
+        """Verify that conditions are met so that this command can
+        run; namely that this command is called with appropriate
+        arguments.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: the command line arguments passed to *basecmd*
+        """
         if len(extcmds) == 0:
             base.usage()
             raise cli.CliError
@@ -1147,28 +1974,85 @@ class HelpCommand(YumCommand):
         return help_output
 
     def doCommand(self, base, basecmd, extcmds):
+        """Execute this command.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: the command line arguments passed to *basecmd*
+        :return: (exit_code, [ errors ])
+
+        exit_code is::
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+            2 = we've got work yet to do, onto the next stage
+        """
         if extcmds[0] in base.yum_cli_commands:
             command = base.yum_cli_commands[extcmds[0]]
             base.verbose_logger.info(self._makeOutput(command))
         return 0, []
 
     def needTs(self, base, basecmd, extcmds):
+        """Return whether a transaction set must be set up before this
+        command can run.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: a list of arguments passed to *basecmd*
+        :return: True if a transaction set is needed, False otherwise
+        """
         return False
 
 class ReInstallCommand(YumCommand):
+    """A class containing methods needed by the cli to execute the
+    reinstall command.
+    """
+
     def getNames(self):
+        """Return a list containing the names of this command.  This
+        command can be called from the command line by using any of these names.
+
+        :return: a list containing the names of this command
+        """
         return ['reinstall']
 
     def getUsage(self):
+        """Return a usage string for this command.
+
+        :return: a usage string for this command
+        """
         return "PACKAGE..."
 
     def doCheck(self, base, basecmd, extcmds):
+        """Verify that conditions are met so that this command can
+        run.  These include that the program is being run by the root
+        user, that there are enabled repositories with gpg keys, and
+        that this command is called with appropriate arguments.
+
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: the command line arguments passed to *basecmd*
+        """
         checkRootUID(base)
         checkGPGKey(base)
         checkPackageArg(base, basecmd, extcmds)
         checkEnabledRepo(base, extcmds)
 
     def doCommand(self, base, basecmd, extcmds):
+        """Execute this command.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: the command line arguments passed to *basecmd*
+        :return: (exit_code, [ errors ])
+
+        exit_code is::
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+            2 = we've got work yet to do, onto the next stage
+        """
         self.doneCommand(base, _("Setting up Reinstall Process"))
         try:
             return base.reinstallPkgs(extcmds)
@@ -1177,25 +2061,73 @@ class ReInstallCommand(YumCommand):
             return 1, [to_unicode(e)]
 
     def getSummary(self):
+        """Return a one line summary of this command.
+
+        :return: a one line summary of this command
+        """
         return _("reinstall a package")
 
     def needTs(self, base, basecmd, extcmds):
+        """Return whether a transaction set must be set up before this
+        command can run.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: a list of arguments passed to *basecmd*
+        :return: True if a transaction set is needed, False otherwise
+        """
         return False
         
 class DowngradeCommand(YumCommand):
+    """A class containing methods needed by the cli to execute the
+    downgrade command.
+    """
+
     def getNames(self):
+        """Return a list containing the names of this command.  This
+        command can be called from the command line by using any of these names.
+
+        :return: a list containing the names of this command
+        """
         return ['downgrade']
 
     def getUsage(self):
+        """Return a usage string for this command.
+
+        :return: a usage string for this command
+        """
         return "PACKAGE..."
 
     def doCheck(self, base, basecmd, extcmds):
+        """Verify that conditions are met so that this command can
+        run.  These include that the program is being run by the root
+        user, that there are enabled repositories with gpg keys, and
+        that this command is called with appropriate arguments.
+
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: the command line arguments passed to *basecmd*
+        """
         checkRootUID(base)
         checkGPGKey(base)
         checkPackageArg(base, basecmd, extcmds)
         checkEnabledRepo(base, extcmds)
 
     def doCommand(self, base, basecmd, extcmds):
+        """Execute this command.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: the command line arguments passed to *basecmd*
+        :return: (exit_code, [ errors ])
+
+        exit_code is::
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+            2 = we've got work yet to do, onto the next stage
+        """
         self.doneCommand(base, _("Setting up Downgrade Process"))
         try:
             return base.downgradePkgs(extcmds)
@@ -1203,23 +2135,65 @@ class DowngradeCommand(YumCommand):
             return 1, [str(e)]
 
     def getSummary(self):
+        """Return a one line summary of this command.
+
+        :return: a one line summary of this command
+        """
         return _("downgrade a package")
 
     def needTs(self, base, basecmd, extcmds):
+        """Return whether a transaction set must be set up before this
+        command can run.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: a list of arguments passed to *basecmd*
+        :return: True if a transaction set is needed, False otherwise
+        """
         return False
 
 
 class VersionCommand(YumCommand):
+    """A class containing methods needed by the cli to execute the
+    version command.
+    """
+
     def getNames(self):
+        """Return a list containing the names of this command.  This
+        command can be called from the command line by using any of these names.
+
+        :return: a list containing the names of this command
+        """
         return ['version']
 
     def getUsage(self):
+        """Return a usage string for this command.
+
+        :return: a usage string for this command
+        """
         return "[all|installed|available]"
 
     def getSummary(self):
+        """Return a one line summary of this command.
+
+        :return: a one line summary of this command
+        """
         return _("Display a version for the machine and/or available repos.")
 
     def doCommand(self, base, basecmd, extcmds):
+        """Execute this command.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: the command line arguments passed to *basecmd*
+        :return: (exit_code, [ errors ])
+
+        exit_code is::
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+            2 = we've got work yet to do, onto the next stage
+        """
         vcmd = 'installed'
         if extcmds:
             vcmd = extcmds[0]
@@ -1344,6 +2318,14 @@ class VersionCommand(YumCommand):
         return 0, ['version']
 
     def needTs(self, base, basecmd, extcmds):
+        """Return whether a transaction set must be set up before this
+        command can run.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: a list of arguments passed to *basecmd*
+        :return: True if a transaction set is needed, False otherwise
+        """
         vcmd = 'installed'
         if extcmds:
             vcmd = extcmds[0]
@@ -1354,13 +2336,30 @@ class VersionCommand(YumCommand):
 
 
 class HistoryCommand(YumCommand):
+    """A class containing methods needed by the cli to execute the
+    history command.
+    """
+
     def getNames(self):
+        """Return a list containing the names of this command.  This
+        command can be called from the command line by using any of these names.
+
+        :return: a list containing the names of this command
+        """
         return ['history']
 
     def getUsage(self):
+        """Return a usage string for this command.
+
+        :return: a usage string for this command
+        """
         return "[info|list|packages-list|summary|addon-info|redo|undo|rollback|new]"
 
     def getSummary(self):
+        """Return a one line summary of this command.
+
+        :return: a one line summary of this command
+        """
         return _("Display, or use, the transaction history")
 
     def _hcmd_redo(self, base, extcmds):
@@ -1426,12 +2425,54 @@ class HistoryCommand(YumCommand):
     def _hcmd_new(self, base, extcmds):
         base.history._create_db_file()
 
+    def _hcmd_stats(self, base, extcmds):
+        print "File        :", base.history._db_file
+        num = os.stat(base.history._db_file).st_size
+        print "Size        :", locale.format("%d", num, True)
+        counts = base.history._pkg_stats()
+        trans_1 = base.history.old("1")[0]
+        trans_N = base.history.last()
+        print _("Transactions:"), trans_N.tid
+        print _("Begin time  :"), time.ctime(trans_1.beg_timestamp)
+        print _("End time    :"), time.ctime(trans_N.end_timestamp)
+        print _("Counts      :")
+        print _("  NEVRAC :"), locale.format("%6d", counts['nevrac'], True)
+        print _("  NEVRA  :"), locale.format("%6d", counts['nevra'],  True)
+        print _("  NA     :"), locale.format("%6d", counts['na'],     True)
+        print _("  NEVR   :"), locale.format("%6d", counts['nevr'],   True)
+        print _("  rpm DB :"), locale.format("%6d", counts['rpmdb'],  True)
+        print _("  yum DB :"), locale.format("%6d", counts['yumdb'],  True)
+
+    def _hcmd_sync(self, base, extcmds):
+        extcmds = extcmds[1:]
+        if not extcmds:
+            extcmds = None
+        for ipkg in sorted(base.rpmdb.returnPackages(patterns=extcmds)):
+            if base.history.pkg2pid(ipkg, create=False) is None:
+                continue
+
+            print "Syncing rpm/yum DB data for:", ipkg, "...",
+            if base.history.sync_alldb(ipkg):
+                print "Done."
+            else:
+                print "FAILED."
+
     def doCheck(self, base, basecmd, extcmds):
+        """Verify that conditions are met so that this command can
+        run.  The exact conditions checked will vary depending on the
+        subcommand that is being called.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: the command line arguments passed to *basecmd*
+        """
         cmds = ('list', 'info', 'summary', 'repeat', 'redo', 'undo', 'new',
                 'rollback',
                 'addon', 'addon-info',
+                'stats', 'statistics', 'sync', 'synchronize'
                 'pkg', 'pkgs', 'pkg-list', 'pkgs-list',
-                'package', 'package-list', 'packages', 'packages-list')
+                'package', 'package-list', 'packages', 'packages-list',
+                'pkg-info', 'pkgs-info', 'package-info', 'packages-info')
         if extcmds and extcmds[0] not in cmds:
             base.logger.critical(_('Invalid history sub-command, use: %s.'),
                                  ", ".join(cmds))
@@ -1444,6 +2485,19 @@ class HistoryCommand(YumCommand):
             raise cli.CliError
 
     def doCommand(self, base, basecmd, extcmds):
+        """Execute this command.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: the command line arguments passed to *basecmd*
+        :return: (exit_code, [ errors ])
+
+        exit_code is::
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+            2 = we've got work yet to do, onto the next stage
+        """
         vcmd = 'list'
         if extcmds:
             vcmd = extcmds[0]
@@ -1468,12 +2522,26 @@ class HistoryCommand(YumCommand):
             ret = self._hcmd_rollback(base, extcmds)
         elif vcmd == 'new':
             ret = self._hcmd_new(base, extcmds)
+        elif vcmd in ('stats', 'statistics'):
+            ret = self._hcmd_stats(base, extcmds)
+        elif vcmd in ('sync', 'synchronize'):
+            ret = self._hcmd_sync(base, extcmds)
+        elif vcmd in ('pkg-info', 'pkgs-info', 'package-info', 'packages-info'):
+            ret = base.historyPackageInfoCmd(extcmds)
 
         if ret is None:
             return 0, ['history %s' % (vcmd,)]
         return ret
 
     def needTs(self, base, basecmd, extcmds):
+        """Return whether a transaction set must be set up before this
+        command can run.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: a list of arguments passed to *basecmd*
+        :return: True if a transaction set is needed, False otherwise
+        """
         vcmd = 'list'
         if extcmds:
             vcmd = extcmds[0]
@@ -1481,16 +2549,46 @@ class HistoryCommand(YumCommand):
 
 
 class CheckRpmdbCommand(YumCommand):
+    """A class containing methods needed by the cli to execute the
+    check-rpmdb command.
+    """
+
     def getNames(self):
+        """Return a list containing the names of this command.  This
+        command can be called from the command line by using any of these names.
+
+        :return: a list containing the names of this command
+        """
         return ['check', 'check-rpmdb']
 
     def getUsage(self):
+        """Return a usage string for this command.
+
+        :return: a usage string for this command
+        """
         return "[dependencies|duplicates|all]"
 
     def getSummary(self):
+        """Return a one line summary of this command.
+
+        :return: a one line summary of this command
+        """
         return _("Check for problems in the rpmdb")
 
     def doCommand(self, base, basecmd, extcmds):
+        """Execute this command.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: the command line arguments passed to *basecmd*
+        :return: (exit_code, [ errors ])
+
+        exit_code is::
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+            2 = we've got work yet to do, onto the next stage
+        """
         chkcmd = 'all'
         if extcmds:
             chkcmd = extcmds
@@ -1505,19 +2603,57 @@ class CheckRpmdbCommand(YumCommand):
         return rc, ['%s %s' % (basecmd, chkcmd)]
 
     def needTs(self, base, basecmd, extcmds):
+        """Return whether a transaction set must be set up before this
+        command can run.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: a list of arguments passed to *basecmd*
+        :return: True if a transaction set is needed, False otherwise
+        """
         return False
 
 class LoadTransactionCommand(YumCommand):
+    """A class containing methods needed by the cli to execute the
+    load-transaction command.
+    """
+
     def getNames(self):
+        """Return a list containing the names of this command.  This
+        command can be called from the command line by using any of these names.
+
+        :return: a list containing the names of this command
+        """
         return ['load-transaction', 'load-ts']
 
     def getUsage(self):
+        """Return a usage string for this command.
+
+        :return: a usage string for this command
+        """
         return "filename"
 
     def getSummary(self):
+        """Return a one line summary of this command.
+
+        :return: a one line summary of this command
+        """
         return _("load a saved transaction from filename")
 
     def doCommand(self, base, basecmd, extcmds):
+        """Execute this command.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: the command line arguments passed to *basecmd*
+        :return: (exit_code, [ errors ])
+
+        exit_code is::
+
+            0 = we're done, exit
+            1 = we've errored, exit with error string
+            2 = we've got work yet to do, onto the next stage
+        """
         if not extcmds:
             base.logger.critical(_("No saved transaction file specified."))
             raise cli.CliError
@@ -1533,5 +2669,13 @@ class LoadTransactionCommand(YumCommand):
 
 
     def needTs(self, base, basecmd, extcmds):
+        """Return whether a transaction set must be set up before this
+        command can run.
+
+        :param base: a :class:`yum.Yumbase` object
+        :param basecmd: the name of the command
+        :param extcmds: a list of arguments passed to *basecmd*
+        :return: True if a transaction set is needed, False otherwise
+        """
         return True
 
diff --git a/yummain.py b/yummain.py
index 9f79f4f..4b1112a 100755
--- a/yummain.py
+++ b/yummain.py
@@ -29,13 +29,13 @@ from yum import Errors
 from yum import plugins
 from yum import logginglevels
 from yum import _
-from yum.i18n import to_unicode, utf8_width
+from yum.i18n import to_unicode, utf8_width, exception2msg
 import yum.misc
 import cli
-from utils import suppress_keyboard_interrupt_message, show_lock_owner, exception2msg
+from utils import suppress_keyboard_interrupt_message, show_lock_owner
 
 def main(args):
-    """This does all the real work"""
+    """Run the yum program from a command line interface."""
 
     yum.misc.setup_locale(override_time=True)
 
@@ -120,16 +120,16 @@ def main(args):
             if exception2msg(e) != lockerr:
                 lockerr = exception2msg(e)
                 logger.critical(lockerr)
-            if (e.errno not in (errno.EPERM, errno.EACCES) and
-                not base.conf.exit_on_lock):
+            if e.errno in (errno.EPERM, errno.EACCES, errno.ENOSPC):
+                logger.critical(_("Can't create lock file; exiting"))
+                return 1
+
+            if not base.conf.exit_on_lock:
                 logger.critical(_("Another app is currently holding the yum lock; waiting for it to exit..."))
                 tm = 0.1
                 if show_lock_owner(e.pid, logger):
                     tm = 2
                 time.sleep(tm)
-            elif e.errno in (errno.EPERM, errno.EACCES):
-                logger.critical(_("Can't create lock file; exiting"))
-                return 1
             else:
                 logger.critical(_("Another app is currently holding the yum lock; exiting as configured by exit_on_lock"))
                 return 1
@@ -238,9 +238,15 @@ def main(args):
         rpmdb_warn_checks()
         return_code = result
         if base._ts_save_file:
-            verbose_logger.info(_("Your transaction was saved, rerun it with: yum load-transaction %s") % base._ts_save_file)
+            verbose_logger.info(_("Your transaction was saved, rerun it with:\n yum load-transaction %s") % base._ts_save_file)
     elif return_code < 0:
         return_code = 1 # Means the pre-transaction checks failed...
+        #  This includes:
+        # . No packages.
+        # . Hitting N at the prompt.
+        # . GPG check failures.
+        if base._ts_save_file:
+            verbose_logger.info(_("Your transaction was saved, rerun it with:\n yum load-transaction %s") % base._ts_save_file)
     else:
         verbose_logger.log(logginglevels.INFO_2, _('Complete!'))
 
@@ -248,6 +254,11 @@ def main(args):
     return return_code
 
 def hotshot(func, *args, **kwargs):
+    """Profile the given function using the hotshot profiler.
+
+    :param func: the function to profile
+    :return: the return code given by the hotshot profiler
+    """
     import hotshot.stats
     fn = os.path.expanduser("~/yum.prof")
     prof = hotshot.Profile(fn)
@@ -257,6 +268,11 @@ def hotshot(func, *args, **kwargs):
     return rc
 
 def cprof(func, *args, **kwargs):
+    """Profile the given function using the cprof profiler.
+
+    :param func: the function to profile
+    :return: the return code given by the cprof profiler
+    """
     import cProfile, pstats
     fn = os.path.expanduser("~/yum.prof")
     prof = cProfile.Profile()
@@ -266,6 +282,10 @@ def cprof(func, *args, **kwargs):
     return rc
 
 def print_stats(stats):
+    """Print out information from a :class:`Stats` object.
+
+    :param stats: the :class:`Stats` object to print information from
+    """
     stats.strip_dirs()
     stats.sort_stats('time', 'calls')
     stats.print_stats(20)
@@ -273,7 +293,14 @@ def print_stats(stats):
     stats.print_stats(40)
 
 def user_main(args, exit_code=False):
-    """ This calls one of the multiple main() functions based on env. vars """
+    """Call one of the multiple main() functions based on environment variables.
+
+    :param args: command line arguments passed into yum
+    :param exit_code: if *exit_code* is True, this function will exit
+       python with its exit code when it has finished executing.
+       Otherwise, it will return its exit code.
+    :return: the exit code from yum execution
+    """
     errcode = None
     if 'YUM_PROF' in os.environ:
         if os.environ['YUM_PROF'] == 'cprof':