| 
									
										
										
										
											2010-03-11 22:53:45 +00:00
										 |  |  | #! /usr/bin/env python3 | 
					
						
							| 
									
										
										
										
											1991-06-04 20:36:54 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | # pdeps | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # Find dependencies between a bunch of Python modules. | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # Usage: | 
					
						
							| 
									
										
										
										
											2001-01-17 08:48:39 +00:00
										 |  |  | #       pdeps file1.py file2.py ... | 
					
						
							| 
									
										
										
										
											1991-06-04 20:36:54 +00:00
										 |  |  | # | 
					
						
							|  |  |  | # Output: | 
					
						
							|  |  |  | # Four tables separated by lines like '--- Closure ---': | 
					
						
							|  |  |  | # 1) Direct dependencies, listing which module imports which other modules | 
					
						
							|  |  |  | # 2) The inverse of (1) | 
					
						
							|  |  |  | # 3) Indirect dependencies, or the closure of the above | 
					
						
							|  |  |  | # 4) The inverse of (3) | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # To do: | 
					
						
							|  |  |  | # - command line options to select output type | 
					
						
							|  |  |  | # - option to automatically scan the Python library for referenced modules | 
					
						
							|  |  |  | # - option to limit output to particular modules | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import sys | 
					
						
							| 
									
										
										
										
											2006-04-21 10:40:58 +00:00
										 |  |  | import re | 
					
						
							| 
									
										
										
										
											1992-12-10 00:00:58 +00:00
										 |  |  | import os | 
					
						
							| 
									
										
										
										
											1991-06-04 20:36:54 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Main program | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | def main(): | 
					
						
							| 
									
										
										
										
											2001-01-17 08:48:39 +00:00
										 |  |  |     args = sys.argv[1:] | 
					
						
							|  |  |  |     if not args: | 
					
						
							| 
									
										
										
										
											2007-08-03 17:06:41 +00:00
										 |  |  |         print('usage: pdeps file.py file.py ...') | 
					
						
							| 
									
										
										
										
											2001-01-17 08:48:39 +00:00
										 |  |  |         return 2 | 
					
						
							|  |  |  |     # | 
					
						
							|  |  |  |     table = {} | 
					
						
							|  |  |  |     for arg in args: | 
					
						
							|  |  |  |         process(arg, table) | 
					
						
							|  |  |  |     # | 
					
						
							| 
									
										
										
										
											2007-08-03 17:06:41 +00:00
										 |  |  |     print('--- Uses ---') | 
					
						
							| 
									
										
										
										
											2001-01-17 08:48:39 +00:00
										 |  |  |     printresults(table) | 
					
						
							|  |  |  |     # | 
					
						
							| 
									
										
										
										
											2007-08-03 17:06:41 +00:00
										 |  |  |     print('--- Used By ---') | 
					
						
							| 
									
										
										
										
											2001-01-17 08:48:39 +00:00
										 |  |  |     inv = inverse(table) | 
					
						
							|  |  |  |     printresults(inv) | 
					
						
							|  |  |  |     # | 
					
						
							| 
									
										
										
										
											2007-08-03 17:06:41 +00:00
										 |  |  |     print('--- Closure of Uses ---') | 
					
						
							| 
									
										
										
										
											2001-01-17 08:48:39 +00:00
										 |  |  |     reach = closure(table) | 
					
						
							|  |  |  |     printresults(reach) | 
					
						
							|  |  |  |     # | 
					
						
							| 
									
										
										
										
											2007-08-03 17:06:41 +00:00
										 |  |  |     print('--- Closure of Used By ---') | 
					
						
							| 
									
										
										
										
											2001-01-17 08:48:39 +00:00
										 |  |  |     invreach = inverse(reach) | 
					
						
							|  |  |  |     printresults(invreach) | 
					
						
							|  |  |  |     # | 
					
						
							|  |  |  |     return 0 | 
					
						
							| 
									
										
										
										
											1991-06-04 20:36:54 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Compiled regular expressions to search for import statements | 
					
						
							|  |  |  | # | 
					
						
							| 
									
										
										
										
											2006-04-21 10:40:58 +00:00
										 |  |  | m_import = re.compile('^[ \t]*from[ \t]+([^ \t]+)[ \t]+') | 
					
						
							|  |  |  | m_from = re.compile('^[ \t]*import[ \t]+([^#]+)') | 
					
						
							| 
									
										
										
										
											1991-06-04 20:36:54 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Collect data from one file | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | def process(filename, table): | 
					
						
							| 
									
										
										
										
											2001-01-17 08:48:39 +00:00
										 |  |  |     fp = open(filename, 'r') | 
					
						
							|  |  |  |     mod = os.path.basename(filename) | 
					
						
							|  |  |  |     if mod[-3:] == '.py': | 
					
						
							|  |  |  |         mod = mod[:-3] | 
					
						
							|  |  |  |     table[mod] = list = [] | 
					
						
							|  |  |  |     while 1: | 
					
						
							|  |  |  |         line = fp.readline() | 
					
						
							|  |  |  |         if not line: break | 
					
						
							|  |  |  |         while line[-1:] == '\\': | 
					
						
							|  |  |  |             nextline = fp.readline() | 
					
						
							|  |  |  |             if not nextline: break | 
					
						
							|  |  |  |             line = line[:-1] + nextline | 
					
						
							|  |  |  |         if m_import.match(line) >= 0: | 
					
						
							|  |  |  |             (a, b), (a1, b1) = m_import.regs[:2] | 
					
						
							|  |  |  |         elif m_from.match(line) >= 0: | 
					
						
							|  |  |  |             (a, b), (a1, b1) = m_from.regs[:2] | 
					
						
							|  |  |  |         else: continue | 
					
						
							| 
									
										
										
										
											2002-09-11 20:36:02 +00:00
										 |  |  |         words = line[a1:b1].split(',') | 
					
						
							| 
									
										
										
										
											2001-01-17 08:48:39 +00:00
										 |  |  |         # print '#', line, words | 
					
						
							|  |  |  |         for word in words: | 
					
						
							| 
									
										
										
										
											2002-09-11 20:36:02 +00:00
										 |  |  |             word = word.strip() | 
					
						
							| 
									
										
										
										
											2001-01-17 08:48:39 +00:00
										 |  |  |             if word not in list: | 
					
						
							|  |  |  |                 list.append(word) | 
					
						
							| 
									
										
										
										
											1991-06-04 20:36:54 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Compute closure (this is in fact totally general) | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | def closure(table): | 
					
						
							| 
									
										
										
										
											2008-05-16 15:23:30 +00:00
										 |  |  |     modules = list(table.keys()) | 
					
						
							| 
									
										
										
										
											2001-01-17 08:48:39 +00:00
										 |  |  |     # | 
					
						
							|  |  |  |     # Initialize reach with a copy of table | 
					
						
							|  |  |  |     # | 
					
						
							|  |  |  |     reach = {} | 
					
						
							|  |  |  |     for mod in modules: | 
					
						
							|  |  |  |         reach[mod] = table[mod][:] | 
					
						
							|  |  |  |     # | 
					
						
							|  |  |  |     # Iterate until no more change | 
					
						
							|  |  |  |     # | 
					
						
							|  |  |  |     change = 1 | 
					
						
							|  |  |  |     while change: | 
					
						
							|  |  |  |         change = 0 | 
					
						
							|  |  |  |         for mod in modules: | 
					
						
							|  |  |  |             for mo in reach[mod]: | 
					
						
							|  |  |  |                 if mo in modules: | 
					
						
							|  |  |  |                     for m in reach[mo]: | 
					
						
							|  |  |  |                         if m not in reach[mod]: | 
					
						
							|  |  |  |                             reach[mod].append(m) | 
					
						
							|  |  |  |                             change = 1 | 
					
						
							|  |  |  |     # | 
					
						
							|  |  |  |     return reach | 
					
						
							| 
									
										
										
										
											1991-06-04 20:36:54 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Invert a table (this is again totally general). | 
					
						
							|  |  |  | # All keys of the original table are made keys of the inverse, | 
					
						
							|  |  |  | # so there may be empty lists in the inverse. | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | def inverse(table): | 
					
						
							| 
									
										
										
										
											2001-01-17 08:48:39 +00:00
										 |  |  |     inv = {} | 
					
						
							|  |  |  |     for key in table.keys(): | 
					
						
							|  |  |  |         if not inv.has_key(key): | 
					
						
							|  |  |  |             inv[key] = [] | 
					
						
							|  |  |  |         for item in table[key]: | 
					
						
							|  |  |  |             store(inv, item, key) | 
					
						
							|  |  |  |     return inv | 
					
						
							| 
									
										
										
										
											1991-06-04 20:36:54 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Store "item" in "dict" under "key". | 
					
						
							|  |  |  | # The dictionary maps keys to lists of items. | 
					
						
							|  |  |  | # If there is no list for the key yet, it is created. | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | def store(dict, key, item): | 
					
						
							| 
									
										
										
										
											2008-05-16 15:23:30 +00:00
										 |  |  |     if key in dict: | 
					
						
							| 
									
										
										
										
											2001-01-17 08:48:39 +00:00
										 |  |  |         dict[key].append(item) | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         dict[key] = [item] | 
					
						
							| 
									
										
										
										
											1991-06-04 20:36:54 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Tabulate results neatly | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | def printresults(table): | 
					
						
							| 
									
										
										
										
											2008-05-16 15:23:30 +00:00
										 |  |  |     modules = sorted(table.keys()) | 
					
						
							| 
									
										
										
										
											2001-01-17 08:48:39 +00:00
										 |  |  |     maxlen = 0 | 
					
						
							|  |  |  |     for mod in modules: maxlen = max(maxlen, len(mod)) | 
					
						
							|  |  |  |     for mod in modules: | 
					
						
							| 
									
										
										
										
											2008-05-16 15:23:30 +00:00
										 |  |  |         list = sorted(table[mod]) | 
					
						
							| 
									
										
										
										
											2007-08-03 17:06:41 +00:00
										 |  |  |         print(mod.ljust(maxlen), ':', end=' ') | 
					
						
							| 
									
										
										
										
											2001-01-17 08:48:39 +00:00
										 |  |  |         if mod in list: | 
					
						
							| 
									
										
										
										
											2007-08-03 17:06:41 +00:00
										 |  |  |             print('(*)', end=' ') | 
					
						
							| 
									
										
										
										
											2001-01-17 08:48:39 +00:00
										 |  |  |         for ref in list: | 
					
						
							| 
									
										
										
										
											2007-08-03 17:06:41 +00:00
										 |  |  |             print(ref, end=' ') | 
					
						
							|  |  |  |         print() | 
					
						
							| 
									
										
										
										
											1991-06-04 20:36:54 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Call main and honor exit status | 
					
						
							| 
									
										
										
										
											2004-08-09 17:27:55 +00:00
										 |  |  | if __name__ == '__main__': | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         sys.exit(main()) | 
					
						
							|  |  |  |     except KeyboardInterrupt: | 
					
						
							|  |  |  |         sys.exit(1) |