#!/usr/bin/python # version 0.04 import sys import os import imp import itertools from distutils import sysconfig sitedirs = [] for libdir in [sysconfig.get_python_lib(0), sysconfig.get_python_lib(1)]: if libdir in sitedirs: continue sitedirs.append(libdir) def inpath(f, path): res = False for lib in path: if f.find("%s%s" % (lib, os.path.sep)) != -1: res = True break return res def majormod(modname): return modname.split('.')[0] def minormod(modname): if modname.find('.') == -1: return modname else: return modname.split('.')[1] def isprivate(modname): return (majormod(modname).startswith('_') or minormod(modname).startswith('_')) def validimport(line): xx = {} yy = {} valid = False try: exec(line) valid = True except ImportError: valid = True except: pass return valid spaths = sorted(sys.path, key=len, reverse=True) def path2mod(path): # Convert a pathname to a module path if possible max = None # Find the longest entry in sys.path that contains the file for lib in spaths: if path.startswith(lib): # Will %buildroot interfere here? -IVA max = len(lib) break else: return # Carve up the path to get the module or package name xx = path[max+1:] xx = os.path.splitext(xx)[0] if xx.endswith("__init__"): # This is a package xx = os.path.split(xx)[0] # TODO: Verify that we're in a valid package mod = xx.replace(os.path.sep, '.') return mod def read_filelist(): return list( itertools.ifilterfalse(os.path.isdir, itertools.imap(str.strip, sys.stdin) ) ) def find_python(files): pyfiles = {} interp = "#!/usr/bin/python" # figure out which of our files are readable python - we're parsing source # line by line so only .py files or scripts with #! will do for f in files: isPy = False (n, ext) = os.path.splitext(f) if ext in ['.pyc', '.pyo']: continue if ext in ['.py']: isPy = True else: try: s = os.stat(f) t = open(f) except IOError: continue if t.read(len(interp)) == interp and (s[0] & 0111): isPy = True t.close() if isPy: modname = os.path.basename(n) pyfiles[f] = modname return pyfiles def find_privates(files): privates = {} # find out module paths and private modules for f in files: if inpath(f, sitedirs): continue (n, ext) = os.path.splitext(f) modname = os.path.basename(n) if privates.has_key(modname): continue if ext in ['.py', '.pyc', '.pyo']: privates[modname] = f return privates def parse_imports(files): imports = {} for f in files: py = open(f) for line in py.readlines(): # strip out comments and other uninteresting parts l = line.rstrip() l = l.split('#')[0] l = l.split(' as ')[0] # in theory one could write "import A; import B" but... l = l.split(';')[0] # For global unconditional imports, check line syntax # and if ok then parse into major module names. We don't want # submodules because they're impossible to distinguish from # other symbols within modules if l.startswith('import'): if not validimport(l): continue mods = l[7:].split(',') for m in mods: mod = m.strip() if isprivate(mod): continue imports[mod] = f elif l.startswith('from'): if not validimport(l): continue mod = l.split()[1] if isprivate(mod): continue imports[mod] = f py.close() return imports def find_module_provides(files): provides = {} for path in files: # if it's python itself, extract abi version if path.startswith("/usr/bin/python"): provides["abi"] = "%d.%d" % sys.version_info[:2] # we're only interested in site-packages contents (f, e) = os.path.splitext(path) if not inpath(path, sitedirs) or not e or '.egg-info/' in path: continue mod = path2mod(path) if mod == None or isprivate(mod): continue else: provides[mod] = path return provides def find_provides(files): # main provide lookup provides = {} modprovides = find_module_provides(files) for mod in modprovides.keys(): major = mod if major.startswith('_'): continue # I'm NOT convinced this is correct -IVA provides[major] = None return provides def find_requires(files): # main require lookup requires = {} pyfiles = find_python(files) imports = parse_imports(pyfiles.keys()) mods_private = find_privates(pyfiles.keys()) mods_public = find_module_provides(pyfiles.keys()) for name in imports.keys(): # ignore private modules if mods_private.has_key(name): continue # self provides if mods_public.has_key(name): requires[name] = None else: submod = False for m in mods_public.keys(): if name == minormod(m): submod = True break if submod: continue # everything else we just try... try: syspath = ['/usr/lib64/python'] (f, path, desc) = imp.find_module(name) # if in site-packages, add dependency, otherwise it's # part of stdlib or internal if inpath(path, sitedirs): requires[name] = None except ImportError: # can't find it, gotta be external dependency requires[name] = None # add abi requirement if pyfiles: requires["abi"] = "%d.%d" % sys.version_info[:2] return requires if __name__ == '__main__': filelist = read_filelist() if sys.argv[1] in ['-R', '--requires']: deps = find_requires(filelist) elif sys.argv[1] in ['-P', '--provides']: deps = find_provides(filelist) else: print "Usage: %s -R|-P" % sys.argv[0] sys.exit(1) depnames = deps.keys() depnames.sort() for dep in depnames: if deps[dep] == None: print "python(%s)" % (dep) else: print "python(%s) = %s" % (dep, deps[dep])