summaryrefslogtreecommitdiffstats
path: root/Doc/tools
diff options
context:
space:
mode:
authorMartin v. Löwis <martin@v.loewis.de>2006-03-01 05:18:07 (GMT)
committerMartin v. Löwis <martin@v.loewis.de>2006-03-01 05:18:07 (GMT)
commit497114e0275ddf59664509146e48638d277ecdd2 (patch)
tree6b37ab62dce10540deb66cca2bc9a39d7faa4dc3 /Doc/tools
parent29fafd8708cb19f1c2350618426dd99b4d76819a (diff)
downloadcpython-497114e0275ddf59664509146e48638d277ecdd2.zip
cpython-497114e0275ddf59664509146e48638d277ecdd2.tar.gz
cpython-497114e0275ddf59664509146e48638d277ecdd2.tar.bz2
Add tool to check documentation against declaration.
Diffstat (limited to 'Doc/tools')
-rwxr-xr-xDoc/tools/cmpcsyms157
1 files changed, 157 insertions, 0 deletions
diff --git a/Doc/tools/cmpcsyms b/Doc/tools/cmpcsyms
new file mode 100755
index 0000000..55f9954
--- /dev/null
+++ b/Doc/tools/cmpcsyms
@@ -0,0 +1,157 @@
+#! /usr/bin/env python
+from __future__ import with_statement
+import errno
+import os
+import re
+import sys
+import string
+
+if __name__ == "__main__":
+ _base = sys.argv[0]
+else:
+ _base = __file__
+
+_script_home = os.path.abspath(os.path.dirname(_base))
+
+srcdir = os.path.dirname(os.path.dirname(_script_home))
+
+EXCLUDES = ["bitset.h", "cStringIO.h", "graminit.h", "grammar.h",
+ "longintrepr.h", "metagrammar.h",
+ "node.h", "opcode.h", "osdefs.h", "pgenheaders.h",
+ "py_curses.h", "parsetok.h", "symtable.h", "token.h"]
+
+
+def list_headers():
+ """Return a list of headers."""
+ incdir = os.path.join(srcdir, "Include")
+ return [os.path.join(incdir, fn) for fn in os.listdir(incdir)
+ if fn.endswith(".h") and fn not in EXCLUDES]
+
+
+def matcher(pattern):
+ return re.compile(pattern).search
+
+MATCHERS = [
+ # XXX this should also deal with ctypedesc, cvardesc and cmemberdesc
+ matcher(r"\\begin\{cfuncdesc\}\{(?P<result>[^}]*)\}\{(?P<sym>[^}]*)\}{(?P<params>[^}]*)\}"),
+ matcher(r"\\cfuncline\{(?P<result>[^})]*)\}\{(?P<sym>[^}]*)\}{(?P<params>[^}]*)\}"),
+ ]
+
+def list_documented_items():
+ """Return a list of everything that's already documented."""
+ apidir = os.path.join(srcdir, "Doc", "api")
+ files = [fn for fn in os.listdir(apidir) if fn.endswith(".tex")]
+ L = []
+ for fn in files:
+ fullname = os.path.join(apidir, fn)
+ data = open(fullname).read()
+ for matcher in MATCHERS:
+ pos = 0
+ while 1:
+ m = matcher(data, pos)
+ if not m: break
+ pos = m.end()
+ sym = m.group("sym")
+ result = m.group("result")
+ params = m.group("params")
+ # replace all whitespace with a single one
+ params = " ".join(params.split())
+ L.append((sym, result, params, fn))
+ return L
+
+def normalize_type(t):
+ t = t.strip()
+ s = t.rfind("*")
+ if s != -1:
+ # strip everything after the pointer name
+ t = t[:s+1]
+ # Drop the variable name
+ s = t.split()
+ typenames = 1
+ if len(s)>1 and s[0]=='unsigned' and s[1]=='int':
+ typenames = 2
+ if len(s) > typenames and s[-1][0] in string.letters:
+ del s[-1]
+ if not s:
+ print "XXX", t
+ return ""
+ # Drop register
+ if s[0] == "register":
+ del s[0]
+ # discard all spaces
+ return ''.join(s)
+
+def compare_type(t1, t2):
+ t1 = normalize_type(t1)
+ t2 = normalize_type(t2)
+ if t1 == r'\moreargs' and t2 == '...':
+ return False
+ if t1 != t2:
+ #print "different:", t1, t2
+ return False
+ return True
+
+
+def compare_types(ret, params, hret, hparams):
+ if not compare_type(ret, hret):
+ return False
+ params = params.split(",")
+ hparams = hparams.split(",")
+ if not params and hparams == ['void']:
+ return True
+ if not hparams and params == ['void']:
+ return True
+ if len(params) != len(hparams):
+ return False
+ for p1, p2 in zip(params, hparams):
+ if not compare_type(p1, p2):
+ return False
+ return True
+
+def main():
+ headers = list_headers()
+ documented = list_documented_items()
+
+ lines = []
+ for h in headers:
+ data = open(h).read()
+ data, n = re.subn(r"PyAPI_FUNC\(([^)]*)\)", r"\1", data)
+ name = os.path.basename(h)
+ with open(name, "w") as f:
+ f.write(data)
+ cmd = ("ctags -f - --file-scope=no --c-kinds=p --fields=S "
+ "-Istaticforward -Istatichere=static " + name)
+ with os.popen(cmd) as f:
+ lines.extend(f.readlines())
+ os.unlink(name)
+ L = {}
+ prevsym = None
+ for line in lines:
+ if not line:
+ break
+ sym, filename, signature = line.split(None, 2)
+ if sym == prevsym:
+ continue
+ expr = "\^(.*)%s" % sym
+ m = re.search(expr, signature)
+ if not m:
+ print "Could not split",signature, "using",expr
+ rettype = m.group(1).strip()
+ m = re.search("signature:\(([^)]*)\)", signature)
+ if not m:
+ print "Could not get signature from", signature
+ params = m.group(1)
+ L[sym] = (rettype, params)
+
+ for sym, ret, params, fn in documented:
+ if sym not in L:
+ print "No declaration for '%s'" % sym
+ continue
+ hret, hparams = L[sym]
+ if not compare_types(ret, params, hret, hparams):
+ print "Declaration error for %s (%s):" % (sym, fn)
+ print ret+": "+params
+ print hret+": "+hparams
+
+if __name__ == "__main__":
+ main()