root/trunk/doc/wikidoc.py

Revision 334, 12.2 kB (checked in by apdavison, 1 year ago)

More tweaks

Line 
1 # coding: utf-8
2 """Writes documentation for the API in Wiki format."""
3
4 import sys
5 from NeuroTools import *
6 from NeuroTools import __all__
7 import types, string, re, logging
8
9 #-- Set up logging -------------------------------------------------------------
10 logging.basicConfig(level=logging.DEBUG,
11                     format='%(asctime)s %(levelname)-8s %(message)s',
12                     datefmt='%Y%m%d-%H%M%S',
13                     filename='wikidoc.log',
14                     filemode='w')
15 # define a Handler which writes WARNING messages or higher to the sys.stderr
16 console = logging.StreamHandler()
17 console.setLevel(logging.WARNING)
18 # set a format which is simpler for console use
19 formatter = logging.Formatter('%(message)s')
20 # tell the handler to use this format
21 console.setFormatter(formatter)
22 # add the handler to the root logger
23 logging.getLogger('').addHandler(console)
24
25 #-- Define global data ---------------------------------------------------------
26
27 exclude = set(['__module__','__doc__','__builtins__','__file__','__class__',
28                '__delattr__', '__dict__', '__getattribute__', '__hash__',
29                '__new__','__reduce__','__reduce_ex__','__repr__','__setattr__',
30                '__str__','__weakref__',] #+ dir(int)
31              )
32 #               'time','types','copy',]
33 #exclude.remove('__init__')
34
35 leftquote = re.compile(r"'\b")
36 leftdblquote = re.compile(r'"\b')
37 camelcase = re.compile(r'(\b([A-Z][a-z]+){2,99})')
38
39 #classes = {}
40 #functions = []
41 #data = []
42
43 #-- Process command line parameters --------------------------------------------
44 output = sys.argv[1]
45 modules = sys.argv[2:]
46
47 logging.info('Generating API documentation in %s format' % output)
48
49 #-- Define templates -----------------------------------------------------------
50 if output == 'wiki':
51     default_arg_fmt  = '%s<span style="color:grey;">=%s</span>'
52     func_sig_fmt     = '%s(<span style="font-weight:normal;">%s</span>)'
53     function_fmt     = '\n====<span style="color:#0066ff;">%s</span>====\n'
54     method_fmt       = '\n====<span style="color:#8888ff;">%s</span>====\n'
55     staticmethod_fmt = '\n====<span style="color:#0066ff;">%s</span> (static)====\n'
56     dict_fmt         = "\n\n'''%s''' = {\n"
57     dict_fmt_end     = '}\n'
58     data_element_fmt = "\n'''%s''' = %s\n"
59     table_begin      = "{|\n"
60     table_end        = "|}\n"
61     table_row_fmt    = "| &nbsp;&nbsp;&nbsp; || %s ||: %s\n|-\n"
62     category_fmt     = '\n==%s==\n'
63     class_fmt        = '\n===<span style="color:green">%s</span>===\n'
64     horiz_line       = '\n----\n'
65 elif output == 'trac':
66     module_fmt       = '\n= %s =\n'
67     default_arg_fmt  = '%s=%s'
68     func_sig_fmt     = '%s(%s)'
69     function_fmt     = '\n==== %s ====\n'
70     method_fmt       = '\n==== %s ====\n'
71     staticmethod_fmt = '\n==== %s ====\n'
72     dict_fmt         = "\n\n'''%s''' = {\n"
73     dict_fmt_end     = '}\n'
74     data_element_fmt = "\n'''%s''' = %s\n"
75     table_begin      = "\n"
76     table_end        = "\n"
77     table_row_fmt    = "|| %s ||: %s ||\n"
78     category_fmt     = '\n== %s ==\n'
79     class_fmt        = '\n=== %s ===\n'
80     horiz_line       = '\n----\n'
81 elif output == 'latex':
82     default_arg_fmt  = '%s{\\color{grey}=%s}'
83     func_sig_fmt     = '%s(\\mdseries %s)'
84     function_fmt     = '\n\\paragraph*{\\color{brightblue}{%s}}\n'
85     method_fmt       = '\n\\paragraph*{\\color{brightblue}{%s}}\n'
86     staticmethod_fmt = '\n\\paragraph*{\\color{brightblue}{%s} (static)}\n'
87     dict_fmt         = '\n\\textbf{%s} = $\\lbrace$\n\n'
88     dict_fmt_end     = '$\\rbrace$\n'
89     data_element_fmt = "\n\\textbf{%s} = %s\n"
90     table_begin      = "\\begin{tabular}{lll}\n"
91     table_end        = "\\end{tabular}\n"
92     table_row_fmt    = '& %s & :%s\\\\\n'
93     category_fmt     = '\n\\subsection{%s}\n'
94     class_fmt        = '\n\\subsubsection*{%s}\n'
95     horiz_line       = ''
96              
97 #-- Define functions -----------------------------------------------------------
98
99 def _(str):
100     """Remove extraneous whitespace."""
101     lines = str.strip().split('\n')
102     lines = [line.strip() for line in lines]
103     return '\n'.join(lines)
104
105 def funcArgs(func):
106     logging.info('Called funcArgs(%s)' % func)
107     if hasattr(func,'im_func'):
108         func = func.im_func
109     code = func.func_code
110     fname = code.co_name
111     callargs = code.co_argcount
112     args = code.co_varnames[:callargs]
113     return "%s(%s)" % (fname, string.join(args,', '))
114
115 def func_sig(func):
116     """Adapted from http://www.lemburg.com/python/hack.py, by  Marc-André Lemburg
117        Returns the signature of a Python function/method as string.
118        Keyword initializers are also shown using
119        repr(). Representations longer than 100 bytes are truncated.
120     """
121     logging.info('Called func_sig(%s)' % func)
122     if hasattr(func,'im_func'): # func is a method
123         func = func.im_func
124     try:
125         code = func.func_code
126         fname = code.co_name
127         callargs = code.co_argcount
128         # XXX Uses hard coded values taken from Include/compile.h
129         args = list(code.co_varnames[:callargs])
130         if func.func_defaults:
131             i = len(args) - len(func.func_defaults)
132             for default in func.func_defaults:
133                 if isinstance(default,float):
134                     r = str(default)
135                 else:
136                     try:
137                         r = repr(default)
138                     except:
139                         r = '<repr-error>'
140                 if len(r) > 100:
141                     r = r[:100] + '...'
142                 arg = args[i]
143                 if arg[0] == '.':
144                     # anonymous arguments
145                     arg = '(...)'
146                 args[i] = default_arg_fmt % (arg,r)
147                 i = i + 1
148         if code.co_flags & 0x0004: # CO_VARARGS
149             args.append('*'+code.co_varnames[callargs])
150             callargs = callargs + 1
151         if code.co_flags & 0x0008: # CO_VARKEYWORDS
152             args.append('**'+code.co_varnames[callargs])
153             callargs = callargs + 1
154         return func_sig_fmt % (fname,string.join(args,', '))
155     except AttributeError:
156         logging.warning("%s has no attribute 'func_code'" % func)
157         return None
158
159 #-- Main block -----------------------------------------------------------------
160
161 outputStr = ''
162 for module in modules: #__all__:
163     classes = {}
164     functions = []
165     data = []
166     logging.info("Gathering information from the %s module." % module)
167     for entry in eval('dir(%s)' % module):
168         if entry not in exclude:
169             instance = eval('%s.%s' % (module,entry))
170             entry_type = type(instance)
171             logging.info('  %-30s %s' % (entry,entry_type))
172             if entry_type in [types.ClassType, types.TypeType]:
173                 classes[entry] = { 'methods': [], 'data': [], 'staticmethods': [] }
174                 for classentry in dir(instance):
175                     if classentry not in exclude and (classentry[0] != '_' or classentry[0:2] == '__'): # don't include private methods
176                         classentry_type = type(eval('%s.%s.%s' % (module,entry,classentry)))
177                         logging.info('    %-28s %s' % (classentry,classentry_type))
178                         if classentry_type == types.MethodType:
179                             classes[entry]['methods'].append(classentry)
180                         elif classentry_type == types.FunctionType:
181                             classes[entry]['staticmethods'].append(classentry)
182                         else:
183                             classes[entry]['data'].append(classentry)
184                     else:
185                         logging.info('    %-28s excluded' % classentry)
186             elif  entry_type == types.FunctionType:
187                 functions.append(entry)
188             elif entry_type == types.ModuleType:
189                 pass
190             else:
191                 data.append(entry)
192         else:
193             logging.info('  %-30s excluded' % entry)
194
195     # output starts here
196     #outputStr = ''
197     if output == 'latex':
198         outputStr += '\definecolor{brightblue}{rgb}{0.0,0.38,1.0}\n'
199         outputStr += '\definecolor{paleblue}{rgb}{0.5,0.5,1.0}\n'
200         outputStr += '\definecolor{grey}{rgb}{0.5,0.5,0.5}\n'
201    
202     outputStr += module_fmt % module
203     #outputStr += _(eval(module).__doc__)
204     logging.info("==== DATA ====")
205     outputStr += category_fmt % "Data"
206     for element in data:
207         instance = eval('%s.%s' % (module,element))
208         if type(instance) == types.DictType:
209             outputStr += dict_fmt % element
210             outputStr += table_begin
211             for k,v in instance.items():
212                 if output == 'latex':
213                     v = str(v).replace('{',' $\\lbrace$').replace('}',' $\\rbrace$')
214                 outputStr += table_row_fmt % (k,v)
215             outputStr += table_end
216             outputStr += dict_fmt_end
217         else:
218             outputStr +=  data_element_fmt % (element, instance)
219        
220     logging.info("==== FUNCTIONS ====")
221     outputStr += category_fmt % "Functions"
222     for funcname in functions:
223         funcinst = eval('%s.%s' % (module,funcname))
224         outputStr += function_fmt % func_sig(funcinst)
225         if funcinst.__doc__:
226             outputStr += _(funcinst.__doc__.strip())
227        
228     logging.info("==== CLASSES ====")
229     # sort classes by type:
230     error_classes = {}
231     celltype_classes = {} ####
232     other_classes = {}
233     for classname in classes.keys():
234         if classname.find('Error') > -1:
235             error_classes[classname] = classes[classname]
236         else:
237             other_classes[classname] = classes[classname]
238    
239     logging.info('Sorting classes...')
240     logging.info('Error classes:    %s' % ', '.join(error_classes.keys()))
241     #logging.info('Celltype classes: %s' % ', '.join(celltype_classes.keys()))
242     logging.info('Other classes:    %s' % ', '.join(other_classes.keys()))
243    
244     # Now iterate through the classes
245     outputStr += category_fmt % "Classes"
246     for classes in [celltype_classes, other_classes, error_classes]:
247         classlist = classes.keys()
248         classlist.sort()
249         for classname in classlist:
250             outputStr += class_fmt % classname
251             docstr = eval('%s.%s.__doc__' % (module,classname))
252             if docstr:
253                 outputStr += _(docstr)
254             for methodname in classes[classname]['methods']:
255                 methodinst = eval('%s.%s.%s' % (module,classname,methodname))
256                 fs = func_sig(methodinst)
257                 if fs:
258                     outputStr += method_fmt % fs
259                     if methodinst.__doc__:
260                         outputStr += _(methodinst.__doc__.strip())
261             for methodname in classes[classname]['staticmethods']:
262                 methodinst = eval('%s.%s.%s' % (module,classname,methodname))
263                 fs = func_sig(methodinst)
264                 if fs:
265                     outputStr += staticmethod_fmt % fs
266                     if methodinst.__doc__:
267                         outputStr += _(methodinst.__doc__.strip())
268             for element in classes[classname]['data']:
269                 instance = eval('%s.%s.%s' % (module,classname,element))
270                 if type(instance) == types.DictType:
271                     outputStr += dict_fmt % element
272                     if len(instance) > 0:
273                         outputStr += table_begin
274                         for k,v in instance.items():
275                             if output == 'latex':
276                                 v = str(v).replace('{',' $\\lbrace$').replace('}',' $\\rbrace$')
277                                 outputStr += table_row_fmt % (k,v)
278                             elif output == 'wiki':
279                                 outputStr += table_row_fmt % ('&quot;%s&quot;' % k,v)
280                             elif output == 'trac':
281                                 outputStr += table_row_fmt % ("'%s'" % k,v)
282                         outputStr += table_end
283                     outputStr += dict_fmt_end
284                 else:
285                     outputStr +=  data_element_fmt % (element, instance)
286                    
287             outputStr += horiz_line
288        
289 if output == 'latex':
290     outputStr = outputStr.replace('_','\_')
291     outputStr = outputStr.replace('>','$>$')
292     outputStr = outputStr.replace('<','$<$')
293     outputStr = leftquote.sub('`',outputStr)
294     outputStr = leftdblquote.sub('``',outputStr)
295 if output == 'trac':
296     outputStr = outputStr.replace('__','!__')
297     outputStr = camelcase.sub(r'!\1',outputStr)
298
299 print outputStr
Note: See TracBrowser for help on using the browser.