| #!/usr/bin/env python |
| |
| # Released to the public domain by Skip Montanaro, 28 March 2002 |
| |
| """ |
| findsyms.py - try to identify undocumented symbols exported by modules |
| |
| Usage: findsyms.py librefdir |
| |
| For each lib*.tex file in the libref manual source directory, identify which |
| module is documented, import the module if possible, then search the LaTeX |
| source for the symbols global to that module. Report any that don't seem to |
| be documented. |
| |
| Certain exceptions are made to the list of undocumented symbols: |
| |
| * don't mention symbols in which all letters are upper case on the |
| assumption they are manifest constants |
| |
| * don't mention symbols that are themselves modules |
| |
| * don't mention symbols that match those exported by os, math, string, |
| types, or __builtin__ modules |
| |
| Finally, if a name is exported by the module but fails a getattr() lookup, |
| that anomaly is reported. |
| """ |
| |
| import __builtin__ |
| import getopt |
| import glob |
| import math |
| import os |
| import re |
| import string |
| import sys |
| import types |
| import warnings |
| |
| def usage(): |
| print >> sys.stderr, """ |
| usage: %s dir |
| where 'dir' is the Library Reference Manual source directory. |
| """ % os.path.basename(sys.argv[0]) |
| |
| def main(): |
| try: |
| opts, args = getopt.getopt(sys.argv[1:], "") |
| except getopt.error: |
| usage() |
| return |
| |
| if not args: |
| usage() |
| return |
| |
| libdir = args[0] |
| |
| warnings.filterwarnings("error") |
| |
| pat = re.compile(r"\\declaremodule\s*{[^}]*}\s*{([^}]*)}") |
| |
| missing = [] |
| filelist = glob.glob(os.path.join(libdir, "lib*.tex")) |
| filelist.sort() |
| for f in filelist: |
| mod = f[3:-4] |
| if not mod: continue |
| data = open(f).read() |
| mods = re.findall(pat, data) |
| if not mods: |
| print "No module declarations found in", f |
| continue |
| for modname in mods: |
| # skip special modules |
| if modname.startswith("__"): |
| continue |
| try: |
| mod = __import__(modname) |
| except ImportError: |
| missing.append(modname) |
| continue |
| except DeprecationWarning: |
| print "Deprecated module:", modname |
| continue |
| if hasattr(mod, "__all__"): |
| all = mod.__all__ |
| else: |
| all = [k for k in dir(mod) if k[0] != "_"] |
| mentioned = 0 |
| all.sort() |
| for name in all: |
| if data.find(name) == -1: |
| # certain names are predominantly used for testing |
| if name in ("main","test","_test"): |
| continue |
| # is it some sort of manifest constant? |
| if name.upper() == name: |
| continue |
| try: |
| item = getattr(mod, name) |
| except AttributeError: |
| print " ", name, "exposed, but not an attribute" |
| continue |
| # don't care about modules that might be exposed |
| if type(item) == types.ModuleType: |
| continue |
| # check a few modules which tend to be import *'d |
| isglobal = 0 |
| for m in (os, math, string, __builtin__, types): |
| if hasattr(m, name) and item == getattr(m, name): |
| isglobal = 1 |
| break |
| if isglobal: continue |
| if not mentioned: |
| print "Not mentioned in", modname, "docs:" |
| mentioned = 1 |
| print " ", name |
| if missing: |
| missing.sort() |
| print "Could not import:" |
| print " ", ", ".join(missing) |
| |
| if __name__ == "__main__": |
| try: |
| main() |
| except KeyboardInterrupt: |
| pass |