blob: 5041574aa31572e011a7e41d55c55e3de80a64ea [file] [log] [blame]
Guido van Rossum00ff4331994-10-03 16:33:08 +00001# Determine the names and filenames of the modules imported by a
2# script, recursively. This is done by scanning for lines containing
3# import statements. (The scanning has only superficial knowledge of
4# Python syntax and no knowledge of semantics, so in theory the result
5# may be incorrect -- however this is quite unlikely if you don't
6# intentionally obscure your Python code.)
7
8import os
9import regex
10import string
11import sys
12
13
14# Top-level interface.
15# First argument is the main program (script).
16# Second optional argument is list of modules to be searched as well.
17
18def findmodules(scriptfile, modules = [], path = sys.path):
19 todo = {}
20 todo['__main__'] = scriptfile
21 for name in modules:
22 mod = os.path.basename(name)
23 if mod[-3:] == '.py': mod = mod[:-3]
24 todo[mod] = name
25 done = closure(todo)
26 return done
27
28
29# Compute the closure of scanfile() and findmodule().
30# Return a dictionary mapping module names to filenames.
31# Writes to stderr if a file can't be or read.
32
33def closure(todo):
34 done = {}
35 while todo:
36 newtodo = {}
37 for modname in todo.keys():
38 if not done.has_key(modname):
39 filename = todo[modname]
40 if filename is None:
41 filename = findmodule(modname)
42 done[modname] = filename
43 if filename in ('<builtin>', '<unknown>'):
44 continue
45 try:
46 modules = scanfile(filename)
47 except IOError, msg:
48 sys.stderr.write("%s: %s\n" %
49 (filename, str(msg)))
50 continue
51 for m in modules:
52 if not done.has_key(m):
53 newtodo[m] = None
54 todo = newtodo
55 return done
56
57
58# Scan a file looking for import statements.
59# Return list of module names.
60# Can raise IOError.
61
62importstr = '\(^\|:\)[ \t]*import[ \t]+\([a-zA-Z0-9_, \t]+\)'
63fromstr = '\(^\|:\)[ \t]*from[ \t]+\([a-zA-Z0-9_]+\)[ \t]+import[ \t]+'
64isimport = regex.compile(importstr)
65isfrom = regex.compile(fromstr)
66
67def scanfile(filename):
68 allmodules = {}
69 f = open(filename, 'r')
70 try:
71 while 1:
72 line = f.readline()
73 if not line: break # EOF
74 while line[-2:] == '\\\n': # Continuation line
75 line = line[:-2] + ' '
76 line = line + f.readline()
77 if isimport.search(line) >= 0:
78 rawmodules = isimport.group(2)
79 modules = string.splitfields(rawmodules, ',')
80 for i in range(len(modules)):
81 modules[i] = string.strip(modules[i])
82 elif isfrom.search(line) >= 0:
83 modules = [isfrom.group(2)]
84 else:
85 continue
86 for mod in modules:
87 allmodules[mod] = None
88 finally:
89 f.close()
90 return allmodules.keys()
91
92
93# Find the file containing a module, given its name.
94# Return filename, or '<builtin>', or '<unknown>'.
95
96builtins = sys.builtin_module_names
97if 'sys' not in builtins: builtins.append('sys')
Guido van Rossum01181341994-10-03 16:43:15 +000098tails = ['.py', '.pyc']
Guido van Rossum00ff4331994-10-03 16:33:08 +000099
100def findmodule(modname, path = sys.path):
101 if modname in builtins: return '<builtin>'
102 for dirname in path:
103 for tail in tails:
104 fullname = os.path.join(dirname, modname + tail)
105 try:
106 f = open(fullname, 'r')
107 except IOError:
108 continue
109 f.close()
110 return fullname
111 return '<unknown>'
112
113
114# Test the above functions.
115
116def test():
117 if not sys.argv[1:]:
118 print 'usage: python findmodules.py scriptfile [morefiles ...]'
119 sys.exit(2)
120 done = findmodules(sys.argv[1], sys.argv[2:])
121 items = done.items()
122 items.sort()
123 for mod, file in [('Module', 'File')] + items:
124 print "%-15s %s" % (mod, file)
125
126if __name__ == '__main__':
127 test()