|
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 |