[Python-modules-commits] r6957 - in tools (find_python_dependencies.py)

morph at users.alioth.debian.org morph at users.alioth.debian.org
Mon Nov 24 20:19:58 UTC 2008


    Date: Monday, November 24, 2008 @ 20:19:58
  Author: morph
Revision: 6957

tool to help identify what depends are needed

Added:
  tools/find_python_dependencies.py

Added: tools/find_python_dependencies.py
===================================================================
--- tools/find_python_dependencies.py	                        (rev 0)
+++ tools/find_python_dependencies.py	2008-11-24 20:19:58 UTC (rev 6957)
@@ -0,0 +1,163 @@
+#!/usr/bin/python
+#
+# Copyright (c) 2008 Sandro Tosi <morph at debian.org>
+# License: Public Domain
+#
+#
+# This tool is written to help Debian Python Modules/Apps Teams
+# packagers identify what debian packages are needed to execute the
+# tool being packaged.
+#
+# It archives it by identify the import statements and then by
+# searching on the installed packages which one contains the Python
+# file of the module.
+#
+# NOTE: the biggest wickness is that it can identify only modules for
+# installed packages; to help you still identify the needed modules,
+# it prints the list of not verified modules.
+#
+# TODO:
+#  * add support for __import__ function (even if it's done only on
+#    some rare cases), it's just another check while parsing AST
+#  * it's not PEP8 complient (pardon me...)
+#  * better formatting of output:
+#    - instead of print <module>: (<deb pkg>, # of occurrence) it
+#      might be better something like <deb pkg>: ((<mod1>,<mod2>,...)
+#      sum of # of occurrence)
+#  * possible package suggestions for unidentified modules, like check
+#    if exists a debpkg name "python-<module>"
+#  * fix all the other tons of bugs I made here and there :)
+
+
+# http://docs.python.org/library/compiler.html
+# deprecated in 2.6, removed in 3.0, but we are still on 2.5...
+import compiler
+# using it to type checking the object in the AST
+from compiler.ast import Import, From
+import sys
+import glob
+import os
+import dircache
+import stat
+import subprocess
+
+class ImportNotFound(Exception):
+    """Errors in importing a module"""
+    pass
+
+class ImportParseError(Exception):
+    """Errors accessing module information"""
+    pass
+
+def convertImportToDebianPkg(imp):
+    """Tries to identify the Debian package from the module name"""
+    try:
+        # import the module, mapping it to 'mod'
+        mod = __import__(imp)
+        # accessing the __file__ attribute
+        file = mod.__file__
+        # adjust the extension
+        file = file.replace('.pyc','.py')
+        # obtain the mode to know if it's a symlink
+        # lstat doesn't follow symlinks
+        mode = os.lstat(file)[stat.ST_MODE]
+        if stat.S_ISLNK(mode):
+            # if it's a symlink, follow it
+            file = os.readlink(file)
+        # exec dpkg -S to obtain the package containing file
+        proc = subprocess.Popen("dpkg -S " + file + " | awk -F':' '{ print $1 }'",
+                                shell=True,
+                                stdout=subprocess.PIPE,
+                                stderr=subprocess.PIPE,
+                                )
+        # get (stdout, stderr) tuple
+        output = proc.communicate()
+        # if no stderr
+        if not output[1]:
+            pkg = output[0].replace('\n','')
+        # else, raise exp
+        else:
+            raise ImportNotFound()
+        return pkg
+    except ImportError, e:
+        # module not found
+        print "E: ImportError while checking %s; exception: %s" % (imp,str(e))
+        raise ImportNotFound()
+    except Exception, e:
+        # __file__ attribute doesn't exist or any other error
+        print "E: error while checking %s; exception: %s" % (imp,str(e))
+        raise ImportParseError()
+
+# find all py files in a given directory
+# thanks to recipe 2.19 from Python Cookbook
+def find_py_files_in_dir(path):
+    # pattern matching any case of "py" extension
+    for match in glob.glob(os.path.join(path, "*.[Pp][Yy]")):
+        yield match
+
+# find all py files in a given directory, then go recursing subdirs
+def find_py_files_in_dir_recursive(path):
+    # check first in the dir passed as parameter
+    for match in find_py_files_in_dir(path):
+        yield match
+    # dircache output is sorted and cached
+    # let's join path and item, since files list
+    # returned from listdir has path stripped off
+    for subpath in [os.path.join(path, item) for item in dircache.listdir(path)]:
+        # if it's a dir, then go recursive on it
+        if os.path.isdir(subpath):
+            # yield every item found in the recursive call!
+            for subfile in find_py_files_in_dir_recursive(subpath):
+                yield subfile
+
+def parseFileImport(file):
+    """Parses file's syntax tree to extract import statements"""
+    try:
+        # creates the syntax tree
+        mod = compiler.parseFile(file)
+
+        nodes = mod.node.nodes
+
+        for node in nodes:
+            if isinstance(node,Import):
+                for name, alias in node.names:
+                    yield name
+            if isinstance(node,From):
+                yield node.modname
+    except Exception, e:
+        print "Error parsing " + file + "; exception: " + str(e)
+
+
+def addValuesToDict(dict, key, value):
+    """Adds value to dict[key], or add the item if missing"""
+    if key in dict:
+        dict[key] += value
+    else:
+        dict[key] = value
+
+# main
+
+import_dict = {}
+
+mod_pkgs = {}
+mod_not_found = {}
+
+# main file parse loop
+for file in find_py_files_in_dir_recursive(sys.argv[1]):
+    for imp in parseFileImport(file):
+        addValuesToDict(import_dict, imp, 1)
+
+# loop to identify the deb pkg containg each module, or add to discards list
+for module, count in import_dict.iteritems():
+    try:
+        pkg = convertImportToDebianPkg(module)
+        mod_pkgs[module] = (pkg, count)
+    except ImportNotFound:
+        mod_not_found[module] = ('module not found on this machine', count)
+    except ImportParseError:
+        mod_not_found[module] = ('error parsing module', count)
+
+# temporary output printing
+import pprint
+pprint.pprint(mod_pkgs)
+pprint.pprint(mod_not_found)


Property changes on: tools/find_python_dependencies.py
___________________________________________________________________
Name: svn:executable
   + *




More information about the Python-modules-commits mailing list