Skip to content

Instantly share code, notes, and snippets.

@ajvpot
Created June 17, 2016 19:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ajvpot/cdec7bdc5f1da6d82da65a0257f19db2 to your computer and use it in GitHub Desktop.
Save ajvpot/cdec7bdc5f1da6d82da65a0257f19db2 to your computer and use it in GitHub Desktop.
from re import match, split
from subprocess import check_output
from debian.deb822 import Deb822
from graph_tool.all import *
graph = Graph()
package_nodes = {}
package_info = {}
def get_package_list():
"""Return a list of packages, both installed and uninstalled."""
output = check_output(['dpkg', '-l', '*'])
packages = []
for line in output.split('\n'):
parts = line.split()
if not parts or not match('[uirph][nicurhWt]', parts[0]):
continue
packages.append(parts[1])
return packages
def get_package_info(pkg_name):
"""Get a dict-like object containing information for a specified package."""
global package_info
if pkg_name in package_info:
return package_info.get(pkg_name)
else:
try:
yaml_stream = check_output(['apt-cache', 'show', pkg_name])
except:
print "Unable to find info for package: '%s'" % pkg_name
package_info[pkg_name] = {}
return {}
d = Deb822(yaml_stream)
package_info[pkg_name] = d
return d
def get_graph_node_for_package(pkg_name):
"""Given a package name, return the graph node for that package. If the graph
node does not exist, it is created, and it's meta-data filled.
"""
global graph
global package_nodes
if pkg_name not in package_nodes:
n = graph.add_vertex()
package_nodes[pkg_name] = n
# add node properties:
pkg_info = get_package_info(pkg_name)
graph.vertex_properties["package-name"][n] = pkg_name
graph.vertex_properties["installed-size"][n] = int(pkg_info.get('Installed-Size', 0))
return n
else:
return package_nodes.get(pkg_name)
def get_sanitised_depends_list(depends_string):
"""Given a Depends string, return a list of package names. Versions are
stripped, and alternatives are all shown.
"""
if depends_string == '':
return []
parts = split('[,\|]', depends_string)
return [match('(\S*)', p.strip()).groups()[0] for p in parts]
if __name__ == '__main__':
# Create property lists we need:
graph.vertex_properties["package-name"] = graph.new_vertex_property("string")
graph.vertex_properties["installed-size"] = graph.new_vertex_property("int")
# get list of packages:
packages = get_package_list()
n = 0
for pkg_name in packages:
node = get_graph_node_for_package(pkg_name)
pkg_info = get_package_info(pkg_name)
# purely virtual packages won't have a package info object:
if pkg_info:
depends = pkg_info.get('Depends', '')
depends = get_sanitised_depends_list(depends)
for dependancy in depends:
graph.add_edge(node, get_graph_node_for_package(dependancy))
n += 1
if n % 10 == 0:
print "%d / /%d" % (n, len(packages))
graph.save('graph.gml')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment