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"""
10Plaintext output generation.
11"""
12__docformat__ = 'epytext en'
13
14from epydoc.apidoc import *
15import re
16
17class 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
277