I found the original code to be nearly incomprehensible. Instead of populating a dict of potential binpkgs to remove and then removing from the to-be-removed list, just selectively add to-be-removed packages.
Signed-off-by: Matt Turner <matts...@gentoo.org> --- I switched from tabs to spaces in the process. I can revert back if desired. pym/gentoolkit/eclean/search.py | 189 ++++++++++++++++---------------- 1 file changed, 94 insertions(+), 95 deletions(-) diff --git a/pym/gentoolkit/eclean/search.py b/pym/gentoolkit/eclean/search.py index 58bd97e..831ba39 100644 --- a/pym/gentoolkit/eclean/search.py +++ b/pym/gentoolkit/eclean/search.py @@ -489,98 +489,97 @@ class DistfilesSearch(object): def findPackages( - options, - exclude=None, - destructive=False, - time_limit=0, - package_names=False, - pkgdir=None, - port_dbapi=portage.db[portage.root]["porttree"].dbapi, - var_dbapi=portage.db[portage.root]["vartree"].dbapi - ): - """Find all obsolete binary packages. - - XXX: packages are found only by symlinks. - Maybe i should also return .tbz2 files from All/ that have - no corresponding symlinks. - - @param options: dict of options determined at runtime - @param exclude: an exclusion dict as defined in - exclude.parseExcludeFile class. - @param destructive: boolean, defaults to False - @param time_limit: integer time value as returned by parseTime() - @param package_names: boolean, defaults to False. - used only if destructive=True - @param pkgdir: path to the binary package dir being checked - @param port_dbapi: defaults to portage.db[portage.root]["porttree"].dbapi - can be overridden for tests. - @param var_dbapi: defaults to portage.db[portage.root]["vartree"].dbapi - can be overridden for tests. - - @rtype: dict - @return clean_me i.e. {'cat/pkg-ver.tbz2': [filepath],} - """ - if exclude is None: - exclude = {} - clean_me = {} - # create a full package dictionary - - # now do an access test, os.walk does not error for "no read permission" - try: - test = os.listdir(pkgdir) - del test - except EnvironmentError as er: - if options['ignore-failure']: - exit(0) - print( pp.error("Error accessing PKGDIR." ), file=sys.stderr) - print( pp.error("(Check your make.conf file and environment)."), file=sys.stderr) - print( pp.error("Error: %s" %str(er)), file=sys.stderr) - exit(1) - - # if portage supports FEATURES=binpkg-multi-instance, then - # cpv_all can return multiple instances per cpv, where - # instances are distinguishable by some extra attributes - # provided by portage's _pkg_str class - bin_dbapi = portage.binarytree(pkgdir=pkgdir, settings=var_dbapi.settings).dbapi - for cpv in bin_dbapi.cpv_all(): - mtime = int(bin_dbapi.aux_get(cpv, ['_mtime_'])[0]) - if time_limit and mtime >= time_limit: - # time-limit exclusion - continue - # dict is cpv->[pkgs] (supports binpkg-multi-instance) - clean_me.setdefault(cpv, []).append(cpv) - - # keep only obsolete ones - if destructive and package_names: - cp_all = dict.fromkeys(var_dbapi.cp_all()) - else: - cp_all = {} - for cpv in list(clean_me): - if exclDictMatchCP(exclude,portage.cpv_getkey(cpv)): - # exclusion because of the exclude file - del clean_me[cpv] - continue - if not destructive and port_dbapi.cpv_exists(cpv): - # exclusion because pkg still exists (in porttree) - del clean_me[cpv] - continue - if destructive and var_dbapi.cpv_exists(cpv): - buildtime = var_dbapi.aux_get(cpv, ['BUILD_TIME'])[0] - clean_me[cpv] = [pkg for pkg in clean_me[cpv] - # only keep path if BUILD_TIME is identical with vartree - if bin_dbapi.aux_get(pkg, ['BUILD_TIME'])[0] != buildtime] - if not clean_me[cpv]: - # nothing we can clean for this package - del clean_me[cpv] - continue - if portage.cpv_getkey(cpv) in cp_all and port_dbapi.cpv_exists(cpv): - # exclusion because of --package-names - del clean_me[cpv] - - # the getname method correctly supports FEATURES=binpkg-multi-instance, - # allowing for multiple paths per cpv (the API used here is also compatible - # with older portage which does not support binpkg-multi-instance) - for cpv, pkgs in clean_me.items(): - clean_me[cpv] = [bin_dbapi.bintree.getname(pkg) for pkg in pkgs] - - return clean_me + options, + exclude=None, + destructive=False, + time_limit=0, + package_names=False, + pkgdir=None, + port_dbapi=portage.db[portage.root]["porttree"].dbapi, + var_dbapi=portage.db[portage.root]["vartree"].dbapi + ): + """Find obsolete binary packages. + + @param options: dict of options determined at runtime + @type options: dict + @param exclude: exclusion dict (as defined in the exclude.parseExcludeFile class) + @type exclude: dict, optional + @param destructive: binpkg is obsolete if not installed (default: `False`) + @type destructive: bool, optional + @param time_limit: binpkg is obsolete if older than time value as returned by parseTime() + @type time_limit: int, optional + @param package_names: exclude all binpkg versions if package is installed + (used with `destructive=True`) (default: `False`) + @type package_names: bool, optional + @param pkgdir: path to the binpkg cache (PKGDIR) + @type pkgdir: str + @param port_dbapi: defaults to portage.db[portage.root]["porttree"].dbapi + Can be overridden for tests. + @param var_dbapi: defaults to portage.db[portage.root]["vartree"].dbapi + Can be overridden for tests. + + @return binary packages to remove. e.g. {'cat/pkg-ver': [filepath]} + @rtype: dict + """ + if exclude is None: + exclude = {} + + # Access test, os.walk does not error for "no read permission" + try: + test = os.listdir(pkgdir) + del test + except EnvironmentError as er: + if options['ignore-failure']: + exit(0) + print(pp.error("Error accessing PKGDIR."), file=sys.stderr) + print(pp.error("(Check your make.conf file and environment)."), file=sys.stderr) + print(pp.error("Error: %s" % str(er)), file=sys.stderr) + exit(1) + + # Create a dictionary of all installed packages + if destructive and package_names: + installed = dict.fromkeys(var_dbapi.cp_all()) + else: + installed = {} + + # Dictionary of binary packages to clean. Organized as cpv->[pkgs] in order + # to support FEATURES=binpkg-multi-instance. + dead_binpkgs = {} + + # Create a dictionary of all binary packages whose mtime is older than + # time_limit, if set. These packages will be considered for removal. + bin_dbapi = portage.binarytree(pkgdir=pkgdir, settings=var_dbapi.settings).dbapi + for cpv in bin_dbapi.cpv_all(): + cp = portage.cpv_getkey(cpv) + + # Exclude per --exclude-file=... + if exclDictMatchCP(exclude, cp): + continue + + # Exclude if binpkg is obsolete per --time-limit=... + if time_limit: + mtime = int(bin_dbapi.aux_get(cpv, ['_mtime_'])) + if mtime >= time_limit: + continue + + # Exclude if binpkg exists in the porttree and not --deep + if not destructive and port_dbapi.cpv_exists(cpv): + continue + + if destructive and var_dbapi.cpv_exists(cpv): + # Exclude if an instance of the package is installed due to + # the --package-names option. + if cp in installed and port_dbapi.cpv_exists(cpv): + continue + + # Exclude if BUILD_TIME of binpkg is same as vartree + buildtime = var_dbapi.aux_get(cpv, ['BUILD_TIME'])[0] + if buildtime == bin_dbapi.aux_get(cpv, ['BUILD_TIME'])[0]: + continue + + binpkg_path = bin_dbapi.bintree.getname(cpv) + dead_binpkgs.setdefault(cpv, []).append(binpkg_path) + + return dead_binpkgs + +# vim: set ts=4 sw=4 tw=79: -- 2.24.1