[Yum-devel] [PATCH] add the functionality of remove_with_leaves to core

Seth Vidal skvidal at fedoraproject.org
Fri Nov 5 21:41:34 UTC 2010


- works much better than remove_with_leaves
- better output
- config makes more sense: clean_requirements_on_remove = BoolOption(False)
- uses yumdb.reason to drive it - dep-installs only
- testing and opt required
---
 test/testbase.py |    5 ++++
 yum/__init__.py  |   58 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
 yum/config.py    |    1 +
 yum/depsolve.py  |    5 ++-
 yum/rpmsack.py   |   28 ++++++++++++++++++++++++-
 5 files changed, 92 insertions(+), 5 deletions(-)

diff --git a/test/testbase.py b/test/testbase.py
index 81591ad..67b3638 100644
--- a/test/testbase.py
+++ b/test/testbase.py
@@ -51,6 +51,7 @@ class FakeConf(object):
         self.uid = 0
         self.groupremove_leaf_only = False
         self.protected_packages = []
+        self.clean_requirements_on_remove = True
 
 class FakeSack:
     """ Fake PackageSack to use with FakeRepository"""
@@ -116,6 +117,10 @@ class FakePackage(packages.YumAvailablePackage):
         self.prco['obsoletes'].append((name, flag, evr))
     def addFile(self, name, ftype='file'):
         self.files[ftype].append(name)
+    def required_packages(self):
+        return []
+    def requiring_packages(self):
+        return []
 
 class _Container(object):
     pass
diff --git a/yum/__init__.py b/yum/__init__.py
index 234b599..9dd40c2 100644
--- a/yum/__init__.py
+++ b/yum/__init__.py
@@ -934,7 +934,10 @@ class YumBase(depsolve.Depsolve):
 
         (rescode, restring) = self.resolveDeps()
         self._limit_installonly_pkgs()
-        
+        # if enabled clean up requirments when removing the things which brought them in.
+        if self.conf.clean_requirements_on_remove:
+            self.verbose_logger.log(logginglevels.INFO_2, _('--> Finding unneeded leftover dependencies'))
+            self._remove_old_deps()
         #  We _must_ get rid of all the used tses before we go on, so that C-c
         # works for downloads / mirror failover etc.
         kern_pkgtup = None
@@ -5001,3 +5004,56 @@ class YumBase(depsolve.Depsolve):
                 raise Errors.YumBaseError(msg)
             
         return self.tsInfo.getMembers()
+
+    def _remove_old_deps(self):
+        """take the set of pkgs being removed and remove any pkgs which are:
+           1. not required anymore
+           2. marked as a 'dep' in the 'reason' in the yumdb. """
+        found_leaves = set()
+        checked = set()
+        beingremoved = [ t.po for t in self.tsInfo.getMembersWithState(output_states=TS_REMOVE_STATES) ]
+        for pkg in beingremoved: # for each package required by the pkg being removed
+            #print 'removal: %s' % pkg.name
+            for required in pkg.required_packages():
+                #if required in checked:
+                #    continue # if we've already checked it, skip it.
+                #checked.add(required)
+                if required.yumdb_info.get('reason', '') != 'dep': # if the required pkg is not a dep, then skip it
+                    continue
+                if required in beingremoved:
+                    continue
+                still_needed = False
+                for requiring in required.requiring_packages(): # so we have required deps - look at all the pkgs which require them
+                    if requiring == required: # if they are self-requiring skip them
+                        continue
+                    if requiring not in beingremoved: # if the requiring pkg is not in the list of pkgs being removed then it is still needed
+                        still_needed = True
+                        break
+                # go through the stuff in the ts to be installed - make sure none of that needs the required pkg, either.
+                for (provn,provf,provevr) in required.provides:
+                    if self.tsInfo.getNewRequires(provn, provf, provevr).keys():
+                        still_needed = True
+                        break
+                for fn in required.filelist + required.dirlist:
+                    if self.tsInfo.getNewRequires(fn, None,(None,None,None)).keys():
+                        still_needed = True
+                        break
+                            
+                    #for tbi_pkg in self.tsInfo.getMembersWithState(output_states=TS_INSTALL_STATES):
+                    #   for reqtuple in tbi_pkg.po.requires:
+                    #        if required.provides_for(reqtuple):
+                    #            still_needed = True
+                    #            break
+                
+                if not still_needed:
+                    print '---> Marking %s to be removed - no longer needed by %s' % (required.name, pkg.name)
+                    txmbrs = self.remove(po=required)
+
+                    for txmbr in txmbrs:
+                        txmbr.setAsDep(po=pkg)
+                        if txmbr.po not in beingremoved:
+                            beingremoved.append(txmbr.po)
+                        found_leaves.add(txmbr)
+        self.verbose_logger.log(logginglevels.INFO_2, "Found and removing %s unneeded dependencies" % len(found_leaves))
+        
+                    
diff --git a/yum/config.py b/yum/config.py
index 2f133ef..aecef44 100644
--- a/yum/config.py
+++ b/yum/config.py
@@ -743,6 +743,7 @@ class YumConf(StartupConf):
     loadts_ignoremissing = BoolOption(False)
     loadts_ignorerpm = BoolOption(False)
     
+    clean_requirements_on_remove = BoolOption(False)
     _reposlist = []
 
     def dump(self):
diff --git a/yum/depsolve.py b/yum/depsolve.py
index 3cc7a7f..b0d1e80 100644
--- a/yum/depsolve.py
+++ b/yum/depsolve.py
@@ -407,8 +407,9 @@ class Depsolve(object):
         if needmode in ['e']:
             self.verbose_logger.log(logginglevels.DEBUG_2, _('TSINFO: %s package requiring %s marked as erase'),
                 requiringPo, needname)
-            txmbr = self.tsInfo.addErase(requiringPo)
-            txmbr.setAsDep(po=inst_po)
+            txmbrs = self.remove(po=requiringPo)
+            for txmbr in txmbrs:
+                txmbr.setAsDep(po=inst_po)
             checkdeps = 1
         
         if needmode in ['i', 'u']:
diff --git a/yum/rpmsack.py b/yum/rpmsack.py
index 346decc..e227729 100644
--- a/yum/rpmsack.py
+++ b/yum/rpmsack.py
@@ -78,8 +78,32 @@ class RPMInstalledPackage(YumInstalledPackage):
                                # Also note that pkg.no_value raises KeyError.
 
         return val
+    
+    def requiring_packages(self):
+        """return list of installed pkgs requiring this package"""
+        pkgset = set()
+        for (reqn, reqf, reqevr) in self.provides:
+            for pkg in self.rpmdb.getRequires(reqn,reqf,reqevr):
+                if pkg != self:
+                    pkgset.add(pkg)
+                
+        for fn in self.filelist + self.dirlist:
+            for pkg in self.rpmdb.getRequires(fn, None, (None, None, None)):
+                if pkg != self:
+                    pkgset.add(pkg)
+                
+        return list(pkgset)
+        
 
-
+    def required_packages(self):
+        pkgset = set()
+        for (reqn, reqf, reqevr) in self.requires:
+            for pkg in self.rpmdb.getProvides(reqn, reqf, reqevr):
+                if pkg != self:
+                    pkgset.add(pkg)
+        
+        return list(pkgset)
+        
 class RPMDBProblem:
     '''
     Represents a problem in the rpmdb, from the check_*() functions.
-- 
1.7.2.1



More information about the Yum-devel mailing list