#!/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