debdu

measure debian package disk space usage
git clone https://a3nm.net/git/debdu/
Log | Files | Refs

debdu.py (2102B)


      1 #!/usr/bin/python3
      2 
      3 import apt_pkg
      4 
      5 apt_pkg.init()
      6 cache = apt_pkg.Cache(None)
      7 depcache = apt_pkg.DepCache(cache)
      8 
      9 graph = {}
     10 roots = []
     11 results = []
     12 
     13 def init(p):
     14   if p not in graph.keys():
     15     graph[p] = {}
     16     graph[p]['n_in'] = 0
     17 
     18 def dfs(v):
     19   if 'full' in graph[v].keys():
     20     return graph[v]['full']
     21   graph[v]['full'] = 0 # TODO this marking is wrong for cycles, think about it
     22   s = graph[v]['size']
     23   expl = [(s, 'the package itself', True, 1)]
     24   for d in graph[v]['deps']:
     25     #print "%s -> %s" % (v, d)
     26     dfs(d)
     27     if graph[d]['auto']:
     28       s += graph[d]['full'] / graph[d]['n_in']
     29       expl.append((graph[d]['full']/graph[d]['n_in'], d,
     30         graph[d]['auto'], graph[d]['n_in']))
     31     else:
     32       expl.append((0, d, graph[d]['auto'], 1))
     33   graph[v]['full'] = s
     34   expl = sorted(expl, reverse=True)
     35   if not graph[v]['auto']:
     36     results.append((s, v, expl))
     37 
     38 for package in cache.packages:
     39   if package.current_state != apt_pkg.CURSTATE_INSTALLED:
     40     continue
     41   version = package.current_ver
     42   name = package.name
     43   #print name
     44   auto = depcache.is_auto_installed(package)
     45   init(name)
     46   graph[name]['auto'] = auto
     47   if not auto:
     48     roots.append(name)
     49   graph[name]['size'] = version.installed_size
     50   graph[name]['deps'] = set()
     51   if 'Depends' not in version.depends_list_str.keys():
     52     continue
     53   for dep in version.depends_list_str['Depends']:
     54     # TODO of course this is wrong...
     55     for or_dep in dep:
     56       try:
     57         if cache[or_dep[0]].current_state != apt_pkg.CURSTATE_INSTALLED:
     58           continue
     59       except KeyError:
     60         continue # Wrong wrong wrong!
     61       graph[name]['deps'].add(or_dep[0])
     62       init(or_dep[0])
     63       graph[or_dep[0]]['n_in'] += 1
     64 
     65 for root in roots:
     66   dfs(root)
     67 
     68 for result in sorted(results, reverse=True):
     69   print (str(result[0]) + ' ' + result[1])
     70   for e in result[2]:
     71     # TODO shared should be between non-descendant packages
     72     print ('  %d from %s%s' % (e[0], e[1],
     73         (" shared between "+str(e[3])+" package(s)" if e[3] != 1 else '') if
     74         e[2] else ' manually installed and accounted in a separate entry'))
     75   print()
     76