| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame^] | 1 | # epydoc -- Plaintext output generation |
| 2 | # |
| 3 | # Copyright (C) 2005 Edward Loper |
| 4 | # Author: Edward Loper <edloper@loper.org> |
| 5 | # URL: <http://epydoc.sf.net> |
| 6 | # |
| 7 | # $Id: plaintext.py 1473 2007-02-13 19:46:05Z edloper $ |
| 8 | |
| 9 | """ |
| 10 | Plaintext output generation. |
| 11 | """ |
| 12 | __docformat__ = 'epytext en' |
| 13 | |
| 14 | from epydoc.apidoc import * |
| 15 | import re |
| 16 | |
| 17 | class PlaintextWriter: |
| 18 | def write(self, api_doc, **options): |
| 19 | result = [] |
| 20 | out = result.append |
| 21 | |
| 22 | self._cols = options.get('cols', 75) |
| 23 | |
| 24 | try: |
| 25 | if isinstance(api_doc, ModuleDoc): |
| 26 | self.write_module(out, api_doc) |
| 27 | elif isinstance(api_doc, ClassDoc): |
| 28 | self.write_class(out, api_doc) |
| 29 | elif isinstance(api_doc, RoutineDoc): |
| 30 | self.write_function(out, api_doc) |
| 31 | else: |
| 32 | assert 0, ('%s not handled yet' % api_doc.__class__) |
| 33 | except Exception, e: |
| 34 | print '\n\n' |
| 35 | print ''.join(result) |
| 36 | raise |
| 37 | |
| 38 | return ''.join(result) |
| 39 | |
| 40 | def write_module(self, out, mod_doc): |
| 41 | #for n,v in mod_doc.variables.items(): |
| 42 | # print n, `v.value`, `v.value.value` |
| 43 | |
| 44 | # The cannonical name of the module. |
| 45 | out(self.section('Module Name')) |
| 46 | out(' %s\n\n' % mod_doc.canonical_name) |
| 47 | |
| 48 | # The module's description. |
| 49 | if mod_doc.descr not in (None, '', UNKNOWN): |
| 50 | out(self.section('Description')) |
| 51 | out(mod_doc.descr.to_plaintext(None, indent=4)) |
| 52 | |
| 53 | #out('metadata: %s\n\n' % mod_doc.metadata) # [xx] testing |
| 54 | |
| 55 | self.write_list(out, 'Classes', mod_doc, value_type='class') |
| 56 | self.write_list(out, 'Functions', mod_doc, value_type='function') |
| 57 | self.write_list(out, 'Variables', mod_doc, value_type='other') |
| 58 | # hmm.. do this as just a flat list?? |
| 59 | #self.write_list(out, 'Imports', mod_doc, imported=True, verbose=False) |
| 60 | |
| 61 | def baselist(self, class_doc): |
| 62 | if class_doc.bases is UNKNOWN: |
| 63 | return '(unknown bases)' |
| 64 | if len(class_doc.bases) == 0: return '' |
| 65 | s = '(' |
| 66 | class_parent = class_doc.canonical_name.container() |
| 67 | for i, base in enumerate(class_doc.bases): |
| 68 | if base.canonical_name is None: |
| 69 | if base.parse_repr is not UNKNOWN: |
| 70 | s += base.parse_repr |
| 71 | else: |
| 72 | s += '??' |
| 73 | elif base.canonical_name.container() == class_parent: |
| 74 | s += str(base.canonical_name[-1]) |
| 75 | else: |
| 76 | s += str(base.canonical_name) |
| 77 | if i < len(class_doc.bases)-1: out(', ') |
| 78 | return s+')' |
| 79 | |
| 80 | def write_class(self, out, class_doc, name=None, prefix='', verbose=True): |
| 81 | baselist = self.baselist(class_doc) |
| 82 | |
| 83 | # If we're at the top level, then list the cannonical name of |
| 84 | # the class; otherwise, our parent will have already printed |
| 85 | # the name of the variable containing the class. |
| 86 | if prefix == '': |
| 87 | out(self.section('Class Name')) |
| 88 | out(' %s%s\n\n' % (class_doc.canonical_name, baselist)) |
| 89 | else: |
| 90 | out(prefix + 'class %s' % self.bold(str(name)) + baselist+'\n') |
| 91 | |
| 92 | if not verbose: return |
| 93 | |
| 94 | # Indent the body |
| 95 | if prefix != '': |
| 96 | prefix += ' | ' |
| 97 | |
| 98 | # The class's description. |
| 99 | if class_doc.descr not in (None, '', UNKNOWN): |
| 100 | if prefix == '': |
| 101 | out(self.section('Description', prefix)) |
| 102 | out(self._descr(class_doc.descr, ' ')) |
| 103 | else: |
| 104 | out(self._descr(class_doc.descr, prefix)) |
| 105 | |
| 106 | # List of nested classes in this class. |
| 107 | self.write_list(out, 'Methods', class_doc, |
| 108 | value_type='instancemethod', prefix=prefix, |
| 109 | noindent=len(prefix)>4) |
| 110 | self.write_list(out, 'Class Methods', class_doc, |
| 111 | value_type='classmethod', prefix=prefix) |
| 112 | self.write_list(out, 'Static Methods', class_doc, |
| 113 | value_type='staticmethod', prefix=prefix) |
| 114 | self.write_list(out, 'Nested Classes', class_doc, |
| 115 | value_type='class', prefix=prefix) |
| 116 | self.write_list(out, 'Instance Variables', class_doc, |
| 117 | value_type='instancevariable', prefix=prefix) |
| 118 | self.write_list(out, 'Class Variables', class_doc, |
| 119 | value_type='classvariable', prefix=prefix) |
| 120 | |
| 121 | self.write_list(out, 'Inherited Methods', class_doc, |
| 122 | value_type='method', prefix=prefix, |
| 123 | inherited=True, verbose=False) |
| 124 | self.write_list(out, 'Inherited Instance Variables', class_doc, |
| 125 | value_type='instancevariable', prefix=prefix, |
| 126 | inherited=True, verbose=False) |
| 127 | self.write_list(out, 'Inherited Class Variables', class_doc, |
| 128 | value_type='classvariable', prefix=prefix, |
| 129 | inherited=True, verbose=False) |
| 130 | self.write_list(out, 'Inherited Nested Classes', class_doc, |
| 131 | value_type='class', prefix=prefix, |
| 132 | inherited=True, verbose=False) |
| 133 | |
| 134 | def write_variable(self, out, var_doc, name=None, prefix='', verbose=True): |
| 135 | if name is None: name = var_doc.name |
| 136 | out(prefix+self.bold(str(name))) |
| 137 | if (var_doc.value not in (UNKNOWN, None) and |
| 138 | var_doc.is_alias is True and |
| 139 | var_doc.value.canonical_name not in (None, UNKNOWN)): |
| 140 | out(' = %s' % var_doc.value.canonical_name) |
| 141 | elif var_doc.value not in (UNKNOWN, None): |
| 142 | val_repr = var_doc.value.summary_pyval_repr( |
| 143 | max_len=self._cols-len(name)-len(prefix)-3) |
| 144 | out(' = %s' % val_repr.to_plaintext(None)) |
| 145 | out('\n') |
| 146 | if not verbose: return |
| 147 | prefix += ' ' # indent the body. |
| 148 | if var_doc.descr not in (None, '', UNKNOWN): |
| 149 | out(self._descr(var_doc.descr, prefix)) |
| 150 | |
| 151 | def write_property(self, out, prop_doc, name=None, prefix='', |
| 152 | verbose=True): |
| 153 | if name is None: name = prop_doc.canonical_name |
| 154 | out(prefix+self.bold(str(name))) |
| 155 | if not verbose: return |
| 156 | prefix += ' ' # indent the body. |
| 157 | |
| 158 | if prop_doc.descr not in (None, '', UNKNOWN): |
| 159 | out(self._descr(prop_doc.descr, prefix)) |
| 160 | |
| 161 | |
| 162 | def write_function(self, out, func_doc, name=None, prefix='', |
| 163 | verbose=True): |
| 164 | if name is None: name = func_doc.canonical_name |
| 165 | self.write_signature(out, func_doc, name, prefix) |
| 166 | if not verbose: return |
| 167 | |
| 168 | prefix += ' ' # indent the body. |
| 169 | |
| 170 | if func_doc.descr not in (None, '', UNKNOWN): |
| 171 | out(self._descr(func_doc.descr, prefix)) |
| 172 | |
| 173 | if func_doc.return_descr not in (None, '', UNKNOWN): |
| 174 | out(self.section('Returns:', prefix)) |
| 175 | out(self._descr(func_doc.return_descr, prefix+' ')) |
| 176 | |
| 177 | if func_doc.return_type not in (None, '', UNKNOWN): |
| 178 | out(self.section('Return Type:', prefix)) |
| 179 | out(self._descr(func_doc.return_type, prefix+' ')) |
| 180 | |
| 181 | def write_signature(self, out, func_doc, name, prefix): |
| 182 | args = [self.fmt_arg(argname, default) for (argname, default) |
| 183 | in zip(func_doc.posargs, func_doc.posarg_defaults)] |
| 184 | if func_doc.vararg: args.append('*'+func_doc.vararg) |
| 185 | if func_doc.kwarg: args.append('**'+func_doc.kwarg) |
| 186 | |
| 187 | out(prefix+self.bold(str(name))+'(') |
| 188 | x = left = len(prefix) + len(name) + 1 |
| 189 | for i, arg in enumerate(args): |
| 190 | if x > left and x+len(arg) > 75: |
| 191 | out('\n'+prefix + ' '*len(name) + ' ') |
| 192 | x = left |
| 193 | out(arg) |
| 194 | x += len(arg) |
| 195 | if i < len(args)-1: |
| 196 | out(', ') |
| 197 | x += 2 |
| 198 | out(')\n') |
| 199 | |
| 200 | # [xx] tuple args! |
| 201 | def fmt_arg(self, name, default): |
| 202 | if default is None: |
| 203 | return '%s' % name |
| 204 | else: |
| 205 | default_repr = default.summary_pyval_repr() |
| 206 | return '%s=%s' % (name, default_repr.to_plaintext(None)) |
| 207 | |
| 208 | def write_list(self, out, heading, doc, value_type=None, imported=False, |
| 209 | inherited=False, prefix='', noindent=False, |
| 210 | verbose=True): |
| 211 | # Get a list of the VarDocs we should describe. |
| 212 | if isinstance(doc, ClassDoc): |
| 213 | var_docs = doc.select_variables(value_type=value_type, |
| 214 | imported=imported, |
| 215 | inherited=inherited) |
| 216 | else: |
| 217 | var_docs = doc.select_variables(value_type=value_type, |
| 218 | imported=imported) |
| 219 | if not var_docs: return |
| 220 | |
| 221 | out(prefix+'\n') |
| 222 | if not noindent: |
| 223 | out(self.section(heading, prefix)) |
| 224 | prefix += ' ' |
| 225 | |
| 226 | for i, var_doc in enumerate(var_docs): |
| 227 | val_doc, name = var_doc.value, var_doc.name |
| 228 | |
| 229 | if verbose: |
| 230 | out(prefix+'\n') |
| 231 | |
| 232 | # hmm: |
| 233 | if not verbose: |
| 234 | if isinstance(doc, ClassDoc): |
| 235 | name = var_doc.canonical_name |
| 236 | elif val_doc not in (None, UNKNOWN): |
| 237 | name = val_doc.canonical_name |
| 238 | |
| 239 | if isinstance(val_doc, RoutineDoc): |
| 240 | self.write_function(out, val_doc, name, prefix, verbose) |
| 241 | elif isinstance(val_doc, PropertyDoc): |
| 242 | self.write_property(out, val_doc, name, prefix, verbose) |
| 243 | elif isinstance(val_doc, ClassDoc): |
| 244 | self.write_class(out, val_doc, name, prefix, verbose) |
| 245 | else: |
| 246 | self.write_variable(out, var_doc, name, prefix, verbose) |
| 247 | |
| 248 | def _descr(self, descr, prefix): |
| 249 | s = descr.to_plaintext(None, indent=len(prefix)).rstrip() |
| 250 | s = '\n'.join([(prefix+l[len(prefix):]) for l in s.split('\n')]) |
| 251 | return s+'\n'#+prefix+'\n' |
| 252 | |
| 253 | |
| 254 | # def drawline(self, s, x): |
| 255 | # s = re.sub(r'(?m)^(.{%s}) ' % x, r'\1|', s) |
| 256 | # return re.sub(r'(?m)^( {,%s})$(?=\n)' % x, x*' '+'|', s) |
| 257 | |
| 258 | |
| 259 | #//////////////////////////////////////////////////////////// |
| 260 | # Helpers |
| 261 | #//////////////////////////////////////////////////////////// |
| 262 | |
| 263 | def bold(self, text): |
| 264 | """Write a string in bold by overstriking.""" |
| 265 | return ''.join([ch+'\b'+ch for ch in text]) |
| 266 | |
| 267 | def title(self, text, indent): |
| 268 | return ' '*indent + self.bold(text.capitalize()) + '\n\n' |
| 269 | |
| 270 | def section(self, text, indent=''): |
| 271 | if indent == '': |
| 272 | return indent + self.bold(text.upper()) + '\n' |
| 273 | else: |
| 274 | return indent + self.bold(text.capitalize()) + '\n' |
| 275 | |
| 276 | |