Skip to content

Commit 912de36

Browse files
Cartuchoalalek
authored andcommitted
Using __doc__ to add Python signatures to the docs.
1 parent 66e09bc commit 912de36

File tree

3 files changed

+266
-1
lines changed

3 files changed

+266
-1
lines changed

doc/CMakeLists.txt

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ if(BUILD_DOCS AND DOXYGEN_FOUND)
205205
list(APPEND js_tutorials_assets_deps "${f}" "${opencv_tutorial_html_dir}/${fname}")
206206
endforeach()
207207

208-
add_custom_target(doxygen
208+
add_custom_target(doxygen_cpp
209209
COMMAND ${DOXYGEN_EXECUTABLE} ${doxyfile}
210210
DEPENDS ${doxyfile} ${rootfile} ${bibfile} ${deps} ${js_tutorials_assets_deps}
211211
)
@@ -214,6 +214,28 @@ if(BUILD_DOCS AND DOXYGEN_FOUND)
214214
COMPONENT "docs" OPTIONAL
215215
)
216216

217+
if(BUILD_opencv_python2)
218+
add_custom_target(doxygen_python
219+
COMMAND ${PYTHON2_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/tools/python_signatures.py" "${CMAKE_CURRENT_BINARY_DIR}/doxygen/html/"
220+
DEPENDS doxygen_cpp opencv_python2
221+
)
222+
add_custom_target(doxygen
223+
DEPENDS doxygen_cpp doxygen_python
224+
)
225+
elseif(BUILD_opencv_python3)
226+
add_custom_target(doxygen_python
227+
COMMAND ${PYTHON3_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/tools/python_signatures.py" "${CMAKE_CURRENT_BINARY_DIR}/doxygen/html/"
228+
DEPENDS doxygen_cpp opencv_python3
229+
)
230+
add_custom_target(doxygen
231+
DEPENDS doxygen_cpp doxygen_python
232+
)
233+
else()
234+
add_custom_target(doxygen
235+
DEPENDS doxygen_cpp
236+
)
237+
endif()
238+
217239
# Alias to build/install docs only
218240
add_custom_target(install_docs
219241
DEPENDS doxygen

doc/tools/html_functions.py

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
import logging
2+
import os
3+
import codecs
4+
import cv2
5+
6+
7+
try:
8+
from bs4 import BeautifulSoup
9+
except ImportError:
10+
raise ImportError('Error: '
11+
'Install BeautifulSoup (bs4) for adding'
12+
' Python & Java signatures documentation')
13+
14+
15+
def is_not_module_link(tmp_link):
16+
""" Checks if a link belongs to a c++ method """
17+
if tmp_link is None:
18+
return True
19+
if "group" not in tmp_link:
20+
return True
21+
if "#" in tmp_link:
22+
return True
23+
return False
24+
25+
26+
def get_links_list(tmp_soup, filter_links):
27+
""" Get a list of links from a soup """
28+
tmp_href_list = []
29+
for tmp_link in tmp_soup.findAll('a'):
30+
tmp_href = tmp_link.get('href')
31+
if filter_links:
32+
if is_not_module_link(tmp_href):
33+
continue
34+
tmp_href_list.append(tmp_href)
35+
return tmp_href_list
36+
37+
38+
def load_html_file(file_dir):
39+
""" Uses BeautifulSoup to load an html """
40+
with open(file_dir) as fp:
41+
tmp_soup = BeautifulSoup(fp, 'html.parser')
42+
return tmp_soup
43+
44+
45+
def add_item(tmp_soup, new_row, is_parameter, text):
46+
""" Adds a new html tag for the table with the signature """
47+
new_item = tmp_soup.new_tag('td')
48+
if is_parameter:
49+
new_item = tmp_soup.new_tag('td', **{'class': 'paramname'})
50+
new_item.append(text)
51+
new_row.append(new_item)
52+
return new_row
53+
54+
55+
def get_text_between_substrings(sig, begin_char, end_char):
56+
return sig.partition(begin_char)[-1].rpartition(end_char)[0]
57+
58+
59+
def add_signature_to_table(tmp_soup, new_row, signature, function_name, language, ident):
60+
""" Add a signature to an html table"""
61+
if ident:
62+
new_item = tmp_soup.new_tag('td', style="padding-left: 0.5cm;")
63+
else:
64+
new_item = tmp_soup.new_tag('td')
65+
66+
if "-> None" in signature:
67+
pass
68+
elif "->" in signature:
69+
new_item.append(signature.split("->", 1)[1] + ' =')
70+
new_row.append(new_item)
71+
72+
if "Python" in language:
73+
function_name = "cv2." + function_name
74+
elif "Java" in language:
75+
# get word before function_name (= output)
76+
str_before_bracket = signature.split('(', 1)[0]
77+
list_of_words = str_before_bracket.split()
78+
output = list_of_words[len(list_of_words) - 2]
79+
new_item.append(output + " ")
80+
new_row.append(new_item)
81+
82+
new_row = add_item(tmp_soup, new_row, False, function_name + '(')
83+
new_row = add_item(tmp_soup, new_row, True, get_text_between_substrings(signature, "(", ")"))
84+
new_row = add_item(tmp_soup, new_row, False, ')')
85+
return new_row
86+
87+
88+
def new_line(tmp_soup, tmp_table, new_row):
89+
""" Adds a new line to the html table """
90+
tmp_table.append(new_row)
91+
new_row = tmp_soup.new_tag('tr')
92+
return new_row
93+
94+
95+
def add_bolded(tmp_soup, new_row, text):
96+
""" Adds bolded text to the table """
97+
new_item = tmp_soup.new_tag('th', style="text-align:left")
98+
new_item.append(text)
99+
new_row.append(new_item)
100+
return new_row
101+
102+
103+
def append_table_to(cpp_table, tmp_soup, language, signature, function_name):
104+
""" Insert the new Python / Java table after the current html c++ table """
105+
if signature != "":
106+
tmp_table = tmp_soup.new_tag('table')
107+
new_row = tmp_soup.new_tag('tr')
108+
new_row = add_bolded(tmp_soup, new_row, language)
109+
ident = False
110+
111+
if len(signature) > 120:
112+
new_row = new_line(tmp_soup, tmp_table, new_row)
113+
ident = True
114+
115+
if " or " in signature:
116+
ident = True
117+
for tmp_sig in signature.split(" or "):
118+
new_row = new_line(tmp_soup, tmp_table, new_row)
119+
new_row = add_signature_to_table(tmp_soup, new_row, tmp_sig, function_name, language, ident)
120+
new_row = new_line(tmp_soup, tmp_table, new_row)
121+
else:
122+
new_row = add_signature_to_table(tmp_soup, new_row, signature, function_name, language, ident)
123+
tmp_table.append(new_row)
124+
125+
cpp_table.insert_after(tmp_table)
126+
return cpp_table
127+
128+
129+
def add_signatures(tmp_soup, tmp_dir, ADD_JAVA, ADD_PYTHON, module_name):
130+
""" Add signatures to the current soup and rewrite the html file"""
131+
logging.debug(tmp_dir)
132+
sign_counter = 0
133+
python_sign_counter = 0
134+
java_sign_counter = 0
135+
136+
if ADD_JAVA:
137+
functions_file = "java_doc_txts/" + module_name + "/functions.txt"
138+
if os.path.exists(functions_file):
139+
with open(functions_file, 'r') as f:
140+
java_signatures = f.read().split("\n")
141+
else:
142+
ADD_JAVA = False # This C++ module (module_name) may not exist in Java
143+
144+
# the HTML tag & class being used to find functions
145+
for function in tmp_soup.findAll("h2", {"class": "memtitle"}):
146+
function_name = function.getText()
147+
if os.name == 'nt': # if Windows
148+
function_name = function_name.encode("ascii","ignore").decode()
149+
150+
# all functions have () in it's name
151+
if "()" not in function_name:
152+
continue
153+
154+
if "[" in function_name:
155+
if "[1/" in function_name:
156+
function_name = function_name.replace(' ', '')[:-7]
157+
else:
158+
continue
159+
else:
160+
function_name = function_name.replace(' ', '')[:-2]
161+
sign_counter += 1
162+
163+
# if not Windows computer
164+
if os.name != 'nt':
165+
function_name = function_name.replace(' ', '')[2:]
166+
167+
cpp_table = function.findNext('table')
168+
169+
if ADD_PYTHON:
170+
try:
171+
print(function_name)
172+
method = getattr(cv2, str(function_name))
173+
description = str(method.__doc__).split("\n")
174+
signature = ""
175+
is_first_sig = True
176+
for line in description:
177+
if line.startswith(".") or line == "":
178+
continue
179+
else:
180+
if is_first_sig:
181+
signature += line
182+
is_first_sig = False
183+
else:
184+
signature += " or " + line
185+
186+
cpp_table = append_table_to(cpp_table, tmp_soup, "Python:", signature, function_name)
187+
python_sign_counter += 1
188+
except AttributeError:
189+
continue
190+
191+
if ADD_JAVA:
192+
for signature in java_signatures:
193+
if function_name in signature:
194+
append_table_to(cpp_table, tmp_soup, "Java:", signature, function_name)
195+
java_sign_counter += 1
196+
break
197+
198+
tmp_str = str(tmp_soup)
199+
if os.name == 'nt': # if Windows
200+
with open(tmp_dir, "wb") as tmp_file:
201+
tmp_file.write(tmp_str.encode("ascii","ignore"))
202+
else:
203+
with open(tmp_dir, "w") as tmp_file:
204+
tmp_file.write(tmp_str)
205+
206+
logging.debug("Added [" + str(python_sign_counter) + \
207+
"/" + str(sign_counter) + "] Python signatures")
208+
logging.debug("Added [" + str(java_sign_counter) + \
209+
"/" + str(sign_counter) + "] Java signatures")

doc/tools/python_signatures.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
"""
2+
This code adds Python signatures to the docs.
3+
4+
TODO:
5+
* clarify when there are several C++ signatures corresponding to a single Python function.
6+
i.e: calcHist():
7+
http://docs.opencv.org/3.2.0/d6/dc7/group__imgproc__hist.html#ga4b2b5fd75503ff9e6844cc4dcdaed35d
8+
* clarify special case:
9+
http://docs.opencv.org/3.2.0/db/de0/group__core__utils.html#ga4910d7f86336cd4eff9dd05575667e41
10+
"""
11+
import re
12+
import sys
13+
import html_functions
14+
15+
ADD_JAVA = False
16+
ADD_PYTHON = True
17+
ROOT_DIR = sys.argv[1]
18+
19+
soup = html_functions.load_html_file(ROOT_DIR + "index.html")
20+
href_list = html_functions.get_links_list(soup, True)
21+
22+
for link in href_list:
23+
# add python signatures to the module
24+
soup = html_functions.load_html_file(ROOT_DIR + link)
25+
sub_href_list = html_functions.get_links_list(soup, True)
26+
module_name = html_functions.get_text_between_substrings(link, "group__", ".html")
27+
html_functions.add_signatures(soup, ROOT_DIR + link, ADD_JAVA, ADD_PYTHON, module_name)
28+
29+
# add python signatures to the sub-modules
30+
link = re.sub(r"group__.+html", "", link)
31+
for sub_link in sub_href_list:
32+
tmp_dir = ROOT_DIR + link + sub_link
33+
soup = html_functions.load_html_file(tmp_dir)
34+
html_functions.add_signatures(soup, tmp_dir, ADD_JAVA, ADD_PYTHON, module_name)

0 commit comments

Comments
 (0)