128 lines
3.2 KiB
Python
128 lines
3.2 KiB
Python
# Determine the names and filenames of the modules imported by a
|
|
# script, recursively. This is done by scanning for lines containing
|
|
# import statements. (The scanning has only superficial knowledge of
|
|
# Python syntax and no knowledge of semantics, so in theory the result
|
|
# may be incorrect -- however this is quite unlikely if you don't
|
|
# intentionally obscure your Python code.)
|
|
|
|
import os
|
|
import regex
|
|
import string
|
|
import sys
|
|
|
|
|
|
# Top-level interface.
|
|
# First argument is the main program (script).
|
|
# Second optional argument is list of modules to be searched as well.
|
|
|
|
def findmodules(scriptfile, modules = [], path = sys.path):
|
|
todo = {}
|
|
todo['__main__'] = scriptfile
|
|
for name in modules:
|
|
mod = os.path.basename(name)
|
|
if mod[-3:] == '.py': mod = mod[:-3]
|
|
elif mod[-4:] == '.pyc': mod = mod[:-4]
|
|
todo[mod] = name
|
|
done = closure(todo)
|
|
return done
|
|
|
|
|
|
# Compute the closure of scanfile() and findmodule().
|
|
# Return a dictionary mapping module names to filenames.
|
|
# Writes to stderr if a file can't be or read.
|
|
|
|
def closure(todo):
|
|
done = {}
|
|
while todo:
|
|
newtodo = {}
|
|
for modname in todo.keys():
|
|
if not done.has_key(modname):
|
|
filename = todo[modname]
|
|
if filename is None:
|
|
filename = findmodule(modname)
|
|
done[modname] = filename
|
|
if filename in ('<builtin>', '<unknown>'):
|
|
continue
|
|
try:
|
|
modules = scanfile(filename)
|
|
except IOError, msg:
|
|
sys.stderr.write("%s: %s\n" %
|
|
(filename, str(msg)))
|
|
continue
|
|
for m in modules:
|
|
if not done.has_key(m):
|
|
newtodo[m] = None
|
|
todo = newtodo
|
|
return done
|
|
|
|
|
|
# Scan a file looking for import statements.
|
|
# Return list of module names.
|
|
# Can raise IOError.
|
|
|
|
importstr = '\(^\|:\)[ \t]*import[ \t]+\([a-zA-Z0-9_, \t]+\)'
|
|
fromstr = '\(^\|:\)[ \t]*from[ \t]+\([a-zA-Z0-9_]+\)[ \t]+import[ \t]+'
|
|
isimport = regex.compile(importstr)
|
|
isfrom = regex.compile(fromstr)
|
|
|
|
def scanfile(filename):
|
|
allmodules = {}
|
|
f = open(filename, 'r')
|
|
try:
|
|
while 1:
|
|
line = f.readline()
|
|
if not line: break # EOF
|
|
while line[-2:] == '\\\n': # Continuation line
|
|
line = line[:-2] + ' '
|
|
line = line + f.readline()
|
|
if isimport.search(line) >= 0:
|
|
rawmodules = isimport.group(2)
|
|
modules = string.splitfields(rawmodules, ',')
|
|
for i in range(len(modules)):
|
|
modules[i] = string.strip(modules[i])
|
|
elif isfrom.search(line) >= 0:
|
|
modules = [isfrom.group(2)]
|
|
else:
|
|
continue
|
|
for mod in modules:
|
|
allmodules[mod] = None
|
|
finally:
|
|
f.close()
|
|
return allmodules.keys()
|
|
|
|
|
|
# Find the file containing a module, given its name.
|
|
# Return filename, or '<builtin>', or '<unknown>'.
|
|
|
|
builtins = sys.builtin_module_names
|
|
tails = ['.py', '.pyc']
|
|
|
|
def findmodule(modname, path = sys.path):
|
|
if modname in builtins: return '<builtin>'
|
|
for dirname in path:
|
|
for tail in tails:
|
|
fullname = os.path.join(dirname, modname + tail)
|
|
try:
|
|
f = open(fullname, 'r')
|
|
except IOError:
|
|
continue
|
|
f.close()
|
|
return fullname
|
|
return '<unknown>'
|
|
|
|
|
|
# Test the above functions.
|
|
|
|
def test():
|
|
if not sys.argv[1:]:
|
|
print 'usage: python findmodules.py scriptfile [morefiles ...]'
|
|
sys.exit(2)
|
|
done = findmodules(sys.argv[1], sys.argv[2:])
|
|
items = done.items()
|
|
items.sort()
|
|
for mod, file in [('Module', 'File')] + items:
|
|
print "%-15s %s" % (mod, file)
|
|
|
|
if __name__ == '__main__':
|
|
test()
|