eric6/DocumentationTools/ModuleDocumentor.py

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

eric ide

mercurial