2 from latexparser import latexparser, TexCmd
3 import distutils.dep_util
5 import cPickle as pickle
8 from qfile import QOpen
12 python_api = pythonapi.reader("../../interfaces/python/api")
16 def __init__(self, filename, language):
17 assert language in ['py', 'c', 'cpp']
18 self.language = language
20 self.f_index = QOpen(os.path.join(self.language, filename), 'wt')
28 self.errors = open('errors', 'wt')
29 self.unhandled_commands = set()
31 self.function_props = {}
32 self.covered = set() # covered functions, used for error report
36 self.freshline = len(s) > 0 and (s[-1] == '\n')
37 self.f.write(s.replace('\n', '\n' + self.indent * " "))
39 def appendspace(self):
40 """ append a space to the output - if we're not at the start of a line """
41 if not self.freshline:
45 if (len(s) > 1) and (s[0] == '$' and s[-1] == '$') and self.state != 'math':
46 s = ":math:`%s`" % s[1:-1].strip()
47 elif self.state != 'math':
49 if self.state == 'fpreamble':
55 if self.state == 'math':
64 meth = self.cmd_gomath
66 cname = "cmd_" + c.cmd
67 meth = getattr(self, cname, self.unrecognized_cmd)
70 def cmd_gomath(self, c):
72 print >>self, "\n\n.. math::"
76 def cmd_chapter(self, c):
77 filename = str(c.params[0]).lower().replace(' ', '_').replace('/','_')
78 self.f_index.write(" %s\n" % filename)
79 self.f_chapter = QOpen(os.path.join(self.language, filename + '.rst'), 'wt')
81 self.f = self.f_chapter
83 title = str(c.params[0])
84 print >>self, '*' * len(title)
86 print >>self, '*' * len(title)
88 self.chapter_intoc = False
90 def cmd_section(self, c):
91 filename = str(c.params[0]).lower().replace(' ', '_').replace('/','_')
92 if not self.chapter_intoc:
93 self.chapter_intoc = True
94 print >>self.f_chapter
95 print >>self.f_chapter, '.. toctree::'
96 print >>self.f_chapter, ' :maxdepth: 2'
97 print >>self.f_chapter
98 self.f_chapter.write(" %s\n" % filename)
99 self.f_section = QOpen(os.path.join(self.language, filename + '.rst'), 'wt')
100 self.f = self.f_section
102 title = str(c.params[0])
104 print >>self, '=' * len(title)
106 print >>self, '.. highlight:: %s' % {'c': 'c', 'cpp': 'cpp', 'py': 'python'}[self.language]
109 def cmd_subsection(self, c):
111 print >>self, str(c.params[0])
112 print >>self, '-' * len(str(c.params[0]))
114 self.function_props = {}
116 def cmd_includegraphics(self, c):
117 filename = os.path.join('..', '..', str(c.params[0]))
118 print >>self, "\n\n.. image:: %s\n\n" % filename
120 def cmd_cvCppCross(self, c):
121 self.write(":ref:`%s`" % str(c.params[0]))
123 def cmd_cvCPyCross(self, c):
124 self.write(":ref:`%s`" % str(c.params[0]))
126 def cmd_cross(self, c):
127 self.write(":ref:`%s`" % str(c.params[0]))
129 def cmd_cvCross(self, c):
130 self.write(":ref:`%s`" % str(c.params[0]))
132 def cmd_cvclass(self, c):
135 nm = self.render(list(c.params[0].str))
136 print >>self, "\n.. index:: %s\n" % nm
137 print >>self, ".. _%s:\n" % nm
139 print >>self, '-' * len(nm)
141 if self.language == 'py':
142 print >>self, ".. class:: " + nm + "\n"
144 print >>self, ".. ctype:: " + nm + "\n"
148 def addtag(self, nm, c):
150 self.report_error(c, "empty name")
151 self.tags[nm] = "%s\t%s\t%d" % (nm, os.path.join(os.getcwd(), c.filename), c.lineno)
153 def cmd_cvfunc(self, c):
154 self.cmd_cvCPyFunc(c)
156 def cmd_cvCPyFunc(self, c):
158 nm = self.render(c.params[0].str)
159 print >>self, "\n.. index:: %s\n" % nm
160 print >>self, ".. _%s:\n" % nm
162 print >>self, '-' * len(nm)
164 self.state = 'fpreamble'
165 if self.description != "":
166 self.report_error(c, "overflow - preceding cvfunc (starting %s) not terminated?" % repr(self.description[:30]))
167 self.description = ""
170 self.function_props = {'name' : nm}
173 def cmd_cvCppFunc(self, c):
175 nm = self.render(c.params[0].str)
176 print >>self, "\n.. index:: %s\n" % nm
177 print >>self, ".. _%s:\n" % nm
179 print >>self, '-' * len(nm)
181 self.state = 'fpreamble'
182 if self.description != "":
183 self.report_error(c, "overflow - preceding cvfunc (starting %s) not terminated?" % repr(self.description[:30]))
184 self.description = ""
187 self.function_props = {'name' : nm}
190 def cmd_cvdefC(self, c):
191 if self.language != 'c':
193 s = str(c.params[0]).replace('\\_', '_')
194 s = s.replace('\\par', '')
195 s = s.replace(';', '')
197 print >>self, ".. cfunction:: " + s + "\n"
198 # print >>self, "=", repr(c.params[0].str)
199 print >>self, ' ' + self.description
200 self.description = ""
203 self.function_props['defpy'] = s
205 def cmd_cvdefCpp(self, c):
206 if self.language != 'cpp':
208 s = str(c.params[0]).replace('\\_', '_')
209 s = s.replace('\\par', '')
210 s = s.replace(';', '')
212 for proto in s.split('\\newline'):
213 if proto.strip() != "":
214 print >>self, "\n\n.. cfunction:: " + proto.strip() + "\n"
215 # print >>self, "=", repr(c.params[0].str)
216 if self.description != "":
217 print >>self, ' ' + self.description
219 self.report_error(c, 'empty description')
220 self.description = ""
223 self.function_props['defpy'] = s
225 def cmd_cvdefPy(self, c):
226 if self.language != 'py':
228 s = str(c.params[0]).replace('\\_', '_')
230 print >>self, ".. function:: " + s + "\n"
231 # print >>self, "=", repr(c.params[0].str)
232 print >>self, ' ' + self.description
234 self.description = ""
236 self.function_props['defpy'] = s
238 pp.ParserElement.setDefaultWhitespaceChars(" \n\t")
240 def listify(s, loc, toks):
242 x.setParseAction(listify)
245 return returnList(pp.Optional(word + pp.ZeroOrMore(pp.Suppress(',') + word)))
247 return pp.Suppress(pp.Literal(s))
249 ident = pp.Word(pp.alphanums + "_.+-")
250 ident_or_tuple = ident | (sl('(') + CommaList(ident) + sl(')'))
251 initializer = ident_or_tuple
252 arg = returnList(ident + pp.Optional(sl('=') + initializer))
254 decl = ident + sl('(') + CommaList(arg) + sl(')') + sl("->") + ident_or_tuple + pp.StringEnd()
257 l = decl.parseString(s)
258 if str(l[0]) != self.function_props['name']:
259 self.report_error(c, 'Decl "%s" does not match function name "%s"' % (str(l[0]), self.function_props['name']))
260 self.function_props['signature'] = l
261 if l[0] in python_api:
262 (ins, outs) = python_api[l[0]]
263 ins = [a for a in ins if not 'O' in a.flags]
265 outs = outs.split(',')
266 if len(ins) != len(l[1]):
267 self.report_error(c, "function %s documented arity %d, code arity %d" % (l[0], len(l[1]), len(ins)))
270 self.report_error(c, "function %s documented None, but code has %s" % (l[0], l[2]))
272 if isinstance(l[2], str):
276 if len(outs) != len(doc_outs):
277 self.report_error(c, "function %s output documented tuple %d, code %d" % (l[0], len(outs), len(doc_outs)))
279 # self.report_error(c, "function %s documented but not found in code" % l[0])
281 except pp.ParseException, pe:
282 self.report_error(c, str(pe))
286 def report_error(self, c, msg):
287 print >>self.errors, "%s:%d: [%s] Error %s" % (c.filename, c.lineno, self.language, msg)
289 def cmd_begin(self, c):
290 if len(c.params) == 0:
291 self.report_error(c, "Malformed begin")
295 self.envstack.append((s, (c.filename, c.lineno)))
296 if s == 'description':
297 if self.language == 'py' and 'name' in self.function_props and not 'defpy' in self.function_props:
298 self.report_error(c, "No cvdefPy for function %s" % self.function_props['name'])
300 elif s == 'lstlisting':
301 print >>self, "\n::\n"
303 elif s in ['itemize', 'enumerate']:
306 self.f = StringIO.StringIO()
310 def cmd_item(self, c):
311 if len(self.ee()) == 0:
312 self.report_error(c, "item without environment")
315 markup = {'itemize' : '*', 'enumerate' : '#.', 'description' : '*'}[self.ee()[-1]]
317 markup += " " + self.render([c.args[0].str])
318 if len(c.params) > 0:
319 markup += " " + self.render(c.params[0].str)
320 self.write("\n\n" + markup)
323 def cmd_end(self, c):
324 if len(c.params) != 1:
325 self.report_error(c, "Malformed end")
327 if len(self.envstack) == 0:
328 self.report_error(c, "end with no env")
332 if self.envstack == []:
333 print "Cannot pop at", (c.filename, c.lineno)
334 if self.envstack[-1][0] != s:
335 self.report_error(c, "end{%s} does not match current stack %s" % (s, repr(self.envstack)))
337 if s == 'description':
340 self.function_props['done'] = True
341 elif s in ['itemize', 'enumerate']:
344 tabletxt = self.f.getvalue()
345 self.f = self.f_section
346 self.f.write(self.handle_table(tabletxt))
347 elif s == 'lstlisting':
351 print >>self, ".." # otherwise a following :param: gets treated as more listing
352 elif s == 'document':
357 def cmd_label(self, c):
360 def cmd_lstinputlisting(self, c):
362 print >>self.f, ".. include:: %s" % os.path.join('..', s)
363 print >>self.f, " :literal:"
367 def cmd_cvC(self, c):
368 self.do_conditional(['c'], c)
369 def cmd_cvCpp(self, c):
370 self.do_conditional(['cpp'], c)
371 def cmd_cvPy(self, c):
372 self.do_conditional(['py'], c)
373 def cmd_cvCPy(self, c):
374 self.do_conditional(['c', 'py'], c)
375 def do_conditional(self, langs, c):
376 if self.language in langs:
377 self.doL(c.params[0].str, False)
380 """ return L rendered as a string """
382 self.f = StringIO.StringIO()
384 if isinstance(x, TexCmd):
388 r = self.f.getvalue()
392 def cmd_cvarg(self, c):
393 if len(c.params) != 2:
394 self.report_error(c, "Malformed cvarg")
396 is_func_arg = self.ee() == ['description'] and not 'done' in self.function_props
398 nm = self.render(c.params[0].str)
399 print >>self, '\n:param %s: ' % nm,
400 type = None # Try to figure out the argument type
401 # For now, multiple args get a pass
402 if (self.language == 'py') and ('signature' in self.function_props) and (not ',' in nm):
403 sig = self.function_props['signature']
404 argnames = [a[0] for a in sig[1]]
405 if isinstance(sig[2], str):
408 resnames = list(sig[2])
409 if not nm in argnames + resnames:
410 self.report_error(c, "Argument %s is not mentioned in signature (%s) (%s)" % (nm, ", ".join(argnames), ", ".join(resnames)))
412 api = python_api.get(self.function_props['name'], None)
415 adict = dict([(a.nm, a) for a in ins])
416 arg = adict.get(nm, None)
420 self.report_error(c, 'cannot find arg %s in code' % nm)
421 elif self.ee() == ['description']:
422 print >>self, '\n* **%s** ' % self.render(c.params[0].str),
423 elif self.ee() == ['description', 'description']:
424 print >>self, '\n* **%s** ' % self.render(c.params[0].str),
426 print 'strange env', self.envstack
428 self.doL(c.params[1].str, False)
431 if is_func_arg and type:
432 type = type.replace('*', '')
434 "ints" : "sequence of int",
435 "floats" : "sequence of int",
436 "IplImages" : "sequence of :class:`IplImage`",
441 "cvarrseq" : ":class:`CvArr` or :class:`CvSeq`",
442 "CvPoint2D32fs" : "sequence of (float, float)",
443 "pts_npts_contours" : "list of lists of (x,y) pairs",
445 print >>self, "\n:type %s: %s" % (nm, translate.get(type, ':class:`%s`' % type))
447 def cmd_genc(self, c): pass
448 def cmd_genpy(self, c): pass
449 def cmd_author(self, c): pass
450 def cmd_cite(self, c): pass
451 def cmd_date(self, c): pass
452 def cmd_def(self, c): pass
453 def cmd_documentclass(self, c): pass
454 def cmd_maketitle(self, c): pass
455 def cmd_newcommand(self, c): pass
456 def cmd_newline(self, c): pass
457 def cmd_setcounter(self, c): pass
458 def cmd_tableofcontents(self, c): pass
459 def cmd_targetlang(self, c): pass
460 def cmd_usepackage(self, c): pass
461 def cmd_title(self, c): pass
462 def cmd_par(self, c): pass
463 def cmd_hline(self, c):
464 print >>self, "\\hline"
466 def cmd_href(self, c):
467 if len(c.params) == 2:
468 self.write("`%s <%s>`_" % (str(c.params[1]), str(c.params[0])))
470 self.report_error(c, "href should have two params")
472 def cmd_url(self, c):
473 self.write(str(c.params[0]))
475 def cmd_emph(self, c):
476 self.write("*" + self.render(c.params[0].str) + "*")
478 def cmd_textit(self, c):
479 self.write("*" + self.render(c.params[0].str) + "*")
481 def cmd_textbf(self, c):
482 self.write("**" + self.render(c.params[0].str) + "**")
484 def cmd_texttt(self, c):
485 self.write("``" + self.render(c.params[0].str) + "``")
487 def default_cmd(self, c):
488 if self.f == self.f_section:
491 def unrecognized_cmd(self, c):
492 if self.f == self.f_section:
494 self.unhandled_commands.add(c.cmd)
496 def doL(self, L, newlines = True):
498 if isinstance(x, TexCmd):
501 if 'lstlisting' in self.ee() or not newlines:
504 self.doplain(x.lstrip())
505 if self.state in ['math'] or not newlines:
510 def handle_table(self, s):
511 oneline = s.replace('\n', ' ').strip()
512 rows = [r.strip() for r in oneline.split('\\hline')]
516 cols = [c.strip() for c in r.split('&')]
518 widths = [max([len(r[i]) for r in tab]) for i in range(len(tab[0]))]
520 st = "" # Sphinx table
523 sep = "+" + "+".join(["-" * w for w in widths]) + "+"
526 st += "|" + "|".join([c.center(w) for (c, w) in zip(r, widths)]) + "|" + '\n'
529 st = '.. table::\n\n'
530 sep = " ".join(["=" * w for w in widths])
531 st += ' ' + sep + '\n'
532 for y,r in enumerate(tab):
533 st += ' ' + " ".join([c.ljust(w) for (c, w) in zip(r, widths)]) + '\n'
535 st += ' ' + sep + '\n'
536 st += ' ' + sep + '\n'
540 """ Return tags of the envstack. envstack[0] is 'document', so skip it """
541 return [n for (n,_) in self.envstack[1:]]
548 if self.envstack != []:
549 print >>self.errors, "Error envstack not empty at end of doc: " + repr(self.envstack)
550 print >>self.errors, "unrecognized commands:"
551 print >>self.errors, "\n ".join(sorted(self.unhandled_commands))
553 print >>self.errors, "The following functions are undocumented"
554 for f in sorted(set(python_api) - self.covered):
555 print >>self.errors, ' ', f
557 print >>self.f_index, """
567 sources = ['../' + f for f in os.listdir('..') if f.endswith('.tex')]
568 if distutils.dep_util.newer_group(["latexparser.py"] + sources, "pickled"):
569 fulldoc = latexparser(sys.argv[1], 0)
570 pickle.dump(fulldoc, open("pickled", 'wb'))
571 raw = open('fulldoc', 'w')
576 fulldoc = pickle.load(open("pickled", "rb"))
578 # Filter on target language
579 def preprocess_conditionals(fd, conditionals):
583 if isinstance(x, TexCmd):
585 loc = (x.filename, x.lineno)
586 if ll.startswith("if"):
587 # print " " * len(ifstack), '{', loc
588 ifstack.append((conditionals.get(ll[2:], False), loc))
589 elif ll.startswith("else"):
590 ifstack[-1] = (not ifstack[-1][0], ifstack[-1][1])
591 elif ll.startswith("fi"):
593 # print " " * len(ifstack), '}', loc
594 elif not False in [p for (p,_) in ifstack]:
597 if not False in [p for (p,_) in ifstack]:
600 print "unterminated if", ifstack
605 for language in ['c', 'cpp', 'py']:
606 doc = preprocess_conditionals(fulldoc, {
608 'Python' : language=='py',
609 'Py' : language=='py',
610 'CPy' : (language=='py' or language == 'c'),
611 'Cpp' : language=='cpp',
614 raw = open('raw.%s' % language, 'w')
618 sr = SphinxWriter('index.rst', language)
620 OpenCV |version| %s Reference
621 =================================
623 The OpenCV Wiki is here: http://opencv.willowgarage.com/
630 """ % {'c': 'C', 'cpp': 'C++', 'py': 'Python'}[language]
633 tags.update(sr.get_tags())
634 open('TAGS', 'w').write("\n".join(sorted(tags.values())) + "\n")