mirror of
https://github.com/opencv/opencv.git
synced 2025-06-07 17:44:04 +08:00
Added Python Docstrings
This commit is contained in:
parent
772a8188c0
commit
078b4cc971
@ -405,7 +405,9 @@ class FuncVariant(object):
|
|||||||
self.name = self.wname = name
|
self.name = self.wname = name
|
||||||
self.isconstructor = isconstructor
|
self.isconstructor = isconstructor
|
||||||
|
|
||||||
self.rettype = decl[4] if len(decl) >=5 else handle_ptr(decl[1])
|
self.docstring = decl[5]
|
||||||
|
|
||||||
|
self.rettype = decl[4] or handle_ptr(decl[1])
|
||||||
if self.rettype == "void":
|
if self.rettype == "void":
|
||||||
self.rettype = ""
|
self.rettype = ""
|
||||||
self.args = []
|
self.args = []
|
||||||
@ -494,7 +496,7 @@ class FuncVariant(object):
|
|||||||
else:
|
else:
|
||||||
outstr = "None"
|
outstr = "None"
|
||||||
|
|
||||||
self.py_docstring = "%s(%s) -> %s" % (self.wname, argstr, outstr)
|
self.py_prototype = "%s(%s) -> %s" % (self.wname, argstr, outstr)
|
||||||
self.py_noptargs = noptargs
|
self.py_noptargs = noptargs
|
||||||
self.py_arglist = arglist
|
self.py_arglist = arglist
|
||||||
for aname, argno in arglist:
|
for aname, argno in arglist:
|
||||||
@ -536,28 +538,49 @@ class FuncInfo(object):
|
|||||||
return "static PyObject* %s(PyObject* %s, PyObject* args, PyObject* kw)" % (full_fname, self_arg)
|
return "static PyObject* %s(PyObject* %s, PyObject* args, PyObject* kw)" % (full_fname, self_arg)
|
||||||
|
|
||||||
def get_tab_entry(self):
|
def get_tab_entry(self):
|
||||||
|
prototype_list = []
|
||||||
docstring_list = []
|
docstring_list = []
|
||||||
|
|
||||||
have_empty_constructor = False
|
have_empty_constructor = False
|
||||||
for v in self.variants:
|
for v in self.variants:
|
||||||
s = v.py_docstring
|
s = v.py_prototype
|
||||||
if (not v.py_arglist) and self.isconstructor:
|
if (not v.py_arglist) and self.isconstructor:
|
||||||
have_empty_constructor = True
|
have_empty_constructor = True
|
||||||
if s not in docstring_list:
|
if s not in prototype_list:
|
||||||
docstring_list.append(s)
|
prototype_list.append(s)
|
||||||
|
docstring_list.append(v.docstring)
|
||||||
|
|
||||||
# if there are just 2 constructors: default one and some other,
|
# if there are just 2 constructors: default one and some other,
|
||||||
# we simplify the notation.
|
# we simplify the notation.
|
||||||
# Instead of ClassName(args ...) -> object or ClassName() -> object
|
# Instead of ClassName(args ...) -> object or ClassName() -> object
|
||||||
# we write ClassName([args ...]) -> object
|
# we write ClassName([args ...]) -> object
|
||||||
if have_empty_constructor and len(self.variants) == 2:
|
if have_empty_constructor and len(self.variants) == 2:
|
||||||
idx = self.variants[1].py_arglist != []
|
idx = self.variants[1].py_arglist != []
|
||||||
s = self.variants[idx].py_docstring
|
s = self.variants[idx].py_prototype
|
||||||
p1 = s.find("(")
|
p1 = s.find("(")
|
||||||
p2 = s.rfind(")")
|
p2 = s.rfind(")")
|
||||||
docstring_list = [s[:p1+1] + "[" + s[p1+1:p2] + "]" + s[p2:]]
|
prototype_list = [s[:p1+1] + "[" + s[p1+1:p2] + "]" + s[p2:]]
|
||||||
|
|
||||||
|
# The final docstring will be: Each prototype, followed by
|
||||||
|
# their relevant doxygen comment
|
||||||
|
full_docstring = ""
|
||||||
|
for prototype, body in zip(prototype_list, docstring_list):
|
||||||
|
full_docstring += Template("$prototype\n$docstring\n\n\n\n").substitute(
|
||||||
|
prototype=prototype,
|
||||||
|
docstring='\n'.join(
|
||||||
|
['. ' + line
|
||||||
|
for line in body.split('\n')]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Escape backslashes, newlines, and double quotes
|
||||||
|
full_docstring = full_docstring.strip().replace("\\", "\\\\").replace('\n', '\\n').replace("\"", "\\\"")
|
||||||
|
# Convert unicode chars to xml representation, but keep as string instead of bytes
|
||||||
|
full_docstring = full_docstring.encode('ascii', errors='xmlcharrefreplace').decode()
|
||||||
|
|
||||||
return Template(' {"$py_funcname", (PyCFunction)$wrap_funcname, METH_VARARGS | METH_KEYWORDS, "$py_docstring"},\n'
|
return Template(' {"$py_funcname", (PyCFunction)$wrap_funcname, METH_VARARGS | METH_KEYWORDS, "$py_docstring"},\n'
|
||||||
).substitute(py_funcname = self.variants[0].wname, wrap_funcname=self.get_wrapper_name(),
|
).substitute(py_funcname = self.variants[0].wname, wrap_funcname=self.get_wrapper_name(),
|
||||||
py_docstring = " or ".join(docstring_list))
|
py_docstring = full_docstring)
|
||||||
|
|
||||||
def gen_code(self, all_classes):
|
def gen_code(self, all_classes):
|
||||||
proto = self.get_wrapper_prototype()
|
proto = self.get_wrapper_prototype()
|
||||||
|
@ -21,12 +21,13 @@ opencv_hdr_list = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Each declaration is [funcname, return_value_type /* in C, not in Python */, <list_of_modifiers>, <list_of_arguments>],
|
Each declaration is [funcname, return_value_type /* in C, not in Python */, <list_of_modifiers>, <list_of_arguments>, original_return_type, docstring],
|
||||||
where each element of <list_of_arguments> is 4-element list itself:
|
where each element of <list_of_arguments> is 4-element list itself:
|
||||||
[argtype, argname, default_value /* or "" if none */, <list_of_modifiers>]
|
[argtype, argname, default_value /* or "" if none */, <list_of_modifiers>]
|
||||||
where the list of modifiers is yet another nested list of strings
|
where the list of modifiers is yet another nested list of strings
|
||||||
(currently recognized are "/O" for output argument, "/S" for static (i.e. class) methods
|
(currently recognized are "/O" for output argument, "/S" for static (i.e. class) methods
|
||||||
and "/A value" for the plain C arrays with counters)
|
and "/A value" for the plain C arrays with counters)
|
||||||
|
original_return_type is None if the original_return_type is the same as return_value_type
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class CppHeaderParser(object):
|
class CppHeaderParser(object):
|
||||||
@ -226,7 +227,7 @@ class CppHeaderParser(object):
|
|||||||
else:
|
else:
|
||||||
prev_val_delta = 0
|
prev_val_delta = 0
|
||||||
prev_val = val = pv[1].strip()
|
prev_val = val = pv[1].strip()
|
||||||
decl.append(["const " + self.get_dotted_name(pv[0].strip()), val, [], []])
|
decl.append(["const " + self.get_dotted_name(pv[0].strip()), val, [], [], None, ""])
|
||||||
return decl
|
return decl
|
||||||
|
|
||||||
def parse_class_decl(self, decl_str):
|
def parse_class_decl(self, decl_str):
|
||||||
@ -256,7 +257,7 @@ class CppHeaderParser(object):
|
|||||||
bases = ll[2:]
|
bases = ll[2:]
|
||||||
return classname, bases, modlist
|
return classname, bases, modlist
|
||||||
|
|
||||||
def parse_func_decl_no_wrap(self, decl_str, static_method = False):
|
def parse_func_decl_no_wrap(self, decl_str, static_method=False, docstring=""):
|
||||||
decl_str = (decl_str or "").strip()
|
decl_str = (decl_str or "").strip()
|
||||||
virtual_method = False
|
virtual_method = False
|
||||||
explicit_method = False
|
explicit_method = False
|
||||||
@ -299,7 +300,7 @@ class CppHeaderParser(object):
|
|||||||
apos = fdecl.find("(", apos+1)
|
apos = fdecl.find("(", apos+1)
|
||||||
|
|
||||||
fname = "cv." + fname.replace("::", ".")
|
fname = "cv." + fname.replace("::", ".")
|
||||||
decl = [fname, rettype, [], []]
|
decl = [fname, rettype, [], [], None, docstring]
|
||||||
|
|
||||||
# inline constructor implementation
|
# inline constructor implementation
|
||||||
implmatch = re.match(r"(\(.*?\))\s*:\s*(\w+\(.*?\),?\s*)+", fdecl[apos:])
|
implmatch = re.match(r"(\(.*?\))\s*:\s*(\w+\(.*?\),?\s*)+", fdecl[apos:])
|
||||||
@ -370,7 +371,7 @@ class CppHeaderParser(object):
|
|||||||
print(decl_str)
|
print(decl_str)
|
||||||
return decl
|
return decl
|
||||||
|
|
||||||
def parse_func_decl(self, decl_str, use_umat=False):
|
def parse_func_decl(self, decl_str, use_umat=False, docstring=""):
|
||||||
"""
|
"""
|
||||||
Parses the function or method declaration in the form:
|
Parses the function or method declaration in the form:
|
||||||
[([CV_EXPORTS] <rettype>) | CVAPI(rettype)]
|
[([CV_EXPORTS] <rettype>) | CVAPI(rettype)]
|
||||||
@ -379,7 +380,7 @@ class CppHeaderParser(object):
|
|||||||
[const] {; | <function_body>}
|
[const] {; | <function_body>}
|
||||||
|
|
||||||
Returns the function declaration entry:
|
Returns the function declaration entry:
|
||||||
[<func name>, <return value C-type>, <list of modifiers>, <list of arguments>] (see above)
|
[<func name>, <return value C-type>, <list of modifiers>, <list of arguments>, <original return type>, <docstring>] (see above)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if self.wrap_mode:
|
if self.wrap_mode:
|
||||||
@ -484,7 +485,7 @@ class CppHeaderParser(object):
|
|||||||
funcname = self.get_dotted_name(funcname)
|
funcname = self.get_dotted_name(funcname)
|
||||||
|
|
||||||
if not self.wrap_mode:
|
if not self.wrap_mode:
|
||||||
decl = self.parse_func_decl_no_wrap(decl_str, static_method)
|
decl = self.parse_func_decl_no_wrap(decl_str, static_method, docstring)
|
||||||
decl[0] = funcname
|
decl[0] = funcname
|
||||||
return decl
|
return decl
|
||||||
|
|
||||||
@ -574,10 +575,7 @@ class CppHeaderParser(object):
|
|||||||
if static_method:
|
if static_method:
|
||||||
func_modlist.append("/S")
|
func_modlist.append("/S")
|
||||||
|
|
||||||
if original_type is None:
|
return [funcname, rettype, func_modlist, args, original_type, docstring]
|
||||||
return [funcname, rettype, func_modlist, args]
|
|
||||||
else:
|
|
||||||
return [funcname, rettype, func_modlist, args, original_type]
|
|
||||||
|
|
||||||
def get_dotted_name(self, name):
|
def get_dotted_name(self, name):
|
||||||
"""
|
"""
|
||||||
@ -612,7 +610,7 @@ class CppHeaderParser(object):
|
|||||||
n = "cv.Algorithm"
|
n = "cv.Algorithm"
|
||||||
return n
|
return n
|
||||||
|
|
||||||
def parse_stmt(self, stmt, end_token, use_umat=False):
|
def parse_stmt(self, stmt, end_token, use_umat=False, docstring=""):
|
||||||
"""
|
"""
|
||||||
parses the statement (ending with ';' or '}') or a block head (ending with '{')
|
parses the statement (ending with ';' or '}') or a block head (ending with '{')
|
||||||
|
|
||||||
@ -659,7 +657,7 @@ class CppHeaderParser(object):
|
|||||||
exit(1)
|
exit(1)
|
||||||
if classname.startswith("_Ipl"):
|
if classname.startswith("_Ipl"):
|
||||||
classname = classname[1:]
|
classname = classname[1:]
|
||||||
decl = [stmt_type + " " + self.get_dotted_name(classname), "", modlist, []]
|
decl = [stmt_type + " " + self.get_dotted_name(classname), "", modlist, [], None, docstring]
|
||||||
if bases:
|
if bases:
|
||||||
decl[1] = ": " + ", ".join([self.get_dotted_name(b).replace(".","::") for b in bases])
|
decl[1] = ": " + ", ".join([self.get_dotted_name(b).replace(".","::") for b in bases])
|
||||||
return stmt_type, classname, True, decl
|
return stmt_type, classname, True, decl
|
||||||
@ -674,7 +672,7 @@ class CppHeaderParser(object):
|
|||||||
exit(1)
|
exit(1)
|
||||||
decl = []
|
decl = []
|
||||||
if ("CV_EXPORTS_W" in stmt) or ("CV_EXPORTS_AS" in stmt) or (not self.wrap_mode):# and ("CV_EXPORTS" 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, []]
|
decl = [stmt_type + " " + self.get_dotted_name(classname), "", modlist, [], None, docstring]
|
||||||
if bases:
|
if bases:
|
||||||
decl[1] = ": " + ", ".join([self.get_dotted_name(b).replace(".","::") for b in bases])
|
decl[1] = ": " + ", ".join([self.get_dotted_name(b).replace(".","::") for b in bases])
|
||||||
return stmt_type, classname, True, decl
|
return stmt_type, classname, True, decl
|
||||||
@ -704,7 +702,7 @@ class CppHeaderParser(object):
|
|||||||
# since we filtered off the other places where '(' can normally occur:
|
# since we filtered off the other places where '(' can normally occur:
|
||||||
# - code blocks
|
# - code blocks
|
||||||
# - function pointer typedef's
|
# - function pointer typedef's
|
||||||
decl = self.parse_func_decl(stmt, use_umat=use_umat)
|
decl = self.parse_func_decl(stmt, use_umat=use_umat, docstring=docstring)
|
||||||
# we return parse_flag == False to prevent the parser to look inside function/method bodies
|
# we return parse_flag == False to prevent the parser to look inside function/method bodies
|
||||||
# (except for tracking the nested blocks)
|
# (except for tracking the nested blocks)
|
||||||
return stmt_type, "", False, decl
|
return stmt_type, "", False, decl
|
||||||
@ -759,11 +757,13 @@ class CppHeaderParser(object):
|
|||||||
SCAN = 0 # outside of a comment or preprocessor directive
|
SCAN = 0 # outside of a comment or preprocessor directive
|
||||||
COMMENT = 1 # inside a multi-line comment
|
COMMENT = 1 # inside a multi-line comment
|
||||||
DIRECTIVE = 2 # inside a multi-line preprocessor directive
|
DIRECTIVE = 2 # inside a multi-line preprocessor directive
|
||||||
|
DOCSTRING = 3 # inside a multi-line docstring
|
||||||
|
|
||||||
state = SCAN
|
state = SCAN
|
||||||
|
|
||||||
self.block_stack = [["file", hname, True, True, None]]
|
self.block_stack = [["file", hname, True, True, None]]
|
||||||
block_head = ""
|
block_head = ""
|
||||||
|
docstring = ""
|
||||||
self.lineno = 0
|
self.lineno = 0
|
||||||
self.wrap_mode = wmode
|
self.wrap_mode = wmode
|
||||||
|
|
||||||
@ -789,6 +789,15 @@ class CppHeaderParser(object):
|
|||||||
l = l[pos+2:]
|
l = l[pos+2:]
|
||||||
state = SCAN
|
state = SCAN
|
||||||
|
|
||||||
|
if state == DOCSTRING:
|
||||||
|
pos = l.find("*/")
|
||||||
|
if pos < 0:
|
||||||
|
docstring += l + "\n"
|
||||||
|
continue
|
||||||
|
docstring += l[:pos] + "\n"
|
||||||
|
l = l[pos+2:]
|
||||||
|
state = SCAN
|
||||||
|
|
||||||
if state != SCAN:
|
if state != SCAN:
|
||||||
print("Error at %d: invlid state = %d" % (self.lineno, state))
|
print("Error at %d: invlid state = %d" % (self.lineno, state))
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
@ -806,11 +815,20 @@ class CppHeaderParser(object):
|
|||||||
|
|
||||||
if token == "/*":
|
if token == "/*":
|
||||||
block_head += " " + l[:pos]
|
block_head += " " + l[:pos]
|
||||||
pos = l.find("*/", pos+2)
|
end_pos = l.find("*/", pos+2)
|
||||||
if pos < 0:
|
if len(l) > pos + 2 and l[pos+2] == "*":
|
||||||
|
# '/**', it's a docstring
|
||||||
|
if end_pos < 0:
|
||||||
|
state = DOCSTRING
|
||||||
|
docstring = l[pos+3:] + "\n"
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
docstring = l[pos+3:end_pos]
|
||||||
|
|
||||||
|
elif end_pos < 0:
|
||||||
state = COMMENT
|
state = COMMENT
|
||||||
break
|
break
|
||||||
l = l[pos+2:]
|
l = l[end_pos+2:]
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if token == "\"":
|
if token == "\"":
|
||||||
@ -840,7 +858,8 @@ class CppHeaderParser(object):
|
|||||||
if stack_top[self.PROCESS_FLAG]:
|
if stack_top[self.PROCESS_FLAG]:
|
||||||
# even if stack_top[PUBLIC_SECTION] is False, we still try to process the statement,
|
# even if stack_top[PUBLIC_SECTION] is False, we still try to process the statement,
|
||||||
# since it can start with "public:"
|
# since it can start with "public:"
|
||||||
stmt_type, name, parse_flag, decl = self.parse_stmt(stmt, token)
|
docstring = docstring.strip()
|
||||||
|
stmt_type, name, parse_flag, decl = self.parse_stmt(stmt, token, docstring=docstring)
|
||||||
if decl:
|
if decl:
|
||||||
if stmt_type == "enum":
|
if stmt_type == "enum":
|
||||||
for d in decl:
|
for d in decl:
|
||||||
@ -854,8 +873,9 @@ class CppHeaderParser(object):
|
|||||||
args = decl[3]
|
args = decl[3]
|
||||||
has_mat = len(list(filter(lambda x: x[0] in {"Mat", "vector_Mat"}, args))) > 0
|
has_mat = len(list(filter(lambda x: x[0] in {"Mat", "vector_Mat"}, args))) > 0
|
||||||
if has_mat:
|
if has_mat:
|
||||||
_, _, _, umat_decl = self.parse_stmt(stmt, token, use_umat=True)
|
_, _, _, umat_decl = self.parse_stmt(stmt, token, use_umat=True, docstring=docstring)
|
||||||
decls.append(umat_decl)
|
decls.append(umat_decl)
|
||||||
|
docstring = ""
|
||||||
if stmt_type == "namespace":
|
if stmt_type == "namespace":
|
||||||
chunks = [block[1] for block in self.block_stack if block[0] == 'namespace'] + [name]
|
chunks = [block[1] for block in self.block_stack if block[0] == 'namespace'] + [name]
|
||||||
self.namespaces.add('.'.join(chunks))
|
self.namespaces.add('.'.join(chunks))
|
||||||
@ -887,6 +907,8 @@ class CppHeaderParser(object):
|
|||||||
"""
|
"""
|
||||||
for d in decls:
|
for d in decls:
|
||||||
print(d[0], d[1], ";".join(d[2]))
|
print(d[0], d[1], ";".join(d[2]))
|
||||||
|
# Uncomment below line to see docstrings
|
||||||
|
# print('"""\n' + d[5] + '\n"""')
|
||||||
for a in d[3]:
|
for a in d[3]:
|
||||||
print(" ", a[0], a[1], a[2], end="")
|
print(" ", a[0], a[1], a[2], end="")
|
||||||
if a[3]:
|
if a[3]:
|
||||||
|
Loading…
Reference in New Issue
Block a user