Update to 2.7.3
[profile/ivi/python.git] / Tools / scripts / pdeps.py
1 #! /usr/bin/env python
2
3 # pdeps
4 #
5 # Find dependencies between a bunch of Python modules.
6 #
7 # Usage:
8 #       pdeps file1.py file2.py ...
9 #
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
23 import sys
24 import re
25 import os
26
27
28 # Main program
29 #
30 def main():
31     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
56
57
58 # Compiled regular expressions to search for import statements
59 #
60 m_import = re.compile('^[ \t]*from[ \t]+([^ \t]+)[ \t]+')
61 m_from = re.compile('^[ \t]*import[ \t]+([^#]+)')
62
63
64 # Collect data from one file
65 #
66 def process(filename, table):
67     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
84         words = line[a1:b1].split(',')
85         # print '#', line, words
86         for word in words:
87             word = word.strip()
88             if word not in list:
89                 list.append(word)
90
91
92 # Compute closure (this is in fact totally general)
93 #
94 def closure(table):
95     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
117
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 #
123 def inverse(table):
124     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
131
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 #
137 def store(dict, key, item):
138     if dict.has_key(key):
139         dict[key].append(item)
140     else:
141         dict[key] = [item]
142
143
144 # Tabulate results neatly
145 #
146 def printresults(table):
147     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()
154         print mod.ljust(maxlen), ':',
155         if mod in list:
156             print '(*)',
157         for ref in list:
158             print ref,
159         print
160
161
162 # Call main and honor exit status
163 if __name__ == '__main__':
164     try:
165         sys.exit(main())
166     except KeyboardInterrupt:
167         sys.exit(1)