| 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 = "| || %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 % ('"%s"' % 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 |
|---|