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