doc: update Python signatures generation
authorAlexander Alekhin <alexander.alekhin@intel.com>
Tue, 5 Sep 2017 15:55:01 +0000 (18:55 +0300)
committerAlexander Alekhin <alexander.a.alekhin@gmail.com>
Sat, 9 Dec 2017 02:56:19 +0000 (02:56 +0000)
- drop dependency on 'import cv2', use pyopencv_signatures.json instead
- try to make generator idempotent

doc/CMakeLists.txt
doc/tools/html_functions.py
doc/tools/python_signatures.py

index 7eaf62e..741c4a0 100644 (file)
@@ -205,10 +205,14 @@ if(BUILD_DOCS AND DOXYGEN_FOUND)
     list(APPEND js_tutorials_assets_deps "${f}" "${opencv_tutorial_html_dir}/${fname}")
   endforeach()
 
+  set(doxygen_result "${CMAKE_CURRENT_BINARY_DIR}/doxygen/html/index.html")
+
   add_custom_target(doxygen_cpp
     COMMAND ${DOXYGEN_EXECUTABLE} ${doxyfile}
     DEPENDS ${doxyfile} ${rootfile} ${bibfile} ${deps} ${js_tutorials_assets_deps}
+    COMMENT "Generate Doxygen documentation"
   )
+
   install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doxygen/html
     DESTINATION "${OPENCV_DOC_INSTALL_PATH}"
     COMPONENT "docs" OPTIONAL
