root/trunk/doc/wikidoc.py

Revision 100, 12.1 kB (checked in by apdavison, 7 months ago)

Adapted the wikidoc.py script from PyNN to produce API documentation.

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 if len(sys.argv) > 1:
45     output = sys.argv[1]
46 else:
47     output = 'wiki'
48 logging.info('Generating API documentation in %s format' % output)
49
50 #-- Define templates -----------------------------------------------------------
51 if output == 'wiki':
52     default_arg_fmt  = '%s<span style="color:grey;">=%s</span>'
53     func_sig_fmt     = '%s(<span style="font-weight:normal;">%s</span>)'
54     function_fmt     = '\n====<span style="color:#0066ff;">%s</span>====\n'
55     method_fmt       = '\n====<span style="color:#8888ff;">%s</span>====\n'
56     staticmethod_fmt = '\n====<span style="color:#0066ff;">%s</span> (static)====\n'
57     dict_fmt         = "\n\n'''%s''' = {\n"
58     dict_fmt_end     = '}\n'
59     data_element_fmt = "\n'''%s''' = %s\n"
60     table_begin      = "{|\n"
61     table_end        = "|}\n"
62     table_row_fmt    = "| &nbsp;&nbsp;&nbsp; || %s ||: %s\n|-\n"
63     category_fmt     = '\n==%s==\n'
64     class_fmt        = '\n===<span style="color:green">%s</span>===\n'
65     horiz_line       = '\n----\n'
66 elif output == 'trac':
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 __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     logging.info("==== DATA ====")
203     outputStr += category_fmt % "Data"
204     for element in data:
205         instance = eval('%s.%s' % (module,element))
206         if type(instance) == types.DictType:
207             outputStr += dict_fmt % element
208             outputStr += table_begin
209             for k,v in instance.items():
210                 if output == 'latex':
211                     v = str(v).replace('{',' $\\lbrace$').replace('}',' $\\rbrace$')
212                 outputStr += table_row_fmt % (k,v)
213             outputStr += table_end
214             outputStr += dict_fmt_end
215         else:
216             outputStr +=  data_element_fmt % (element, instance)
217        
218     logging.info("==== FUNCTIONS ====")
219     outputStr += category_fmt % "Functions"
220     for funcname in functions:
221         funcinst = eval('%s.%s' % (module,funcname))
222         outputStr += function_fmt % func_sig(funcinst)
223         if funcinst.__doc__:
224             outputStr += _(funcinst.__doc__.strip())
225        
226     logging.info("==== CLASSES ====")
227     # sort classes by type:
228     error_classes = {}
229     celltype_classes = {} ####
230     other_classes = {}
231     for classname in classes.keys():
232         if classname.find('Error') > -1:
233             error_classes[classname] = classes[classname]
234         else:
235             other_classes[classname] = classes[classname]
236    
237     logging.info('Sorting classes...')
238     logging.info('Error classes:    %s' % ', '.join(error_classes.keys()))
239     #logging.info('Celltype classes: %s' % ', '.join(celltype_classes.keys()))
240     logging.info('Other classes:    %s' % ', '.join(other_classes.keys()))
241    
242     # Now iterate through the classes
243     outputStr += category_fmt % "Classes"
244     for classes in [celltype_classes, other_classes, error_classes]:
245         classlist = classes.keys()
246         classlist.sort()
247         for classname in classlist:
248             outputStr += class_fmt % classname
249             docstr = eval('%s.%s.__doc__' % (module,classname))
250             if docstr:
251                 outputStr += _(docstr)
252             for methodname in classes[classname]['methods']:
253                 methodinst = eval('%s.%s.%s' % (module,classname,methodname))
254                 fs = func_sig(methodinst)
255                 if fs:
256                     outputStr += method_fmt % fs
257                     if methodinst.__doc__:
258                         outputStr += _(methodinst.__doc__.strip())
259             for methodname in classes[classname]['staticmethods']:
260                 methodinst = eval('%s.%s.%s' % (module,classname,methodname))
261                 fs = func_sig(methodinst)
262                 if fs:
263                     outputStr += staticmethod_fmt % fs
264                     if methodinst.__doc__:
265                         outputStr += _(methodinst.__doc__.strip())
266             for element in classes[classname]['data']:
267                 instance = eval('%s.%s.%s' % (module,classname,element))
268                 if type(instance) == types.DictType:
269                     outputStr += dict_fmt % element
270                     if len(instance) > 0:
271                         outputStr += table_begin
272                         for k,v in instance.items():
273                             if output == 'latex':
274                                 v = str(v).replace('{',' $\\lbrace$').replace('}',' $\\rbrace$')
275                                 outputStr += table_row_fmt % (k,v)
276                             elif output == 'wiki':
277                                 outputStr += table_row_fmt % ('&quot;%s&quot;' % k,v)
278                             elif output == 'trac':
279                                 outputStr += table_row_fmt % ("'%s'" % k,v)
280                         outputStr += table_end
281                     outputStr += dict_fmt_end
282                 else:
283                     outputStr +=  data_element_fmt % (element, instance)
284                    
285             outputStr += horiz_line
286        
287 if output == 'latex':
288     outputStr = outputStr.replace('_','\_')
289     outputStr = outputStr.replace('>','$>$')
290     outputStr = outputStr.replace('<','$<$')
291     outputStr = leftquote.sub('`',outputStr)
292     outputStr = leftdblquote.sub('``',outputStr)
293 if output == 'trac':
294     outputStr = outputStr.replace('__','!__')
295     outputStr = camelcase.sub(r'!\1',outputStr)
296
297 print outputStr
Note: See TracBrowser for help on using the browser.