From 124967eefb3c67b920eb33bb90e2df6dc1fedc29 Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Wed, 1 Jun 2011 21:15:36 +0000 Subject: [PATCH] very first version of the documentation checking script --- doc/check_docs.py | 108 ++++++++++++++++++++++++++++++ modules/python/src2/hdr_parser.py | 89 +++++++++++++++++------- 2 files changed, 174 insertions(+), 23 deletions(-) create mode 100644 doc/check_docs.py diff --git a/doc/check_docs.py b/doc/check_docs.py new file mode 100644 index 0000000000..bd47f9560d --- /dev/null +++ b/doc/check_docs.py @@ -0,0 +1,108 @@ +import sys, glob + +sys.path.append("../modules/python/src2/") +import hdr_parser as hp + +opencv_hdr_list = [ +"../modules/core/include/opencv2/core/core.hpp", +"../modules/ml/include/opencv2/ml/ml.hpp", +"../modules/imgproc/include/opencv2/imgproc/imgproc.hpp", +"../modules/calib3d/include/opencv2/calib3d/calib3d.hpp", +"../modules/features2d/include/opencv2/features2d/features2d.hpp", +"../modules/video/include/opencv2/video/tracking.hpp", +"../modules/video/include/opencv2/video/background_segm.hpp", +"../modules/objdetect/include/opencv2/objdetect/objdetect.hpp", +"../modules/highgui/include/opencv2/highgui/highgui.hpp", +] + +opencv_module_list = [ +"core", +"imgproc", +"calib3d", +"features2d", +"video", +"objdetect", +"highgui", +"ml" +] + +class RSTParser(object): + + def process_rst(self, docname): + df = open(docname, "rt") + fdecl = "" + balance = 0 + lineno = 0 + + for l in df.readlines(): + lineno += 1 + ll = l.strip() + if balance == 0: + if not ll.startswith(".. c:function::") and not ll.startswith(".. cpp:function::"): + continue + fdecl = ll[ll.find("::") + 3:] + elif balance > 0: + fdecl += ll + balance = fdecl.count("(") - fdecl.count(")") + assert balance >= 0 + if balance > 0: + continue + rst_decl = self.parser.parse_func_decl_no_wrap(fdecl) + hdr_decls = self.fmap.get(rst_decl[0], []) + if not hdr_decls: + print "Documented function %s (%s) in %s:%d is not in the headers" % (fdecl, rst_decl[0], docname, lineno) + continue + decl_idx = 0 + for hd in hdr_decls: + if len(hd[3]) != len(rst_decl[3]): + continue + idx = 0 + for a in hd[3]: + if a[0] != rst_decl[3][idx][0]: + break + idx += 1 + if idx == len(hd[3]): + break + decl_idx += 1 + if decl_idx < len(hdr_decls): + self.fmap[rst_decl[0]] = hdr_decls[:decl_idx] + hdr_decls[decl_idx+1:] + continue + print "Documented function %s in %s:%d does not have a match" % (fdecl, docname, lineno) + + def check_module_docs(self, name): + self.parser = hp.CppHeaderParser() + decls = [] + self.fmap = {} + + for hname in opencv_hdr_list: + if hname.startswith("../modules/" + name): + decls += self.parser.parse(hname, wmode=False) + + #parser.print_decls(decls) + + for d in decls: + fname = d[0] + if not fname.startswith("struct") and not fname.startswith("class") and not fname.startswith("const"): + dlist = self.fmap.get(fname, []) + dlist.append(d) + self.fmap[fname] = dlist + + self.missing_docfunc_list = [] + + doclist = glob.glob("../modules/" + name + "/doc/*.rst") + for d in doclist: + self.process_rst(d) + + print "\n\n########## The list of undocumented functions: ###########\n\n" + fkeys = sorted(self.fmap.keys()) + for f in fkeys: + decls = self.fmap[f] + for d in decls: + print "%s %s(%s)" % (d[1], d[0], ", ".join([a[0] + " " + a[1] for a in d[3]])) + +p = RSTParser() +for m in opencv_module_list: + print "\n\n*************************** " + m + " *************************\n" + p.check_module_docs(m) + + \ No newline at end of file diff --git a/modules/python/src2/hdr_parser.py b/modules/python/src2/hdr_parser.py index 24332533b9..3baf3644ac 100755 --- a/modules/python/src2/hdr_parser.py +++ b/modules/python/src2/hdr_parser.py @@ -242,6 +242,41 @@ class CppHeaderParser(object): bases = ll[2:] return classname, bases, modlist + def parse_func_decl_no_wrap(self, decl_str): + fdecl = decl_str.replace("CV_OUT", "").replace("CV_IN_OUT", "") + fdecl = fdecl.strip().replace("\t", " ") + while " " in fdecl: + fdecl = fdecl.replace(" ", " ") + fname = fdecl[:fdecl.find("(")].strip() + fnpos = fname.rfind(" ") + if fnpos < 0: + fnpos = 0 + fname = fname[fnpos:].strip() + rettype = fdecl[:fnpos].strip() + args0 = fdecl[fdecl.find("(")+1:fdecl.rfind(")")].strip().split(",") + args = [] + narg = "" + for arg in args0: + narg += arg.strip() + balance_paren = narg.count("(") - narg.count(")") + balance_angle = narg.count("<") - narg.count(">") + if balance_paren == 0 and balance_angle == 0: + args.append(narg.strip()) + narg = "" + fname = "cv." + fname.replace("::", ".") + decl = [fname, rettype, [], []] + for arg in args: + dfpos = arg.find("=") + defval = "" + if dfpos >= 0: + defval = arg[dfpos+1:].strip() + arg = arg[:dfpos].strip() + pos = arg.rfind(" ") + aname = arg[pos+1:] + atype = arg[:pos] + decl[3].append([atype, aname, defval, []]) + return decl + def parse_func_decl(self, decl_str): """ Parses the function or method declaration in the form: @@ -254,9 +289,10 @@ class CppHeaderParser(object): [, , , ] (see above) """ - if not (("CV_EXPORTS_AS" in decl_str) or ("CV_EXPORTS_W" in decl_str) or \ - ("CV_WRAP" in decl_str) or ("CV_WRAP_AS" in decl_str)): - return [] + if self.wrap_mode: + if not (("CV_EXPORTS_AS" in decl_str) or ("CV_EXPORTS_W" in decl_str) or \ + ("CV_WRAP" in decl_str) or ("CV_WRAP_AS" in decl_str)): + return [] top = self.block_stack[-1] func_modlist = [] @@ -276,7 +312,7 @@ class CppHeaderParser(object): # note that we do not strip "static" prefix, which does matter; # it means class methods, not instance methods decl_str = self.batch_replace(decl_str, [("virtual", ""), ("static inline", ""), ("inline", ""),\ - ("CV_EXPORTS_W", ""), ("CV_EXPORTS", ""), ("CV_WRAP ", " "), ("static CV_INLINE", ""), ("CV_INLINE", "")]).strip() + ("CV_EXPORTS_W", ""), ("CV_EXPORTS", ""), ("CV_CDECL", ""), ("CV_WRAP ", " "), ("static CV_INLINE", ""), ("CV_INLINE", "")]).strip() static_method = False context = top[0] @@ -315,7 +351,7 @@ class CppHeaderParser(object): print "Error at %d. the function/method name is missing: '%s'" % (self.lineno, decl_start) sys.exit(-1) - if ("::" in funcname) or funcname.startswith("~"): + if self.wrap_mode and (("::" in funcname) or funcname.startswith("~")): # if there is :: in function name (and this is in the header file), # it means, this is inline implementation of a class method. # Thus the function has been already declared within the class and we skip this repeated @@ -325,6 +361,11 @@ class CppHeaderParser(object): funcname = self.get_dotted_name(funcname) + if not self.wrap_mode: + decl = self.parse_func_decl_no_wrap(decl_str) + decl[0] = funcname + return decl + arg_start = args_begin+1 npos = arg_start-1 balance = 1 @@ -375,22 +416,23 @@ class CppHeaderParser(object): if eqpos >= 0: a = a[:eqpos].strip() arg_type, arg_name, modlist, argno = self.parse_arg(a, argno) - if arg_type == "InputArray" or arg_type == "InputOutputArray": - arg_type = "Mat" - elif arg_type == "OutputArray": - arg_type = "Mat" - modlist.append("/O") - elif arg_type == "InputArrayOfArrays" or arg_type == "InputOutputArrayOfArrays": - arg_type = "vector_Mat" - elif arg_type == "OutputArrayOfArrays": - arg_type = "vector_Mat" - modlist.append("/O") - defval = self.batch_replace(defval, [("InputArrayOfArrays", "vector"), - ("InputOutputArrayOfArrays", "vector"), - ("OutputArrayOfArrays", "vector"), - ("InputArray", "Mat"), - ("InputOutputArray", "Mat"), - ("OutputArray", "Mat")]).strip() + if self.wrap_mode: + if arg_type == "InputArray" or arg_type == "InputOutputArray": + arg_type = "Mat" + elif arg_type == "OutputArray": + arg_type = "Mat" + modlist.append("/O") + elif arg_type == "InputArrayOfArrays" or arg_type == "InputOutputArrayOfArrays": + arg_type = "vector_Mat" + elif arg_type == "OutputArrayOfArrays": + arg_type = "vector_Mat" + modlist.append("/O") + defval = self.batch_replace(defval, [("InputArrayOfArrays", "vector"), + ("InputOutputArrayOfArrays", "vector"), + ("OutputArrayOfArrays", "vector"), + ("InputArray", "Mat"), + ("InputOutputArray", "Mat"), + ("OutputArray", "Mat")]).strip() args.append([arg_type, arg_name, defval, modlist]) npos = arg_start-1 @@ -473,7 +515,7 @@ class CppHeaderParser(object): stmt_type = stmt.split()[0] classname, bases, modlist = self.parse_class_decl(stmt) decl = [] - if ("CV_EXPORTS_W" in stmt) or ("CV_EXPORTS_AS" in stmt): + if ("CV_EXPORTS_W" in stmt) or ("CV_EXPORTS_AS" in stmt) or (not self.wrap_mode and ("CV_EXPORTS" in stmt)): decl = [stmt_type + " " + self.get_dotted_name(classname), "", modlist, []] if bases: decl[1] = ": " + " ".join(bases) @@ -542,7 +584,7 @@ class CppHeaderParser(object): token = t return token, tpos - def parse(self, hname): + def parse(self, hname, wmode=True): """ The main method. Parses the input file. Returns the list of declarations (that can be print using print_decls) @@ -562,6 +604,7 @@ class CppHeaderParser(object): self.block_stack = [["file", hname, True, True, None]] block_head = "" self.lineno = 0 + self.wrap_mode = wmode for l0 in linelist: self.lineno += 1