@@ -216,16 +220,16 @@ if(BUILD_DOCS AND DOXYGEN_FOUND)
 
   if(BUILD_opencv_python2)
     add_custom_target(doxygen_python
-      COMMAND ${PYTHON2_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/tools/python_signatures.py" "${CMAKE_CURRENT_BINARY_DIR}/doxygen/html/"
-      DEPENDS doxygen_cpp opencv_python2
+      COMMAND ${PYTHON2_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/tools/python_signatures.py" "${CMAKE_CURRENT_BINARY_DIR}/doxygen/html/" "${OPENCV_PYTHON2_SIGNATURES_FILE}"
+      DEPENDS "${doxygen_result}" gen_opencv_python2
     )
     add_custom_target(doxygen
       DEPENDS doxygen_cpp doxygen_python
     )
   elseif(BUILD_opencv_python3)
     add_custom_target(doxygen_python
-      COMMAND ${PYTHON3_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/tools/python_signatures.py" "${CMAKE_CURRENT_BINARY_DIR}/doxygen/html/"
-      DEPENDS doxygen_cpp opencv_python3
+      COMMAND ${PYTHON3_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/tools/python_signatures.py" "${CMAKE_CURRENT_BINARY_DIR}/doxygen/html/" "${OPENCV_PYTHON3_SIGNATURES_FILE}"
+      DEPENDS "${doxygen_result}" gen_opencv_python3
     )
     add_custom_target(doxygen
       DEPENDS doxygen_cpp doxygen_python
index bf9577b..215a058 100644 (file)
@@ -1,10 +1,11 @@
+from __future__ import print_function
 import logging
 import os
 import codecs
-import cv2
-
+from pprint import pprint
 
 try:
+    import bs4
     from bs4 import BeautifulSoup
 except ImportError:
     raise ImportError('Error: '
@@ -63,14 +64,12 @@ def add_signature_to_table(tmp_soup, new_row, signature, function_name, language
     else:
         new_item = tmp_soup.new_tag('td')
 
-    if "-> None" in signature:
-        pass
-    elif "->" in signature:
-        new_item.append(signature.split("->", 1)[1] + ' =')
+    if str(signature.get('ret', None)) != "None":
+        new_item.append(signature.get('ret') + ' =')
         new_row.append(new_item)
 
     if "Python" in language:
-        function_name = "cv2." + function_name
+        pass  # function_name = "cv2." + function_name
     elif "Java" in language:
         # get word before function_name (= output)
         str_before_bracket = signature.split('(', 1)[0]
@@ -79,8 +78,8 @@ def add_signature_to_table(tmp_soup, new_row, signature, function_name, language
         new_item.append(output + " ")
         new_row.append(new_item)
 
-    new_row = add_item(tmp_soup, new_row, False, function_name + '(')
-    new_row = add_item(tmp_soup, new_row, True, get_text_between_substrings(signature, "(", ")"))
+    new_row = add_item(tmp_soup, new_row, False, signature.get('name', function_name) + '(')
+    new_row = add_item(tmp_soup, new_row, True, signature['arg'])
     new_row = add_item(tmp_soup, new_row, False, ')')
     return new_row
 
@@ -100,98 +99,81 @@ def add_bolded(tmp_soup, new_row, text):
     return new_row
 
 
-def append_table_to(cpp_table, tmp_soup, language, signature, function_name):
+def create_description(tmp_soup, language, signatures, function_name):
     """ Insert the new Python / Java table after the current html c++ table """
-    if signature != "":
-        tmp_table = tmp_soup.new_tag('table')
-        new_row = tmp_soup.new_tag('tr')
-        new_row = add_bolded(tmp_soup, new_row, language)
-        ident = False
-
-        if len(signature) > 120:
-            new_row = new_line(tmp_soup, tmp_table, new_row)
-            ident = True
-
-        if " or " in signature:
-            ident = True
-            for tmp_sig in signature.split(" or "):
-                new_row = new_line(tmp_soup, tmp_table, new_row)
-                new_row = add_signature_to_table(tmp_soup, new_row, tmp_sig, function_name, language, ident)
-                new_row = new_line(tmp_soup, tmp_table, new_row)
-        else:
-            new_row = add_signature_to_table(tmp_soup, new_row, signature, function_name, language, ident)
-            tmp_table.append(new_row)
+    assert signatures
+    tmp_table = tmp_soup.new_tag('table')
+    new_row = tmp_soup.new_tag('tr')
+    new_row = add_bolded(tmp_soup, new_row, language)
+    ident = False
 
-        cpp_table.insert_after(tmp_table)
-    return cpp_table
+    new_row = new_line(tmp_soup, tmp_table, new_row)
+    ident = True
 
+    for s in signatures:
+        new_row = new_line(tmp_soup, tmp_table, new_row)
+        new_row = add_signature_to_table(tmp_soup, new_row, s, function_name, language, ident)
+        new_row = new_line(tmp_soup, tmp_table, new_row)
 
-def add_signatures(tmp_soup, tmp_dir, ADD_JAVA, ADD_PYTHON, module_name):
+    return tmp_table
+
+
+def add_signatures(tmp_soup, tmp_dir, module_name, config):
     """ Add signatures to the current soup and rewrite the html file"""
+
     logging.debug(tmp_dir)
     sign_counter = 0
     python_sign_counter = 0
     java_sign_counter = 0
 
-    if ADD_JAVA:
+    if config.ADD_JAVA:
         functions_file = "java_doc_txts/" + module_name + "/functions.txt"
         if os.path.exists(functions_file):
             with open(functions_file, 'r') as f:
                 java_signatures = f.read().split("\n")
         else:
-            ADD_JAVA = False # This C++ module (module_name) may not exist in Java
+            config.ADD_JAVA = False # This C++ module (module_name) may not exist in Java
 
     # the HTML tag & class being used to find functions
     for function in tmp_soup.findAll("h2", {"class": "memtitle"}):
-        function_name = function.getText()
-        if os.name == 'nt': # if Windows
-            function_name = function_name.encode("ascii","ignore").decode()
-
-        # all functions have () in it's name
-        if "()" not in function_name:
+        function_name = None
+        for c in function.contents:
+             if isinstance(c, bs4.element.NavigableString):
+                 fn = str(c).encode("ascii","ignore").decode().strip()
+                 if not fn.endswith('()'): # all functions have () in it's name
+                     # enums, structures, etc
+                     continue
+                 function_name = fn[:-2]
+
+        if not function_name:
             continue
 
-        if "[" in function_name:
-            if "[1/" in function_name:
-                function_name = function_name.replace(' ', '')[:-7]
-            else:
-                continue
-        else:
-            function_name = function_name.replace(' ', '')[:-2]
         sign_counter += 1
 
-        # if not Windows computer
-        if os.name != 'nt':
-            function_name = function_name.replace(' ', '')[2:]
-
         cpp_table = function.findNext('table')
 
-        if ADD_PYTHON:
-            try:
+        if config.ADD_PYTHON:
+            signatures = config.python_signatures.get("cv::" + str(function_name), None)
+            if signatures:
                 print(function_name)
-                method = getattr(cv2, str(function_name))
-                description = str(method.__doc__).split("\n")
-                signature = ""
-                is_first_sig = True
-                for line in description:
-                    if line.startswith(".") or line == "":
-                        continue
-                    else:
-                        if is_first_sig:
-                            signature += line
-                            is_first_sig = False
-                        else:
-                            signature += " or " + line
-
-                cpp_table = append_table_to(cpp_table, tmp_soup, "Python:", signature, function_name)
+
+                description = create_description(tmp_soup, "Python:", signatures, function_name)
+                description['class'] = 'python_language'
+                old = cpp_table.next_sibling
+                if old.name != 'table':
+                    old = None
+                elif not 'python_language' in old.get('class', []):
+                    old = None
+                if old is None:
+                    cpp_table.insert_after(description)
+                else:
+                    old.replace_with(description)
                 python_sign_counter += 1
-            except AttributeError:
-                continue
 
-        if ADD_JAVA:
+        if config.ADD_JAVA:
             for signature in java_signatures:
                 if function_name in signature:
-                    append_table_to(cpp_table, tmp_soup, "Java:", signature, function_name)
+                    create_description(cpp_table, tmp_soup, "Java:", signature, function_name)
                     java_sign_counter += 1
                     break
 
index 34ea322..6908de2 100644 (file)
@@ -8,13 +8,37 @@ TODO:
 * clarify special case:
     http://docs.opencv.org/3.2.0/db/de0/group__core__utils.html#ga4910d7f86336cd4eff9dd05575667e41
 """
+from __future__ import print_function
+import os
 import re
 import sys
-import html_functions
+import logging
+
+loglevel=os.environ.get("LOGLEVEL", None)
+if loglevel:
+    logging.basicConfig(level=loglevel)
 
 ADD_JAVA = False
 ADD_PYTHON = True
 ROOT_DIR = sys.argv[1]
+PYTHON_SIGNATURES_FILE = sys.argv[2]
+
+import json
+python_signatures = dict()
+with open(PYTHON_SIGNATURES_FILE, "rt") as f:
+    python_signatures = json.load(f)
+    print("Loaded Python signatures: %d" % len(python_signatures))
+
+class Configuration():
+    def __init__(self):
+        self.ADD_PYTHON = ADD_PYTHON
+        self.python_signatures = python_signatures
+        self.ADD_JAVA = ADD_JAVA
+
+config = Configuration()
+
+
+import html_functions
 
 soup = html_functions.load_html_file(ROOT_DIR + "index.html")
 href_list = html_functions.get_links_list(soup, True)
@@ -24,11 +48,11 @@ for link in href_list:
     soup = html_functions.load_html_file(ROOT_DIR + link)
     sub_href_list = html_functions.get_links_list(soup, True)
     module_name = html_functions.get_text_between_substrings(link, "group__", ".html")
-    html_functions.add_signatures(soup, ROOT_DIR + link, ADD_JAVA, ADD_PYTHON, module_name)
+    html_functions.add_signatures(soup, ROOT_DIR + link, module_name, config)
 
     # add python signatures to the sub-modules
     link = re.sub(r"group__.+html", "", link)
     for sub_link in sub_href_list:
         tmp_dir = ROOT_DIR + link + sub_link
         soup = html_functions.load_html_file(tmp_dir)
-        html_functions.add_signatures(soup, tmp_dir, ADD_JAVA, ADD_PYTHON, module_name)
+        html_functions.add_signatures(soup, tmp_dir, module_name, config)