blob: e835f848cbba90a5bcbb524640529e79923b5871 [file] [log] [blame]
Guido van Rossumf06ee5f1996-11-27 19:52:01 +00001#! /usr/bin/env python
Guido van Rossumec758ea1991-06-04 20:36:54 +00002
3# pdeps
4#
5# Find dependencies between a bunch of Python modules.
6#
7# Usage:
Tim Peters70c43782001-01-17 08:48:39 +00008# pdeps file1.py file2.py ...
Guido van Rossumec758ea1991-06-04 20:36:54 +00009#
10# Output:
11# Four tables separated by lines like '--- Closure ---':
12# 1) Direct dependencies, listing which module imports which other modules
13# 2) The inverse of (1)
14# 3) Indirect dependencies, or the closure of the above
15# 4) The inverse of (3)
16#
17# To do:
18# - command line options to select output type
19# - option to automatically scan the Python library for referenced modules
20# - option to limit output to particular modules
21
22
23import sys
Guido van Rossum15f27fb1992-12-10 00:00:58 +000024import regex
25import os
Guido van Rossumec758ea1991-06-04 20:36:54 +000026
27
28# Main program
29#
30def main():
Tim Peters70c43782001-01-17 08:48:39 +000031 args = sys.argv[1:]
32 if not args:
33 print 'usage: pdeps file.py file.py ...'
34 return 2
35 #
36 table = {}
37 for arg in args:
38 process(arg, table)
39 #
40 print '--- Uses ---'
41 printresults(table)
42 #
43 print '--- Used By ---'
44 inv = inverse(table)
45 printresults(inv)
46 #
47 print '--- Closure of Uses ---'
48 reach = closure(table)
49 printresults(reach)
50 #
51 print '--- Closure of Used By ---'
52 invreach = inverse(reach)
53 printresults(invreach)
54 #
55 return 0
Guido van Rossumec758ea1991-06-04 20:36:54 +000056
57
58# Compiled regular expressions to search for import statements
59#
Guido van Rossum15f27fb1992-12-10 00:00:58 +000060m_import = regex.compile('^[ \t]*from[ \t]+\([^ \t]+\)[ \t]+')
61m_from = regex.compile('^[ \t]*import[ \t]+\([^#]+\)')
Guido van Rossumec758ea1991-06-04 20:36:54 +000062
63
64# Collect data from one file
65#
66def process(filename, table):
Tim Peters70c43782001-01-17 08:48:39 +000067 fp = open(filename, 'r')
68 mod = os.path.basename(filename)
69 if mod[-3:] == '.py':
70 mod = mod[:-3]
71 table[mod] = list = []
72 while 1:
73 line = fp.readline()
74 if not line: break
75 while line[-1:] == '\\':
76 nextline = fp.readline()
77 if not nextline: break
78 line = line[:-1] + nextline
79 if m_import.match(line) >= 0:
80 (a, b), (a1, b1) = m_import.regs[:2]
81 elif m_from.match(line) >= 0:
82 (a, b), (a1, b1) = m_from.regs[:2]
83 else: continue
Walter Dörwaldaaab30e2002-09-11 20:36:02 +000084 words = line[a1:b1].split(',')
Tim Peters70c43782001-01-17 08:48:39 +000085 # print '#', line, words
86 for word in words:
Walter Dörwaldaaab30e2002-09-11 20:36:02 +000087 word = word.strip()
Tim Peters70c43782001-01-17 08:48:39 +000088 if word not in list:
89 list.append(word)
Guido van Rossumec758ea1991-06-04 20:36:54 +000090
91
92# Compute closure (this is in fact totally general)
93#
94def closure(table):
Tim Peters70c43782001-01-17 08:48:39 +000095 modules = table.keys()
96 #
97 # Initialize reach with a copy of table
98 #
99 reach = {}
100 for mod in modules:
101 reach[mod] = table[mod][:]
102 #
103 # Iterate until no more change
104 #
105 change = 1
106 while change:
107 change = 0
108 for mod in modules:
109 for mo in reach[mod]:
110 if mo in modules:
111 for m in reach[mo]:
112 if m not in reach[mod]:
113 reach[mod].append(m)
114 change = 1
115 #
116 return reach
Guido van Rossumec758ea1991-06-04 20:36:54 +0000117
118
119# Invert a table (this is again totally general).
120# All keys of the original table are made keys of the inverse,
121# so there may be empty lists in the inverse.
122#
123def inverse(table):
Tim Peters70c43782001-01-17 08:48:39 +0000124 inv = {}
125 for key in table.keys():
126 if not inv.has_key(key):
127 inv[key] = []
128 for item in table[key]:
129 store(inv, item, key)
130 return inv
Guido van Rossumec758ea1991-06-04 20:36:54 +0000131
132
133# Store "item" in "dict" under "key".
134# The dictionary maps keys to lists of items.
135# If there is no list for the key yet, it is created.
136#
137def store(dict, key, item):
Tim Peters70c43782001-01-17 08:48:39 +0000138 if dict.has_key(key):
139 dict[key].append(item)
140 else:
141 dict[key] = [item]
Guido van Rossumec758ea1991-06-04 20:36:54 +0000142
143
144# Tabulate results neatly
145#
146def printresults(table):
Tim Peters70c43782001-01-17 08:48:39 +0000147 modules = table.keys()
148 maxlen = 0
149 for mod in modules: maxlen = max(maxlen, len(mod))
150 modules.sort()
151 for mod in modules:
152 list = table[mod]
153 list.sort()
Walter Dörwaldaaab30e2002-09-11 20:36:02 +0000154 print mod.ljust(maxlen), ':',
Tim Peters70c43782001-01-17 08:48:39 +0000155 if mod in list:
156 print '(*)',
157 for ref in list:
158 print ref,
159 print
Guido van Rossumec758ea1991-06-04 20:36:54 +0000160
161
162# Call main and honor exit status
Andrew M. Kuchlinge236b382004-08-09 17:27:55 +0000163if __name__ == '__main__':
164 try:
165 sys.exit(main())
166 except KeyboardInterrupt:
167 sys.exit(1)