/Tools/scripts/pdeps.py

http://unladen-swallow.googlecode.com/ · Python · 167 lines · 96 code · 19 blank · 52 comment · 31 complexity · effdbd5d0c43249ba34f702696d0858c MD5 · raw file

  1. #! /usr/bin/env python
  2. # pdeps
  3. #
  4. # Find dependencies between a bunch of Python modules.
  5. #
  6. # Usage:
  7. # pdeps file1.py file2.py ...
  8. #
  9. # Output:
  10. # Four tables separated by lines like '--- Closure ---':
  11. # 1) Direct dependencies, listing which module imports which other modules
  12. # 2) The inverse of (1)
  13. # 3) Indirect dependencies, or the closure of the above
  14. # 4) The inverse of (3)
  15. #
  16. # To do:
  17. # - command line options to select output type
  18. # - option to automatically scan the Python library for referenced modules
  19. # - option to limit output to particular modules
  20. import sys
  21. import re
  22. import os
  23. # Main program
  24. #
  25. def main():
  26. args = sys.argv[1:]
  27. if not args:
  28. print 'usage: pdeps file.py file.py ...'
  29. return 2
  30. #
  31. table = {}
  32. for arg in args:
  33. process(arg, table)
  34. #
  35. print '--- Uses ---'
  36. printresults(table)
  37. #
  38. print '--- Used By ---'
  39. inv = inverse(table)
  40. printresults(inv)
  41. #
  42. print '--- Closure of Uses ---'
  43. reach = closure(table)
  44. printresults(reach)
  45. #
  46. print '--- Closure of Used By ---'
  47. invreach = inverse(reach)
  48. printresults(invreach)
  49. #
  50. return 0
  51. # Compiled regular expressions to search for import statements
  52. #
  53. m_import = re.compile('^[ \t]*from[ \t]+([^ \t]+)[ \t]+')
  54. m_from = re.compile('^[ \t]*import[ \t]+([^#]+)')
  55. # Collect data from one file
  56. #
  57. def process(filename, table):
  58. fp = open(filename, 'r')
  59. mod = os.path.basename(filename)
  60. if mod[-3:] == '.py':
  61. mod = mod[:-3]
  62. table[mod] = list = []
  63. while 1:
  64. line = fp.readline()
  65. if not line: break
  66. while line[-1:] == '\\':
  67. nextline = fp.readline()
  68. if not nextline: break
  69. line = line[:-1] + nextline
  70. if m_import.match(line) >= 0:
  71. (a, b), (a1, b1) = m_import.regs[:2]
  72. elif m_from.match(line) >= 0:
  73. (a, b), (a1, b1) = m_from.regs[:2]
  74. else: continue
  75. words = line[a1:b1].split(',')
  76. # print '#', line, words
  77. for word in words:
  78. word = word.strip()
  79. if word not in list:
  80. list.append(word)
  81. # Compute closure (this is in fact totally general)
  82. #
  83. def closure(table):
  84. modules = table.keys()
  85. #
  86. # Initialize reach with a copy of table
  87. #
  88. reach = {}
  89. for mod in modules:
  90. reach[mod] = table[mod][:]
  91. #
  92. # Iterate until no more change
  93. #
  94. change = 1
  95. while change:
  96. change = 0
  97. for mod in modules:
  98. for mo in reach[mod]:
  99. if mo in modules:
  100. for m in reach[mo]:
  101. if m not in reach[mod]:
  102. reach[mod].append(m)
  103. change = 1
  104. #
  105. return reach
  106. # Invert a table (this is again totally general).
  107. # All keys of the original table are made keys of the inverse,
  108. # so there may be empty lists in the inverse.
  109. #
  110. def inverse(table):
  111. inv = {}
  112. for key in table.keys():
  113. if not inv.has_key(key):
  114. inv[key] = []
  115. for item in table[key]:
  116. store(inv, item, key)
  117. return inv
  118. # Store "item" in "dict" under "key".
  119. # The dictionary maps keys to lists of items.
  120. # If there is no list for the key yet, it is created.
  121. #
  122. def store(dict, key, item):
  123. if dict.has_key(key):
  124. dict[key].append(item)
  125. else:
  126. dict[key] = [item]
  127. # Tabulate results neatly
  128. #
  129. def printresults(table):
  130. modules = table.keys()
  131. maxlen = 0
  132. for mod in modules: maxlen = max(maxlen, len(mod))
  133. modules.sort()
  134. for mod in modules:
  135. list = table[mod]
  136. list.sort()
  137. print mod.ljust(maxlen), ':',
  138. if mod in list:
  139. print '(*)',
  140. for ref in list:
  141. print ref,
  142. print
  143. # Call main and honor exit status
  144. if __name__ == '__main__':
  145. try:
  146. sys.exit(main())
  147. except KeyboardInterrupt:
  148. sys.exit(1)