DocumentationTools/ModuleDocumentor.py

changeset 0
de9c2efb9d02
child 12
1d8dd9706f46
equal deleted inserted replaced
-1:000000000000 0:de9c2efb9d02
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2003 - 2009 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the builtin documentation generator.
8
9 The different parts of the module document are assembled from the parsed
10 Python file. The appearance is determined by several templates defined within
11 this module.
12 """
13
14 import sys
15 import re
16
17 import TemplatesListsStyle
18 import TemplatesListsStyleCSS
19
20 from Utilities import html_uencode
21 from Utilities.ModuleParser import RB_SOURCE
22
23 _signal = re.compile(r"""
24 ^@signal [ \t]+
25 (?P<SignalName1>
26 [a-zA-Z_] \w* [ \t]* \( [^)]* \)
27 )
28 [ \t]* (?P<SignalDescription1> .*)
29 |
30 ^@signal [ \t]+
31 (?P<SignalName2>
32 [a-zA-Z_] \w*
33 )
34 [ \t]+ (?P<SignalDescription2> .*)
35 """, re.VERBOSE | re.DOTALL | re.MULTILINE).search
36
37 _event = re.compile(r"""
38 ^@event [ \t]+
39 (?P<EventName1>
40 [a-zA-Z_] \w* [ \t]* \( [^)]* \)
41 )
42 [ \t]* (?P<EventDescription1> .*)
43 |
44 ^@event [ \t]+
45 (?P<EventName2>
46 [a-zA-Z_] \w*
47 )
48 [ \t]+ (?P<EventDescription2> .*)
49 """, re.VERBOSE | re.DOTALL | re.MULTILINE).search
50
51 class TagError(Exception):
52 """
53 Exception class raised, if an invalid documentation tag was found.
54 """
55
56 class ModuleDocument(object):
57 """
58 Class implementing the builtin documentation generator.
59 """
60 def __init__(self, module, colors, stylesheet = None):
61 """
62 Constructor
63
64 @param module the information of the parsed Python file
65 @param colors dictionary specifying the various colors for the output
66 (dictionary of strings)
67 @param stylesheet the style to be used for the generated pages (string)
68 """
69 self.module = module
70 self.empty = True
71
72 self.stylesheet = stylesheet
73
74 if self.stylesheet:
75 self.headerTemplate = TemplatesListsStyleCSS.headerTemplate
76 self.footerTemplate = TemplatesListsStyleCSS.footerTemplate
77 self.moduleTemplate = TemplatesListsStyleCSS.moduleTemplate
78 self.rbFileTemplate = TemplatesListsStyleCSS.rbFileTemplate
79 self.classTemplate = TemplatesListsStyleCSS.classTemplate
80 self.methodTemplate = TemplatesListsStyleCSS.methodTemplate
81 self.constructorTemplate = TemplatesListsStyleCSS.constructorTemplate
82 self.rbModuleTemplate = TemplatesListsStyleCSS.rbModuleTemplate
83 self.rbModulesClassTemplate = TemplatesListsStyleCSS.rbModulesClassTemplate
84 self.functionTemplate = TemplatesListsStyleCSS.functionTemplate
85 self.listTemplate = TemplatesListsStyleCSS.listTemplate
86 self.listEntryTemplate = TemplatesListsStyleCSS.listEntryTemplate
87 self.listEntryNoneTemplate = TemplatesListsStyleCSS.listEntryNoneTemplate
88 self.listEntryDeprecatedTemplate = \
89 TemplatesListsStyleCSS.listEntryDeprecatedTemplate
90 self.listEntrySimpleTemplate = TemplatesListsStyleCSS.listEntrySimpleTemplate
91 self.paragraphTemplate = TemplatesListsStyleCSS.paragraphTemplate
92 self.parametersListTemplate = TemplatesListsStyleCSS.parametersListTemplate
93 self.parametersListEntryTemplate = \
94 TemplatesListsStyleCSS.parametersListEntryTemplate
95 self.returnsTemplate = TemplatesListsStyleCSS.returnsTemplate
96 self.exceptionsListTemplate = TemplatesListsStyleCSS.exceptionsListTemplate
97 self.exceptionsListEntryTemplate = \
98 TemplatesListsStyleCSS.exceptionsListEntryTemplate
99 self.signalsListTemplate = TemplatesListsStyleCSS.signalsListTemplate
100 self.signalsListEntryTemplate = \
101 TemplatesListsStyleCSS.signalsListEntryTemplate
102 self.eventsListTemplate = TemplatesListsStyleCSS.eventsListTemplate
103 self.eventsListEntryTemplate = TemplatesListsStyleCSS.eventsListEntryTemplate
104 self.deprecatedTemplate = TemplatesListsStyleCSS.deprecatedTemplate
105 self.authorInfoTemplate = TemplatesListsStyleCSS.authorInfoTemplate
106 self.seeListTemplate = TemplatesListsStyleCSS.seeListTemplate
107 self.seeListEntryTemplate = TemplatesListsStyleCSS.seeListEntryTemplate
108 self.seeLinkTemplate = TemplatesListsStyleCSS.seeLinkTemplate
109 self.sinceInfoTemplate = TemplatesListsStyleCSS.sinceInfoTemplate
110 else:
111 self.headerTemplate = TemplatesListsStyle.headerTemplate % colors
112 self.footerTemplate = TemplatesListsStyle.footerTemplate % colors
113 self.moduleTemplate = TemplatesListsStyle.moduleTemplate % colors
114 self.rbFileTemplate = TemplatesListsStyle.rbFileTemplate % colors
115 self.classTemplate = TemplatesListsStyle.classTemplate % colors
116 self.methodTemplate = TemplatesListsStyle.methodTemplate % colors
117 self.constructorTemplate = TemplatesListsStyle.constructorTemplate % colors
118 self.rbModuleTemplate = TemplatesListsStyle.rbModuleTemplate % colors
119 self.rbModulesClassTemplate = \
120 TemplatesListsStyle.rbModulesClassTemplate % colors
121 self.functionTemplate = TemplatesListsStyle.functionTemplate % colors
122 self.listTemplate = TemplatesListsStyle.listTemplate % colors
123 self.listEntryTemplate = TemplatesListsStyle.listEntryTemplate % colors
124 self.listEntryNoneTemplate = \
125 TemplatesListsStyle.listEntryNoneTemplate % colors
126 self.listEntryDeprecatedTemplate = \
127 TemplatesListsStyle.listEntryDeprecatedTemplate % colors
128 self.listEntrySimpleTemplate = \
129 TemplatesListsStyle.listEntrySimpleTemplate % colors
130 self.paragraphTemplate = TemplatesListsStyle.paragraphTemplate % colors
131 self.parametersListTemplate = \
132 TemplatesListsStyle.parametersListTemplate % colors
133 self.parametersListEntryTemplate = \
134 TemplatesListsStyle.parametersListEntryTemplate % colors
135 self.returnsTemplate = TemplatesListsStyle.returnsTemplate % colors
136 self.exceptionsListTemplate = \
137 TemplatesListsStyle.exceptionsListTemplate % colors
138 self.exceptionsListEntryTemplate = \
139 TemplatesListsStyle.exceptionsListEntryTemplate % colors
140 self.signalsListTemplate = TemplatesListsStyle.signalsListTemplate % colors
141 self.signalsListEntryTemplate = \
142 TemplatesListsStyle.signalsListEntryTemplate % colors
143 self.eventsListTemplate = TemplatesListsStyle.eventsListTemplate % colors
144 self.eventsListEntryTemplate = \
145 TemplatesListsStyle.eventsListEntryTemplate % colors
146 self.deprecatedTemplate = TemplatesListsStyle.deprecatedTemplate % colors
147 self.authorInfoTemplate = TemplatesListsStyle.authorInfoTemplate % colors
148 self.seeListTemplate = TemplatesListsStyle.seeListTemplate % colors
149 self.seeListEntryTemplate = TemplatesListsStyle.seeListEntryTemplate % colors
150 self.seeLinkTemplate = TemplatesListsStyle.seeLinkTemplate % colors
151 self.sinceInfoTemplate = TemplatesListsStyle.sinceInfoTemplate % colors
152
153 self.keywords = [] # list of tuples containing the name (string) and
154 # the ref (string). The ref is without the filename part.
155 self.generated = False
156
157 def isEmpty(self):
158 """
159 Public method to determine, if the module contains any classes or functions.
160
161 @return Flag indicating an empty module (i.e. __init__.py without
162 any contents)
163 """
164 return self.empty
165
166 def name(self):
167 """
168 Public method used to get the module name.
169
170 @return The name of the module. (string)
171 """
172 return self.module.name
173
174 def description(self):
175 """
176 Public method used to get the description of the module.
177
178 @return The description of the module. (string)
179 """
180 return self.__formatDescription(self.module.description)
181
182 def shortDescription(self):
183 """
184 Public method used to get the short description of the module.
185
186 The short description is just the first line of the modules
187 description.
188
189 @return The short description of the module. (string)
190 """
191 return self.__getShortDescription(self.module.description)
192
193 def genDocument(self):
194 """
195 Public method to generate the source code documentation.
196
197 @return The source code documentation. (string)
198 """
199 doc = self.headerTemplate % { \
200 'Title' : self.module.name,
201 'Style' : self.stylesheet} + \
202 self.__genModuleSection() + \
203 self.footerTemplate
204 self.generated = True
205 return doc
206
207 def __genModuleSection(self):
208 """
209 Private method to generate the body of the document.
210
211 @return The body of the document. (string)
212 """
213 globalsList = self.__genGlobalsListSection()
214 classList = self.__genClassListSection()
215 functionList = self.__genFunctionListSection()
216 try:
217 if self.module.type == RB_SOURCE:
218 rbModulesList = self.__genRbModulesListSection()
219 modBody = self.rbFileTemplate % { \
220 'Module' : self.module.name,
221 'ModuleDescription' : \
222 self.__formatDescription(self.module.description),
223 'GlobalsList' : globalsList,
224 'ClassList' : classList,
225 'RbModulesList' : rbModulesList,
226 'FunctionList' : functionList,
227 }
228 else:
229 modBody = self.moduleTemplate % { \
230 'Module' : self.module.name,
231 'ModuleDescription' : \
232 self.__formatDescription(self.module.description),
233 'GlobalsList' : globalsList,
234 'ClassList' : classList,
235 'FunctionList' : functionList,
236 }
237 except TagError, e:
238 sys.stderr.write("Error in tags of description of module %s.\n" % \
239 self.module.name)
240 sys.stderr.write("%s\n" % e)
241 return ""
242
243 classesSection = self.__genClassesSection()
244 functionsSection = self.__genFunctionsSection()
245 if self.module.type == RB_SOURCE:
246 rbModulesSection = self.__genRbModulesSection()
247 else:
248 rbModulesSection = ""
249 return "%s%s%s%s" % (modBody, classesSection, rbModulesSection, functionsSection)
250
251 def __genListSection(self, names, dict, kwSuffix = ""):
252 """
253 Private method to generate a list section of the document.
254
255 @param names The names to appear in the list. (list of strings)
256 @param dict A dictionary containing all relevant information.
257 @param kwSuffix suffix to be used for the QtHelp keywords (string)
258 @return The list section. (string)
259 """
260 lst = []
261 for name in names:
262 lst.append(self.listEntryTemplate % { \
263 'Link' : "%s" % name,
264 'Name' : dict[name].name,
265 'Description' : self.__getShortDescription(dict[name].description),
266 'Deprecated' : self.__checkDeprecated(dict[name].description) and \
267 self.listEntryDeprecatedTemplate or "",
268 })
269 if kwSuffix:
270 n = "%s (%s)" % (name, kwSuffix)
271 else:
272 n = "%s" % name
273 self.keywords.append((n, "#%s" % name))
274 return ''.join(lst)
275
276 def __genGlobalsListSection(self, class_ = None):
277 """
278 Private method to generate the section listing all global attributes of
279 the module.
280
281 @param class_ reference to a class object (Class)
282 @return The globals list section. (string)
283 """
284 if class_ is not None:
285 attrNames = sorted(class_.globals.keys())
286 else:
287 attrNames = sorted(self.module.globals.keys())
288 if attrNames:
289 s = ''.join(
290 [self.listEntrySimpleTemplate % {'Name' : name} for name in attrNames])
291 else:
292 s = self.listEntryNoneTemplate
293 return self.listTemplate % { \
294 'Entries' : s,
295 }
296
297 def __genClassListSection(self):
298 """
299 Private method to generate the section listing all classes of the module.
300
301 @return The classes list section. (string)
302 """
303 names = self.module.classes.keys()
304 names.sort()
305 if names:
306 self.empty = False
307 s = self.__genListSection(names, self.module.classes)
308 else:
309 s = self.listEntryNoneTemplate
310 return self.listTemplate % { \
311 'Entries' : s,
312 }
313
314 def __genRbModulesListSection(self):
315 """
316 Private method to generate the section listing all modules of the file
317 (Ruby only).
318
319 @return The modules list section. (string)
320 """
321 names = self.module.modules.keys()
322 names.sort()
323 if names:
324 self.empty = False
325 s = self.__genListSection(names, self.module.modules)
326 else:
327 s = self.listEntryNoneTemplate
328 return self.listTemplate % { \
329 'Entries' : s,
330 }
331
332 def __genFunctionListSection(self):
333 """
334 Private method to generate the section listing all functions of the module.
335
336 @return The functions list section. (string)
337 """
338 names = self.module.functions.keys()
339 names.sort()
340 if names:
341 self.empty = False
342 s = self.__genListSection(names, self.module.functions)
343 else:
344 s = self.listEntryNoneTemplate
345 return self.listTemplate % { \
346 'Entries' : s,
347 }
348
349 def __genClassesSection(self):
350 """
351 Private method to generate the document section with details about classes.
352
353 @return The classes details section. (string)
354 """
355 classNames = self.module.classes.keys()
356 classNames.sort()
357 classes = []
358 for className in classNames:
359 _class = self.module.classes[className]
360 supers = _class.super
361 if len(supers) > 0:
362 supers = ', '.join(supers)
363 else:
364 supers = 'None'
365
366 globalsList = self.__genGlobalsListSection(_class)
367 methList, methBodies = self.__genMethodSection(_class, className)
368
369 try:
370 clsBody = self.classTemplate % { \
371 'Anchor' : className,
372 'Class' : _class.name,
373 'ClassSuper' : supers,
374 'ClassDescription' : self.__formatDescription(_class.description),
375 'GlobalsList' : globalsList,
376 'MethodList' : methList,
377 'MethodDetails' : methBodies,
378 }
379 except TagError, e:
380 sys.stderr.write("Error in tags of description of class %s.\n" % \
381 className)
382 sys.stderr.write("%s\n" % e)
383 clsBody = ""
384
385 classes.append(clsBody)
386
387 return ''.join(classes)
388
389 def __genMethodsListSection(self, names, dict, className, clsName):
390 """
391 Private method to generate the methods list section of a class.
392
393 @param names The names to appear in the list. (list of strings)
394 @param dict A dictionary containing all relevant information.
395 @param className The class name containing the names.
396 @param clsName The visible class name containing the names.
397 @return The list section. (string)
398 """
399 lst = []
400 try:
401 lst.append(self.listEntryTemplate % { \
402 'Link' : "%s.%s" % (className, '__init__'),
403 'Name' : clsName,
404 'Description' : self.__getShortDescription(dict['__init__'].description),
405 'Deprecated' : self.__checkDeprecated(dict['__init__'].description) and \
406 self.listEntryDeprecatedTemplate or "",
407 })
408 self.keywords.append(("%s (Constructor)" % className,
409 "#%s.%s" % (className, '__init__')))
410 except KeyError:
411 pass
412
413 for name in names:
414 lst.append(self.listEntryTemplate % { \
415 'Link' : "%s.%s" % (className, name),
416 'Name' : dict[name].name,
417 'Description' : self.__getShortDescription(dict[name].description),
418 'Deprecated' : self.__checkDeprecated(dict[name].description) and \
419 self.listEntryDeprecatedTemplate or "",
420 })
421 self.keywords.append(("%s.%s" % (className, name),
422 "#%s.%s" % (className, name)))
423 return ''.join(lst)
424
425 def __genMethodSection(self, obj, className):
426 """
427 Private method to generate the method details section.
428
429 @param obj Reference to the object being formatted.
430 @param className Name of the class containing the method. (string)
431 @return The method list and method details section. (tuple of two string)
432 """
433 methList = []
434 methBodies = []
435 methods = obj.methods.keys()
436 methods.sort()
437
438 # first do the constructor
439 if '__init__' in methods:
440 methods.remove('__init__')
441 try:
442 methBody = self.constructorTemplate % { \
443 'Anchor' : className,
444 'Class' : obj.name,
445 'Method' : '__init__',
446 'MethodDescription' : \
447 self.__formatDescription(obj.methods['__init__'].description),
448 'Params' : ', '.join(obj.methods['__init__'].parameters[1:]),
449 }
450 except TagError, e:
451 sys.stderr.write("Error in tags of description of method %s.%s.\n" % \
452 (className, '__init__'))
453 sys.stderr.write("%s\n" % e)
454 methBody = ""
455 methBodies.append(methBody)
456
457 for method in methods:
458 try:
459 methBody = self.methodTemplate % { \
460 'Anchor' : className,
461 'Class' : obj.name,
462 'Method' : obj.methods[method].name,
463 'MethodDescription' : \
464 self.__formatDescription(obj.methods[method].description),
465 'Params' : ', '.join(obj.methods[method].parameters[1:]),
466 }
467 except TagError, e:
468 sys.stderr.write("Error in tags of description of method %s.%s.\n" % \
469 (className, method))
470 sys.stderr.write("%s\n" % e)
471 methBody = ""
472 methBodies.append(methBody)
473
474 methList = self.__genMethodsListSection(methods, obj.methods, className, obj.name)
475
476 if not methList:
477 methList = self.listEntryNoneTemplate
478 return self.listTemplate % { \
479 'Entries' : methList,
480 }, ''.join(methBodies)
481
482 def __genRbModulesSection(self):
483 """
484 Private method to generate the document section with details about Ruby modules.
485
486 @return The Ruby modules details section. (string)
487 """
488 rbModulesNames = self.module.modules.keys()
489 rbModulesNames.sort()
490 rbModules = []
491 for rbModuleName in rbModulesNames:
492 rbModule = self.module.modules[rbModuleName]
493 globalsList = self.__genGlobalsListSection(rbModule)
494 methList, methBodies = self.__genMethodSection(rbModule, rbModuleName)
495 classList, classBodies = \
496 self.__genRbModulesClassesSection(rbModule, rbModuleName)
497
498 try:
499 rbmBody = self.rbModuleTemplate % { \
500 'Anchor' : rbModuleName,
501 'Module' : rbModule.name,
502 'ModuleDescription' : self.__formatDescription(rbModule.description),
503 'GlobalsList' : globalsList,
504 'ClassesList' : classList,
505 'ClassesDetails' : classBodies,
506 'FunctionsList' : methList,
507 'FunctionsDetails' : methBodies,
508 }
509 except TagError, e:
510 sys.stderr.write("Error in tags of description of Ruby module %s.\n" % \
511 rbModuleName)
512 sys.stderr.write("%s\n" % e)
513 rbmBody = ""
514
515 rbModules.append(rbmBody)
516
517 return ''.join(rbModules)
518
519 def __genRbModulesClassesSection(self, obj, modName):
520 """
521 Private method to generate the Ruby module classes details section.
522
523 @param obj Reference to the object being formatted.
524 @param modName Name of the Ruby module containing the classes. (string)
525 @return The classes list and classes details section. (tuple of two string)
526 """
527 classNames = obj.classes.keys()
528 classNames.sort()
529 classes = []
530 for className in classNames:
531 _class = obj.classes[className]
532 supers = _class.super
533 if len(supers) > 0:
534 supers = ', '.join(supers)
535 else:
536 supers = 'None'
537
538 classname = "%s.%s" % (modName, className)
539 methList, methBodies = self.__genMethodSection(_class, className)
540
541 try:
542 clsBody = self.rbModulesClassTemplate % { \
543 'Anchor' : className,
544 'Class' : _class.name,
545 'ClassSuper' : supers,
546 'ClassDescription' : self.__formatDescription(_class.description),
547 'MethodList' : methList,
548 'MethodDetails' : methBodies,
549 }
550 except TagError, e:
551 sys.stderr.write("Error in tags of description of class %s.\n" % \
552 className)
553 sys.stderr.write("%s\n" % e)
554 clsBody = ""
555
556 classes.append(clsBody)
557
558
559 classesList = \
560 self.__genRbModulesClassesListSection(classNames, obj.classes, modName)
561
562 if not classesList:
563 classesList = self.listEntryNoneTemplate
564 return self.listTemplate % { \
565 'Entries' : classesList,
566 }, ''.join(classes)
567
568 def __genRbModulesClassesListSection(self, names, dict, moduleName):
569 """
570 Private method to generate the classes list section of a Ruby module.
571
572 @param names The names to appear in the list. (list of strings)
573 @param dict A dictionary containing all relevant information.
574 @param moduleName Name of the Ruby module containing the classes. (string)
575 @return The list section. (string)
576 """
577 lst = []
578 for name in names:
579 lst.append(self.listEntryTemplate % { \
580 'Link' : "%s.%s" % (moduleName, name),
581 'Name' : dict[name].name,
582 'Description' : self.__getShortDescription(dict[name].description),
583 'Deprecated' : self.__checkDeprecated(dict[name].description) and \
584 self.listEntryDeprecatedTemplate or "",
585 })
586 self.keywords.append(("%s.%s" % (moduleName, name),
587 "#%s.%s" % (moduleName, name)))
588 return ''.join(lst)
589
590 def __genFunctionsSection(self):
591 """
592 Private method to generate the document section with details about functions.
593
594 @return The functions details section. (string)
595 """
596 funcBodies = []
597 funcNames = self.module.functions.keys()
598 funcNames.sort()
599 for funcName in funcNames:
600 try:
601 funcBody = self.functionTemplate % { \
602 'Anchor' : funcName,
603 'Function' : self.module.functions[funcName].name,
604 'FunctionDescription' : self.__formatDescription(\
605 self.module.functions[funcName].description),
606 'Params' : ', '.join(self.module.functions[funcName].parameters),
607 }
608 except TagError, e:
609 sys.stderr.write("Error in tags of description of function %s.\n" % \
610 funcName)
611 sys.stderr.write("%s\n" % e)
612 funcBody = ""
613
614 funcBodies.append(funcBody)
615
616 return ''.join(funcBodies)
617
618 def __getShortDescription(self, desc):
619 """
620 Private method to determine the short description of an object.
621
622 The short description is just the first non empty line of the
623 documentation string.
624
625 @param desc The documentation string. (string)
626 @return The short description. (string)
627 """
628 dlist = desc.splitlines()
629 sdlist = []
630 descfound = 0
631 for desc in dlist:
632 desc = desc.strip()
633 if desc:
634 descfound = 1
635 dotpos = desc.find('.')
636 if dotpos == -1:
637 sdlist.append(desc.strip())
638 else:
639 while dotpos+1 < len(desc) and not desc[dotpos+1].isspace():
640 # don't recognize '.' inside a number or word as stop condition
641 dotpos = desc.find('.', dotpos+1)
642 if dotpos == -1:
643 break
644 if dotpos == -1:
645 sdlist.append(desc.strip())
646 else:
647 sdlist.append(desc[:dotpos+1].strip())
648 break # break if a '.' is found
649 else:
650 if descfound:
651 break # break if an empty line is found
652 if sdlist:
653 return html_uencode(' '.join(sdlist))
654 else:
655 return ''
656
657 def __checkDeprecated(self, descr):
658 """
659 Private method to check, if the object to be documented contains a
660 deprecated flag.
661
662 @param desc The documentation string. (string)
663 @return Flag indicating the deprecation status. (boolean)
664 """
665 dlist = descr.splitlines()
666 for desc in dlist:
667 desc = desc.strip()
668 if desc.startswith("@deprecated"):
669 return True
670 return False
671
672 def __genParagraphs(self, lines):
673 """
674 Private method to assemble the descriptive paragraphs of a docstring.
675
676 A paragraph is made up of a number of consecutive lines without
677 an intermediate empty line. Empty lines are treated as a paragraph
678 delimiter.
679
680 @param lines A list of individual lines. (list of strings)
681 @return Ready formatted paragraphs. (string)
682 """
683 lst = []
684 linelist = []
685 for line in lines:
686 if line.strip():
687 if line == '.':
688 linelist.append("")
689 else:
690 linelist.append(html_uencode(line))
691 else:
692 lst.append(self.paragraphTemplate % { \
693 'Lines' : '\n'.join(linelist)
694 })
695 linelist = []
696 if linelist:
697 lst.append(self.paragraphTemplate % { \
698 'Lines' : '\n'.join(linelist)
699 })
700 return ''.join(lst)
701
702 def __genDescriptionListSection(self, dictionary, template):
703 """
704 Private method to generate the list section of a description.
705
706 @param dictionary Dictionary containing the info for the
707 list section.
708 @param template The template to be used for the list. (string)
709 @return The list section. (string)
710 """
711 lst = []
712 keys = dictionary.keys()
713 keys.sort()
714 for key in keys:
715 lst.append(template % { \
716 'Name' : key,
717 'Description' : html_uencode('\n'.join(dictionary[key])),
718 })
719 return ''.join(lst)
720
721 def __genParamDescriptionListSection(self, _list, template):
722 """
723 Private method to generate the list section of a description.
724
725 @param _list List containing the info for the
726 list section.
727 @param template The template to be used for the list. (string)
728 @return The list section. (string)
729 """
730 lst = []
731 for name, lines in _list:
732 lst.append(template % { \
733 'Name' : name,
734 'Description' : html_uencode('\n'.join(lines)),
735 })
736 return ''.join(lst)
737
738 def __formatCrossReferenceEntry(self, entry):
739 """
740 Private method to format a cross reference entry.
741
742 This cross reference entry looks like "package.module#member label".
743
744 @param entry the entry to be formatted (string)
745 @return formatted entry (string)
746 """
747 if entry.startswith('"'):
748 return entry
749 elif entry.startswith('<'):
750 entry = entry[3:]
751 else:
752 try:
753 reference, label = entry.split(None, 1)
754 except ValueError:
755 reference = seeEntryString
756 label = seeEntryString
757 try:
758 path, anchor = reference.split('#', 1)
759 except ValueError:
760 path = reference
761 anchor = ''
762 reference = path and "%s.html" % path or ''
763 if anchor:
764 reference = "%s#%s" % (reference, anchor)
765 entry = 'href="%s">%s</a>' % (reference, label)
766
767 return self.seeLinkTemplate % { \
768 'Link' : entry,
769 }
770
771 def __genSeeListSection(self, _list, template):
772 """
773 Private method to generate the "see also" list section of a description.
774
775 @param _list List containing the info for the section.
776 @param template The template to be used for the list. (string)
777 @return The list section. (string)
778 """
779 lst = []
780 for seeEntry in _list:
781 seeEntryString = ''.join(seeEntry)
782 lst.append(template % { \
783 'Link' : html_uencode(self.__formatCrossReferenceEntry(seeEntryString)),
784 })
785 return '\n'.join(lst)
786
787 def __processInlineTags(self, desc):
788 """
789 Private method to process inline tags.
790
791 @param desc One line of the description (string)
792 @return processed line with inline tags expanded (string)
793 """
794 start = desc.find('{@')
795 while start != -1:
796 stop = desc.find('}', start + 2)
797 if stop == -1:
798 raise TagError, "Unterminated inline tag.\n%s" % desc
799
800 tagText = desc[start + 1:stop]
801 if tagText.startswith('@link'):
802 parts = tagText.split(None, 1)
803 if len(parts) < 2:
804 raise TagError, "Wrong format in inline tag %s.\n%s" % \
805 (parts[0], desc)
806
807 formattedTag = self.__formatCrossReferenceEntry(parts[1])
808 desc = desc.replace("{%s}" % tagText, formattedTag)
809 else:
810 tag = tagText.split(None, 1)[0]
811 raise TagError, "Unknown inline tag encountered, %s.\n%s" % \
812 (tag, desc)
813
814 start = desc.find('{@')
815
816 return desc
817
818 def __formatDescription(self, descr):
819 """
820 Private method to format the contents of the documentation string.
821
822 @param descr The contents of the documentation string. (string)
823 @exception TagError A tag doesn't have the correct number
824 of arguments.
825 @return The formated contents of the documentation string. (string)
826 """
827 if not descr:
828 return ""
829
830 paragraphs = []
831 paramList = []
832 returns = []
833 exceptionDict = {}
834 signalDict = {}
835 eventDict = {}
836 deprecated = []
837 authorInfo = []
838 sinceInfo = []
839 seeList = []
840 lastItem = paragraphs
841 inTagSection = False
842
843 dlist = descr.splitlines()
844 while dlist and not dlist[0]:
845 del dlist[0]
846 for ditem in dlist:
847 ditem = self.__processInlineTags(ditem)
848 desc = ditem.strip()
849 if desc:
850 if desc.startswith("@param") or desc.startswith("@keyparam"):
851 inTagSection = True
852 parts = desc.split(None, 2)
853 if len(parts) < 2:
854 raise TagError, "Wrong format in %s line.\n" % parts[0]
855 paramName = parts[1]
856 if parts[0] == "@keyparam":
857 paramName += '='
858 try:
859 paramList.append([paramName, [parts[2]]])
860 except IndexError:
861 paramList.append([paramName, []])
862 lastItem = paramList[-1][1]
863 elif desc.startswith("@return"):
864 inTagSection = True
865 parts = desc.split(None, 1)
866 if len(parts) < 2:
867 raise TagError, "Wrong format in %s line.\n" % parts[0]
868 returns = [parts[1]]
869 lastItem = returns
870 elif desc.startswith("@exception") or \
871 desc.startswith("@throws") or \
872 desc.startswith("@raise"):
873 inTagSection = True
874 parts = desc.split(None, 2)
875 if len(parts) < 2:
876 raise TagError, "Wrong format in %s line.\n" % parts[0]
877 excName = parts[1]
878 try:
879 exceptionDict[excName] = [parts[2]]
880 except IndexError:
881 exceptionDict[excName] = []
882 lastItem = exceptionDict[excName]
883 elif desc.startswith("@signal"):
884 inTagSection = True
885 m = _signal(desc,0)
886 if m is None:
887 raise TagError, "Wrong format in %s line.\n" % parts[0]
888 signalName = 1 and m.group("SignalName1") \
889 or m.group("SignalName2")
890 signalDesc = 1 and m.group("SignalDescription1") \
891 or m.group("SignalDescription2")
892 signalDict[signalName] = []
893 if signalDesc is not None:
894 signalDict[signalName].append(signalDesc)
895 lastItem = signalDict[signalName]
896 elif desc.startswith("@event"):
897 inTagSection = True
898 m = _event(desc,0)
899 if m is None:
900 raise TagError, "Wrong format in %s line.\n" % parts[0]
901 eventName = 1 and m.group("EventName1") \
902 or m.group("EventName2")
903 eventDesc = 1 and m.group("EventDescription1") \
904 or m.group("EventDescription2")
905 eventDict[eventName] = []
906 if eventDesc is not None:
907 eventDict[eventName].append(eventDesc)
908 lastItem = eventDict[eventName]
909 elif desc.startswith("@deprecated"):
910 inTagSection = True
911 parts = desc.split(None, 1)
912 if len(parts) < 2:
913 raise TagError, "Wrong format in %s line.\n" % parts[0]
914 deprecated = [parts[1]]
915 lastItem = deprecated
916 elif desc.startswith("@author"):
917 inTagSection = True
918 parts = desc.split(None, 1)
919 if len(parts) < 2:
920 raise TagError, "Wrong format in %s line.\n" % parts[0]
921 authorInfo = [parts[1]]
922 lastItem = authorInfo
923 elif desc.startswith("@since"):
924 inTagSection = True
925 parts = desc.split(None, 1)
926 if len(parts) < 2:
927 raise TagError, "Wrong format in %s line.\n" % parts[0]
928 sinceInfo = [parts[1]]
929 lastItem = sinceInfo
930 elif desc.startswith("@see"):
931 inTagSection = True
932 parts = desc.split(None, 1)
933 if len(parts) < 2:
934 raise TagError, "Wrong format in %s line.\n" % parts[0]
935 seeList.append([parts[1]])
936 lastItem = seeList[-1]
937 elif desc.startswith("@@"):
938 lastItem.append(desc[1:])
939 elif desc.startswith("@"):
940 tag = desc.split(None, 1)[0]
941 raise TagError, "Unknown tag encountered, %s.\n" % tag
942 else:
943 lastItem.append(ditem)
944 elif not inTagSection:
945 lastItem.append(ditem)
946
947 if paragraphs:
948 description = self.__genParagraphs(paragraphs)
949 else:
950 description = ""
951
952 if paramList:
953 parameterSect = self.parametersListTemplate % { \
954 'Parameters' : self.__genParamDescriptionListSection(paramList,
955 self.parametersListEntryTemplate)
956 }
957 else:
958 parameterSect = ""
959
960 if returns:
961 returnSect = self.returnsTemplate % html_uencode('\n'.join(returns))
962 else:
963 returnSect = ""
964
965 if exceptionDict:
966 exceptionSect = self.exceptionsListTemplate % { \
967 'Exceptions' : self.__genDescriptionListSection(exceptionDict,
968 self.exceptionsListEntryTemplate)
969 }
970 else:
971 exceptionSect = ""
972
973 if signalDict:
974 signalSect = self.signalsListTemplate % { \
975 'Signals' : self.__genDescriptionListSection(signalDict,
976 self.signalsListEntryTemplate)
977 }
978 else:
979 signalSect = ""
980
981 if eventDict:
982 eventSect = self.eventsListTemplate % { \
983 'Events' : self.__genDescriptionListSection(eventDict,
984 self.eventsListEntryTemplate)
985 }
986 else:
987 eventSect = ""
988
989 if deprecated:
990 deprecatedSect = self.deprecatedTemplate % { \
991 'Lines' : html_uencode('\n'.join(deprecated)),
992 }
993 else:
994 deprecatedSect = ""
995
996 if authorInfo:
997 authorInfoSect = self.authorInfoTemplate % { \
998 'Authors' : html_uencode('\n'.join(authorInfo)),
999 }
1000 else:
1001 authorInfoSect = ""
1002
1003 if sinceInfo:
1004 sinceInfoSect = self.sinceInfoTemplate % { \
1005 'Info' : html_uencode(sinceInfo[0]),
1006 }
1007 else:
1008 sinceInfoSect = ""
1009
1010 if seeList:
1011 seeSect = self.seeListTemplate % { \
1012 'Links' : self.__genSeeListSection(seeList, self.seeListEntryTemplate),
1013 }
1014 else:
1015 seeSect = ''
1016
1017 return "%s%s%s%s%s%s%s%s%s%s" % ( \
1018 deprecatedSect, description, parameterSect, returnSect,
1019 exceptionSect, signalSect, eventSect, authorInfoSect,
1020 seeSect, sinceInfoSect
1021 )
1022
1023 def getQtHelpKeywords(self):
1024 """
1025 Public method to retrieve the parts for the QtHelp keywords section.
1026
1027 @return list of tuples containing the name (string) and the ref (string). The ref
1028 is without the filename part.
1029 """
1030 if not self.generated:
1031 self.genDocument()
1032
1033 return self.keywords

eric ide

mercurial