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