blob: 18b582b693e06924842f1c048f3872389decd7e6 [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 +000026import string
27
28
29# Main program
30#
31def main():
Tim Peters70c43782001-01-17 08:48:39 +000032 args = sys.argv[1:]
33 if not args:
34 print 'usage: pdeps file.py file.py ...'
35 return 2
36 #
37 table = {}
38 for arg in args:
39 process(arg, table)
40 #
41 print '--- Uses ---'
42 printresults(table)
43 #
44 print '--- Used By ---'
45 inv = inverse(table)
46 printresults(inv)
47 #
48 print '--- Closure of Uses ---'
49 reach = closure(table)
50 printresults(reach)
51 #
52 print '--- Closure of Used By ---'
53 invreach = inverse(reach)
54 printresults(invreach)
55 #
56 return 0
Guido van Rossumec758ea1991-06-04 20:36:54 +000057
58
59# Compiled regular expressions to search for import statements
60#
Guido van Rossum15f27fb1992-12-10 00:00:58 +000061m_import = regex.compile('^[ \t]*from[ \t]+\([^ \t]+\)[ \t]+')
62m_from = regex.compile('^[ \t]*import[ \t]+\([^#]+\)')
Guido van Rossumec758ea1991-06-04 20:36:54 +000063
64
65# Collect data from one file
66#
67def process(filename, table):
Tim Peters70c43782001-01-17 08:48:39 +000068 fp = open(filename, 'r')
69 mod = os.path.basename(filename)
70 if mod[-3:] == '.py':
71 mod = mod[:-3]
72 table[mod] = list = []
73 while 1:
74 line = fp.readline()
75 if not line: break
76 while line[-1:] == '\\':
77 nextline = fp.readline()
78 if not nextline: break
79 line = line[:-1] + nextline
80 if m_import.match(line) >= 0:
81 (a, b), (a1, b1) = m_import.regs[:2]
82 elif m_from.match(line) >= 0:
83 (a, b), (a1, b1) = m_from.regs[:2]
84 else: continue
85 words = string.splitfields(line[a1:b1], ',')
86 # print '#', line, words
87 for word in words:
88 word = string.strip(word)
89 if word not in list:
90 list.append(word)
Guido van Rossumec758ea1991-06-04 20:36:54 +000091
92
93# Compute closure (this is in fact totally general)
94#
95def closure(table):
Tim Peters70c43782001-01-17 08:48:39 +000096 modules = table.keys()
97 #
98 # Initialize reach with a copy of table
99 #
100 reach = {}
101 for mod in modules:
102 reach[mod] = table[mod][:]
103 #
104 # Iterate until no more change
105 #
106 change = 1
107 while change:
108 change = 0
109 for mod in modules:
110 for mo in reach[mod]:
111 if mo in modules:
112 for m in reach[mo]:
113 if m not in reach[mod]:
114 reach[mod].append(m)
115 change = 1
116 #
117 return reach
Guido van Rossumec758ea1991-06-04 20:36:54 +0000118
119
120# Invert a table (this is again totally general).
121# All keys of the original table are made keys of the inverse,
122# so there may be empty lists in the inverse.
123#
124def inverse(table):
Tim Peters70c43782001-01-17 08:48:39 +0000125 inv = {}
126 for key in table.keys():
127 if not inv.has_key(key):
128 inv[key] = []
129 for item in table[key]:
130 store(inv, item, key)
131 return inv
Guido van Rossumec758ea1991-06-04 20:36:54 +0000132
133
134# Store "item" in "dict" under "key".
135# The dictionary maps keys to lists of items.
136# If there is no list for the key yet, it is created.
137#
138def store(dict, key, item):
Tim Peters70c43782001-01-17 08:48:39 +0000139 if dict.has_key(key):
140 dict[key].append(item)
141 else:
142 dict[key] = [item]
Guido van Rossumec758ea1991-06-04 20:36:54 +0000143
144
145# Tabulate results neatly
146#
147def printresults(table):
Tim Peters70c43782001-01-17 08:48:39 +0000148 modules = table.keys()
149 maxlen = 0
150 for mod in modules: maxlen = max(maxlen, len(mod))
151 modules.sort()
152 for mod in modules:
153 list = table[mod]
154 list.sort()
155 print string.ljust(mod, maxlen), ':',
156 if mod in list:
157 print '(*)',
158 for ref in list:
159 print ref,
160 print
Guido van Rossumec758ea1991-06-04 20:36:54 +0000161
162
163# Call main and honor exit status
164try:
Tim Peters70c43782001-01-17 08:48:39 +0000165 sys.exit(main())
Guido van Rossumec758ea1991-06-04 20:36:54 +0000166except KeyboardInterrupt:
Tim Peters70c43782001-01-17 08:48:39 +0000167 sys.exit(1)